aboutsummaryrefslogtreecommitdiffstats
path: root/atk16_asm/parser.py
blob: 4823450a6ee4d624d90e4bc628c7f27ddff219d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
from typing import Callable, Literal, cast
from dataclasses import dataclass
import re

RegisterValue = Literal["RA", "RB", "RC", "RD", "RE", "RF", "RG", "RH"]
REGISTERS: list[RegisterValue] = ["RA", "RB", "RC", "RD", "RE", "RF", "RG", "RH"]
@dataclass
class RegisterRef:
  """Reference to a register, e.g. RA"""
  reg: RegisterValue

  def __str__(self) -> str:
    return self.reg

@dataclass
class Immediate:
  """Immediate value, e.g. 0x1234"""
  value: int
  base: Literal["dec", "hex", "bin"]

  def __str__(self) -> str:
    if self.base == "dec":
      return str(self.value)
    elif self.base == "hex":
      return f"0x{self.value:>04x}"
    elif self.base == "bin":
      return f"0b{self.value:>016b}"

    raise Exception("Invalid base: {self.base}")

@dataclass
class Label:
  """Label declaration, e.g. @loop"""
  label: str

  def __str__(self) -> str:
    return f"@{self.label}"

@dataclass
class LabelRef:
  """Label reference, e.g. &loop"""
  label: str

  def __str__(self) -> str:
    return f"&{self.label}"

FlagValue = Literal[0, 1, 2, 3]
FLAGS = ["carry", "overflow", "zero", "sign"]
@dataclass
class Flag:
  """ALU Flag, e.g. zero"""
  num: FlagValue

  @staticmethod
  def from_str(flag: str) -> "Flag":
    return Flag(cast(FlagValue, FLAGS.index(flag)))

  def __str__(self) -> str:
    return FLAGS[self.num]

ALUCodeValue = Literal[0, 1, 2, 3, 4, 5, 6, 7]
ALU_CODES = ["al_plus", "al_minus", "al_and", "al_or", "al_xor", "al_sll", "al_slr", "al_sar"]
@dataclass
class ALUCode:
  """ALU operation code, e.g. al_plus"""
  code: ALUCodeValue

  @staticmethod
  def from_str(code: str) -> "ALUCode":
    return ALUCode(cast(ALUCodeValue, ALU_CODES.index(code)))

  def __str__(self) -> str:
    return ALU_CODES[self.code]

@dataclass
class Expr:
  """Python expression that can be evaluated to an immediate value"""
  expr: str

  def __str__(self) -> str:
    return f"${{{self.expr}}}"

@dataclass
class Directive:
  """Directive, e.g. #address"""
  directive: str

  def __str__(self) -> str:
    return f"#${self.directive}"

@dataclass
class StringLiteral:
  """String literal, e.g. "hello world" """
  string: str

  def __str__(self) -> str:
    return f'"{self.string}"'

@dataclass
class Symbol:
  """Symbol, e.g. loop"""
  symbol: str

  def __str__(self) -> str:
    return self.symbol

Term = RegisterRef | Immediate | LabelRef | Label | Flag | ALUCode | Expr | Directive | StringLiteral | Symbol

symbol_pattern = "[a-zA-Z_][a-zA-Z0-9_]*"

def parse(tokens: list[str]) -> list[Term]:
  def parse_token(term: str, idx: int) -> Term:
    if term in REGISTERS:
      return RegisterRef(term)
    elif term in FLAGS:
      return Flag.from_str(term)
    elif term.startswith("${") and term.endswith("}"):
      return Expr(term[2:-1])
    elif term.startswith("\"") and term.endswith("\""):
      return StringLiteral(term[1:-1])
    elif term.startswith("&"):
      return LabelRef(term[1:])
    elif term.startswith("@"):
      return Label(term[1:])
    elif term.startswith("#"):
      return Directive(term[1:])
    elif term.startswith("0x"):
      return Immediate(int(term.replace("_", ""), 16), "hex")
    elif term.startswith("0b"):
      return Immediate(int(term.replace("_", ""), 2), "bin")
    elif term.isdigit():
      return Immediate(int(term.replace("_", ""), 10), "dec")
    elif re.match(symbol_pattern, term) is not None:
      return Symbol(term)
    else:
      raise Exception(f"Invalid term \"{term}\" at index {idx} in {tokens}")

  return [parse_token(token, idx) for idx, token in enumerate(tokens)]