210 lines
7.1 KiB
Scala
210 lines
7.1 KiB
Scala
package xyz.hyperreal.rosettacodeCompiler
|
|
|
|
import scala.io.Source
|
|
|
|
object SyntaxAnalyzer {
|
|
|
|
val symbols =
|
|
Map[String, (PrefixOperator, InfixOperator)](
|
|
"Op_or" -> (null, InfixOperator(10, LeftAssoc, BranchNode("Or", _, _))),
|
|
"Op_and" -> (null, InfixOperator(20, LeftAssoc, BranchNode("And", _, _))),
|
|
"Op_equal" -> (null, InfixOperator(30, LeftAssoc, BranchNode("Equal", _, _))),
|
|
"Op_notequal" -> (null, InfixOperator(30, LeftAssoc, BranchNode("NotEqual", _, _))),
|
|
"Op_less" -> (null, InfixOperator(40, LeftAssoc, BranchNode("Less", _, _))),
|
|
"Op_lessequal" -> (null, InfixOperator(40, LeftAssoc, BranchNode("LessEqual", _, _))),
|
|
"Op_greater" -> (null, InfixOperator(40, LeftAssoc, BranchNode("Greater", _, _))),
|
|
"Op_greaterequal" -> (null, InfixOperator(40, LeftAssoc, BranchNode("GreaterEqual", _, _))),
|
|
"Op_add" -> (PrefixOperator(30, identity), InfixOperator(50, LeftAssoc, BranchNode("Add", _, _))),
|
|
"Op_minus" -> (PrefixOperator(70, BranchNode("Negate", _, TerminalNode)), InfixOperator(
|
|
50,
|
|
LeftAssoc,
|
|
BranchNode("Subtract", _, _))),
|
|
"Op_multiply" -> (null, InfixOperator(60, LeftAssoc, BranchNode("Multiply", _, _))),
|
|
"Op_divide" -> (null, InfixOperator(60, LeftAssoc, BranchNode("Divide", _, _))),
|
|
"Op_mod" -> (null, InfixOperator(60, RightAssoc, BranchNode("Mod", _, _))),
|
|
"Op_not" -> (PrefixOperator(70, BranchNode("Not", _)), null),
|
|
"LeftParen" -> null,
|
|
"RightParen" -> null
|
|
)
|
|
|
|
def apply = new SyntaxAnalyzer(symbols)
|
|
|
|
abstract class Node
|
|
case class LeafNode(name: String, value: String) extends Node
|
|
case class BranchNode(name: String, left: Node, right: Node = TerminalNode) extends Node
|
|
case object TerminalNode extends Node
|
|
|
|
abstract class Assoc
|
|
case object LeftAssoc extends Assoc
|
|
case object RightAssoc extends Assoc
|
|
|
|
abstract class Operator
|
|
case class InfixOperator(prec: Int, assoc: Assoc, compute: (Node, Node) => Node) extends Operator
|
|
case class PrefixOperator(prec: Int, compute: Node => Node) extends Operator
|
|
|
|
}
|
|
|
|
class SyntaxAnalyzer(symbols: Map[String, (SyntaxAnalyzer.PrefixOperator, SyntaxAnalyzer.InfixOperator)]) {
|
|
import SyntaxAnalyzer.{BranchNode, InfixOperator, LeafNode, LeftAssoc, Node, PrefixOperator, TerminalNode}
|
|
|
|
def fromStdin = fromSource(Source.stdin)
|
|
|
|
def fromString(src: String) = fromSource(Source.fromString(src))
|
|
|
|
def fromSource(s: Source) = {
|
|
val tokens = ((s.getLines map (_.trim.split(" +", 4)) map {
|
|
case Array(line, col, name) =>
|
|
symbols get name match {
|
|
case None | Some(null) => SimpleToken(line.toInt, col.toInt, name)
|
|
case Some(operators) => OperatorToken(line.toInt, col.toInt, name, operators)
|
|
}
|
|
case Array(line, col, name, value) => ValueToken(line.toInt, col.toInt, name, value)
|
|
}) toStream)
|
|
|
|
flatten(parse(tokens))
|
|
}
|
|
|
|
def flatten(n: Node): Unit =
|
|
n match {
|
|
case TerminalNode => println(";")
|
|
case LeafNode(name, value) => println(s"$name $value")
|
|
case BranchNode(name, left, right) =>
|
|
println(name)
|
|
flatten(left)
|
|
flatten(right)
|
|
}
|
|
|
|
def parse(toks: Stream[Token]) = {
|
|
var cur = toks
|
|
|
|
def next = cur = cur.tail
|
|
|
|
def token = cur.head
|
|
|
|
def consume = {
|
|
val res = token
|
|
|
|
next
|
|
res
|
|
}
|
|
|
|
def accept(name: String) =
|
|
if (token.name == name) {
|
|
next
|
|
true
|
|
} else
|
|
false
|
|
|
|
def expect(name: String, error: String = null) =
|
|
if (token.name != name)
|
|
sys.error(if (error eq null) s"expected $name, found ${token.name}" else s"$error: $token")
|
|
else
|
|
next
|
|
|
|
def expression(minPrec: Int): Node = {
|
|
def infixOperator = token.asInstanceOf[OperatorToken].operators._2
|
|
|
|
def isInfix = token.isInstanceOf[OperatorToken] && infixOperator != null
|
|
|
|
var result =
|
|
consume match {
|
|
case SimpleToken(_, _, "LeftParen") =>
|
|
val result = expression(0)
|
|
|
|
expect("RightParen", "expected closing parenthesis")
|
|
result
|
|
case ValueToken(_, _, name, value) => LeafNode(name, value)
|
|
case OperatorToken(_, _, _, (prefix, _)) if prefix ne null => prefix.compute(expression(prefix.prec))
|
|
case OperatorToken(_, _, _, (_, infix)) if infix ne null =>
|
|
sys.error(s"expected a primitive expression, not an infix operator: $token")
|
|
}
|
|
|
|
while (isInfix && infixOperator.prec >= minPrec) {
|
|
val InfixOperator(prec, assoc, compute) = infixOperator
|
|
val nextMinPrec = if (assoc == LeftAssoc) prec + 1 else prec
|
|
|
|
next
|
|
result = compute(result, expression(nextMinPrec))
|
|
}
|
|
|
|
result
|
|
}
|
|
|
|
def parenExpression = {
|
|
expect("LeftParen")
|
|
|
|
val e = expression(0)
|
|
|
|
expect("RightParen")
|
|
e
|
|
}
|
|
|
|
def statement: Node = {
|
|
var stmt: Node = TerminalNode
|
|
|
|
if (accept("Keyword_if"))
|
|
stmt = BranchNode("If",
|
|
parenExpression,
|
|
BranchNode("If", statement, if (accept("Keyword_else")) statement else TerminalNode))
|
|
else if (accept("Keyword_putc")) {
|
|
stmt = BranchNode("Prtc", parenExpression)
|
|
expect("Semicolon")
|
|
} else if (accept("Keyword_print")) {
|
|
expect("LeftParen")
|
|
|
|
do {
|
|
val e =
|
|
if (token.name == "String")
|
|
BranchNode("Prts", LeafNode("String", consume.asInstanceOf[ValueToken].value))
|
|
else
|
|
BranchNode("Prti", expression(0))
|
|
|
|
stmt = BranchNode("Sequence", stmt, e)
|
|
} while (accept("Comma"))
|
|
|
|
expect("RightParen")
|
|
expect("Semicolon")
|
|
} else if (token.name == "Semicolon")
|
|
next
|
|
else if (token.name == "Identifier") {
|
|
val ident = LeafNode("Identifier", consume.asInstanceOf[ValueToken].value)
|
|
|
|
expect("Op_assign")
|
|
stmt = BranchNode("Assign", ident, expression(0))
|
|
expect("Semicolon")
|
|
} else if (accept("Keyword_while"))
|
|
stmt = BranchNode("While", parenExpression, statement)
|
|
else if (accept("LeftBrace")) {
|
|
while (token.name != "RightBrace" && token.name != "End_of_input") {
|
|
stmt = BranchNode("Sequence", stmt, statement)
|
|
}
|
|
|
|
expect("RightBrace")
|
|
} else
|
|
sys.error(s"syntax error: $token")
|
|
|
|
stmt
|
|
}
|
|
|
|
var tree: Node = TerminalNode
|
|
|
|
do {
|
|
tree = BranchNode("Sequence", tree, statement)
|
|
} while (token.name != "End_of_input")
|
|
|
|
expect("End_of_input")
|
|
tree
|
|
}
|
|
|
|
abstract class Token {
|
|
val line: Int;
|
|
val col: Int;
|
|
val name: String
|
|
}
|
|
|
|
case class SimpleToken(line: Int, col: Int, name: String) extends Token
|
|
case class ValueToken(line: Int, col: Int, name: String, value: String) extends Token
|
|
case class OperatorToken(line: Int, col: Int, name: String, operators: (PrefixOperator, InfixOperator)) extends Token
|
|
|
|
}
|