diff options
Diffstat (limited to 'trunk/src/examples/pymicko.py')
-rw-r--r-- | trunk/src/examples/pymicko.py | 1387 |
1 files changed, 0 insertions, 1387 deletions
diff --git a/trunk/src/examples/pymicko.py b/trunk/src/examples/pymicko.py deleted file mode 100644 index b136689..0000000 --- a/trunk/src/examples/pymicko.py +++ /dev/null @@ -1,1387 +0,0 @@ -#!/usr/bin/python
-
-# Python/pyparsing educational microC compiler v1.0
-# Copyright (C) 2009 Zarko Zivanov
-# (largely based on flex/bison microC compiler by Zorica Suvajdzin, used with her permission;
-# current version can be found at http://www.acs.uns.ac.rs, under "Programski Prevodioci" [Serbian site])
-#
-# This program 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.
-#
-# This program 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.
-#
-# A copy of the GNU General Public License can be found at <http://www.gnu.org/licenses/>.
-
-from pyparsing import *
-from sys import stdin, stdout, stderr, argv, exit
-
-#defines debug level
-# 0 - no debug
-# 1 - print parsing results
-# 2 - print parsing results and symbol table
-# 3 - print parsing results only, without executing parse actions (grammar-only testing)
-DEBUG = 0
-
-##########################################################################################
-##########################################################################################
-
-# About microC language and microC compiler
-
-# microC language and microC compiler are educational tools, and their goal is to show some basic principles
-# of writing a C language compiler. Compiler represents one (relatively simple) solution, not necessarily the best one.
-# This Python/pyparsing version is made using Python 2.6.4 and pyparsing 1.5.2 (and it may contain errors :) )
-
-##########################################################################################
-##########################################################################################
-
-# Model of the used hypothetical processor
-
-# The reason behind using a hypothetical processor is to simplify code generation and to concentrate on the compiler itself.
-# This compiler can relatively easily be ported to x86, but one must know all the little details about which register
-# can be used for what, which registers are default for various operations, etc.
-
-# The hypothetical processor has 16 registers, called %0 to %15. Register %13 is used for the function return value (x86's eax),
-# %14 is the stack frame pointer (x86's ebp) and %15 is the stack pointer (x86's esp). All data-handling instructions can be
-# unsigned (suffix U), or signed (suffix S). These are ADD, SUB, MUL and DIV. These are three-address instructions,
-# the first two operands are input, the third one is output. Whether these operands are registers, memory or constant
-# is not relevant, all combinations are possible (except that output cannot be a constant). Constants are writen with a $ prefix (10-base only).
-# Conditional jumps are handled by JXXY instructions, where XX is LT, GT, LE, GE, EQ, NE (less than, greater than, less than or equal, etc.)
-# and Y is U or S (unsigned or signed, except for JEQ i JNE). Unconditional jump is JMP. The move instruction is MOV.
-# Function handling is done using CALL, RET, PUSH and POP (C style function calls). Static data is defined using the WORD directive
-# (example: variable: WORD 1), whose only argument defines the number of locations that are reserved.
-
-##########################################################################################
-##########################################################################################
-
-# Grammar of The microC Programming Language
-# (small subset of C made for compiler course at Faculty of Technical Sciences, Chair for Applied Computer Sciences, Novi Sad, Serbia)
-
-# Patterns:
-
-# letter
-# -> "_" | "a" | "A" | "b" | "B" | "c" | "C" | "d" | "D" | "e" | "E" | "f"
-# | "F" | "g" | "G" | "h" | "H" | "i" | "I" | "j" | "J" | "k" | "K" | "l"
-# | "L" | "m" | "M" | "n" | "N" | "o" | "O" | "p" | "P" | "q" | "Q" | "r"
-# | "R" | "s" | "S" | "t" | "T" | "u" | "U" | "v" | "V" | "w" | "W" | "x"
-# | "X" | "y" | "Y" | "z" | "Z"
-
-# digit
-# -> "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
-
-# identifier
-# -> letter ( letter | digit )*
-
-# int_constant
-# -> digit +
-
-# unsigned_constant
-# -> digit + ( "u" | "U" )
-
-# Productions:
-
-# program
-# -> variable_list function_list
-# -> function_list
-
-# variable_list
-# -> variable ";"
-# -> variable_list variable ";"
-
-# variable
-# -> type identifier
-
-# type
-# -> "int"
-# -> "unsigned"
-
-# function_list
-# -> function
-# -> function_list function
-
-# function
-# -> type identifier "(" parameters ")" body
-
-# parameters
-# -> <empty>
-# -> parameter_list
-
-# parameter_list
-# -> variable
-# -> parameter_list "," variable
-
-# body
-# -> "{" variable_list statement_list "}"
-# -> "{" statement_list "}"
-
-# statement_list
-# -> <empty>
-# -> statement_list statement
-
-# statement
-# -> assignement_statement
-# -> function_call_statement
-# -> if_statement
-# -> while_statement
-# -> return_statement
-# -> compound_statement
-
-# assignement_statement
-# -> identifier "=" num_exp ";"
-
-# num_exp
-# -> mul_exp
-# -> num_exp "+" mul_exp
-# -> num_exp "-" mul_exp
-
-# mul_exp
-# -> exp
-# -> mul_exp "*" exp
-# -> mul_exp "/" exp
-
-# exp
-# -> constant
-# -> identifier
-# -> function_call
-# -> "(" num_exp ")"
-# -> "+" exp
-# -> "-" exp
-
-# constant
-# -> int_constant
-# -> unsigned_constant
-
-# function_call
-# -> identifier "(" arguments ")"
-
-# arguments
-# -> <empty>
-# -> argument_list
-
-# argument_list
-# -> num_exp
-# -> argument_list "," num_exp
-
-# function_call_statement
-# -> function_call ";"
-
-# if_statement
-# -> "if" "(" log_exp ")" statement
-# -> "if" "(" log_exp ")" statement "else" statement
-# -> -> -> -> -> -> -> -> 2
-
-# log_exp
-# -> and_exp
-# -> log_exp "||" and_exp
-
-# and_exp
-# -> rel_exp
-# -> and_exp "&&" rel_exp
-
-# rel_exp
-# -> num_exp "<" num_exp
-# -> num_exp ">" num_exp
-# -> num_exp "<=" num_exp
-# -> num_exp ">=" num_exp
-# -> num_exp "==" num_exp
-# -> num_exp "!=" num_exp
-
-# while_statement
-# -> "while" "(" log_exp ")" statement
-
-# return_statement
-# -> "return" num_exp ";"
-
-# compound_statement
-# -> "{" statement_list "}"
-
-# Comment: /* a comment */
-
-##########################################################################################
-##########################################################################################
-
-class Enumerate(dict):
- """C enum emulation (original by Scott David Daniels)"""
- def __init__(self, names):
- for number, name in enumerate(names.split()):
- setattr(self, name, number)
- self[number] = name
-
-class SharedData(object):
- """Data used in all three main classes"""
-
- #Possible kinds of symbol table entries
- KINDS = Enumerate("NO_KIND WORKING_REGISTER GLOBAL_VAR FUNCTION PARAMETER LOCAL_VAR CONSTANT")
- #Supported types of functions and variables
- TYPES = Enumerate("NO_TYPE INT UNSIGNED")
-
- #bit size of variables
- TYPE_BIT_SIZE = 16
- #min/max values of constants
- MIN_INT = -2 ** (TYPE_BIT_SIZE - 1)
- MAX_INT = 2 ** (TYPE_BIT_SIZE - 1) - 1
- MAX_UNSIGNED = 2 ** TYPE_BIT_SIZE - 1
- #available working registers (the last one is the register for function's return value!)
- REGISTERS = "%0 %1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12 %13".split()
- #register for function's return value
- FUNCTION_REGISTER = len(REGISTERS) - 1
- #the index of last working register
- LAST_WORKING_REGISTER = len(REGISTERS) - 2
- #list of relational operators
- RELATIONAL_OPERATORS = "< > <= >= == !=".split()
-
- def __init__(self):
- #index of the currently parsed function
- self.functon_index = 0
- #name of the currently parsed function
- self.functon_name = 0
- #number of parameters of the currently parsed function
- self.function_params = 0
- #number of local variables of the currently parsed function
- self.function_vars = 0
-
-##########################################################################################
-##########################################################################################
-
-class ExceptionSharedData(object):
- """Class for exception handling data"""
-
- def __init__(self):
- #position in currently parsed text
- self.location = 0
- #currently parsed text
- self.text = ""
-
- def setpos(self, location, text):
- """Helper function for setting curently parsed text and position"""
- self.location = location
- self.text = text
-
-exshared = ExceptionSharedData()
-
-class SemanticException(Exception):
- """Exception for semantic errors found during parsing, similar to ParseException.
- Introduced because ParseException is used internally in pyparsing and custom
- messages got lost and replaced by pyparsing's generic errors.
- """
-
- def __init__(self, message, print_location=True):
- super(SemanticException,self).__init__()
- self._message = message
- self.location = exshared.location
- self.print_location = print_location
- if exshared.location != None:
- self.line = lineno(exshared.location, exshared.text)
- self.col = col(exshared.location, exshared.text)
- self.text = line(exshared.location, exshared.text)
- else:
- self.line = self.col = self.text = None
-
- def _get_message(self):
- return self._message
- def _set_message(self, message):
- self._message = message
- message = property(_get_message, _set_message)
-
- def __str__(self):
- """String representation of the semantic error"""
- msg = "Error"
- if self.print_location and (self.line != None):
- msg += " at line %d, col %d" % (self.line, self.col)
- msg += ": %s" % self.message
- if self.print_location and (self.line != None):
- msg += "\n%s" % self.text
- return msg
-
-##########################################################################################
-##########################################################################################
-
-class SymbolTableEntry(object):
- """Class which represents one symbol table entry."""
-
- def __init__(self, sname = "", skind = 0, stype = 0, sattr = None, sattr_name = "None"):
- """Initialization of symbol table entry.
- sname - symbol name
- skind - symbol kind
- stype - symbol type
- sattr - symbol attribute
- sattr_name - symbol attribute name (used only for table display)
- """
- self.name = sname
- self.kind = skind
- self.type = stype
- self.attribute = sattr
- self.attribute_name = sattr_name
- self.param_types = []
-
- def set_attribute(self, name, value):
- """Sets attribute's name and value"""
- self.attribute_name = name
- self.attribute = value
-
- def attribute_str(self):
- """Returns attribute string (used only for table display)"""
- return "{0}={1}".format(self.attribute_name, self.attribute) if self.attribute != None else "None"
-
-class SymbolTable(object):
- """Class for symbol table of microC program"""
-
- def __init__(self, shared):
- """Initialization of the symbol table"""
- self.table = []
- self.lable_len = 0
- #put working registers in the symbol table
- for reg in range(SharedData.FUNCTION_REGISTER+1):
- self.insert_symbol(SharedData.REGISTERS[reg], SharedData.KINDS.WORKING_REGISTER, SharedData.TYPES.NO_TYPE)
- #shared data
- self.shared = shared
-
- def error(self, text=""):
- """Symbol table error exception. It should happen only if index is out of range while accessing symbol table.
- This exeption is not handled by the compiler, so as to allow traceback printing
- """
- if text == "":
- raise Exception("Symbol table index out of range")
- else:
- raise Exception("Symbol table error: %s" % text)
-
- def display(self):
- """Displays the symbol table content"""
- #Finding the maximum length for each column
- sym_name = "Symbol name"
- sym_len = max(max(len(i.name) for i in self.table),len(sym_name))
- kind_name = "Kind"
- kind_len = max(max(len(SharedData.KINDS[i.kind]) for i in self.table),len(kind_name))
- type_name = "Type"
- type_len = max(max(len(SharedData.TYPES[i.type]) for i in self.table),len(type_name))
- attr_name = "Attribute"
- attr_len = max(max(len(i.attribute_str()) for i in self.table),len(attr_name))
- #print table header
- print("{0:3s} | {1:^{2}s} | {3:^{4}s} | {5:^{6}s} | {7:^{8}} | {9:s}".format(" No", sym_name, sym_len, kind_name, kind_len, type_name, type_len, attr_name, attr_len, "Parameters"))
- print("-----------------------------" + "-" * (sym_len + kind_len + type_len + attr_len))
- #print symbol table
- for i,sym in enumerate(self.table):
- parameters = ""
- for p in sym.param_types:
- if parameters == "":
- parameters = "{0}".format(SharedData.TYPES[p])
- else:
- parameters += ", {0}".format(SharedData.TYPES[p])
- print("{0:3d} | {1:^{2}s} | {3:^{4}s} | {5:^{6}s} | {7:^{8}} | ({9})".format(i, sym.name, sym_len, SharedData.KINDS[sym.kind], kind_len, SharedData.TYPES[sym.type], type_len, sym.attribute_str(), attr_len, parameters))
-
- def insert_symbol(self, sname, skind, stype):
- """Inserts new symbol at the end of the symbol table.
- Returns symbol index
- sname - symbol name
- skind - symbol kind
- stype - symbol type
- """
- self.table.append(SymbolTableEntry(sname, skind, stype))
- self.table_len = len(self.table)
- return self.table_len-1
-
- def clear_symbols(self, index):
- """Clears all symbols begining with the index to the end of table"""
- try:
- del self.table[index:]
- except Exception:
- self.error()
- self.table_len = len(self.table)
-
- def lookup_symbol(self, sname, skind=list(SharedData.KINDS.keys()), stype=list(SharedData.TYPES.keys())):
- """Searches for symbol, from the end to the begining.
- Returns symbol index or None
- sname - symbol name
- skind - symbol kind (one kind, list of kinds, or None) deafult: any kind
- stype - symbol type (or None) default: any type
- """
- skind = skind if isinstance(skind, list) else [skind]
- stype = stype if isinstance(stype, list) else [stype]
- for i, sym in [[x, self.table[x]] for x in range(len(self.table) - 1, SharedData.LAST_WORKING_REGISTER, -1)]:
- if (sym.name == sname) and (sym.kind in skind) and (sym.type in stype):
- return i
- return None
-
- def insert_id(self, sname, skind, skinds, stype):
- """Inserts a new identifier at the end of the symbol table, if possible.
- Returns symbol index, or raises an exception if the symbol alredy exists
- sname - symbol name
- skind - symbol kind
- skinds - symbol kinds to check for
- stype - symbol type
- """
- index = self.lookup_symbol(sname, skinds)
- if index == None:
- index = self.insert_symbol(sname, skind, stype)
- return index
- else:
- raise SemanticException("Redefinition of '%s'" % sname)
-
- def insert_global_var(self, vname, vtype):
- "Inserts a new global variable"
- return self.insert_id(vname, SharedData.KINDS.GLOBAL_VAR, [SharedData.KINDS.GLOBAL_VAR, SharedData.KINDS.FUNCTION], vtype)
-
- def insert_local_var(self, vname, vtype, position):
- "Inserts a new local variable"
- index = self.insert_id(vname, SharedData.KINDS.LOCAL_VAR, [SharedData.KINDS.LOCAL_VAR, SharedData.KINDS.PARAMETER], vtype)
- self.table[index].attribute = position
-
- def insert_parameter(self, pname, ptype):
- "Inserts a new parameter"
- index = self.insert_id(pname, SharedData.KINDS.PARAMETER, SharedData.KINDS.PARAMETER, ptype)
- #set parameter's attribute to it's ordinal number
- self.table[index].set_attribute("Index", self.shared.function_params)
- #set parameter's type in param_types list of a function
- self.table[self.shared.function_index].param_types.append(ptype)
- return index
-
- def insert_function(self, fname, ftype):
- "Inserts a new function"
- index = self.insert_id(fname, SharedData.KINDS.FUNCTION, [SharedData.KINDS.GLOBAL_VAR, SharedData.KINDS.FUNCTION], ftype)
- self.table[index].set_attribute("Params",0)
- return index
-
- def insert_constant(self, cname, ctype):
- """Inserts a constant (or returns index if the constant already exists)
- Additionally, checks for range.
- """
- index = self.lookup_symbol(cname, stype=ctype)
- if index == None:
- num = int(cname)
- if ctype == SharedData.TYPES.INT:
- if (num < SharedData.MIN_INT) or (num > SharedData.MAX_INT):
- raise SemanticException("Integer constant '%s' out of range" % cname)
- elif ctype == SharedData.TYPES.UNSIGNED:
- if (num < 0) or (num > SharedData.MAX_UNSIGNED):
- raise SemanticException("Unsigned constant '%s' out of range" % cname)
- index = self.insert_symbol(cname, SharedData.KINDS.CONSTANT, ctype)
- return index
-
- def same_types(self, index1, index2):
- """Returns True if both symbol table elements are of the same type"""
- try:
- same = self.table[index1].type == self.table[index2].type != SharedData.TYPES.NO_TYPE
- except Exception:
- self.error()
- return same
-
- def same_type_as_argument(self, index, function_index, argument_number):
- """Returns True if index and function's argument are of the same type
- index - index in symbol table
- function_index - function's index in symbol table
- argument_number - # of function's argument
- """
- try:
- same = self.table[function_index].param_types[argument_number] == self.table[index].type
- except Exception:
- self.error()
- return same
-
- def get_attribute(self, index):
- try:
- return self.table[index].attribute
- except Exception:
- self.error()
-
- def set_attribute(self, index, value):
- try:
- self.table[index].attribute = value
- except Exception:
- self.error()
-
- def get_name(self, index):
- try:
- return self.table[index].name
- except Exception:
- self.error()
-
- def get_kind(self, index):
- try:
- return self.table[index].kind
- except Exception:
- self.error()
-
- def get_type(self, index):
- try:
- return self.table[index].type
- except Exception:
- self.error()
-
- def set_type(self, index, stype):
- try:
- self.table[index].type = stype
- except Exception:
- self.error()
-
-##########################################################################################
-##########################################################################################
-
-class CodeGenerator(object):
- """Class for code generation methods."""
-
- #dictionary of relational operators
- RELATIONAL_DICT = dict([op,i] for i, op in enumerate(SharedData.RELATIONAL_OPERATORS))
- #conditional jumps for relational operators
- CONDITIONAL_JUMPS = ["JLTS", "JGTS", "JLES", "JGES", "JEQ ", "JNE ",
- "JLTU", "JGTU", "JLEU", "JGEU", "JEQ ", "JNE "]
- #opposite conditional jumps for relational operators
- OPPOSITE_JUMPS = ["JGES", "JLES", "JGTS", "JLTS", "JNE ", "JEQ ",
- "JGEU", "JLEU", "JGTU", "JLTU", "JNE ", "JEQ "]
- #supported operations
- OPERATIONS = {"+" : "ADD", "-" : "SUB", "*" : "MUL", "/" : "DIV"}
- #suffixes for signed and unsigned operations (if no type is specified, unsigned will be assumed)
- OPSIGNS = {SharedData.TYPES.NO_TYPE : "U", SharedData.TYPES.INT : "S", SharedData.TYPES.UNSIGNED : "U"}
- #text at start of data segment
- DATA_START_TEXT = "#DATA"
- #text at start of code segment
- CODE_START_TEXT = "#CODE"
-
- def __init__(self, shared, symtab):
- #generated code
- self.code = ""
- #prefix for internal labels
- self.internal = "@"
- #suffix for label definition
- self.definition = ":"
- #list of free working registers
- self.free_registers = list(range(SharedData.FUNCTION_REGISTER, -1, -1))
- #list of used working registers
- self.used_registers = []
- #list of used registers needed when function call is inside of a function call
- self.used_registers_stack = []
- #shared data
- self.shared = shared
- #symbol table
- self.symtab = symtab
-
- def error(self, text):
- """Compiler error exception. It should happen only if something is wrong with compiler.
- This exeption is not handled by the compiler, so as to allow traceback printing
- """
- raise Exception("Compiler error: %s" % text)
-
- def take_register(self, rtype = SharedData.TYPES.NO_TYPE):
- """Reserves one working register and sets its type"""
- if len(self.free_registers) == 0:
- self.error("no more free registers")
- reg = self.free_registers.pop()
- self.used_registers.append(reg)
- self.symtab.set_type(reg, rtype)
- return reg
-
- def take_function_register(self, rtype = SharedData.TYPES.NO_TYPE):
- """Reserves register for function return value and sets its type"""
- reg = SharedData.FUNCTION_REGISTER
- if reg not in self.free_registers:
- self.error("function register already taken")
- self.free_registers.remove(reg)
- self.used_registers.append(reg)
- self.symtab.set_type(reg, rtype)
- return reg
-
- def free_register(self, reg):
- """Releases working register"""
- if reg not in self.used_registers:
- self.error("register %s is not taken" % self.REGISTERS[reg])
- self.used_registers.remove(reg)
- self.free_registers.append(reg)
- self.free_registers.sort(reverse = True)
-
- def free_if_register(self, index):
- """If index is a working register, free it, otherwise just return (helper function)"""
- if (index < 0) or (index > SharedData.FUNCTION_REGISTER):
- return
- else:
- self.free_register(index)
-
- def label(self, name, internal=False, definition=False):
- """Generates label name (helper function)
- name - label name
- internal - boolean value, adds "@" prefix to label
- definition - boolean value, adds ":" suffix to label
- """
- return "{0}{1}{2}".format(self.internal if internal else "", name, self.definition if definition else "")
-
- def symbol(self, index):
- """Generates symbol name from index"""
- #if index is actually a string, just return it
- if isinstance(index, str):
- return index
- elif (index < 0) or (index >= self.symtab.table_len):
- self.error("symbol table index out of range")
- sym = self.symtab.table[index]
- #local variables are located at negative offset from frame pointer register
- if sym.kind == SharedData.KINDS.LOCAL_VAR:
- return "-{0}(%14)".format(sym.attribute * 4 + 4)
- #parameters are located at positive offset from frame pointer register
- elif sym.kind == SharedData.KINDS.PARAMETER:
- return "{0}(%14)".format(8 + sym.attribute * 4)
- elif sym.kind == SharedData.KINDS.CONSTANT:
- return "${0}".format(sym.name)
- else:
- return "{0}".format(sym.name)
-
- def save_used_registers(self):
- """Pushes all used working registers before function call"""
- used = self.used_registers[:]
- del self.used_registers[:]
- self.used_registers_stack.append(used[:])
- used.sort()
- for reg in used:
- self.newline_text("PUSH\t%s" % SharedData.REGISTERS[reg], True)
- self.free_registers.extend(used)
- self.free_registers.sort(reverse = True)
-
- def restore_used_registers(self):
- """Pops all used working registers after function call"""
- used = self.used_registers_stack.pop()
- self.used_registers = used[:]
- used.sort(reverse = True)
- for reg in used:
- self.newline_text("POP \t%s" % SharedData.REGISTERS[reg], True)
- self.free_registers.remove(reg)
-
- def text(self, text):
- """Inserts text into generated code"""
- self.code += text
-
- def prepare_data_segment(self):
- """Inserts text at the start of data segment"""
- self.text(self.DATA_START_TEXT)
-
- def prepare_code_segment(self):
- """Inserts text at the start of code segment"""
- self.newline_text(self.CODE_START_TEXT)
-
- def newline(self, indent=False):
- """Inserts a newline, optionally with indentation."""
- self.text("\n")
- if indent:
- self.text("\t\t\t")
-
- def newline_text(self, text, indent = False):
- """Inserts a newline and text, optionally with indentation (helper function)"""
- self.newline(indent)
- self.text(text)
-
- def newline_label(self, name, internal=False, definition=False):
- """Inserts a newline and a label (helper function)
- name - label name
- internal - boolean value, adds "@" prefix to label
- definition - boolean value, adds ":" suffix to label
- """
- self.newline_text(self.label("{0}{1}{2}".format("@" if internal else "", name, ":" if definition else "")))
-
- def global_var(self, name):
- """Inserts a new static (global) variable definition"""
- self.newline_label(name, False, True)
- self.newline_text("WORD\t1", True)
-
- def arithmetic_mnemonic(self, op_name, op_type):
- """Generates an arithmetic instruction mnemonic"""
- return self.OPERATIONS[op_name] + self.OPSIGNS[op_type]
-
- def arithmetic(self, operation, operand1, operand2, operand3 = None):
- """Generates an arithmetic instruction
- operation - one of supporetd operations
- operandX - index in symbol table or text representation of operand
- First two operands are input, third one is output
- """
- if isinstance(operand1, int):
- output_type = self.symtab.get_type(operand1)
- self.free_if_register(operand1)
- else:
- output_type = None
- if isinstance(operand2, int):
- output_type = self.symtab.get_type(operand2) if output_type == None else output_type
- self.free_if_register(operand2)
- else:
- output_type = SharedData.TYPES.NO_TYPE if output_type == None else output_type
- #if operand3 is not defined, reserve one free register for it
- output = self.take_register(output_type) if operand3 == None else operand3
- mnemonic = self.arithmetic_mnemonic(operation, output_type)
- self.newline_text("{0}\t{1},{2},{3}".format(mnemonic, self.symbol(operand1), self.symbol(operand2), self.symbol(output)), True)
- return output
-
- def relop_code(self, relop, operands_type):
- """Returns code for relational operator
- relop - relational operator
- operands_type - int or unsigned
- """
- code = self.RELATIONAL_DICT[relop]
- offset = 0 if operands_type == SharedData.TYPES.INT else len(SharedData.RELATIONAL_OPERATORS)
- return code + offset
-
- def jump(self, relcode, opposite, label):
- """Generates a jump instruction
- relcode - relational operator code
- opposite - generate normal or opposite jump
- label - jump label
- """
- jump = self.OPPOSITE_JUMPS[relcode] if opposite else self.CONDITIONAL_JUMPS[relcode]
- self.newline_text("{0}\t{1}".format(jump, label), True)
-
- def unconditional_jump(self, label):
- """Generates an unconditional jump instruction
- label - jump label
- """
- self.newline_text("JMP \t{0}".format(label), True)
-
- def move(self,operand1, operand2):
- """Generates a move instruction
- If the output operand (opernad2) is a working register, sets it's type
- operandX - index in symbol table or text representation of operand
- """
- if isinstance(operand1, int):
- output_type = self.symtab.get_type(operand1)
- self.free_if_register(operand1)
- else:
- output_type = SharedData.TYPES.NO_TYPE
- self.newline_text("MOV \t{0},{1}".format(self.symbol(operand1), self.symbol(operand2)), True)
- if isinstance(operand2, int):
- if self.symtab.get_kind(operand2) == SharedData.KINDS.WORKING_REGISTER:
- self.symtab.set_type(operand2, output_type)
-
- def push(self, operand):
- """Generates a push operation"""
- self.newline_text("PUSH\t%s" % self.symbol(operand), True)
-
- def pop(self, operand):
- """Generates a pop instruction"""
- self.newline_text("POP \t%s" % self.symbol(operand), True)
-
- def compare(self, operand1, operand2):
- """Generates a compare instruction
- operandX - index in symbol table
- """
- typ = self.symtab.get_type(operand1)
- self.free_if_register(operand1)
- self.free_if_register(operand2)
- self.newline_text("CMP{0}\t{1},{2}".format(self.OPSIGNS[typ], self.symbol(operand1), self.symbol(operand2)), True)
-
- def function_begin(self):
- """Inserts function name label and function frame initialization"""
- self.newline_label(self.shared.function_name, False, True)
- self.push("%14")
- self.move("%15", "%14")
-
- def function_body(self):
- """Inserts a local variable initialization and body label"""
- if self.shared.function_vars > 0:
- const = self.symtab.insert_constant("{0}".format(self.shared.function_vars * 4), SharedData.TYPES.UNSIGNED)
- self.arithmetic("-", "%15", const, "%15")
- self.newline_label(self.shared.function_name + "_body", True, True)
-
- def function_end(self):
- """Inserts an exit label and function return instructions"""
- self.newline_label(self.shared.function_name + "_exit", True, True)
- self.move("%14", "%15")
- self.pop("%14")
- self.newline_text("RET", True)
-
- def function_call(self, function, arguments):
- """Generates code for a function call
- function - function index in symbol table
- arguments - list of arguments (indexes in symbol table)
- """
- #push each argument to stack
- for arg in arguments:
- self.push(self.symbol(arg))
- self.free_if_register(arg)
- self.newline_text("CALL\t"+self.symtab.get_name(function), True)
- args = self.symtab.get_attribute(function)
- #generates stack cleanup if function has arguments
- if args > 0:
- args_space = self.symtab.insert_constant("{0}".format(args * 4), SharedData.TYPES.UNSIGNED)
- self.arithmetic("+", "%15", args_space, "%15")
-
-##########################################################################################
-##########################################################################################
-
-class MicroC(object):
- """Class for microC parser/compiler"""
-
- def __init__(self):
- #Definitions of terminal symbols for microC programming language
- self.tId = Word(alphas+"_",alphanums+"_")
- self.tInteger = Word(nums).setParseAction(lambda x : [x[0], SharedData.TYPES.INT])
- self.tUnsigned = Regex(r"[0-9]+[uU]").setParseAction(lambda x : [x[0][:-1], SharedData.TYPES.UNSIGNED])
- self.tConstant = (self.tUnsigned | self.tInteger).setParseAction(self.constant_action)
- self.tType = Keyword("int").setParseAction(lambda x : SharedData.TYPES.INT) | \
- Keyword("unsigned").setParseAction(lambda x : SharedData.TYPES.UNSIGNED)
- self.tRelOp = oneOf(SharedData.RELATIONAL_OPERATORS)
- self.tMulOp = oneOf("* /")
- self.tAddOp = oneOf("+ -")
-
- #Definitions of rules for global variables
- self.rGlobalVariable = (self.tType("type") + self.tId("name") +
- FollowedBy(";")).setParseAction(self.global_variable_action)
- self.rGlobalVariableList = ZeroOrMore(self.rGlobalVariable + Suppress(";"))
-
- #Definitions of rules for numeric expressions
- self.rExp = Forward()
- self.rMulExp = Forward()
- self.rNumExp = Forward()
- self.rArguments = delimitedList(self.rNumExp("exp").setParseAction(self.argument_action))
- self.rFunctionCall = ((self.tId("name") + FollowedBy("(")).setParseAction(self.function_call_prepare_action) +
- Suppress("(") + Optional(self.rArguments)("args") + Suppress(")")).setParseAction(self.function_call_action)
- self.rExp << (self.rFunctionCall |
- self.tConstant |
- self.tId("name").setParseAction(self.lookup_id_action) |
- Group(Suppress("(") + self.rNumExp + Suppress(")")) |
- Group("+" + self.rExp) |
- Group("-" + self.rExp)).setParseAction(lambda x : x[0])
- self.rMulExp << ((self.rExp + ZeroOrMore(self.tMulOp + self.rExp))).setParseAction(self.mulexp_action)
- self.rNumExp << (self.rMulExp + ZeroOrMore(self.tAddOp + self.rMulExp)).setParseAction(self.numexp_action)
-
- #Definitions of rules for logical expressions (these are without parenthesis support)
- self.rAndExp = Forward()
- self.rLogExp = Forward()
- self.rRelExp = (self.rNumExp + self.tRelOp + self.rNumExp).setParseAction(self.relexp_action)
- self.rAndExp << (self.rRelExp("exp") + ZeroOrMore(Literal("&&").setParseAction(self.andexp_action) +
- self.rRelExp("exp")).setParseAction(lambda x : self.relexp_code))
- self.rLogExp << (self.rAndExp("exp") + ZeroOrMore(Literal("||").setParseAction(self.logexp_action) +
- self.rAndExp("exp")).setParseAction(lambda x : self.andexp_code))
-
- #Definitions of rules for statements
- self.rStatement = Forward()
- self.rStatementList = Forward()
- self.rReturnStatement = (Keyword("return") + self.rNumExp("exp") +
- Suppress(";")).setParseAction(self.return_action)
- self.rAssignmentStatement = (self.tId("var") + Suppress("=") + self.rNumExp("exp") +
- Suppress(";")).setParseAction(self.assignment_action)
- self.rFunctionCallStatement = self.rFunctionCall + Suppress(";")
- self.rIfStatement = ( (Keyword("if") + FollowedBy("(")).setParseAction(self.if_begin_action) +
- (Suppress("(") + self.rLogExp + Suppress(")")).setParseAction(self.if_body_action) +
- (self.rStatement + Empty()).setParseAction(self.if_else_action) +
- Optional(Keyword("else") + self.rStatement)).setParseAction(self.if_end_action)
- self.rWhileStatement = ( (Keyword("while") + FollowedBy("(")).setParseAction(self.while_begin_action) +
- (Suppress("(") + self.rLogExp + Suppress(")")).setParseAction(self.while_body_action) +
- self.rStatement).setParseAction(self.while_end_action)
- self.rCompoundStatement = Group(Suppress("{") + self.rStatementList + Suppress("}"))
- self.rStatement << (self.rReturnStatement | self.rIfStatement | self.rWhileStatement |
- self.rFunctionCallStatement | self.rAssignmentStatement | self.rCompoundStatement)
- self.rStatementList << ZeroOrMore(self.rStatement)
-
- self.rLocalVariable = (self.tType("type") + self.tId("name") + FollowedBy(";")).setParseAction(self.local_variable_action)
- self.rLocalVariableList = ZeroOrMore(self.rLocalVariable + Suppress(";"))
- self.rFunctionBody = Suppress("{") + Optional(self.rLocalVariableList).setParseAction(self.function_body_action) + \
- self.rStatementList + Suppress("}")
- self.rParameter = (self.tType("type") + self.tId("name")).setParseAction(self.parameter_action)
- self.rParameterList = delimitedList(self.rParameter)
- self.rFunction = ( (self.tType("type") + self.tId("name")).setParseAction(self.function_begin_action) +
- Group(Suppress("(") + Optional(self.rParameterList)("params") + Suppress(")") +
- self.rFunctionBody)).setParseAction(self.function_end_action)
-
- self.rFunctionList = OneOrMore(self.rFunction)
- self.rProgram = (Empty().setParseAction(self.data_begin_action) + self.rGlobalVariableList +
- Empty().setParseAction(self.code_begin_action) + self.rFunctionList).setParseAction(self.program_end_action)
-
- #shared data
- self.shared = SharedData()
- #symbol table
- self.symtab = SymbolTable(self.shared)
- #code generator
- self.codegen = CodeGenerator(self.shared, self.symtab)
-
- #index of the current function call
- self.function_call_index = -1
- #stack for the nested function calls
- self.function_call_stack = []
- #arguments of the current function call
- self.function_arguments = []
- #stack for arguments of the nested function calls
- self.function_arguments_stack = []
- #number of arguments for the curent function call
- self.function_arguments_number = -1
- #stack for the number of arguments for the nested function calls
- self.function_arguments_number_stack = []
-
- #last relational expression
- self.relexp_code = None
- #last and expression
- self.andexp_code = None
- #label number for "false" internal labels
- self.false_label_number = -1
- #label number for all other internal labels
- self.label_number = None
- #label stack for nested statements
- self.label_stack = []
-
- def warning(self, message, print_location=True):
- """Displays warning message. Uses exshared for current location of parsing"""
- msg = "Warning"
- if print_location and (exshared.location != None):
- wline = lineno(exshared.location, exshared.text)
- wcol = col(exshared.location, exshared.text)
- wtext = line(exshared.location, exshared.text)
- msg += " at line %d, col %d" % (wline, wcol)
- msg += ": %s" % message
- if print_location and (exshared.location != None):
- msg += "\n%s" % wtext
- print(msg)
-
-
- def data_begin_action(self):
- """Inserts text at start of data segment"""
- self.codegen.prepare_data_segment()
-
- def code_begin_action(self):
- """Inserts text at start of code segment"""
- self.codegen.prepare_code_segment()
-
- def global_variable_action(self, text, loc, var):
- """Code executed after recognising a global variable"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("GLOBAL_VAR:",var)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- index = self.symtab.insert_global_var(var.name, var.type)
- self.codegen.global_var(var.name)
- return index
-
- def local_variable_action(self, text, loc, var):
- """Code executed after recognising a local variable"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("LOCAL_VAR:",var, var.name, var.type)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- index = self.symtab.insert_local_var(var.name, var.type, self.shared.function_vars)
- self.shared.function_vars += 1
- return index
-
- def parameter_action(self, text, loc, par):
- """Code executed after recognising a parameter"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("PARAM:",par)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- index = self.symtab.insert_parameter(par.name, par.type)
- self.shared.function_params += 1
- return index
-
- def constant_action(self, text, loc, const):
- """Code executed after recognising a constant"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("CONST:",const)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- return self.symtab.insert_constant(const[0], const[1])
-
- def function_begin_action(self, text, loc, fun):
- """Code executed after recognising a function definition (type and function name)"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("FUN_BEGIN:",fun)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- self.shared.function_index = self.symtab.insert_function(fun.name, fun.type)
- self.shared.function_name = fun.name
- self.shared.function_params = 0
- self.shared.function_vars = 0
- self.codegen.function_begin();
-
- def function_body_action(self, text, loc, fun):
- """Code executed after recognising the beginning of function's body"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("FUN_BODY:",fun)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- self.codegen.function_body()
-
- def function_end_action(self, text, loc, fun):
- """Code executed at the end of function definition"""
- if DEBUG > 0:
- print("FUN_END:",fun)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- #set function's attribute to number of function parameters
- self.symtab.set_attribute(self.shared.function_index, self.shared.function_params)
- #clear local function symbols (but leave function name)
- self.symtab.clear_symbols(self.shared.function_index + 1)
- self.codegen.function_end()
-
- def return_action(self, text, loc, ret):
- """Code executed after recognising a return statement"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("RETURN:",ret)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- if not self.symtab.same_types(self.shared.function_index, ret.exp[0]):
- raise SemanticException("Incompatible type in return")
- #set register for function's return value to expression value
- reg = self.codegen.take_function_register()
- self.codegen.move(ret.exp[0], reg)
- #after return statement, register for function's return value is available again
- self.codegen.free_register(reg)
- #jump to function's exit
- self.codegen.unconditional_jump(self.codegen.label(self.shared.function_name+"_exit", True))
-
- def lookup_id_action(self, text, loc, var):
- """Code executed after recognising an identificator in expression"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("EXP_VAR:",var)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- var_index = self.symtab.lookup_symbol(var.name, [SharedData.KINDS.GLOBAL_VAR, SharedData.KINDS.PARAMETER, SharedData.KINDS.LOCAL_VAR])
- if var_index == None:
- raise SemanticException("'%s' undefined" % var.name)
- return var_index
-
- def assignment_action(self, text, loc, assign):
- """Code executed after recognising an assignment statement"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("ASSIGN:",assign)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- var_index = self.symtab.lookup_symbol(assign.var, [SharedData.KINDS.GLOBAL_VAR, SharedData.KINDS.PARAMETER, SharedData.KINDS.LOCAL_VAR])
- if var_index == None:
- raise SemanticException("Undefined lvalue '%s' in assignment" % assign.var)
- if not self.symtab.same_types(var_index, assign.exp[0]):
- raise SemanticException("Incompatible types in assignment")
- self.codegen.move(assign.exp[0], var_index)
-
- def mulexp_action(self, text, loc, mul):
- """Code executed after recognising a mulexp expression (something *|/ something)"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("MUL_EXP:",mul)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- #iterate through all multiplications/divisions
- m = list(mul)
- while len(m) > 1:
- if not self.symtab.same_types(m[0], m[2]):
- raise SemanticException("Invalid opernads to binary '%s'" % m[1])
- reg = self.codegen.arithmetic(m[1], m[0], m[2])
- #replace first calculation with it's result
- m[0:3] = [reg]
- return m[0]
-
- def numexp_action(self, text, loc, num):
- """Code executed after recognising a numexp expression (something +|- something)"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("NUM_EXP:",num)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- #iterate through all additions/substractions
- n = list(num)
- while len(n) > 1:
- if not self.symtab.same_types(n[0], n[2]):
- raise SemanticException("Invalid opernads to binary '%s'" % n[1])
- reg = self.codegen.arithmetic(n[1], n[0], n[2])
- #replace first calculation with it's result
- n[0:3] = [reg]
- return n[0]
-
- def function_call_prepare_action(self, text, loc, fun):
- """Code executed after recognising a function call (type and function name)"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("FUN_PREP:",fun)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- index = self.symtab.lookup_symbol(fun.name, SharedData.KINDS.FUNCTION)
- if index == None:
- raise SemanticException("'%s' is not a function" % fun.name)
- #save any previous function call data (for nested function calls)
- self.function_call_stack.append(self.function_call_index)
- self.function_call_index = index
- self.function_arguments_stack.append(self.function_arguments[:])
- del self.function_arguments[:]
- self.codegen.save_used_registers()
-
- def argument_action(self, text, loc, arg):
- """Code executed after recognising each of function's arguments"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("ARGUMENT:",arg.exp)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- arg_ordinal = len(self.function_arguments)
- #check argument's type
- if not self.symtab.same_type_as_argument(arg.exp, self.function_call_index, arg_ordinal):
- raise SemanticException("Incompatible type for argument %d in '%s'" % (arg_ordinal + 1, self.symtab.get_name(self.function_call_index)))
- self.function_arguments.append(arg.exp)
-
- def function_call_action(self, text, loc, fun):
- """Code executed after recognising the whole function call"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("FUN_CALL:",fun)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- #check number of arguments
- if len(self.function_arguments) != self.symtab.get_attribute(self.function_call_index):
- raise SemanticException("Wrong number of arguments for function '%s'" % fun.name)
- #arguments should be pushed to stack in reverse order
- self.function_arguments.reverse()
- self.codegen.function_call(self.function_call_index, self.function_arguments)
- self.codegen.restore_used_registers()
- return_type = self.symtab.get_type(self.function_call_index)
- #restore previous function call data
- self.function_call_index = self.function_call_stack.pop()
- self.function_arguments = self.function_arguments_stack.pop()
- register = self.codegen.take_register(return_type)
- #move result to a new free register, to allow the next function call
- self.codegen.move(self.codegen.take_function_register(return_type), register)
- return register
-
- def relexp_action(self, text, loc, arg):
- """Code executed after recognising a relexp expression (something relop something)"""
- if DEBUG > 0:
- print("REL_EXP:",arg)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- exshared.setpos(loc, text)
- if not self.symtab.same_types(arg[0], arg[2]):
- raise SemanticException("Invalid operands for operator '{0}'".format(arg[1]))
- self.codegen.compare(arg[0], arg[2])
- #return relational operator's code
- self.relexp_code = self.codegen.relop_code(arg[1], self.symtab.get_type(arg[0]))
- return self.relexp_code
-
- def andexp_action(self, text, loc, arg):
- """Code executed after recognising a andexp expression (something and something)"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("AND+EXP:",arg)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- label = self.codegen.label("false{0}".format(self.false_label_number), True, False)
- self.codegen.jump(self.relexp_code, True, label)
- self.andexp_code = self.relexp_code
- return self.andexp_code
-
- def logexp_action(self, text, loc, arg):
- """Code executed after recognising logexp expression (something or something)"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("LOG_EXP:",arg)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- label = self.codegen.label("true{0}".format(self.label_number), True, False)
- self.codegen.jump(self.relexp_code, False, label)
- self.codegen.newline_label("false{0}".format(self.false_label_number), True, True)
- self.false_label_number += 1
-
- def if_begin_action(self, text, loc, arg):
- """Code executed after recognising an if statement (if keyword)"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("IF_BEGIN:",arg)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- self.false_label_number += 1
- self.label_number = self.false_label_number
- self.codegen.newline_label("if{0}".format(self.label_number), True, True)
-
- def if_body_action(self, text, loc, arg):
- """Code executed after recognising if statement's body"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("IF_BODY:",arg)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- #generate conditional jump (based on last compare)
- label = self.codegen.label("false{0}".format(self.false_label_number), True, False)
- self.codegen.jump(self.relexp_code, True, label)
- #generate 'true' label (executes if condition is satisfied)
- self.codegen.newline_label("true{0}".format(self.label_number), True, True)
- #save label numbers (needed for nested if/while statements)
- self.label_stack.append(self.false_label_number)
- self.label_stack.append(self.label_number)
-
- def if_else_action(self, text, loc, arg):
- """Code executed after recognising if statement's else body"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("IF_ELSE:",arg)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- #jump to exit after all statements for true condition are executed
- self.label_number = self.label_stack.pop()
- label = self.codegen.label("exit{0}".format(self.label_number), True, False)
- self.codegen.unconditional_jump(label)
- #generate final 'false' label (executes if condition isn't satisfied)
- self.codegen.newline_label("false{0}".format(self.label_stack.pop()), True, True)
- self.label_stack.append(self.label_number)
-
- def if_end_action(self, text, loc, arg):
- """Code executed after recognising a whole if statement"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("IF_END:",arg)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- self.codegen.newline_label("exit{0}".format(self.label_stack.pop()), True, True)
-
- def while_begin_action(self, text, loc, arg):
- """Code executed after recognising a while statement (while keyword)"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("WHILE_BEGIN:",arg)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- self.false_label_number += 1
- self.label_number = self.false_label_number
- self.codegen.newline_label("while{0}".format(self.label_number), True, True)
-
- def while_body_action(self, text, loc, arg):
- """Code executed after recognising while statement's body"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("WHILE_BODY:",arg)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- #generate conditional jump (based on last compare)
- label = self.codegen.label("false{0}".format(self.false_label_number), True, False)
- self.codegen.jump(self.relexp_code, True, label)
- #generate 'true' label (executes if condition is satisfied)
- self.codegen.newline_label("true{0}".format(self.label_number), True, True)
- self.label_stack.append(self.false_label_number)
- self.label_stack.append(self.label_number)
-
- def while_end_action(self, text, loc, arg):
- """Code executed after recognising a whole while statement"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("WHILE_END:",arg)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- #jump to condition checking after while statement body
- self.label_number = self.label_stack.pop()
- label = self.codegen.label("while{0}".format(self.label_number), True, False)
- self.codegen.unconditional_jump(label)
- #generate final 'false' label and exit label
- self.codegen.newline_label("false{0}".format(self.label_stack.pop()), True, True)
- self.codegen.newline_label("exit{0}".format(self.label_number), True, True)
-
- def program_end_action(self, text, loc, arg):
- """Checks if there is a 'main' function and the type of 'main' function"""
- exshared.setpos(loc, text)
- if DEBUG > 0:
- print("PROGRAM_END:",arg)
- if DEBUG == 2: self.symtab.display()
- if DEBUG > 2: return
- index = self.symtab.lookup_symbol("main",SharedData.KINDS.FUNCTION)
- if index == None:
- raise SemanticException("Undefined reference to 'main'", False)
- elif self.symtab.get_type(index) != SharedData.TYPES.INT:
- self.warning("Return type of 'main' is not int", False)
-
- def parse_text(self,text):
- """Parse string (helper function)"""
- try:
- return self.rProgram.ignore(cStyleComment).parseString(text, parseAll=True)
- except SemanticException as err:
- print(err)
- exit(3)
- except ParseException as err:
- print(err)
- exit(3)
-
- def parse_file(self,filename):
- """Parse file (helper function)"""
- try:
- return self.rProgram.ignore(cStyleComment).parseFile(filename, parseAll=True)
- except SemanticException as err:
- print(err)
- exit(3)
- except ParseException as err:
- print(err)
- exit(3)
-
-##########################################################################################
-##########################################################################################
-if 0:
- #main program
- mc = MicroC()
- output_file = "output.asm"
-
- if len(argv) == 1:
- input_file = stdin
- elif len(argv) == 2:
- input_file = argv[1]
- elif len(argv) == 3:
- input_file = argv[1]
- output_file = argv[2]
- else:
- usage = """Usage: {0} [input_file [output_file]]
- If output file is omitted, output.asm is used
- If input file is omitted, stdin is used""".format(argv[0])
- print(usage)
- exit(1)
- try:
- parse = stdin if input_file == stdin else open(input_file,'r')
- except Exception:
- print("Input file '%s' open error" % input_file)
- exit(2)
- mc.parse_file(parse)
- #if you want to see the final symbol table, uncomment next line
- #mc.symtab.display()
- try:
- out = open(output_file, 'w')
- out.write(mc.codegen.code)
- out.close
- except Exception:
- print("Output file '%s' open error" % output_file)
- exit(2)
-
-##########################################################################################
-##########################################################################################
-
-if __name__ == "__main__":
-
- test_program_example = """
- int a;
- int b;
- int c;
- unsigned d;
-
- int fun1(int x, unsigned y) {
- return 123;
- }
-
- int fun2(int a) {
- return 1 + a * fun1(a, 456u);
- }
-
- int main(int x, int y) {
- int w;
- unsigned z;
- if (9 > 8 && 2 < 3 || 6 != 5 && a <= b && c < x || w >= y) {
- a = b + 1;
- if (x == y)
- while (d < 4u)
- x = x * w;
- else
- while (a + b < c - y && x > 3 || y < 2)
- if (z > d)
- a = a - 4;
- else
- b = a * b * c * x / y;
- }
- else
- c = 4;
- a = fun1(x,d) + fun2(fun1(fun2(w + 3 * 2) + 2 * c, 2u));
- return 2;
- }
- """
-
- mc = MicroC()
- mc.parse_text(test_program_example)
- print(mc.codegen.code)
\ No newline at end of file |