|
|
@ -5,6 +5,7 @@ import re |
|
|
|
|
|
|
|
module = sys.argv[1] |
|
|
|
|
|
|
|
|
|
|
|
class Token: |
|
|
|
def __init__(self, kind, value, line_nr): |
|
|
|
self.kind = kind |
|
|
@ -19,22 +20,27 @@ class Token: |
|
|
|
|
|
|
|
|
|
|
|
class Tokenizer: |
|
|
|
TOKEN_REGEX = re.compile('|'.join('(?P<%s>%s)' % pair for pair in [ |
|
|
|
('HEX', r'0x[0-9A-Fa-f]+'), |
|
|
|
('ASSIGN', r'='), |
|
|
|
('ALABEL', r'\d+\$'), |
|
|
|
('NUMBER', r'\d+(\.\d*)?'), |
|
|
|
('COMMENT', r';[^\n]*'), |
|
|
|
('LABEL', r':+'), |
|
|
|
('EXPR', r'#'), |
|
|
|
('STRING', '"[^"]*"'), |
|
|
|
('DIRECTIVE', r'\.[A-Za-z_][A-Za-z0-9_\.]*'), |
|
|
|
('ID', r'[A-Za-z_][A-Za-z0-9_\.]*'), |
|
|
|
('OP', r'[+\-*/,\(\)<>]'), |
|
|
|
('NEWLINE', r'\n'), |
|
|
|
('SKIP', r'[ \t]+'), |
|
|
|
('MISMATCH', r'.'), |
|
|
|
])) |
|
|
|
TOKEN_REGEX = re.compile( |
|
|
|
"|".join( |
|
|
|
"(?P<%s>%s)" % pair |
|
|
|
for pair in [ |
|
|
|
("HEX", r"0x[0-9A-Fa-f]+"), |
|
|
|
("ASSIGN", r"="), |
|
|
|
("ALABEL", r"\d+\$"), |
|
|
|
("NUMBER", r"\d+(\.\d*)?"), |
|
|
|
("COMMENT", r";[^\n]*"), |
|
|
|
("LABEL", r":+"), |
|
|
|
("EXPR", r"#"), |
|
|
|
("STRING", '"[^"]*"'), |
|
|
|
("DIRECTIVE", r"\.[A-Za-z_][A-Za-z0-9_\.]*"), |
|
|
|
("ID", r"[A-Za-z_][A-Za-z0-9_\.]*"), |
|
|
|
("OP", r"[+\-*/,\(\)<>]"), |
|
|
|
("NEWLINE", r"\n"), |
|
|
|
("SKIP", r"[ \t]+"), |
|
|
|
("MISMATCH", r"."), |
|
|
|
] |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
def __init__(self, code): |
|
|
|
self.__tokens = [] |
|
|
@ -42,21 +48,21 @@ class Tokenizer: |
|
|
|
for mo in self.TOKEN_REGEX.finditer(code): |
|
|
|
kind = mo.lastgroup |
|
|
|
value = mo.group() |
|
|
|
if kind == 'MISMATCH': |
|
|
|
print("ERR:", code.split("\n")[line_num-1]) |
|
|
|
if kind == "MISMATCH": |
|
|
|
print("ERR:", code.split("\n")[line_num - 1]) |
|
|
|
raise RuntimeError("Syntax error on line: %d: %s\n%s", line_num, value) |
|
|
|
elif kind == 'SKIP': |
|
|
|
elif kind == "SKIP": |
|
|
|
pass |
|
|
|
else: |
|
|
|
if kind == 'HEX': |
|
|
|
if kind == "HEX": |
|
|
|
value = "$" + value[2:] |
|
|
|
if kind == 'ALABEL': |
|
|
|
if kind == "ALABEL": |
|
|
|
value = "._ANNO_" + value[:-1] |
|
|
|
kind = "ID" |
|
|
|
self.__tokens.append(Token(kind, value, line_num)) |
|
|
|
if kind == 'NEWLINE': |
|
|
|
if kind == "NEWLINE": |
|
|
|
line_num += 1 |
|
|
|
self.__tokens.append(Token('NEWLINE', '\n', line_num)) |
|
|
|
self.__tokens.append(Token("NEWLINE", "\n", line_num)) |
|
|
|
|
|
|
|
def peek(self): |
|
|
|
return self.__tokens[0] |
|
|
@ -74,67 +80,107 @@ class Tokenizer: |
|
|
|
def __bool__(self): |
|
|
|
return bool(self.__tokens) |
|
|
|
|
|
|
|
|
|
|
|
tok = Tokenizer(sys.stdin.read()) |
|
|
|
|
|
|
|
|
|
|
|
def processExpression(): |
|
|
|
while True: |
|
|
|
t = tok.peek() |
|
|
|
if t.isA('EXPR'): |
|
|
|
if t.isA("EXPR"): |
|
|
|
tok.pop() |
|
|
|
t = tok.peek() |
|
|
|
if t.isA('OP', '<'): |
|
|
|
sys.stdout.write('LOW') |
|
|
|
if t.isA("OP", "<"): |
|
|
|
sys.stdout.write("LOW") |
|
|
|
tok.pop() |
|
|
|
t = tok.peek() |
|
|
|
if t.isA('OP', '>'): |
|
|
|
sys.stdout.write('HIGH') |
|
|
|
if t.isA("OP", ">"): |
|
|
|
sys.stdout.write("HIGH") |
|
|
|
tok.pop() |
|
|
|
t = tok.peek() |
|
|
|
if t.isA('OP', '('): |
|
|
|
if t.isA("OP", "("): |
|
|
|
tok.pop() |
|
|
|
sys.stdout.write('(') |
|
|
|
sys.stdout.write("(") |
|
|
|
processExpression() |
|
|
|
t = tok.pop() |
|
|
|
assert t.isA('OP', ')') |
|
|
|
sys.stdout.write(')') |
|
|
|
if t.isA('ID') and t.value.startswith("b_"): |
|
|
|
assert t.isA("OP", ")") |
|
|
|
sys.stdout.write(")") |
|
|
|
if t.isA("ID") and t.value.startswith("b_"): |
|
|
|
t.value = "BANK(%s)" % (t.value[1:]) |
|
|
|
if t.isA('NEWLINE') or t.isA('OP', ')') or t.isA('OP', ','): |
|
|
|
if t.isA("NEWLINE") or t.isA("OP", ")") or t.isA("OP", ","): |
|
|
|
break |
|
|
|
sys.stdout.write(t.value) |
|
|
|
tok.pop() |
|
|
|
|
|
|
|
|
|
|
|
def processParameter(): |
|
|
|
t = tok.pop() |
|
|
|
if t.isA('EXPR'): |
|
|
|
if t.isA("EXPR"): |
|
|
|
processExpression() |
|
|
|
elif t.isA('NEWLINE'): |
|
|
|
elif t.isA("NEWLINE"): |
|
|
|
return |
|
|
|
elif t.isA('ID') or t.isA('NUMBER') or t.isA('HEX'): |
|
|
|
elif t.isA("ID") or t.isA("NUMBER") or t.isA("HEX"): |
|
|
|
sys.stdout.write(t.value) |
|
|
|
elif t.isA('OP', '('): |
|
|
|
elif t.isA("OP", "("): |
|
|
|
sys.stdout.write("[") |
|
|
|
processExpression() |
|
|
|
t = tok.pop() |
|
|
|
while not t.isA('OP', ')'): |
|
|
|
while not t.isA("OP", ")"): |
|
|
|
sys.stdout.write(t.value) |
|
|
|
t = tok.pop() |
|
|
|
assert t.isA('OP', ')'), t |
|
|
|
assert t.isA("OP", ")"), t |
|
|
|
sys.stdout.write("]") |
|
|
|
else: |
|
|
|
raise Exception(t) |
|
|
|
|
|
|
|
|
|
|
|
class AnyStr: |
|
|
|
def __eq__(self, other) -> bool: |
|
|
|
return isinstance(other, str) |
|
|
|
|
|
|
|
|
|
|
|
BYTE_ADDR_PATTERN = [ |
|
|
|
"(", |
|
|
|
AnyStr(), |
|
|
|
"+", |
|
|
|
"0", |
|
|
|
")", |
|
|
|
",", |
|
|
|
"(", |
|
|
|
"(", |
|
|
|
AnyStr(), |
|
|
|
"+", |
|
|
|
"0", |
|
|
|
")", |
|
|
|
">", |
|
|
|
">", |
|
|
|
"8", |
|
|
|
")", |
|
|
|
"\n", |
|
|
|
] |
|
|
|
ID_PATTERN_IDX1 = 1 |
|
|
|
ID_PATTERN_IDX2 = 8 |
|
|
|
|
|
|
|
|
|
|
|
def processByteAddr(): |
|
|
|
tokens = [tok.pop().value for _ in range(17)] |
|
|
|
assert tokens == BYTE_ADDR_PATTERN |
|
|
|
assert tokens[ID_PATTERN_IDX1] == tokens[ID_PATTERN_IDX2] |
|
|
|
addr = tokens[ID_PATTERN_IDX1] |
|
|
|
sys.stdout.write(f"LOW({addr}), HIGH({addr})") |
|
|
|
|
|
|
|
|
|
|
|
while tok: |
|
|
|
start = tok.pop() |
|
|
|
if start.isA('NEWLINE'): |
|
|
|
if start.isA("NEWLINE"): |
|
|
|
pass |
|
|
|
elif start.isA('COMMENT'): |
|
|
|
elif start.isA("COMMENT"): |
|
|
|
print(start.value) |
|
|
|
elif start.isA('DIRECTIVE'): |
|
|
|
if start.value in {'.module', '.optsdcc', '.globl'}: |
|
|
|
while not tok.pop().isA('NEWLINE'): |
|
|
|
elif start.isA("DIRECTIVE"): |
|
|
|
if start.value in {".module", ".optsdcc", ".globl"}: |
|
|
|
while not tok.pop().isA("NEWLINE"): |
|
|
|
pass |
|
|
|
elif start.value == '.area': |
|
|
|
elif start.value == ".area": |
|
|
|
area_name = tok.pop().value |
|
|
|
if area_name == "_DATA": |
|
|
|
print('SECTION "%s_%s", WRAM0' % (module, area_name)) |
|
|
@ -145,7 +191,10 @@ while tok: |
|
|
|
elif area_name == "_CODE": |
|
|
|
print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name)) |
|
|
|
elif area_name.startswith("_CODE_"): |
|
|
|
print('SECTION FRAGMENT "%s_%s", ROMX, BANK[%d]' % (module, area_name, int(area_name[6:]))) |
|
|
|
print( |
|
|
|
'SECTION FRAGMENT "%s_%s", ROMX, BANK[%d]' |
|
|
|
% (module, area_name, int(area_name[6:])) |
|
|
|
) |
|
|
|
elif area_name == "_CABS": |
|
|
|
print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name)) |
|
|
|
elif area_name == "_GSINIT": |
|
|
@ -156,72 +205,77 @@ while tok: |
|
|
|
print('SECTION FRAGMENT "code_%s", ROMX' % (module)) |
|
|
|
else: |
|
|
|
raise Exception(area_name) |
|
|
|
while not tok.pop().isA('NEWLINE'): |
|
|
|
while not tok.pop().isA("NEWLINE"): |
|
|
|
pass |
|
|
|
elif start.value == '.ds': |
|
|
|
elif start.value == ".ds": |
|
|
|
sys.stdout.write("ds ") |
|
|
|
processExpression() |
|
|
|
sys.stdout.write("\n") |
|
|
|
elif start.value == '.ascii': |
|
|
|
elif start.value == ".ascii": |
|
|
|
sys.stdout.write("db ") |
|
|
|
sys.stdout.write(tok.pop().value) |
|
|
|
sys.stdout.write("\n") |
|
|
|
elif start.value == '.db': |
|
|
|
elif start.value == ".db": |
|
|
|
sys.stdout.write("db ") |
|
|
|
processParameter() |
|
|
|
while tok.peek().isA('OP', ','): |
|
|
|
while tok.peek().isA("OP", ","): |
|
|
|
sys.stdout.write(",") |
|
|
|
tok.pop() |
|
|
|
processParameter() |
|
|
|
sys.stdout.write("\n") |
|
|
|
elif start.value == '.dw': |
|
|
|
elif start.value == ".byte": |
|
|
|
sys.stdout.write("db ") |
|
|
|
processByteAddr() |
|
|
|
sys.stdout.write("\n") |
|
|
|
elif start.value == ".dw": |
|
|
|
sys.stdout.write("dw ") |
|
|
|
processParameter() |
|
|
|
while tok.peek().isA('OP', ','): |
|
|
|
while tok.peek().isA("OP", ","): |
|
|
|
sys.stdout.write(",") |
|
|
|
tok.pop() |
|
|
|
processParameter() |
|
|
|
sys.stdout.write("\n") |
|
|
|
elif start.value == '.incbin': |
|
|
|
elif start.value == ".incbin": |
|
|
|
sys.stdout.write("incbin ") |
|
|
|
while not tok.peek().isA('NEWLINE'): |
|
|
|
while not tok.peek().isA("NEWLINE"): |
|
|
|
sys.stdout.write(tok.pop().value) |
|
|
|
sys.stdout.write("\n") |
|
|
|
tok.pop() |
|
|
|
else: |
|
|
|
sys.stderr.write(f"{module}: error: could not parse\n") |
|
|
|
raise Exception(start, tok.peek()) |
|
|
|
elif start.isA('ID'): |
|
|
|
if tok.peek().isA('ASSIGN'): |
|
|
|
elif start.isA("ID"): |
|
|
|
if tok.peek().isA("ASSIGN"): |
|
|
|
tok.pop() |
|
|
|
sys.stdout.write("%s = " % (start.value)) |
|
|
|
processExpression() |
|
|
|
sys.stdout.write("\n") |
|
|
|
elif tok.peek().isA('LABEL'): |
|
|
|
elif tok.peek().isA("LABEL"): |
|
|
|
print("%s%s" % (start.value, tok.pop().value)) |
|
|
|
elif start.isA('ID', 'ldhl'): |
|
|
|
tok.expect('ID', 'sp') |
|
|
|
tok.expect('OP', ',') |
|
|
|
elif start.isA("ID", "ldhl"): |
|
|
|
tok.expect("ID", "sp") |
|
|
|
tok.expect("OP", ",") |
|
|
|
sys.stdout.write("ld hl, sp + ") |
|
|
|
processParameter() |
|
|
|
sys.stdout.write("\n") |
|
|
|
elif start.isA('ID', 'lda'): |
|
|
|
tok.expect('ID', 'hl') |
|
|
|
tok.expect('OP', ',') |
|
|
|
elif start.isA("ID", "lda"): |
|
|
|
tok.expect("ID", "hl") |
|
|
|
tok.expect("OP", ",") |
|
|
|
t = tok.pop() |
|
|
|
assert t.isA('NUMBER') or t.isA('HEX') |
|
|
|
tok.expect('OP', '(') |
|
|
|
tok.expect('ID', 'sp') |
|
|
|
tok.expect('OP', ')') |
|
|
|
assert t.isA("NUMBER") or t.isA("HEX") |
|
|
|
tok.expect("OP", "(") |
|
|
|
tok.expect("ID", "sp") |
|
|
|
tok.expect("OP", ")") |
|
|
|
sys.stdout.write("ld hl, sp + %s\n" % (t.value)) |
|
|
|
else: |
|
|
|
sys.stdout.write("%s " % (start.value)) |
|
|
|
if not tok.peek().isA('NEWLINE'): |
|
|
|
if not tok.peek().isA("NEWLINE"): |
|
|
|
processParameter() |
|
|
|
if tok.peek().isA('OP', ','): |
|
|
|
if tok.peek().isA("OP", ","): |
|
|
|
tok.pop() |
|
|
|
sys.stdout.write(", ") |
|
|
|
processParameter() |
|
|
|
sys.stdout.write("\n") |
|
|
|
tok.expect('NEWLINE') |
|
|
|
tok.expect("NEWLINE") |
|
|
|
else: |
|
|
|
raise Exception(start) |