RosettaCodeData/Task/Compiler-syntax-analyzer/Wren/compiler-syntax-analyzer.wren

318 lines
9.8 KiB
Plaintext

import "./dynamic" for Enum, Struct, Tuple
import "./fmt" for Fmt
import "./ioutil" for FileUtil
var tokens = [
"EOI",
"Mul",
"Div",
"Mod",
"Add",
"Sub",
"Negate",
"Not",
"Lss",
"Leq",
"Gtr",
"Geq",
"Eql",
"Neq",
"Assign",
"And",
"Or",
"If",
"Else",
"While",
"Print",
"Putc",
"Lparen",
"Rparen",
"Lbrace",
"Rbrace",
"Semi",
"Comma",
"Ident",
"Integer",
"String"
]
var Token = Enum.create("Token", tokens)
var nodes = [
"Ident",
"String",
"Integer",
"Sequence",
"If",
"Prtc",
"Prts",
"Prti",
"While",
"Assign",
"Negate",
"Not",
"Mul",
"Div",
"Mod",
"Add",
"Sub",
"Lss",
"Leq",
"Gtr",
"Geq",
"Eql",
"Neq",
"And",
"Or"
]
var Node = Enum.create("Node", nodes)
// 'text' field represents ident ot string literal or integer value
var TokS = Struct.create("TokS", ["tok", "errLn", "errCol", "text"])
var Tree = Struct.create("Tree", ["nodeType", "left", "right", "value"])
// dependency: Ordered by tok, must remain in same order as Token enum constants
var Atr = Tuple.create("Atr", ["text", "enumText", "tok", "rightAssociative", "isBinary",
"isUnary", "precedence", "nodeType"])
var atrs = [
Atr.new("EOI", "End_of_input", Token.EOI, false, false, false, -1, -1),
Atr.new("*", "Op_multiply", Token.Mul, false, true, false, 13, Node.Mul),
Atr.new("/", "Op_divide", Token.Div, false, true, false, 13, Node.Div),
Atr.new("\%", "Op_mod", Token.Mod, false, true, false, 13, Node.Mod),
Atr.new("+", "Op_add", Token.Add, false, true, false, 12, Node.Add),
Atr.new("-", "Op_subtract", Token.Sub, false, true, false, 12, Node.Sub),
Atr.new("-", "Op_negate", Token.Negate, false, false, true, 14, Node.Negate),
Atr.new("!", "Op_not", Token.Not, false, false, true, 14, Node.Not),
Atr.new("<", "Op_less", Token.Lss, false, true, false, 10, Node.Lss),
Atr.new("<=", "Op_lessequal", Token.Leq, false, true, false, 10, Node.Leq),
Atr.new(">", "Op_greater", Token.Gtr, false, true, false, 10, Node.Gtr),
Atr.new(">=", "Op_greaterequal", Token.Geq, false, true, false, 10, Node.Geq),
Atr.new("==", "Op_equal", Token.Eql, false, true, false, 9, Node.Eql),
Atr.new("!=", "Op_notequal", Token.Neq, false, true, false, 9, Node.Neq),
Atr.new("=", "Op_assign", Token.Assign, false, false, false, -1, Node.Assign),
Atr.new("&&", "Op_and", Token.And, false, true, false, 5, Node.And),
Atr.new("||", "Op_or", Token.Or, false, true, false, 4, Node.Or),
Atr.new("if", "Keyword_if", Token.If, false, false, false, -1, Node.If),
Atr.new("else", "Keyword_else", Token.Else, false, false, false, -1, -1),
Atr.new("while", "Keyword_while", Token.While, false, false, false, -1, Node.While),
Atr.new("print", "Keyword_print", Token.Print, false, false, false, -1, -1),
Atr.new("putc", "Keyword_putc", Token.Putc, false, false, false, -1, -1),
Atr.new("(", "LeftParen", Token.Lparen, false, false, false, -1, -1),
Atr.new(")", "RightParen", Token.Rparen, false, false, false, -1, -1),
Atr.new("{", "LeftBrace", Token.Lbrace, false, false, false, -1, -1),
Atr.new("}", "RightBrace", Token.Rbrace, false, false, false, -1, -1),
Atr.new(";", "Semicolon", Token.Semi, false, false, false, -1, -1),
Atr.new(",", "Comma", Token.Comma, false, false, false, -1, -1),
Atr.new("Ident", "Identifier", Token.Ident, false, false, false, -1, Node.Ident),
Atr.new("Integer literal", "Integer", Token.Integer, false, false, false, -1, Node.Integer),
Atr.new("String literal", "String", Token.String, false, false, false, -1, Node.String),
]
var displayNodes = [
"Identifier", "String", "Integer", "Sequence", "If", "Prtc", "Prts", "Prti",
"While", "Assign", "Negate", "Not", "Multiply", "Divide", "Mod", "Add",
"Subtract", "Less", "LessEqual", "Greater", "GreaterEqual", "Equal",
"NotEqual", "And", "Or"
]
var token = TokS.new(0, 0, 0, "")
var reportError = Fn.new { |eline, ecol, msg| Fiber.abort("(%(eline):%(ecol)) error : %(msg)") }
// return internal version of name
var getEnum = Fn.new { |name|
for (atr in atrs) {
if (atr.enumText == name) return atr.tok
}
reportError.call(0, 0, "Unknown token %(name)")
}
var lines = []
var lineCount = 0
var lineNum = 0
var getTok = Fn.new {
var tok = TokS.new(0, 0, 0, "")
if (lineNum < lineCount) {
var line = lines[lineNum].trimEnd(" \t")
lineNum = lineNum + 1
var fields = line.split(" ").where { |s| s != "" }.toList
// [ ]*{lineno}[ ]+{colno}[ ]+token[ ]+optional
tok.errLn = Num.fromString(fields[0])
tok.errCol = Num.fromString(fields[1])
tok.tok = getEnum.call(fields[2])
var le = fields.count
if (le == 4) {
tok.text = fields[3]
} else if (le > 4) {
var idx = line.indexOf("\"")
tok.text = line[idx..-1]
}
}
return tok
}
var makeNode = Fn.new { |nodeType, left, right| Tree.new(nodeType, left, right, "") }
var makeLeaf = Fn.new { |nodeType, value| Tree.new(nodeType, null, null, value) }
var expect = Fn.new { |msg, s|
if (token.tok == s) {
token = getTok.call()
return
}
reportError.call(token.errLn, token.errCol,
Fmt.swrite("$s: Expecting '$s', found '$s'", msg, atrs[s].text, atrs[token.tok].text))
}
var parenExpr // forward reference
var expr // recursive function
expr = Fn.new { |p|
var x
var node
var t = token.tok
if (t == Token.Lparen) {
x = parenExpr.call()
} else if (t == Token.Sub || t == Token.Add) {
var op = t
token = getTok.call()
node = expr.call(atrs[Token.Negate].precedence)
if (op == Token.Sub) {
x = makeNode.call(Node.negate, node, null)
} else {
x = node
}
} else if (t == Token.Not) {
token = getTok.call()
x = makeNode.call(Node.Not, expr.call(atrs[Token.Not].precedence), null)
} else if (t == Token.Ident) {
x = makeLeaf.call(Node.Ident, token.text)
token = getTok.call()
} else if (t == Token.Integer) {
x = makeLeaf.call(Node.Integer, token.text)
token = getTok.call()
} else {
reportError.call(token.errLn, token.errCol,
Fmt.swrite("Expecting a primary, found: $s", atrs[token.tok].text))
}
while (atrs[token.tok].isBinary && atrs[token.tok].precedence >= p) {
var op = token.tok
token = getTok.call()
var q = atrs[op].precedence
if (!atrs[op].rightAssociative) q = q + 1
node = expr.call(q)
x = makeNode.call(atrs[op].nodeType, x, node)
}
return x
}
parenExpr = Fn.new {
expect.call("parenExpr", Token.Lparen)
var t = expr.call(0)
expect.call("parenExpr", Token.Rparen)
return t
}
var stmt // recursive function
stmt = Fn.new {
var t
var v
var e
var s
var s2
var tt = token.tok
if (tt == Token.If) {
token = getTok.call()
e = parenExpr.call()
s = stmt.call()
s2 = null
if (token.tok == Token.Else) {
token = getTok.call()
s2 = stmt.call()
}
t = makeNode.call(Node.If, e, makeNode.call(Node.If, s, s2))
} else if (tt == Token.Putc) {
token = getTok.call()
e = parenExpr.call()
t = makeNode.call(Node.Prtc, e, null)
expect.call("Putc", Token.Semi)
} else if (tt == Token.Print) { // print '(' expr {',' expr} ')'
token = getTok.call()
expect.call("Print", Token.Lparen)
while (true) {
if (token.tok == Token.String) {
e = makeNode.call(Node.Prts, makeLeaf.call(Node.String, token.text), null)
token = getTok.call()
} else {
e = makeNode.call(Node.Prti, expr.call(0), null)
}
t = makeNode.call(Node.Sequence, t, e)
if (token.tok != Token.Comma) break
expect.call("Print", Token.Comma)
}
expect.call("Print", Token.Rparen)
expect.call("Print", Token.Semi)
} else if (tt == Token.Semi) {
token = getTok.call()
} else if (tt == Token.Ident) {
v = makeLeaf.call(Node.Ident, token.text)
token = getTok.call()
expect.call("assign", Token.Assign)
e = expr.call(0)
t = makeNode.call(Node.Assign, v, e)
expect.call("assign", Token.Semi)
} else if (tt == Token.While) {
token = getTok.call()
e = parenExpr.call()
s = stmt.call()
t = makeNode.call(Node.While, e, s)
} else if (tt == Token.Lbrace) { // {stmt}
expect.call("Lbrace", Token.Lbrace)
while (token.tok != Token.Rbrace && token.tok != Token.EOI) {
t = makeNode.call(Node.Sequence, t, stmt.call())
}
expect.call("Lbrace", Token.Rbrace)
} else if (tt == Token.EOI) {
// do nothing
} else {
reportError.call(token.errLn, token.errCol,
Fmt.Swrite("expecting start of statement, found '$s'", atrs[token.tok].text))
}
return t
}
var parse = Fn.new {
var t
token = getTok.call()
while (true) {
t = makeNode.call(Node.Sequence, t, stmt.call())
if (!t || token.tok == Token.EOI) break
}
return t
}
var prtAst // recursive function
prtAst = Fn.new { |t|
if (!t) {
System.print(";")
} else {
Fmt.write("$-14s ", displayNodes[t.nodeType])
if (t.nodeType == Node.Ident || t.nodeType == Node.Integer || t.nodeType == Node.String) {
System.print(t.value)
} else {
System.print()
prtAst.call(t.left)
prtAst.call(t.right)
}
}
}
lines = FileUtil.readLines("source.txt")
lineCount = lines.count
prtAst.call(parse.call())