222 lines
6.2 KiB
Text
222 lines
6.2 KiB
Text
import "./dynamic" for Enum, Struct, Tuple
|
|
import "./fmt" for Conv
|
|
import "./ioutil" for FileUtil
|
|
|
|
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)
|
|
|
|
var Tree = Struct.create("Tree", ["nodeType", "left", "right", "value"])
|
|
|
|
// dependency: Ordered by Node value, must remain in same order as Node enum
|
|
var Atr = Tuple.create("Atr", ["enumText", "nodeType"])
|
|
|
|
var atrs = [
|
|
Atr.new("Identifier", Node.Ident),
|
|
Atr.new("String", Node.String),
|
|
Atr.new("Integer", Node.Integer),
|
|
Atr.new("Sequence", Node.Sequence),
|
|
Atr.new("If", Node.If),
|
|
Atr.new("Prtc", Node.Prtc),
|
|
Atr.new("Prts", Node.Prts),
|
|
Atr.new("Prti", Node.Prti),
|
|
Atr.new("While", Node.While),
|
|
Atr.new("Assign", Node.Assign),
|
|
Atr.new("Negate", Node.Negate),
|
|
Atr.new("Not", Node.Not),
|
|
Atr.new("Multiply", Node.Mul),
|
|
Atr.new("Divide", Node.Div),
|
|
Atr.new("Mod", Node.Mod),
|
|
Atr.new("Add", Node.Add),
|
|
Atr.new("Subtract", Node.Sub),
|
|
Atr.new("Less", Node.Lss),
|
|
Atr.new("LessEqual", Node.Leq),
|
|
Atr.new("Greater", Node.Gtr),
|
|
Atr.new("GreaterEqual", Node.Geq),
|
|
Atr.new("Equal", Node.Eql),
|
|
Atr.new("NotEqual", Node.Neq),
|
|
Atr.new("And", Node.And),
|
|
Atr.new("Or", Node.Or),
|
|
]
|
|
|
|
var stringPool = []
|
|
var globalNames = []
|
|
var globalValues = {}
|
|
|
|
var reportError = Fn.new { |msg| Fiber.abort("error : %(msg)") }
|
|
|
|
var makeNode = Fn.new { |nodeType, left, right| Tree.new(nodeType, left, right, 0) }
|
|
|
|
var makeLeaf = Fn.new { |nodeType, value| Tree.new(nodeType, null, null, value) }
|
|
|
|
// interpret the parse tree
|
|
var interp // recursive function
|
|
interp = Fn.new { |x|
|
|
if (!x) return 0
|
|
var nt = x.nodeType
|
|
if (nt == Node.Integer) return x.value
|
|
if (nt == Node.Ident) return globalValues[x.value]
|
|
if (nt == Node.String) return x.value
|
|
if (nt == Node.Assign) {
|
|
var n = interp.call(x.right)
|
|
globalValues[x.left.value] = n
|
|
return n
|
|
}
|
|
if (nt == Node.Add) return interp.call(x.left) + interp.call(x.right)
|
|
if (nt == Node.Sub) return interp.call(x.left) - interp.call(x.right)
|
|
if (nt == Node.Mul) return interp.call(x.left) * interp.call(x.right)
|
|
if (nt == Node.Div) return (interp.call(x.left) / interp.call(x.right)).truncate
|
|
if (nt == Node.Mod) return interp.call(x.left) % interp.call(x.right)
|
|
if (nt == Node.Lss) return Conv.btoi(interp.call(x.left) < interp.call(x.right))
|
|
if (nt == Node.Gtr) return Conv.btoi(interp.call(x.left) > interp.call(x.right))
|
|
if (nt == Node.Leq) return Conv.btoi(interp.call(x.left) <= interp.call(x.right))
|
|
if (nt == Node.Eql) return Conv.btoi(interp.call(x.left) == interp.call(x.right))
|
|
if (nt == Node.Neq) return Conv.btoi(interp.call(x.left) != interp.call(x.right))
|
|
if (nt == Node.And) return Conv.btoi(Conv.itob(interp.call(x.left)) && Conv.itob(interp.call(x.right)))
|
|
if (nt == Node.Or) return Conv.btoi(Conv.itob(interp.call(x.left)) || Conv.itob(interp.call(x.right)))
|
|
if (nt == Node.Negate) return -interp.call(x.left)
|
|
if (nt == Node.Not) return (interp.call(x.left) == 0) ? 1 : 0
|
|
if (nt == Node.If) {
|
|
if (interp.call(x.left) != 0) {
|
|
interp.call(x.right.left)
|
|
} else {
|
|
interp.call(x.right.right)
|
|
}
|
|
return 0
|
|
}
|
|
if (nt == Node.While) {
|
|
while (interp.call(x.left) != 0) interp.call(x.right)
|
|
return 0
|
|
}
|
|
if (nt == Node.Prtc) {
|
|
System.write(String.fromByte(interp.call(x.left)))
|
|
return 0
|
|
}
|
|
if (nt == Node.Prti) {
|
|
System.write(interp.call(x.left))
|
|
return 0
|
|
}
|
|
if (nt == Node.Prts) {
|
|
System.write(stringPool[interp.call(x.left)])
|
|
return 0
|
|
}
|
|
if (nt == Node.Sequence) {
|
|
interp.call(x.left)
|
|
interp.call(x.right)
|
|
return 0
|
|
}
|
|
reportError.call("interp: unknown tree type %(x.nodeType)")
|
|
}
|
|
|
|
var getEnumValue = Fn.new { |name|
|
|
for (atr in atrs) {
|
|
if (atr.enumText == name) return atr.nodeType
|
|
}
|
|
reportError.call("Unknown token %(name)")
|
|
}
|
|
|
|
var fetchStringOffset = Fn.new { |s|
|
|
var d = ""
|
|
s = s[1...-1]
|
|
var i = 0
|
|
while (i < s.count) {
|
|
if (s[i] == "\\" && (i+1) < s.count) {
|
|
if (s[i+1] == "n") {
|
|
d = d + "\n"
|
|
i = i + 1
|
|
} else if (s[i+1] == "\\") {
|
|
d = d + "\\"
|
|
i = i + 1
|
|
}
|
|
} else {
|
|
d = d + s[i]
|
|
}
|
|
i = i + 1
|
|
}
|
|
s = d
|
|
for (i in 0...stringPool.count) {
|
|
if (s == stringPool[i]) return i
|
|
}
|
|
stringPool.add(s)
|
|
return stringPool.count - 1
|
|
}
|
|
|
|
var fetchVarOffset = Fn.new { |name|
|
|
for (i in 0...globalNames.count) {
|
|
if (globalNames[i] == name) return i
|
|
}
|
|
globalNames.add(name)
|
|
return globalNames.count - 1
|
|
}
|
|
|
|
var lines = []
|
|
var lineCount = 0
|
|
var lineNum = 0
|
|
|
|
var loadAst // recursive function
|
|
loadAst = Fn.new {
|
|
var nodeType = 0
|
|
var s = ""
|
|
if (lineNum < lineCount) {
|
|
var line = lines[lineNum].trimEnd(" \t")
|
|
lineNum = lineNum + 1
|
|
var tokens = line.split(" ").where { |s| s != "" }.toList
|
|
var first = tokens[0]
|
|
if (first[0] == ";") return null
|
|
nodeType = getEnumValue.call(first)
|
|
var le = tokens.count
|
|
if (le == 2) {
|
|
s = tokens[1]
|
|
} else if (le > 2) {
|
|
var idx = line.indexOf("\"")
|
|
s = line[idx..-1]
|
|
}
|
|
}
|
|
if (s != "") {
|
|
var n
|
|
if (nodeType == Node.Ident) {
|
|
n = fetchVarOffset.call(s)
|
|
} else if (nodeType == Node.Integer) {
|
|
n = Num.fromString(s)
|
|
} else if (nodeType == Node.String) {
|
|
n = fetchStringOffset.call(s)
|
|
} else {
|
|
reportError.call("Unknown node type: %(s)")
|
|
}
|
|
return makeLeaf.call(nodeType, n)
|
|
}
|
|
var left = loadAst.call()
|
|
var right = loadAst.call()
|
|
return makeNode.call(nodeType, left, right)
|
|
}
|
|
|
|
lines = FileUtil.readLines("ast.txt")
|
|
lineCount = lines.count
|
|
var x = loadAst.call()
|
|
interp.call(x)
|