Skip to content

Commit

Permalink
Make symbol table an explicit parameter of typechecker and preprocessor
Browse files Browse the repository at this point in the history
  • Loading branch information
iafisher committed Dec 14, 2018
1 parent 91a6c2d commit 8f0537b
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 28 deletions.
7 changes: 5 additions & 2 deletions hera/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from . import config
from .parser import parse
from .preprocessor import preprocess
from .symtab import get_symtab
from .typechecker import typecheck
from .utils import emit_error, print_register_debug, HERAError
from .vm import VirtualMachine
Expand Down Expand Up @@ -96,11 +97,13 @@ def execute_program(program, *, lines_to_exec=None, no_dump_state=False, vm=None
except HERAError as e:
emit_error(str(e), line=e.line, column=e.column, exit=True)

typecheck(program)
symtab = get_symtab(program)

typecheck(program, symtab)
if config.SEEN_ERROR:
sys.exit(3)

program = preprocess(program)
program = preprocess(program, symtab)
if config.SEEN_ERROR:
sys.exit(3)

Expand Down
15 changes: 5 additions & 10 deletions hera/preprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
from lark import Token

from .parser import Op
from .symtab import get_symtab
from .utils import copy_token, emit_error, is_symbol, to_u16


def preprocess(program):
def preprocess(program, symtab):
"""Preprocess the program (a list of Op objects) into valid input for the
exec_many method on the VirtualMachine class.
Expand All @@ -19,22 +18,18 @@ def preprocess(program):
- Resolves labels into their line numbers.
"""
program = [op for old_op in program for op in convert(old_op)]

labels = get_symtab(program)

program = [substitute_label(op, labels) for op in program]
program = [substitute_label(op, symtab) for op in program]
program = [op for op in program if op.name not in ("LABEL", "DLABEL", "CONSTANT")]

return program


def substitute_label(op, labels):
def substitute_label(op, symtab):
"""Substitute any label in the instruction with its concrete value."""
if op.name == "SETLO" and is_symbol(op.args[1]):
d, v = op.args
name = copy_token("SETLO", op.name)
try:
label = labels[v]
label = symtab[v]
except KeyError:
emit_error(
"undefined symbol `{}`".format(v), line=op.name.line, column=v.column
Expand All @@ -44,7 +39,7 @@ def substitute_label(op, labels):
elif op.name == "SETHI" and is_symbol(op.args[1]):
d, v = op.args
name = copy_token("SETHI", op.name)
return Op(name, [d, labels[v] >> 8])
return Op(name, [d, symtab[v] >> 8])
else:
return op

Expand Down
3 changes: 2 additions & 1 deletion hera/symtab.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .preprocessor import convert
from .utils import emit_error


Expand Down Expand Up @@ -25,7 +26,7 @@ def get_symtab(program):
elif op.name == "LP_STRING":
dc += len(op.args[0]) + 1
else:
pc += 1
pc += len(convert(op))

if dc >= 0xFFFF and odc < 0xFFFF:
emit_error("past the end of available memory", line=op.name.line)
Expand Down
10 changes: 5 additions & 5 deletions hera/typechecker.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
DATA_STATEMENTS = set(["CONSTANT", "DLABEL", "INTEGER", "LP_STRING", "DSKIP"])


def typecheck(program):
def typecheck(program, symtab):
"""Type-check the program and emit errors as appropriate."""
end_of_data = False
for op in program:
Expand All @@ -21,14 +21,14 @@ def typecheck(program):
else:
if op.name in DATA_STATEMENTS:
emit_error("data statement after instruction", line=op.name.line)
typecheck_one(op)
typecheck_one(op, symtab)


def typecheck_one(op):
def typecheck_one(op, symtab):
"""Type-check a single HERA op and emit errors as appropriate."""
params = _types_map.get(op.name)
if params is not None:
check_types(op.name, params, op.args)
check_types(op.name, params, op.args, symtab)
else:
emit_error("unknown instruction `{}`".format(op.name), line=op.name.line)

Expand Down Expand Up @@ -131,7 +131,7 @@ def typecheck_one(op):
}


def check_types(name, expected, got):
def check_types(name, expected, got, symtab):
"""Verify that the given args match the expected ones and emit errors as
appropriate. `name` is the name of the HERA op, as a Token object. `expected` is a
tuple or list of constants (REGISTER, U16, etc., defined above) representing the
Expand Down
15 changes: 5 additions & 10 deletions test/test_preprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@
from lark import Token

from hera.parser import Op
from hera.preprocessor import (
convert,
convert_set,
preprocess,
substitute_label,
)
from hera.preprocessor import convert, convert_set, preprocess, substitute_label
from hera.utils import HERAError, IntToken


Expand Down Expand Up @@ -167,8 +162,8 @@ def test_convert_not():


def test_preprocess_constant():
program = [
Op(Token("SYMBOL", "CONSTANT"), [Token("SYMBOL", "n"), IntToken(100)]),
Op(Token("SYMBOL", "SET"), [R("R1"), Token("SYMBOL", "n")]),
program = [Op(Token("SYMBOL", "SET"), [R("R1"), Token("SYMBOL", "n")])]
assert preprocess(program, {"n": 100}) == [
Op("SETLO", ["R1", 100]),
Op("SETHI", ["R1", 0]),
]
assert preprocess(program) == [Op("SETLO", ["R1", 100]), Op("SETHI", ["R1", 0])]
7 changes: 7 additions & 0 deletions test/test_typechecker.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import functools
import pytest
from unittest.mock import patch

Expand All @@ -17,6 +18,12 @@
from hera.utils import HERAError, IntToken


# TODO: Get rid of these and explicitly pass the empty symbol table in.
typecheck = functools.partial(typecheck, symtab={})
typecheck_one = functools.partial(typecheck_one, symtab={})
check_types = functools.partial(check_types, symtab={})


def R(s):
return Token("REGISTER", s)

Expand Down

0 comments on commit 8f0537b

Please sign in to comment.