def test_assembly_parser_error(symbol_table: f1_t) -> None: # unknown token test = Parser('FAIL\nADD B #$10\n', symbol_table('FAIL\nADD B #$10\n')) with pytest.raises(AssemblerParserError): test.take(Mnemonic.T_ADD) # unexpected test = Parser('ADD B #$10\n', symbol_table('ADD B #$10\n')) with pytest.raises(AssemblerParserError): test.take(Token.T_VARIABLE)
def operand_state_machine(parser: Parser, operands: Deque[yylex_t], mode_stack: List[AddressingMode]) -> AddressingMode: """Get addressing mode and validate instruction operands Runs a parser combinator on the operands based on the amount of operands for an instruction and the operand types. Builds a stack of addressing modes by running on each n operand. The final instruction addressing mode is thus determined by the n_0 operand since the operand `deque` is provided in reverse order. By running for each operand recursively for `n-1` we validate order of operands state sets and their respective type and addressing modes at once. """ size: int = len(operands) if size > 0: if size > 1 and operands[1]['token'] == Token.T_COMMA: del operands[1] test = operands[0]['token'] mode: Mode_T = match( len(operands), 3, lambda k: AddressingMode.IDX if operands[0]['token'] == Register.T_X else ('error', Register.T_X.name, test), 2, lambda k: match( test, Token.T_IMM_UINT8, AddressingMode.IMM, Token. T_IMM_UINT16, AddressingMode.IMM, Token.T_DIR_ADDR_UINT8, AddressingMode.DIR, Token.T_EXT_ADDR_UINT16, AddressingMode. EXT, Register.T_X, AddressingMode.IDX, _, ('error', ', '.join( list(map(lambda x: x.name, Second_Operand_States))), test )), 1, lambda k: match(test, Token.T_IMM_UINT16, AddressingMode.IMM, Token. T_DIR_ADDR_UINT8, AddressingMode.DIR, Token.T_DISP_ADDR_INT8, AddressingMode.REL, Token.T_EXT_ADDR_UINT16, AddressingMode. EXT, Register.T_A, AddressingMode.ACC, Register.T_B, AddressingMode.ACC, _, ('error', ', '.join( list(map(lambda x: x.name, First_Operand_States))), test )), 0, lambda k: AddressingMode.INH) if not isinstance(mode, AddressingMode): parser.error(mode[1], mode[2]) else: operands.popleft() mode_stack.append(mode) return operand_state_machine(parser, operands, mode_stack) return AddressingMode.INH if len(mode_stack) == 0 else mode_stack[0]
def adc(addr_mode: AddressingMode, operands: Deque[yylex_t], registers: Register_T) -> bytearray: """The ADC instruction. """ opcode: bytearray = bytearray() data: int = 0 status: bitarray = registers.SR o = operands[0]['data'] if not isinstance(o, str): raise AssemblerParserError(f'Invalid instruction operand') # Immediate addressing: if addr_mode == AddressingMode.IMM: opcode = bytearray.fromhex('89') if operands[-1]['data'] == 'A' \ else bytearray.fromhex('C9') operand = int(Parser.parse_immediate_value(o).hex(), 16) if status[0] is True: b = bin(operand) data = int('0b' + status.to01()[0] + b[2:], 2) else: data = int(bin(operand), 2) registers.AccA += data return opcode + bytearray.fromhex(hex(data)[2:])
def test_assembly_parser(symbol_table: f1_t) -> None: with open(f'{pathlib.Path(__file__).parent.parent}/etc/fixture.asm') as f: source = f.read() test = Parser(source, symbol_table(source)) assert test.line() is True # variable assert test.line() is True # variable assert test.line() is True # variable # note: skips empty lines line = test.line() if isinstance(line, bool): raise AssertionError('failed test') # test instruction parsing while line is not False: expect = expected.popleft() instruction, operands = line # type: ignore assert instruction == expect[0] # type: ignore operands.reverse() index = 1 for ops in operands: assert ops['token'] == expect[index] # type: ignore index += 1 line = test.line()
def _get_parser(source: str) -> Parser: parser = Parser(source) return parser
def test_parse_immediate_value(parser: f1_t, code: f3_t) -> None: assert Parser.parse_immediate_value('#$10') == b'\x10' assert Parser.parse_immediate_value('$10') == b'\x10'
def _get_parser(source: str, symbols: Optional[Symbol_Table]) -> Parser: return Parser(source, symbols or Symbol_Table())
""" axel is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. axel is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with axel. If not, see <https://www.gnu.org/licenses/>. """ from axel.lexer import Lexer from axel.parser import Parser with open('./etc/healthkit.asm') as f: source = f.read() test = Lexer(source) for token in test: pass test2 = Parser(source, test.symbols) line = test2.line() print('\nInstructions:') while line: if not isinstance(line, bool): print(line) line = test2.line() print('\nSymbols:\n', test2.symbols.table)
def __init__(self, source: str) -> None: self.lexer = Optional[Lexer] self.symbol_table: Symbol_Table = self._construct_symbol_table(source) self.parser: Parser = Parser(source, self.symbol_table) self.program: BytesIO = BytesIO()