73 lines
1.6 KiB
Python
73 lines
1.6 KiB
Python
import dataclasses
|
|
import fractions
|
|
|
|
from parser import BinOp, UnaryOp, Atom
|
|
from lexer import kinds, Token
|
|
from typesystem import Type, UnknownType, Rational, Integer
|
|
|
|
@dataclasses.dataclass
|
|
class Literal:
|
|
value: int | fractions.Fraction
|
|
|
|
@dataclasses.dataclass
|
|
class Application:
|
|
op: str
|
|
inputs: [int]
|
|
|
|
@dataclasses.dataclass
|
|
class Node:
|
|
value: Application | Literal
|
|
type: Type
|
|
|
|
def toIr(tree):
|
|
nodes = []
|
|
|
|
def newNode(value, type = None):
|
|
if type is None:
|
|
type = UnknownType()
|
|
nodes.append(Node(
|
|
value=value,
|
|
type=type
|
|
))
|
|
return len(nodes) - 1
|
|
|
|
def numberLiteral(string):
|
|
if '.' in string:
|
|
return newNode(Literal(fractions.Fraction(string)), Rational())
|
|
else:
|
|
value = int(string)
|
|
return newNode(Literal(value), Integer(value, value))
|
|
|
|
def inner(tree):
|
|
if isinstance(tree, BinOp):
|
|
left = inner(tree.left)
|
|
right = inner(tree.right)
|
|
return newNode(Application(
|
|
op=tree.operator.text,
|
|
inputs=[left, right]
|
|
))
|
|
elif isinstance(tree, UnaryOp):
|
|
return newNode(Application(
|
|
op=tree.operator.text,
|
|
inputs=[inner(tree.inner)]
|
|
))
|
|
else:
|
|
assert isinstance(tree, Atom)
|
|
assert isinstance(tree.value, Token)
|
|
assert tree.value.kind == kinds.number
|
|
return numberLiteral(tree.value.text)
|
|
|
|
result = inner(tree)
|
|
return nodes, result
|
|
|
|
def evaluate(context, nodes):
|
|
for node in nodes:
|
|
if isinstance(node.value, Literal):
|
|
continue
|
|
assert isinstance(node.value, Application)
|
|
assert all(isinstance(nodes[i].value, Literal) for i in node.value.inputs)
|
|
args = [nodes[i].value.value for i in node.value.inputs]
|
|
result = context[node.value.op](*args)
|
|
node.value = Literal(result)
|
|
|
|
# TODO: unit tests
|