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, STRINGG, IDENT, NUMBER, ERROR, END_OF_PROGRAM = Value } import DomainTags._ class Scanner { def scan(start: Pos): (Tag, Pos) = (ERROR, start.inc) } class Token(val start: Pos, scanner: Scanner) { val (tag, follow) = start.ch match { case -1 => (END_OF_PROGRAM, start) case _ => scanner.scan(start) } def image = start.prog.substring(start.offs, follow.offs) def next = new Token(follow, scanner) } 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) = { val follow = missWhitespace(start) if (start != follow) (WHITESPACE, follow) else super.scan(start) } } trait Numbers extends Scanner { private def missNumber(pos: Pos): Pos = pos.ch match { case x if (x >= '0' && x <= '1') => missNumberdop1 (pos.inc) case x if (x >= '0' && x <= '9') => missNumberdop2 (pos.inc) case _ => pos } private def missNumberdop1(pos: Pos): Pos = pos.ch match { case x if (x == 'b') => pos.inc case x if (x >= '0' && x <= '1') => missNumberdop1 (pos.inc) case x if (x >= '0' && x <= '9') => missNumberdop2 (pos.inc) case _ => pos } private def missNumberdop2(pos: Pos): Pos = pos.ch match { case x if (x >= '0' && x <= '9') => missNumberdop2 (pos.inc) case _ => pos } override def scan(start: Pos) = { val follow = missNumber(start) if (start == follow) super.scan(start) else (NUMBER, follow) } } trait Idents extends Scanner { private def missIdent(pos: Pos): Pos = pos.ch match { case '?' => missIdentdop(pos.inc) case '*' => missIdentdop(pos.inc) case '|' => missIdentdop(pos.inc) case _ => pos } private def missIdentdop(pos: Pos): Pos = pos.ch match { case '?' => missIdentdop(pos.inc) case '*' => missIdentdop(pos.inc) case '|' => missIdentdop(pos.inc) case a if (a >= '0' && a <= '9') => missIdentdop(pos.inc) case _ => pos } override def scan(start: Pos) = { val follow = missIdent(start) if (start == follow) super.scan(start) else (IDENT, follow) } } trait Stringgs extends Scanner { private def missStringg(pos: Pos): Pos = pos.ch match { case '\'' => missStringgdop(pos.inc) case _ => pos } private def missStringgdop(pos: Pos): Pos = pos.ch match { case '\'' => missStringg(pos.inc) case -1 => pos case _ => missStringgdop(pos.inc) } override def scan(start: Pos) = { val follow = missStringg(start) if (start == follow) super.scan(start) else (STRINGG, follow) } } var t = new Token( new Pos(" ?1 d 101b & 23 'sadf3'''"), new Scanner with Idents with Numbers with Stringgs with Whitespaces ) while (t.tag != END_OF_PROGRAM) { println(t.tag.toString + " " + t.start + "-" + t.follow + ": " + t.image) t = t.next }