# Copyright (c) 2003-2005 The Regents of The University of Michigan # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer; # redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution; # neither the name of the copyright holders nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import os import sys import re import traceback # get type names from types import * from ply import lex from ply import yacc ########################################################################## # # Base classes for use outside of the assembler # ########################################################################## class MicroContainer: def __init__(self, name): self.microops = [] self.name = name self.directives = {} self.micro_classes = {} self.labels = {} def add_microop(self, mnemonic, microop): microop.mnemonic = mnemonic microop.micropc = len(self.microops) self.microops.append(microop) def __str__(self): string = f"{self.name}:\n" for microop in self.microops: string += f" {microop}\n" return string class CombinationalMacroop(MicroContainer): pass class RomMacroop: def __init__(self, name, target): self.name = name self.target = target def __str__(self): return f"{self.name}: {self.target}\n" class Rom(MicroContainer): def __init__(self, name): super().__init__(name) self.externs = {} ########################################################################## # # Support classes # ########################################################################## class Label(object): def __init__(self): self.extern = False self.name = "" class Block(object): def __init__(self): self.statements = [] class Statement(object): def __init__(self): self.is_microop = False self.is_directive = False self.params = "" class Microop(Statement): def __init__(self): super().__init__() self.mnemonic = "" self.labels = [] self.is_microop = True class Directive(Statement): def __init__(self): super().__init__() self.name = "" self.is_directive = True ########################################################################## # # Functions that handle common tasks # ########################################################################## def print_error(message): print() print(f"*** {message}") print() def handle_statement(parser, container, statement): if statement.is_microop: if statement.mnemonic not in parser.microops.keys(): raise Exception(f"Unrecognized mnemonic: {statement.mnemonic}") parser.symbols[ "__microopClassFromInsideTheAssembler" ] = parser.microops[statement.mnemonic] try: microop = eval( f"__microopClassFromInsideTheAssembler({statement.params})", {}, parser.symbols, ) except: print_error( f"Error creating microop object with mnemonic {statement.mnemonic}." ) raise try: for label in statement.labels: container.labels[label.text] = microop if label.is_extern: container.externs[label.text] = microop container.add_microop(statement.mnemonic, microop) except: print_error("Error adding microop.") raise elif statement.is_directive: if statement.name not in container.directives.keys(): raise Exception(f"Unrecognized directive: {statement.name}") parser.symbols[ "__directiveFunctionFromInsideTheAssembler" ] = container.directives[statement.name] try: eval( f"__directiveFunctionFromInsideTheAssembler({statement.params})", {}, parser.symbols, ) except: print_error("Error executing directive.") print(container.directives) raise else: raise Exception(f"Didn't recognize the type of statement {statement}") ########################################################################## # # Lexer specification # ########################################################################## # Error handler. Just call exit. Output formatted to work under # Emacs compile-mode. Optional 'print_traceback' arg, if set to True, # prints a Python stack backtrace too (can be handy when trying to # debug the parser itself). def error(lineno, string, print_traceback=False): # Print a Python stack backtrace if requested. if print_traceback: traceback.print_exc() if lineno != 0: line_str = "%d:" % lineno else: line_str = "" sys.exit(f"{line_str} {string}") reserved = ("DEF", "MACROOP", "ROM", "EXTERN") tokens = reserved + ( # identifier "ID", # arguments for microops and directives "PARAMS", "LPAREN", "RPAREN", "LBRACE", "RBRACE", "COLON", "SEMI", "DOT", "NEWLINE", ) # New lines are ignored at the top level, but they end statements in the # assembler states = ( ("asm", "exclusive"), ("params", "exclusive"), ("header", "exclusive"), ) reserved_map = {} for r in reserved: reserved_map[r.lower()] = r # Ignore comments def t_ANY_COMMENT(t): r"\#[^\n]*(?=\n)" def t_ANY_MULTILINECOMMENT(t): r"/\*([^/]|((?