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
}