class Pos private(val prog: String, val offs: Int, val line: Int, val col: Int) { def this(prog: String) = this(prog, 0, 1, 1) def ch = if (offs == prog.length) -1 else prog(offs) def inc = ch match { case '\n' => new Pos(prog, offs+1, line+1, 1) case -1 => this case _ => new Pos(prog, offs+1, line, col+1) } override def toString = "(" + line + ", " + col + ")" } object DomainTags extends Enumeration { type Tag = Value val WHITESPACE, IDENT, NUMBER, OPER, ERROR, END_OF_PROGRAM = Value } import DomainTags._ class Scanner { def scan(start: Pos, errs: List[String]): (Tag, Pos, List[String]) = { (ERROR, start.inc, ("unexpected symbol " + start)::errs) } } class Token(val start: Pos, scanner: Scanner, errs: List[String]) { val (tag, follow, errsl) = start.ch match { case -1 => (END_OF_PROGRAM, start, errs) case _ => scanner.scan(start, errs) } def image = start.prog.substring(start.offs, follow.offs) def next = new Token(follow, scanner, errsl) def listacc = errs } trait Oper extends Scanner { private def findoper(pos: Pos, fstate: Boolean, sstate: Boolean, tstate: Boolean): (Pos, Boolean, Boolean, Boolean) = pos.ch match { case a if (( a == '<' ) && !fstate && !sstate && !tstate ) => findoper(pos.inc, true, false, false) case a if ((a == '=') && fstate && !sstate && !tstate ) => (pos.inc, true, true, false) case a if ((a == '=') && !fstate && !sstate && !tstate ) => findoper(pos.inc, false, true, false) case a if ((a == '=') && !fstate && sstate && !tstate ) => (pos.inc, false, true, true) case a if ((a != '=') && !fstate && sstate && !tstate ) => (pos, false, true, true) case _ => (pos, false, false, false) } override def scan(start: Pos, errs: List[String]) = { val (follow, flag1, flag2, flag3) = findoper(start, false, false, false) if ((start != follow) && ((flag1 && flag2) | (flag2) | (flag2 && flag3)) ) (OPER, follow, errs) else super.scan(start, errs) } } trait Number extends Scanner { private def findnumber(pos: Pos, fstate: Boolean, sstate: Boolean, tstate: Boolean, errstate: Boolean): (Pos, Boolean, Boolean, Boolean, Boolean) = pos.ch match { case a if ( (a == '<') && (!tstate) ) => findnumber(pos.inc, true, sstate, tstate, false) case a if ( (a >= '0') && (a <= '9') ) => findnumber(pos.inc, fstate, true, tstate, errstate) case a if ( (a == '>') && (fstate) ) => (pos.inc, fstate, sstate, true, errstate) case a if ( ( ( (a >= 'a') && (a <= 'z') ) || (a == ' ')) && (fstate) && (!tstate) && (a != '=') ) => findnumber(pos.inc, fstate, false, tstate, true) case _ => (pos, fstate, sstate, tstate, fstate) } override def scan(start: Pos, errs: List[String]) = { val (follow, flag1, flag2, flag3, flag4) = findnumber(start, false, false, false, false) if ((start != follow)&& flag1 && flag2 && flag3 && (!flag2 || !flag4)) (NUMBER, follow, errs) else if ((start != follow) && flag1 && flag3 && (!flag2 || flag4)) { //println("error in digit part") //(ERROR, follow) (NUMBER, follow, ("error in digit part" + follow)::errs) } else super.scan(start, errs) } } trait Ident extends Scanner { private def findident(pos: Pos, fstate: Boolean, sstate: Boolean): (Pos, Boolean, Boolean) = pos.ch match { case a if ((a >= 'a') && (a <= 'z')) => findident(pos.inc, true, false) case a if ((a >= '0') && (a <= '9')) => findident(pos.inc, fstate, true) case _ => (pos, fstate, sstate) } override def scan(start: Pos, errs: List[String]) = { val (follow, flag1, flag2) = findident(start, false, false) if ((start != follow) && flag2) (IDENT, follow, errs) else if ((start != follow) && flag1 && !flag2) { //println("digit expected") //(ERROR, follow) (IDENT, follow, ("digit expected" + follow)::errs) } else super.scan(start, errs) } } trait Whitespaces extends Scanner { private def missWhitespace(pos: Pos): Pos = pos.ch match { case ' ' => missWhitespace(pos.inc) case '\t' => missWhitespace(pos.inc) case '\n' => missWhitespace(pos.inc) case _ => pos } override def scan(start: Pos, errs: List[String]) = { val follow = missWhitespace(start) if (start != follow) (WHITESPACE, follow, errs) else super.scan(start, errs) } } val str = "<1 2>= q1q 1q1 \n w1w1 <2><==== <2q> 11 &" println(str) var t = new Token( new Pos(str), new Scanner with Oper with Number with Ident with Whitespaces, Nil ) while (t.tag != END_OF_PROGRAM) { if (t.tag != ERROR && t.tag != WHITESPACE) println(t.tag.toString + " " + t.start + "-" + t.follow + ": " + t.image) t = t.next } def printerrs(errs: List[String]): List[String] = errs match { case Nil => Nil case x::l => {println("err:" + x) printerrs(l)} } printerrs(t.listacc)