Skip to content

Instantly share code, notes, and snippets.

@aolo2
Created March 22, 2018 11:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aolo2/19b31c7203d76ecb4bf249e3eedbdb44 to your computer and use it in GitHub Desktop.
Save aolo2/19b31c7203d76ecb4bf249e3eedbdb44 to your computer and use it in GitHub Desktop.
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, COMMENT, KEYWORD, IDENT, END_OF_PROGRAM = Value
}
import DomainTags._
class Scanner {
def scan(start: Pos): (Tag, Pos) =
sys.error(" syntax error at " + start)
}
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 Comments extends Scanner {
private def skipComment(pos: Pos): Pos = pos.ch match {
case '\n' => pos
case _ => skipComment(pos.inc)
}
override def scan(start: Pos) = {
val follow = if (start.col == 1 && start.ch == '*') skipComment(start) else start
if (start != follow) (COMMENT, follow)
else super.scan(start)
}
}
trait KeyWords extends Scanner {
private def scanKeyWord(pos: Pos, keyword: String, ch: Int): (Pos, Int) = {
if (ch == keyword.length() - 1) (pos, ch)
else if (keyword(ch) == pos.ch) scanKeyWord(pos.inc, keyword, ch + 1)
else (pos, ch)
}
private def getKeyWord(pos: Pos): Pos = pos.ch match {
case '*' =>
val (follow, len) = scanKeyWord(pos, "**", 0)
if (len + 1 == 2) follow
else pos
case 'w' =>
val (follow, len) = scanKeyWord(pos, "with", 0)
if (len + 1 == 4) follow
else pos
case 'e' =>
val (follow, len) = scanKeyWord(pos, "end", 0)
if (len + 1 == 3) follow
else pos
}
override def scan(start: Pos) = {
val follow = getKeyWord(start)
if (start != follow) (KEYWORD, follow)
else super.scan(start)
}
}
trait Idents extends Scanner {
private def skipAstIdent(pos: Pos): Pos = pos.ch match {
case '*' => skipAstIdent(pos.inc)
case _ => pos
}
private def skipIdent(pos: Pos): Pos = pos.ch match {
case x if 'a' until 'z' contains x => skipIdent(pos.inc)
case _ => pos
}
private def getIdent(pos: Pos): Pos = pos.ch match {
case '*' => skipAstIdent(pos)
case x if 'a' until 'z' contains x => skipIdent(pos)
}
override def scan(start: Pos) = {
val follow = getIdent(start)
if (start != follow) (IDENT, follow)
else super.scan(start)
}
}
object Lab3 extends App {
var t = new Token(
new Pos("program source goes here!"),
new Scanner
with KeyWords
with Idents
with Comments
with Whitespaces
)
while (t.tag != END_OF_PROGRAM) {
println(t.tag.toString + " " + t.start + "-" + t.follow + ": " + t.image)
t = t.next
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment