165 lines
4.4 KiB
Nim
165 lines
4.4 KiB
Nim
import os, strutils, streams, tables
|
|
|
|
import ast_parser
|
|
|
|
type
|
|
|
|
ValueKind = enum valNil, valInt, valString
|
|
|
|
# Representation of a value.
|
|
Value = object
|
|
case kind: ValueKind
|
|
of valNil: nil
|
|
of valInt: intVal: int
|
|
of valString: stringVal: string
|
|
|
|
# Range of binary operators.
|
|
BinaryOperator = range[nMultiply..nOr]
|
|
|
|
# Table of variables.
|
|
var variables: Table[string, Value]
|
|
|
|
type RunTimeError = object of CatchableError
|
|
|
|
#---------------------------------------------------------------------------------------------------
|
|
|
|
template newInt(val: typed): Value =
|
|
## Create an integer value.
|
|
Value(kind: valInt, intVal: val)
|
|
|
|
#---------------------------------------------------------------------------------------------------
|
|
|
|
proc interp(node: Node): Value =
|
|
## Interpret code starting at "node".
|
|
|
|
if node.isNil:
|
|
return Value(kind: valNil)
|
|
|
|
case node.kind
|
|
|
|
of nInteger:
|
|
result = Value(kind: valInt, intVal: node.intVal)
|
|
|
|
of nIdentifier:
|
|
if node.name notin variables:
|
|
raise newException(RunTimeError, "Variable {node.name} is not initialized.")
|
|
result = variables[node.name]
|
|
|
|
of nString:
|
|
result = Value(kind: valString, stringVal: node.stringVal)
|
|
|
|
of nAssign:
|
|
variables[node.left.name] = interp(node.right)
|
|
|
|
of nNegate:
|
|
result = newInt(-interp(node.left).intVal)
|
|
|
|
of nNot:
|
|
result = newInt(not interp(node.left).intVal)
|
|
|
|
of BinaryOperator.low..BinaryOperator.high:
|
|
|
|
let left = interp(node.left)
|
|
let right = interp(node.right)
|
|
|
|
case BinaryOperator(node.kind)
|
|
of nMultiply:
|
|
result = newInt(left.intVal * right.intVal)
|
|
of nDivide:
|
|
result = newInt(left.intVal div right.intVal)
|
|
of nMod:
|
|
result = newInt(left.intVal mod right.intVal)
|
|
of nAdd:
|
|
result = newInt(left.intVal + right.intVal)
|
|
of nSubtract:
|
|
result = newInt(left.intVal - right.intVal)
|
|
of nLess:
|
|
result = newInt(ord(left.intVal < right.intVal))
|
|
of nLessEqual:
|
|
result = newInt(ord(left.intVal <= right.intVal))
|
|
of nGreater:
|
|
result = newInt(ord(left.intVal > right.intVal))
|
|
of nGreaterEqual:
|
|
result = newInt(ord(left.intVal >= right.intVal))
|
|
of nEqual:
|
|
result = newInt(ord(left.intVal == right.intVal))
|
|
of nNotEqual:
|
|
result = newInt(ord(left.intVal != right.intVal))
|
|
of nAnd:
|
|
result = newInt(left.intVal and right.intVal)
|
|
of nOr:
|
|
result = newInt(left.intVal or right.intVal)
|
|
|
|
of nIf:
|
|
if interp(node.left).intVal != 0:
|
|
discard interp(node.right.left)
|
|
else:
|
|
discard interp(node.right.right)
|
|
|
|
of nWhile:
|
|
while interp(node.left).intVal != 0:
|
|
discard interp(node.right)
|
|
|
|
of nPrtc:
|
|
stdout.write(chr(interp(node.left).intVal))
|
|
|
|
of nPrti:
|
|
stdout.write(interp(node.left).intVal)
|
|
|
|
of nPrts:
|
|
stdout.write(interp(node.left).stringVal)
|
|
|
|
of nSequence:
|
|
discard interp(node.left)
|
|
discard interp(node.right)
|
|
|
|
#---------------------------------------------------------------------------------------------------
|
|
|
|
import re
|
|
|
|
proc loadAst(stream: Stream): Node =
|
|
## Load a linear AST and build a binary tree.
|
|
|
|
let line = stream.readLine().strip()
|
|
if line.startsWith(';'):
|
|
return nil
|
|
|
|
var fields = line.split(' ', 1)
|
|
let kind = parseEnum[NodeKind](fields[0])
|
|
if kind in {nIdentifier, nString, nInteger}:
|
|
if fields.len < 2:
|
|
raise newException(ValueError, "Missing value field for " & fields[0])
|
|
else:
|
|
fields[1] = fields[1].strip()
|
|
case kind
|
|
of nIdentifier:
|
|
return Node(kind: nIdentifier, name: fields[1])
|
|
of nString:
|
|
str = fields[1].replacef(re"([^\\])(\\n)", "$1\n").replace(r"\\", r"\").replace("\"", "")
|
|
return Node(kind: nString, stringVal: str)
|
|
of nInteger:
|
|
return Node(kind: nInteger, intVal: parseInt(fields[1]))
|
|
else:
|
|
if fields.len > 1:
|
|
raise newException(ValueError, "Extra field for " & fields[0])
|
|
|
|
let left = stream.loadAst()
|
|
let right = stream.loadAst()
|
|
result = newNode(kind, left, right)
|
|
|
|
#———————————————————————————————————————————————————————————————————————————————————————————————————
|
|
|
|
var stream: Stream
|
|
var toClose = false
|
|
|
|
if paramCount() < 1:
|
|
stream = newFileStream(stdin)
|
|
else:
|
|
stream = newFileStream(paramStr(1))
|
|
toClose = true
|
|
|
|
let ast = loadAst(stream)
|
|
if toClose: stream.close()
|
|
|
|
discard ast.interp()
|