import sys import re ## Need to convert asxxxx syntax to rgbds syntax module = sys.argv[1] class Token: def __init__(self, kind, value, line_nr): self.kind = kind self.value = value self.line_nr = line_nr def isA(self, kind, value=None): return self.kind == kind and (value is None or value == self.value) def __repr__(self): return "[%s:%s:%d]" % (self.kind, self.value, self.line_nr) 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"."), ] ) ) def __init__(self, code): self.__tokens = [] line_num = 1 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]) raise RuntimeError("Syntax error on line: %d: %s\n%s", line_num, value) elif kind == "SKIP": pass else: if kind == "HEX": value = "$" + value[2:] if kind == "ALABEL": value = "._ANNO_" + value[:-1] kind = "ID" self.__tokens.append(Token(kind, value, line_num)) if kind == "NEWLINE": line_num += 1 self.__tokens.append(Token("NEWLINE", "\n", line_num)) def peek(self): return self.__tokens[0] def pop(self): return self.__tokens.pop(0) def expect(self, kind, value=None): pop = self.pop() if not pop.isA(kind, value): if value is not None: raise SyntaxError("%s != %s:%s" % (pop, kind, value)) raise SyntaxError("%s != %s" % (pop, kind)) def __bool__(self): return bool(self.__tokens) tok = Tokenizer(sys.stdin.read()) global_names = set() def processExpression(): while True: t = tok.peek() if t.isA("EXPR"): tok.pop() t = tok.peek() if t.isA("OP", "<"): sys.stdout.write("LOW") tok.pop() t = tok.peek() if t.isA("OP", ">"): sys.stdout.write("HIGH") tok.pop() t = tok.peek() if t.isA("OP", "("): tok.pop() sys.stdout.write("(") processExpression() t = tok.pop() 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", ","): break sys.stdout.write(t.value) tok.pop() def processParameter(): t = tok.pop() if t.isA("EXPR"): processExpression() elif t.isA("NEWLINE"): return elif t.isA("ID") or t.isA("NUMBER") or t.isA("HEX"): sys.stdout.write(t.value) elif t.isA("OP", "("): sys.stdout.write("[") processExpression() t = tok.pop() while not t.isA("OP", ")"): sys.stdout.write(t.value) t = tok.pop() 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"): pass elif start.isA("COMMENT"): print(start.value) elif start.isA("DIRECTIVE"): if start.value in {".module", ".optsdcc"}: while not tok.pop().isA("NEWLINE"): pass elif start.value == ".globl": global_name = tok.pop().value global_names.add(global_name) assert tok.pop().isA("NEWLINE") elif start.value == ".area": area_name = tok.pop().value if area_name == "_DATA": print('SECTION "%s_%s", WRAM0' % (module, area_name)) elif area_name == "_DABS": print('SECTION "%s_%s", SRAM' % (module, area_name)) elif area_name == "_HOME": print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name)) 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:])) ) elif area_name == "_CABS": print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name)) elif area_name == "_GSINIT": print('SECTION FRAGMENT "GSINIT", ROMX, BANK[1]') elif area_name == "_GSFINAL": print('SECTION FRAGMENT "GSFINAL", ROMX, BANK[1]') elif area_name == "_auto": print('SECTION FRAGMENT "code_%s", ROMX' % (module)) else: raise Exception(area_name) while not tok.pop().isA("NEWLINE"): pass elif start.value == ".ds": sys.stdout.write("ds ") processExpression() sys.stdout.write("\n") elif start.value == ".ascii": sys.stdout.write("db ") sys.stdout.write(tok.pop().value) sys.stdout.write("\n") elif start.value == ".db": sys.stdout.write("db ") processParameter() while tok.peek().isA("OP", ","): sys.stdout.write(",") tok.pop() processParameter() sys.stdout.write("\n") 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", ","): sys.stdout.write(",") tok.pop() processParameter() sys.stdout.write("\n") elif start.value == ".incbin": sys.stdout.write("incbin ") 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"): tok.pop() sys.stdout.write("%s = " % (start.value)) processExpression() sys.stdout.write("\n") elif tok.peek().isA("LABEL"): label_suffix = tok.pop().value if label_suffix == ":" and start.value in global_names: label_suffix = "::" print("%s%s" % (start.value, label_suffix)) 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", ",") t = tok.pop() 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"): processParameter() if tok.peek().isA("OP", ","): tok.pop() sys.stdout.write(", ") processParameter() sys.stdout.write("\n") tok.expect("NEWLINE") else: raise Exception(start)