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, OPERATOR, END_OF_PROGRAM, ERROR = Value
}
import DomainTags._
class Scanner {
def scan(start: Pos, list:List[String]): (Tag, Pos, List[String]) = (NUMBER, start.inc, ("undefined lexem at "+start)::list)
//sys.error("syntax error at " + start)
}
class Token(val start: Pos, scanner: Scanner, list: List[String]) {
val (tag, follow, l) = start.ch match {
case -1 => (END_OF_PROGRAM, start, list)
case _ => scanner.scan(start, list)
}
def image = start.prog.substring(start.offs, follow.offs)
def next = new Token(follow, scanner, l)
def getlist = list
}
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, list: List[String]) = {
val follow = missWhitespace(start)
if (start != follow) (WHITESPACE, follow, list)
else super.scan(start, list)
}
}
trait Operators extends Scanner {
private def checkD(pos : Pos): Pos = pos.ch match {
case '=' => pos.inc
case _ => pos
}
private def checkS(pos : Pos, list:List[String]): (Pos, Boolean, List[String]) = pos.ch match {
case ')' => (pos.inc, true, list)
case _ => (pos, true, ("expected ')' in OPERATOR at "+pos)::list)
}
private def checkOperator(pos: Pos, list:List[String]): (Pos, Boolean, List[String]) = pos.ch match {
case '(' => checkS(pos.inc, list)
case ':' => (checkD(pos.inc), true, list)
case _ => (pos, false, list)
}
override def scan(start: Pos, list: List[String]) = {
val pos = checkOperator(start, list)
pos match {
case(pos, false, list) => super.scan(start, list)
case(pos, true, list) => (OPERATOR, pos, list)
}
}
}
trait Ident extends Scanner {
private def ddopcheckIdent(pos: Pos, list:List[String]): (Pos, Boolean, List[String]) = pos.ch match {
case ')' => (pos.inc, true, list)
case p if(p >= '0' && p <= '9') => ddopcheckIdent(pos.inc, list)
case _ => (pos, true, ("expected \')\' in IDENT at " +pos)::list)
}
private def dopcheckIdent(pos: Pos, list:List[String]): (Pos, Boolean, List[String]) = pos.ch match {
case ')' => (pos, false, list)
case a if(a >= '0' && a <= '9') => ddopcheckIdent(pos.inc, list)
case _ => (pos, false, list)
}
private def checkIdentl(pos: Pos, list:List[String]): (Pos, Boolean, List[String]) = pos.ch match {
case p if(p >= 'a' && p <= 'z') => checkIdentl(pos.inc, list)
case _ => (pos, true, list)
}
private def checkIdent(pos: Pos, list:List[String]): (Pos, Boolean, List[String]) = pos.ch match {
case p if(p >= 'a' && p <= 'z') => checkIdentl(pos.inc, list)
case p if(p == '(') => dopcheckIdent(pos.inc, list)
case _ => (pos, false, list)
}
override def scan(start: Pos, list: List[String]) = {
val pos = checkIdent(start, list)
pos match {
case(pos, false, l) => super.scan(start, l)
case(pos, true, l) => (IDENT, pos, l)
}
}
}
trait Numbers extends Scanner {
private def dopcheckNumber(pos: Pos, list: List[String]): (Pos, Boolean, List[String]) = pos.ch match {
case p if(p >= '0' && p <= '9') => dopcheckNumber(pos.inc, list)
case _ => (pos, true, list)
}
private def checkNul(pos: Pos, list: List[String]): (Pos, Boolean, List[String]) = pos.ch match {
case p if(p >= '0' && p <= '9') => (pos, true, ("no number after '0' expected at "+pos)::list)
case _ => (pos, true, list)
}
private def checkNumber(pos: Pos, list: List[String]): (Pos, Boolean, List[String]) = pos.ch match {
case '0' => checkNul(pos.inc, list)
case p if(p >= '0' && p <= '9') => dopcheckNumber(pos.inc, list)
case _ => (pos, false, list)
}
override def scan(start: Pos, list: List[String]) = {
val pos = checkNumber(start, list)
pos match {
case(pos, false, list) => super.scan(start, list)
case(pos, true, list) => (NUMBER, pos, list)
}
}
}
var t = new Token(
new Pos("1234\n 0 01 sfsd (567 ( := : () 453 "),
new Scanner with Operators with Numbers with Whitespaces with Ident ,
Nil
)
while (t.tag != END_OF_PROGRAM) {
if(t.tag != ERROR && t.tag != WHITESPACE)
println(t.tag.toString + " " + t.start + "-" + t.follow + ": \t" + t.image)
t = t.next
}
def func(l:List[String]):List[String] = l match {
case Nil => Nil
case a::tail => { println("error: " + a)
func(tail) }
}
func(t.getlist)