Files
langkit/doc/kalint.py
Laurent Thévenoux e26e3f78dc doc: Apply stylechecks on kalint.py
Apply proper coding style.
2021-09-16 16:52:20 +02:00

126 lines
4.1 KiB
Python

import libkaleidoscopelang as lkl
import sys
class ExecutionError(Exception):
def __init__(self, sloc_range, message):
self.sloc_range = sloc_range
self.message = message
class Interpreter(object):
# Mapping: enumerators for the Operator type -> callables to perform the
# operations themselves.
BINOPS = {lkl.OperatorPlus: lambda x, y: x + y,
lkl.OperatorMinus: lambda x, y: x - y,
lkl.OperatorMult: lambda x, y: x * y,
lkl.OperatorDiv: lambda x, y: x / y}
def __init__(self):
# Mapping: function name -> FunctionNode instance
self.functions = {}
# Method for the Interpreter class
def execute(self, ast):
assert isinstance(ast, lkl.KaleidoscopeNodeList)
for node in ast:
if isinstance(node, lkl.FunctionNode):
self.functions[node.f_proto.f_name.text] = node
elif isinstance(node, lkl.ExternDecl):
raise ExecutionError(
node.sloc_range,
'External declarations are not supported'
)
elif isinstance(node, lkl.Expr):
print(self.evaluate(node))
else:
# There should be no other kind of node at top-level
assert False
# Method for the Interpreter class
def evaluate(self, node, env=None):
if env is None:
env = {}
if isinstance(node, lkl.Number):
return float(node.text)
elif isinstance(node, lkl.Identifier):
try:
return env[node.text]
except KeyError:
raise ExecutionError(
node.sloc_range,
'Unknown identifier: {}'.format(node.text)
)
elif isinstance(node, lkl.BinaryExpr):
lhs = self.evaluate(node.f_lhs, env)
rhs = self.evaluate(node.f_rhs, env)
return self.BINOPS[type(node.f_op)](lhs, rhs)
elif isinstance(node, lkl.CallExpr):
name = node.f_callee.text
try:
func = self.functions[name]
except KeyError:
raise ExecutionError(
node.f_callee.sloc_range,
'No such function: "{}"'.format(name)
)
formals = func.f_proto.f_args
actuals = node.f_args
# Check that the call is consistent with the function prototype
if len(formals) != len(actuals):
raise ExecutionError(
node.sloc_range,
'"{}" expects {} arguments, but got {} ones'.format(
node.f_callee.f_name.text,
len(formals), len(actuals)
)
)
# Evaluate arguments and then evaluate the call itself
new_env = {f.text: self.evaluate(a, env)
for f, a in zip(formals, actuals)}
result = self.evaluate(func.f_body, new_env)
return result
else:
# There should be no other kind of node in expressions
assert False
def print_error(filename, sloc_range, message):
line = sloc_range.start.line
column = sloc_range.start.column
print('In {}, line {}:'.format(filename, line), file=sys.stderr)
with open(filename) as f:
# Get the corresponding line in the source file and display it
for _ in range(sloc_range.start.line - 1):
f.readline()
print(' {}'.format(f.readline().rstrip()), file=sys.stderr)
print(' {}^'.format(' ' * (column - 1)), file=sys.stderr)
print('Error: {}'.format(message), file=sys.stderr)
def execute(filename):
ctx = lkl.AnalysisContext()
unit = ctx.get_from_file(filename)
if unit.diagnostics:
for diag in unit.diagnostics:
print_error(filename, diag.sloc_range, diag.message)
sys.exit(1)
try:
Interpreter().execute(unit.root)
except ExecutionError as exc:
print_error(filename, exc.sloc_range, exc.message)
sys.exit(1)
execute(sys.argv[1])