Exemplo n.º 1
0
    def test_scope_local(self):
        u = Scope(None)
        i = Integer()
        u.insert("Integer", i)
        p = Scope(u)
        c = Constant(i, 5)
        p.insert("const", c)

        self.assertTrue(u.local("Integer"))
        self.assertTrue(p.local("const"))
        self.assertEqual(None, u.find("const"))
        self.assertEqual(i, p.find("Integer"))
Exemplo n.º 2
0
 def test_scope_insert(self):
     s = Scope(None)
     i = Integer()
     s.insert("Integer", i)
     self.assertEqual(i, s.find("Integer"))
Exemplo n.º 3
0
class Parser:
    def __init__(self, scanner, output_object, parser_type, style):
        """Initializes the parser with a scanner and output style (text or
        graph).
        """
        self.scanner = scanner
        self.last = None
        self.current = None
        self.style = style
        self.parser_type = parser_type
        self.output_object = output_object

        # The stack is used to store non-terminals for output. The
        # ParserObserver uses it to determine indentation and construct
        # edges for the graph.
        self.stack = []

        # Used as IDs for non-terminals in the graph.
        self.graph_index = 0

        # Number of tokens skipped after an error has occured.
        self.num_tokens_skipped = 0

        # Whether to supress error messages.
        self.supress = False

        # Whether to display output (will be false when an error occurs).
        self.display = True

        # The last token mismatched. Used to make sure mismatched tokens do not
        # increment supression counters more than once.
        self.last_mismatch = None

        self.weak_tokens = {";", ":", ",", ")", "]", "END"}

    def next(self):
        """Get the next token. From Peter's lecture."""
        self.last = self.current
        self.current = self.scanner.next()
        while self.current.kind == "INVALID":
            self.current = self.scanner.next()

    def invalid_token(self, e):
        """Handles an invalid token. Will print an error message if not
        supressed.
        """

        if not self.supress:
            self.supress = True
            self.num_tokens_skipped = 0
            self.display = False
            sys.stderr.write(e.message + "\n")

        self.last_mismatch = self.current

    def increment_skip(self):
        """If errors are being supressed, increment tokens skipped."""

        # Do not increment the counter if the token has already been
        # mismatched before.
        if self.supress and (self.last_mismatch == None or self.last_mismatch != self.current):
            self.num_tokens_skipped += 1
            if self.num_tokens_skipped > 7:
                self.supress = False
                self.num_tokens_skipped = 0

    @ParserObserver.terminal
    def match(self, kind):
        """Match a token to kind(s). Parts from Peter's lecture.
        Return the token matched. """
        try:

            self.increment_skip()

            # Match token with a set of kinds.
            if type(kind) is set:

                # Token matched.
                if self.current.kind in kind:
                    self.next()
                    return self.last
                # Token mismatch. Throw an exception.
                else:
                    raise ParserInvalidKindError(kind, self.current)

            # Match token with a single kind
            else:
                # Token matched.
                if self.current.kind == kind:
                    self.next()
                    return self.last
                # Token mismatch. Throw an exception
                else:
                    raise ParserInvalidKindError(kind, self.current)
        except ParserInvalidKindError as e:
            # Handle the exception.
            self.invalid_token(e)

    def sync(self, kinds, terminals):
        """Resync the parser when there is a mismatch of non-weak symbols.
        The function will skip tokens until reaching a strong token in kinds
        or a symbol that terminates the program.
        """

        # Skip until finding a strong symbol in kinds.
        while self.current.kind not in kinds:
            # Raise an exception due to mismatch
            try:
                raise ParserInvalidKindError("Type", self.current)
            except ParserInvalidKindError as e:
                self.invalid_token(e)

            # The program ended before the parser is able to sync. end the loop.
            if self.current.kind in terminals:
                return False

            # Skip the current token.
            self.next()

        # Sucessfully re-synced the parser.
        return True

    def insert(self, name, value):
        """Insert an entry into the symbol table."""
        try:
            if self.current_scope.local(name.value):
                raise DuplicateDeclarationError(name)
            self.current_scope.insert(name.value, value)
        except DuplicateDeclarationError as e:
            self.invalid_token(e)

    def find(self, name):
        """Find an identifier in the symbol table."""
        try:
            entry = self.current_scope.find(name)
            if entry == None:
                raise EntryNotFoundInSymbolTable(self.last)
            return entry
        except EntryNotFoundInSymbolTable as e:
            self.invalid_token(e)

    def set_record_scope(self, entry):
        """Set the current scope to be a record's scope."""
        if isinstance(entry, Variable) or isinstance(entry, Field):
            # Record can be either in variable or field.
            if isinstance(entry.type, Array) and isinstance(entry.type.element_type, Record):
                # An array of record.
                self.record_scope = entry.type.element_type.scope
            elif isinstance(entry.type, Record):
                # a record variable.
                self.record_scope = entry.type.scope
            else:
                self.record_scope = None
        else:
            self.record_scope = None

    def negate_relation(self, relation):
        """Negate the relation in a condition."""
        negation_map = {"=": "#", "#": "=", ">": "<=", "<": ">=", ">=": "<", "<=": ">"}
        return negation_map[relation]

    @ParserObserver.selector
    def selector(self, location):
        """Match the grammar of a selector."""
        ret = None

        # 0 to many patterns.
        while True:
            if self.current.kind == "[":
                self.match("[")

                self.stack.append("ExpressionList")
                expressions = self.expression_list()

                self.match("]")

                for expression in expressions:
                    if not isinstance(location, Location) or not isinstance(location.type, Array):
                        # Not an array variable.
                        try:
                            raise InvalidArray(location.type, self.last)
                        except InvalidArray as e:
                            self.invalid_token(e)

                    cur = Index(location, expression)
                    cur.token = self.last
                    ret = cur
                    location = cur

            elif self.current.kind == ".":
                self.match(".")
                name = self.match("identifier")
                if name == None:
                    continue

                if self.record_scope == None:
                    # Not a record variable.
                    try:
                        raise InvalidRecord(location.type, self.last)
                    except InvalidRecord as e:
                        self.invalid_token(e)
                        break

                # Access the record's scope and find the variable with name.
                self.current_scope = self.record_scope
                entry = self.find(name.value)
                var = VariableAST(name.value, entry)

                self.set_record_scope(entry)

                cur = FieldAST(location, var)
                ret = cur
                location = cur
            else:
                # Pattern ended.
                break

        self.current_scope = self.program_scope
        return ret

    @ParserObserver.non_terminal
    def designator(self):
        """Match the grammar for a designator."""
        name = self.match("identifier")
        if name == None:
            return None
        entry = self.find(name.value)
        if isinstance(entry, Constant):
            var = Number(entry)
        elif isinstance(entry, Variable):
            var = VariableAST(name.value, entry)
        else:
            # Identifier in designator cannot be a type.
            try:
                raise InvalidDesignator(entry, self.last)
            except InvalidDesignator as e:
                self.invalid_token(e)
                return VariableAST("INVALID", Variable(Invalid()))

        self.set_record_scope(entry)

        self.stack.append("Selector")
        ret = self.selector(var)

        if ret == None:
            ret = var

        if not isinstance(ret, Location) and not isinstance(ret, Number):
            try:
                raise InvalidDesignator(ret.type, self.last)
            except InvalidDesignator as e:
                self.invalid_token(e)
                return VariableAST("INVALID", Variable(Invalid()))

        return ret

    @ParserObserver.non_terminal
    def identifier_list(self):
        """Match the grammar for a identifier list."""
        id_list = []
        identifier = self.match("identifier")
        id_list.append(identifier)

        # 0 to many patterns.
        while True:
            if self.current.kind == ",":
                self.match(",")
                identifier = self.match("identifier")
                id_list.append(identifier)
            else:
                # Pattern ended.
                break

        return id_list

    @ParserObserver.non_terminal
    def expression_list(self):
        """Match the grammar for a expression list."""
        exp_list = []
        self.stack.append("Expression")
        exp_list.append(self.expression())

        # 0 to many patterns.
        while True:
            if self.current.kind == ",":
                self.match(",")

                self.stack.append("Expression")
                expression = self.expression()
                exp_list.append(expression)
            else:
                # Pattern ended.
                break

        return exp_list

    @ParserObserver.non_terminal
    def read(self):
        """Match the grammar for a read."""
        self.match("READ")

        self.stack.append("Designator")
        location = self.designator()

        if not isinstance(location, Location) or location.type != self.integer_type:
            # Not an integer variable.
            try:
                raise InvalidRead(location.type, self.last)
            except InvalidRead as e:
                self.invalid_token(e)

        r = Read(location)
        r.token = self.last
        return r

    @ParserObserver.non_terminal
    def write(self):
        """Match the grammar for a write."""
        self.match("WRITE")

        self.stack.append("Expression")
        expression = self.expression()

        if expression.type != self.integer_type:
            # Not an integer.
            try:
                raise InvalidWrite(expression.type, self.last)
            except InvalidWrite as e:
                self.invalid_token(e)

        return Write(expression)

    @ParserObserver.non_terminal
    def condition(self):
        """Match the grammar for a condition."""
        self.stack.append("Expression")
        left = self.expression()

        # Match a token with a set of kinds.
        kinds = {"=", "#", "<", ">", "<=", ">="}
        relation = self.match(kinds)

        self.stack.append("Expression")
        right = self.expression()

        if left.type != self.integer_type or right.type != self.integer_type:
            # Either is not of Constant or Variable of type integer.
            try:
                raise InvalidCondition(left.type, right.type, self.last)
            except InvalidCondition as e:
                self.invalid_token(e)

        return Condition(left, right, relation.kind)

    @ParserObserver.non_terminal
    def while_instruction(self):
        """Match the grammar for a while."""
        self.match("WHILE")

        self.stack.append("Condition")
        condition = self.condition()

        self.match("DO")

        self.stack.append("Instructions")
        instructions = self.instructions()

        self.match("END")

        # Replaces while with repeat in AST
        left = condition.left
        right = condition.right
        inverse = self.negate_relation(condition.relation)

        inverse_left = copy.deepcopy(left)
        inverse_right = copy.deepcopy(right)

        inverse_condition = Condition(inverse_left, inverse_right, inverse)

        repeat = Repeat(inverse_condition, instructions)
        repeat.next = None
        return If(condition, repeat, None)

    @ParserObserver.non_terminal
    def repeat(self):
        """Match the grammar for a repeat."""
        self.match("REPEAT")

        self.stack.append("Instructions")
        instructions = self.instructions()

        self.match("UNTIL")

        self.stack.append("Condition")
        condition = self.condition()

        self.match("END")

        return Repeat(condition, instructions)

    @ParserObserver.non_terminal
    def if_instruction(self):
        """Match the grammar for an if."""
        self.match("IF")

        self.stack.append("Condition")
        condition = self.condition()

        self.match("THEN")

        self.stack.append("Instructions")
        instructions_true = self.instructions()
        instructions_false = None

        # Optional match.
        if self.current.kind == "ELSE":
            self.match("ELSE")
            self.stack.append("Instructions")
            instructions_false = self.instructions()

        self.match("END")

        return If(condition, instructions_true, instructions_false)

    @ParserObserver.non_terminal
    def assign(self):
        """Match the grammar for an assign."""
        self.stack.append("Designator")
        location = self.designator()

        self.match(":=")

        self.stack.append("Expression")
        expression = self.expression()

        if not isinstance(location, Location) or location.type != expression.type:
            # Left side is not variable, or right side is a type, or types of
            # both sides do not match up.
            try:
                raise InvalidAssignment(location.type, expression.type, self.last)
            except InvalidAssignment as e:
                self.invalid_token(e)

        return Assign(location, expression)

    @ParserObserver.non_terminal
    def instruction(self):
        """Match the grammar for an instruction."""

        # The set of kinds to be matched.
        kinds = {"identifier", "IF", "REPEAT", "WHILE", "READ", "WRITE"}

        # The symbols that signal end of an instruction. In this case,
        # . and eof singal the end of the program, which must be the
        # end of an instruction.
        terminals = {"END", ".", "eof"}

        if self.current.kind == "identifier":
            self.stack.append("Assign")
            return self.assign()
        elif self.current.kind == "IF":
            self.stack.append("If")
            return self.if_instruction()
        elif self.current.kind == "REPEAT":
            self.stack.append("Repeat")
            return self.repeat()
        elif self.current.kind == "WHILE":
            self.stack.append("While")
            return self.while_instruction()
        elif self.current.kind == "READ":
            self.stack.append("Read")
            return self.read()
        elif self.current.kind == "WRITE":
            self.stack.append("Write")
            return self.write()
        elif self.current.kind == "END":
            # END signifies the end of instructions.
            return None
        else:
            # No match is made and no END of ending instructions. An error has
            # occured and the symbol missing is not weak. The parser must resync
            # with the code.
            if not self.sync(kinds, terminals):
                return None

    @ParserObserver.non_terminal
    def instructions(self):
        """Match the grammar for instructions."""
        self.stack.append("Instruction")
        head = instruction = self.instruction()

        # 0 or more patterns.
        while True:

            # These symbols preceed or end instructions. End the loop.
            if self.current.kind in {"THEN", "ELSE", "REPEAT", "DO", "END", "UNTIL", ".", "eof"}:
                break

            # A normal or a mismatch. If necessary resync will happen in
            # instruction.
            else:
                self.match(";")

                self.stack.append("Instruction")
                next_instruction = self.instruction()
                if next_instruction == None:
                    continue
                instruction.next = next_instruction
                instruction = instruction.next

        if instruction != None:
            instruction.next = None
        return head

    @ParserObserver.non_terminal
    def factor(self):
        """Match the grammar for a factor."""
        ret = None
        if self.current.kind == "integer":
            num = self.match("integer")
            const = Constant(self.integer_type, num.value)
            ret = Number(const)
        elif self.current.kind == "identifier":
            self.stack.append("Designator")
            ret = self.designator()

        elif self.current.kind == "(":
            self.match("(")

            self.stack.append("Expression")
            ret = self.expression()

            self.match(")")
        else:
            # An error has occured. Raise and catch it so we can handle the
            # error without crashing the program.
            try:
                self.increment_skip()
                raise ParserInvalidKindError("Factor", self.current)
            except ParserInvalidKindError as e:
                self.invalid_token(e)
        return ret

    @ParserObserver.non_terminal
    def term(self):
        """Match the grammar for a term."""
        self.stack.append("Factor")
        operator_map = {"*": "*", "DIV": "/", "MOD": "%"}
        cur = self.factor()

        # 0 or more patterns.
        while True:
            if self.current.kind == "*" or self.current.kind == "DIV" or self.current.kind == "MOD":
                if self.current.kind == "*":
                    operator = self.match("*")
                elif self.current.kind == "DIV":
                    operator = self.match("DIV")
                else:
                    operator = self.match("MOD")

                self.stack.append("Factor")
                var = self.factor()

                if isinstance(cur, Number) and isinstance(var, Number):
                    # Constant folding.
                    try:
                        val = int(eval(str(cur.entry.value) + str(operator_map[operator.kind]) + str(var.entry.value)))
                    except ZeroDivisionError:
                        # Divide by 0 error.
                        self.invalid_token(DivideByZero(self.last))
                        break

                    const = Constant(self.integer_type, val)
                    cur = Number(const)
                else:
                    if isinstance(cur, VariableAST) and (
                        isinstance(cur.variable, Array) or isinstance(cur.variable, Record)
                    ):
                        # Trying to perform an operation on non-integers.
                        try:
                            raise InvalidArithmeticOperation(cur, self.last)
                        except InvalidArithmeticOperation as e:
                            self.invalid_token(e)

                    else:
                        cur = Binary(operator.kind, cur, var, self.integer_type)
                        # Remember the token for node in case of run time errors.
                        cur.token = self.last

            else:
                # Pattern ended.
                break

        return cur

    @ParserObserver.non_terminal
    def expression(self):
        """Match the grammar of an expression."""
        operator = None

        if self.current.kind == "+":
            operator = self.match("+")
        elif self.current.kind == "-":
            operator = self.match("-")

        self.stack.append("Term")
        cur = self.term()
        if operator != None and operator.kind == "-":
            # Negate the expression if needed.
            if isinstance(cur, Number):
                # Constant folding.
                cur = Number(Constant(self.integer_type, eval(str(operator.kind) + str(cur.entry.value))))
            else:
                if isinstance(cur, VariableAST) and (
                    isinstance(cur.variable, Array) or isinstance(cur.variable, Record)
                ):
                    # Trying to perform an operation on non-integers.
                    try:
                        raise InvalidArithmeticOperation(cur, self.last)
                    except InvalidArithmeticOperation as e:
                        self.invalid_token(e)

                else:
                    cur = Binary(operator.kind, Number(Constant(self.integer_type, 0)), cur, self.integer_type)

                    # Remember the token for node in case of run time errors.
                    cur.token = self.last

        # 0 or more patterns.
        while True:
            if self.current.kind in {"+", "-"}:
                if self.current.kind == "+":
                    operator = self.match("+")
                else:
                    operator = self.match("-")

                self.stack.append("Term")
                var = self.term()
                if isinstance(cur, Number) and isinstance(var, Number):
                    # Constant folding.
                    cur = Number(
                        Constant(
                            self.integer_type, eval(str(cur.entry.value) + str(operator.kind) + str(var.entry.value))
                        )
                    )
                else:
                    if isinstance(cur, VariableAST) and (
                        isinstance(cur.variable, Array) or isinstance(cur.variable, Record)
                    ):
                        # Trying to perform an operation on non-integers.
                        try:
                            raise InvalidArithmeticOperation(cur, self.last)
                        except InvalidArithmeticOperation as e:
                            self.invalid_token(e)
                    else:
                        cur = Binary(operator.kind, cur, var, self.integer_type)

                        # Remember token for node in case of run time errors.
                        cur.token = self.last
            else:
                # Pattern ended.
                break

        if cur == None:
            # Return an invalid variable node in case of parsing errors.
            cur = VariableAST("INVALID", Variable(Invalid()))
        return cur

    @ParserObserver.non_terminal
    def type(self):
        """Match the grammar of a type."""
        type_obj = None
        if self.current.kind == "identifier":
            name = self.match("identifier")

            # Return the type object found in current scope
            try:
                type_obj = self.current_scope.find(name.value)
                if type_obj == None:
                    raise IdentifierUsedBeforeDeclared(name)
                elif not isinstance(type_obj, Type):
                    raise IdentifierDoesNotDenoteType(name, type_obj)
            except (IdentifierUsedBeforeDeclared, IdentifierDoesNotDenoteType) as e:
                type_obj = Invalid()
                self.invalid_token(e)

        elif self.current.kind == "ARRAY":
            self.match("ARRAY")

            self.stack.append("Expression")
            size = self.expression()

            try:
                if not isinstance(size, Number) or (isinstance(size, Number) and size.entry.value <= 0):
                    # Arrays need to have size greater than 0.
                    raise InvalidArraySize(size, self.last)
            except InvalidArraySize as e:
                type_obj = Invalid()
                self.invalid_token(e)
                return type_obj

            self.match("OF")

            self.stack.append("Type")
            t = self.type()

            # Create an Array type object.
            type_obj = Array(t, size.entry.value)
        elif self.current.kind == "RECORD":
            self.match("RECORD")

            # Create a new scope for the record and set the current scope
            # to the record scope.
            record_scope = Scope(self.current_scope)
            self.current_scope = record_scope

            # 0 or more patterns.
            while True:
                if self.current.kind == "identifier":
                    self.stack.append("IdentifierList")
                    id_list = self.identifier_list()

                    self.match(":")

                    self.stack.append("Type")
                    t = self.type()

                    self.match(";")

                    # Insert the type objects into the current scope.
                    field = Field(t)
                    for name in id_list:
                        self.insert(name, field)
                else:
                    # pattern ended.
                    break
            self.match("END")

            # Create a Record object with the current scope. Also set the
            # current scope to the outer scope and remove the outer pointer.
            type_obj = Record(self.current_scope)
            outer = self.current_scope.outer
            self.current_scope.outer = None
            self.current_scope = outer
        else:
            # An error has occured. Raise and catch it so we can handle the
            # error without crashing the program.
            try:
                self.increment_skip()
                raise ParserInvalidKindError("Type", self.current)
            except ParserInvalidKindError as e:
                self.invalid_token(e)

        return type_obj

    @ParserObserver.non_terminal
    def const_decl(self):
        """Match the grammar of a const declaration."""
        self.match("CONST")

        # 0 or more patterns.
        while True:
            if self.current.kind == "identifier":
                name = self.match("identifier")

                self.match("=")

                self.stack.append("Expression")
                value = self.expression()

                self.match(";")

                try:
                    if isinstance(value, Number):
                        # Create Constant object and insert into symbol table.
                        const = Constant(self.integer_type, value.entry.value)
                        self.insert(name, const)
                    else:
                        self.insert(name, Variable(Invalid()))
                        raise InvalidConstantValue(value, self.last)

                except InvalidConstantValue as e:
                    self.invalid_token(e)

            else:
                # Pattern ended.
                break

    @ParserObserver.non_terminal
    def type_decl(self):
        """Match the grammar of a type declaration."""
        self.match("TYPE")

        # 0 or more patterns.
        while True:
            if self.current.kind == "identifier":
                name = self.match("identifier")
                self.match("=")

                self.stack.append("Type")
                t = self.type()

                self.match(";")

                self.insert(name, t)
            else:
                # Pattern ended.
                break

    @ParserObserver.non_terminal
    def var_decl(self):
        """Match the grammar of a var declaration."""
        self.match("VAR")

        # 0 or more patterns.
        while True:
            if self.current.kind == "identifier":
                self.stack.append("IdentifierList")
                id_list = self.identifier_list()

                self.match(":")

                self.stack.append("Type")
                var_type = self.type()

                self.match(";")

                # Create Variable object(s) and insert them into symbol table.
                for name in id_list:
                    var = Variable(var_type)
                    self.insert(name, var)
            else:
                # Pattern ended.
                break

    @ParserObserver.non_terminal
    def declarations(self):
        """Match the grammar of declarations."""

        # Strong symbols for resync
        kinds = {"CONST", "TYPE", "VAR"}

        # Signify end of declaration.
        terminals = {".", "eof", "BEGIN"}

        # 0 or more patterns.
        while True:
            if self.current.kind == "CONST":
                self.stack.append("ConstDecl")
                self.const_decl()
            elif self.current.kind == "TYPE":
                self.stack.append("TypeDecl")
                self.type_decl()
            elif self.current.kind == "VAR":
                self.stack.append("VarDecl")
                self.var_decl()

            # A normal end of declaration.
            elif self.current.kind in {"BEGIN", "END"}:
                break

            # A strong symbol mismatch. Need to resync the parser.
            else:
                if not self.sync(kinds, terminals):
                    return

    @ParserObserver.non_terminal
    def program(self):
        """Match the grammar of the program."""

        # Create the program scope
        self.program_scope = Scope(self.universe_scope)
        self.current_scope = self.program_scope

        self.match("PROGRAM")
        begin_name = self.match("identifier")
        self.match(";")

        self.stack.append("Declarations")
        self.declarations()

        # AST instruction tree.
        tree = None

        # Optional begin symbol.
        if self.current.kind == "BEGIN":
            self.match("BEGIN")

            self.stack.append("Instructions")
            tree = self.instructions()

        self.match("END")
        end_name = self.match("identifier")

        # Throw an exception if there is a program name mismatch
        if (
            begin_name == None
            or end_name == None
            or begin_name.value == None
            or end_name.value == None
            or begin_name.value != end_name.value
        ):
            try:
                raise ProgramNameMismatch(begin_name, end_name)
            except ProgramNameMismatch as e:
                self.invalid_token(e)

        self.match(".")
        self.match("eof")

        return tree

    def parse(self):
        """Method called by sc to parse the code."""

        # Create the universe scope.
        self.universe_scope = Scope(None)
        self.record_scope = None
        self.integer_type = Integer()
        self.universe_scope.insert("INTEGER", self.integer_type)

        # Start by getting the first token.
        self.next()

        # Start parsing by matching the grammar of program.
        self.stack.append("Program")
        tree = self.program()

        # Display the output only if no error has occured.
        if self.display:
            if self.parser_type == "-t":
                # Set the program scope if printing symbol table.
                self.output_object.program_scope = self.program_scope
            elif self.parser_type == "-a":
                # Set the program scope and instruction tree for AST output.
                self.output_object.program_scope = self.program_scope
                self.output_object.tree = tree
            elif self.parser_type == "-i":
                # Set the environment and instruction tree for interpreter.
                visitor = SymbolTableVisitor()
                self.output_object.env = self.current_scope.accept(visitor)
                self.output_object.tree = tree

            self.output_object.display()
Exemplo n.º 4
0
class Parser:

    """Takes a list of tokens and performs semantic analysis
       to check if input adheres to grammar. Outputs to stdout
       a textual representation of the CST via the call stack.
       Parser is also capable of outputting graphical output
    """

    # initializes Parser instance and parses a list of tokens
    # cmd line arguments determine output type
    def __init__(self, observer = Observer(), token_list=[], print_symbol_table = 0, visitor = Visitor()):
        self.current = 0 # current position in token list
        self.token_list = token_list # token list received from scanner
        self.kind_map = Token.kind_map # dictionary of token kinds
        self.observer = observer # output class determined by cmd line arguments
        self.total_error_flag = 0 # detects if an error occurs anywhere in the program
        self.universe = Scope(None) # universe scope
        self.universe.insert("INTEGER", integerInstance) # universe scope only holds integer
        self.program_scope = Scope(self.universe) # program scope to hold program names
        self.current_scope = self.program_scope # current scope for switching between scopes
        self.print_symbol_table = print_symbol_table # determines whether to print cst or st
        self.visitor = visitor

    # parse the token list
    def parse(self):
        instructions = self._program()
        # do not print any output if error occurs
        if self.total_error_flag == 0:
            if self.print_symbol_table == 0:
                self.observer.print_output()
            elif self.print_symbol_table == 1:
                self.visitor.visitScope(self.program_scope)
                self.visitor.end()
            elif self.print_symbol_table == 2:
                currinstruction = instructions
                self.visitor.start()
                # while(currinstruction is not None):
                currinstruction.visit(self.visitor)
                # currinstruction = currinstruction._next
            elif self.print_symbol_table == 3:
                # build environment
                pass

    def build_environment(self):
        pass

    # check if the currently parsed token is a token we are
    # expecting to find
    # kind = expected kind of token
    def match(self, kind):
        if self.token_list[self.current].kind == self.kind_map[kind]:
            self.observer.print_token(self.token_list[self.current])
            self.current += 1
            ### for returning identifier names
            return self.token_list[self.current-1].get_token_name()
        else:
            self.total_error_flag = 1
            sys.stderr.write("error: expected token kind \'{0}\', "
                             "received unexpected token \'{1}\'"
                             " @({2}, {3})".format(kind, self.token_list[self.current],
                                                   self.token_list[self.current].start_position,
                                                   self.token_list[self.current].end_position) + '\n')

    # set expectation of creating a program
    # by following the program production
    def _program(self):
        # print "Program"
        self.observer.begin_program()
        self.match("PROGRAM")
        name = self.match("IDENTIFIER")
        self.match(";")
        self._declarations()
        instructions = None
        if self.token_list[self.current].kind == self.kind_map["BEGIN"]:
            self.match("BEGIN")
            instructions = self._instructions()
        self.match("END")
        end_name = self.match("IDENTIFIER")
        self.match(".")
        self.observer.end_program()
        if not name == end_name:
            self.total_error_flag = 1
            sys.stderr.write("error: program identifier does not match end identifier\n")
        if not self.token_list[self.current].kind == self.kind_map["EOF"]:
            self.total_error_flag = 1
            sys.stderr.write("error: trash detected after program end:\n"
                             "Token \'{0}\'".format(self.token_list[self.current]) + '\n')
        return instructions

    # set expectation of creating a declaration
    # by following the declaration production
    def _declarations(self):
        self.observer.begin_declarations()
        while (self.token_list[self.current].kind == self.kind_map["CONST"]) or \
            (self.token_list[self.current].kind == self.kind_map["TYPE"]) or \
            (self.token_list[self.current].kind == self.kind_map["VAR"]):
            if self.token_list[self.current].kind == self.kind_map["CONST"]:
                self._constdecl()
            elif self.token_list[self.current].kind == self.kind_map["TYPE"]:
                self._typedecl()
            elif self.token_list[self.current].kind == self.kind_map["VAR"]:
                self._vardecl()
            else:
                pass
        self.observer.end_declarations()

    # set expectation of creating a ConstDecl
    # by following the ConstDecl production
    def _constdecl(self):
        # print "ConstDecl"
        self.observer.begin_constdecl()
        self.match("CONST")
        # return_obj = None
        while self.token_list[self.current].kind == self.kind_map["IDENTIFIER"]:
            name = self.match("IDENTIFIER")
            # check if const name in local scope
            if self.program_scope.local(name):
                self.total_error_flag = 1
                sys.stderr.write("error: attempted to redefine identifier\n")
            self.match("=")
            e = self._expression()
            if not isinstance(e, NumberNode):
                self.total_error_flag = 1
                sys.stderr.write("error: constdecl received nonconst exp\n")
                # exit(1)
            self.match(";")
            #return_obj = e
            # add it we formed a constant
            if isinstance(e.constant, Constant): # is it constant object or constant name
                self.program_scope.insert(name, e.constant)
            else:
                self.total_error_flag = 1
                sys.stderr.write("error: attempted to define const with nonconst object\n")
        self.observer.end_constdecl()
        # return return_obj

    # set expectation of creating a TypeDecl
    # by following the TypeDecl production
    def _typedecl(self):
        # print "TypeDecl"
        self.observer.begin_typedecl()
        self.match("TYPE")
        #return_type = None
        while self.token_list[self.current].kind == self.kind_map["IDENTIFIER"]:
            name = self.match("IDENTIFIER")
            self.match("=")
            # type of current Type
            return_type = self._type()
            self.match(";")
            if return_type is None:
                self.total_error_flag = 1
                sys.stderr.write("error: type not found\n")
                return None
            if not self.current_scope.local(name):
                self.current_scope.insert(name, return_type)
            else:
                self.total_error_flag = 1
                sys.stderr.write("error: attempting to redefine variable\n")
        self.observer.end_typedecl()
        # return return_type

    # set expectation of creating a VarDecl
    # by following the VarDecl production
    def _vardecl(self):
        # print "VarDecl"
        self.observer.begin_vardecl()
        self.match("VAR")
        id_list = []
        return_type = None
        while self.token_list[self.current].kind == self.kind_map["IDENTIFIER"]:
            id_list = self._identifier_list()
            self.match(":")
            return_type = self._type()
            # type of current identifier
            if return_type is None:
                self.total_error_flag = 1
                sys.stderr.write("error: type not found\n")
                return None
            self.match(";")
            for name in id_list:
                if not self.current_scope.local(name):
                    self.current_scope.insert(name, Variable(return_type))
                else:
                    self.total_error_flag = 1
                    sys.stderr.write("error: attempting to redefine var\n")
        self.observer.end_vardecl()
        return return_type

    # set expectation of creating a Type
    # by following the Type production
    def _type(self):
        # print "Type"
        self.observer.begin_type()
        return_type = None
        if self.token_list[self.current].kind == self.kind_map["IDENTIFIER"]:
            name = self.match("IDENTIFIER")
            # get the name of an identifier
            return_type = self.current_scope.find(name)
            if return_type is None:
                self.total_error_flag = 1
                sys.stderr.write("error: indentifier not found. attempting to assign "
                                 "uncreated type\n")
                self.observer.end_type()
                return None
            if isinstance(return_type, Type):
                self.observer.end_type()
                return return_type
            else:
                self.total_error_flag = 1
                sys.stderr.write("error: found not Type object\n")
                return None
        elif self.token_list[self.current].kind == self.kind_map["ARRAY"]:
            self.match("ARRAY")
            length = None
            e = self._expression()
            if isinstance(e, NumberNode):
                length = e.constant.value
            else:
                self.total_error_flag = 1
                sys.stderr.write("error: not a valid type for array length\n")
            self.match("OF")
            array_type = self._type()
            if array_type is None:
                self.total_error_flag = 1
                sys.stderr.write("error: array type not found\n")
                return None
            return_type = Array(length, array_type)
            self.observer.end_type()
            return return_type
        elif self.token_list[self.current].kind == self.kind_map["RECORD"]:
            self.match("RECORD")
            id_list = []
            outer_scope = self.current_scope
            self.current_scope = Scope(outer_scope)
            while self.token_list[self.current].kind == self.kind_map["IDENTIFIER"]:
                id_list = self._identifier_list()
                self.match(":")
                record_field_type = self._type()
                if record_field_type is None:
                    self.total_error_flag = 1
                    sys.stderr.write("error: record field type nonexistent\n")

                self.match(";")
                for name in id_list:
                    if not self.current_scope.local(name):
                        self.current_scope.insert(name, Variable(record_field_type))
                    else:
                        self.total_error_flag = 1
                        sys.stderr.write("error: attempting to redefine field\n")
                        return None
            self.match("END")
            return_type = Record(self.current_scope)
            outer_scope = self.current_scope.outer_scope
            self.current_scope.outer_scope = None
            self.current_scope = outer_scope
            self.observer.end_type()
            return return_type
        else:
            self.total_error_flag = 1
            sys.stderr.out("error: expecting Identifier, ARRAY, or RECORD\n")
        self.observer.end_type()

    # set expectation of creating a Expression
    # by following the Expression production
    def _expression(self):
        self.observer.begin_expression()
        node = self.nexpression()
        self.observer.end_expression()
        return node

    def nexpression(self):
        outer_operation = -1
        if self.token_list[self.current].kind == self.kind_map["+"]:
            outer_operation = self.match("+")
        elif self.token_list[self.current].kind == self.kind_map["-"]:
            outer_operation = self.match("-")
        subtree = self._term()
        node = subtree
        if (self.token_list[self.current].kind == self.kind_map["+"]) or \
                (self.token_list[self.current].kind == self.kind_map["-"]):
            inner_operation = ""
            if self.token_list[self.current].kind == self.kind_map["+"]:
                inner_operation = self.match("+")
            elif self.token_list[self.current].kind == self.kind_map["-"]:
                inner_operation = self.match("-")
            else:
                self.total_error_flag = 1
                sys.stderr.write("error: expecting \'+\' or \'-\'\n")
            subtree_right = self._term()
            if isinstance(subtree, NumberNode) and isinstance(subtree_right, NumberNode):
                result = 0
                if inner_operation == "+":
                    result = int(subtree.type.value) + int(subtree_right.type.value)
                elif inner_operation == "-":
                    result = int(subtree.type.value) + int(subtree_right.type.value)
                else:
                    pass
                c = Constant(integerInstance, result)
                num_node = NumberNode(c)
                node = num_node
            else:
                bn = BinaryNode(inner_operation, subtree, subtree_right)
                node = bn
        else:
            node = subtree
        if outer_operation == "-":
            # how to do negative numbers?
            if isinstance(node, BinaryNode):
                if isinstance(node.exp_left, NumberNode) and isinstance(node.exp_right, NumberNode):
                    op = node.operator
                    left_value = node.exp_left.value
                    right_value = node.exp_right.value
                    op_result = 0
                    if op == "+":
                        op_result = left_value + right_value
                    elif op == "-":
                        op_result = left_value - right_value
                    elif op == "*":
                        op_result = left_value * right_value
                    elif op == "DIV":
                        op_result = left_value / right_value
                    elif op == "MOD":
                        op_result = left_value % right_value
                    else:
                        sys.stderr.write("error: invalid op")
                    result = -1*op_result
                    c = Constant(integerInstance, result)
                    num_node = NumberNode(c)
                    node = num_node
            elif isinstance(node, NumberNode):
                c = Constant(integerInstance, 0 - node.constant.value)
                num_node = NumberNode(c)
                node = num_node
        return node

    # set expectation of creating a Term
    # by following the Term production
    def _term(self):
        self.observer.begin_term()
        node = self.nterm()
        #return singular factor or binary node
        self.observer.end_term()
        return node

    def nterm(self):
        sub_left = self._factor()
        node = sub_left
        operation = 0
        if (self.token_list[self.current].kind == self.kind_map["*"])or \
            (self.token_list[self.current].kind == self.kind_map["DIV"]) or \
            (self.token_list[self.current].kind == self.kind_map["MOD"]):
            if self.token_list[self.current].kind == self.kind_map["*"]:
                 operation = self.match("*")
            elif self.token_list[self.current].kind == self.kind_map["DIV"]:
                 operation = self.match("DIV")
            elif self.token_list[self.current].kind == self.kind_map["MOD"]:
                 operation = self.match("MOD")
            else:
                self.total_error_flag = 1
                sys.stderr.out("error: expecting \'*\', \'DIV\', or \'MOD\'\n")
            sub_right = self.nterm()
            if isinstance(sub_left, NumberNode) and isinstance(sub_right, NumberNode):
                result = 0
                if operation == "*":
                    result = int(sub_left.constant.value) * int(sub_right.constant.value)
                elif operation == "DIV":
                    result = int(sub_left.constant.value) / int(sub_right.constant.value)
                elif operation == "MOD":
                    result = int(sub_left.constant.value) % int(sub_right.constant.value)
                c = Constant(integerInstance, result)
                num_node = NumberNode(c)
                return num_node
            else:
                bn = BinaryNode(operation, sub_left, sub_right)
                return bn
        else:
            return sub_left

    # set expectation of creating a Factor
    # by following the Factor production
    def _factor(self):
        self.observer.begin_factor()
        node = None
        if self.token_list[self.current].kind == self.kind_map["INTEGER"]:
            int_value = self.match("INTEGER")
            c = Constant(integerInstance, int_value)
            node = NumberNode(c)
            # make a number node of out of the constant
            # return number node
        elif self.token_list[self.current].kind == self.kind_map["IDENTIFIER"]:
            sub_tree = self._designator()
            node = sub_tree
        elif self.token_list[self.current].kind == self.kind_map["("]:
            self.match("(")
            sub_tree = self._expression()
            self.match(")")
            node = sub_tree
        else:
            self.total_error_flag = 1
            sys.stdout.error("error: expecting integer, identifier or \'(\'\n")
        self.observer.end_factor()
        return node

    # set expectation of creating a Instructions
    # by following the Instructions production
    def _instructions(self):
        self.observer.begin_instructions()
        head = self._instruction()
        curr = head
        while self.token_list[self.current].kind == self.kind_map[";"]:
            self.match(";")
            temp = self._instruction()
            curr._next = temp
            curr = temp
        self.observer.end_instructions()
        return head

    # set expectation of creating a Instruction
    # by following the Instruction production
    def _instruction(self):
        # print "Instruction"
        self.observer.begin_instruction()
        node = None
        if self.token_list[self.current].kind == self.kind_map["IDENTIFIER"]:
            node = self._assign()
        elif self.token_list[self.current].kind == self.kind_map["IF"]:
            node = self._if()
        elif self.token_list[self.current].kind == self.kind_map["REPEAT"]:
            node = self._repeat()
        elif self.token_list[self.current].kind == self.kind_map["WHILE"]:
            node = self._while()
        elif self.token_list[self.current].kind == self.kind_map["READ"]:
            node = self._read()
        elif self.token_list[self.current].kind == self.kind_map["WRITE"]:
            node = self._write()
        else:
            self.total_error_flag = 1
            sys.stderr.write("error: not a valid instruction\n"
                             "@({0}, {1})".format(self.token_list[self.current].start_position,
                                                  self.token_list[self.current].end_position))
        self.observer.end_instruction()
        return node

    # set expectation of creating a Assign
    # by following the Assign production
    def _assign(self):
        # print "Assign"
        self.observer.begin_assign()
        subtree_left = self._designator()
        if not (isinstance(subtree_left, VariableNode) or isinstance(subtree_left, FieldNode)
                or isinstance(subtree_left, IndexNode)):
            print type(subtree_left)
            sys.stderr.write("error: assign")
        stl_type = subtree_left.type
        self.match(":=")
        subtree_right = self._expression()
        str_type = subtree_right.type
        if not type(stl_type) == type(str_type):
            sys.stderr.write("error: assigning things that don't have the same type\n")
        assign_node = AssignNode(None, subtree_left, subtree_right)
        self.observer.end_assign()
        return assign_node

    # set expectation of creating a If
    # by following the If production
    def _if(self):
        # print "If"
        self.observer.begin_if()
        self.match("IF")
        condition = self._condition()
        self.match("THEN")
        instructions_true = self._instructions()
        instructions_false = None
        if self.token_list[self.current].kind == self.kind_map["ELSE"]:
            self.match("ELSE")
            instructions_false = self._instructions()
        self.match("END")
        self.observer.end_if()
        if_node = IfNode(None, condition, instructions_true, instructions_false)
        return if_node

    # set expectation of creating a Repeat
    # by following the Repeat production
    def _repeat(self):
        self.observer.begin_repeat()
        self.match("REPEAT")
        instructions = self._instructions()
        self.match("UNTIL")
        condition = self._condition()
        self.match("END")
        self.observer.end_repeat()
        repeat_node = RepeatNode(None, condition, instructions)
        return repeat_node


    # set expectation of creating a While
    # by following the While production
    def _while(self):
        self.observer.begin_while()
        self.match("WHILE")
        condition = self._condition()
        self.match("DO")
        instructions = self._instructions()
        self.match("END")
        self.observer.end_while()
        negation_condition_node = self.get_negation(condition)
        repeat_node = RepeatNode(None, negation_condition_node, instructions)
        if_node = IfNode(None, condition, repeat_node, None)
        return if_node

    def get_negation(self, condition_node):
        relation_negation = {"=":"#",
                             "#":"=",
                             "<":">",
                             ">":"<",
                             "<=":">=",
                             ">=":"<="}
        negation_condition_node = ConditionNode(condition_node.exp_left, condition_node.exp_right,
                                                relation_negation[condition_node.relation])
        return negation_condition_node

    # set expectation of creating a Condition
    # by following the Condition production
    def _condition(self):
        self.observer.begin_condition()
        left = self._expression()
        relation = ""
        if self.token_list[self.current].kind == self.kind_map["="]:
             # self.match("=")
             relation = self.match("=")
        elif self.token_list[self.current].kind == self.kind_map["#"]:
             # self.match("#")
             relation = self.match("#")
        elif self.token_list[self.current].kind == self.kind_map["<"]:
             # self.match("<")
             relation = self.match("<")
        elif self.token_list[self.current].kind == self.kind_map[">"]:
            # self.match(">")
            relation = self.match(">")
        elif self.token_list[self.current].kind == self.kind_map["<="]:
            # self.match("<=")
            relation = self.match("<=")
        elif self.token_list[self.current].kind == self.kind_map[">="]:
            # self.match(">=")
            relation = self.match(">=")
        else:
            self.total_error_flag = 1
            sys.stderr.write("error: not a valid condition\n"
                             "@({0}, {1})".format(self.token_list[self.current].start_position,
                                                  self.token_list[self.current].end_position))
        right = self._expression()
        self.observer.end_condition()
        condition_subtree = ConditionNode(left, right, relation)
        return condition_subtree

    # set expectation of creating a Write
    # by following the Write production
    def _write(self):
        self.observer.begin_write()
        self.match("WRITE")
        expression = self._expression()
        if not isinstance(expression.type, Integer):
            self.total_error_flag = 1
            sys.stderr.write("error: expression in write not of type integer")
            exit(1)
        self.observer.end_write()
        write_node = WriteNode(None, expression)
        return write_node

    # set expectation of creating a Read
    # by following the Read production
    def _read(self):
        self.observer.begin_read()
        self.match("READ")
        designator = self._designator()
        if not isinstance(designator.type, Integer):
            self.total_error_flag = 1
            sys.stderr.write("error: designator in read not an integer")
            exit(1)
        self.observer.end_read()
        read_node = ReadNode(None, designator)
        return read_node

    # set expectation of creating a Designator
    # by following the Designator production
    def _designator(self):
        self.observer.begin_designator()
        var_name = self.match("IDENTIFIER")
        ret_obj = self.program_scope.find(var_name)
        pass_obj = None
        if isinstance(ret_obj, Variable):
            pass_obj = VariableNode(ret_obj._type, ret_obj, var_name)
        elif isinstance(ret_obj, Constant):
            pass_obj = NumberNode(ret_obj)
        else:
            self.total_error_flag = 1
            sys.stderr.write("error: variable name not pointing var or const\n")
            exit(1)
        subtree = self._selector(pass_obj)
        self.observer.end_designator()
        return subtree

    # set expectation of creating a Selector
    # by following the Selector production
    def _selector(self, variable_node):
        self.observer.begin_selector()
        return_object = variable_node
        while (self.token_list[self.current].kind == self.kind_map["["]) \
                or (self.token_list[self.current].kind == self.kind_map["."]):
            if self.token_list[self.current].kind == self.kind_map["["]:
                if not isinstance(return_object.type, Array):
                    sys.stderr.write("error: not an array")
                self.match("[")
                exp_list = self._expression_list()
                self.match("]")
                node = return_object
                for e in exp_list:
                    if not isinstance(e.type, Integer):
                        sys.stderr.write("error: noninteger found in selector\n")
                index_type = return_object.type._type
                # print "it", type(index_type)
                # print "index type: ", type(index_type)
                index_node = IndexNode(index_type, node, exp_list[0])
                for i in range(1, len(exp_list)):
                    node = index_node
                    index_type = node.type._type
                    index_node = IndexNode(index_type, node, exp_list[i])
                return_object = index_node
            elif self.token_list[self.current].kind == self.kind_map["."]:
                self.match(".")
                field_var_name = self.match("IDENTIFIER")
                if not isinstance(return_object.type, Record):
                    sys.stderr.write("error: attempting to select field from non-record type\n")
                if(return_object.type.scope.local(field_var_name)):
                    field_var_obj = return_object.type.scope.find(field_var_name)
                field_type = field_var_obj._type
                field_right_var_obj = VariableNode(field_type, field_var_obj, field_var_name)
                node = FieldNode(field_type, return_object, field_right_var_obj)
                return_object = node
            else:
                self.total_error_flag = 1
                sys.stderr.write("error: not a valid selector\n"
                             "@({0}, {1})".format(self.token_list[self.current].start_position,
                                                  self.token_list[self.current].end_position))
        self.observer.end_selector()
        return return_object

    # set expectation of creating a IdentifierList
    # by following the IdentifierList production
    def _identifier_list(self):
        self.observer.begin_identifier_list()
        name = self.match("IDENTIFIER")
        id_list = []
        id_list.append(name)
        while self.token_list[self.current].kind == self.kind_map[","]:
            self.match(",")
            name = self.match("IDENTIFIER")
            id_list.append(name)
        self.observer.end_identifier_list()
        return id_list

    # set expectation of creating a ExpressionList
    # by following the ExpressionList production
    def _expression_list(self):
        # return list of expressions - added return in assignment 5
        self.observer.begin_expression_list()
        exp_list = []
        name = self._expression()
        exp_list.append(name)
        while self.token_list[self.current].kind == self.kind_map[","]:
            self.match(",")
            name = self._expression()
            exp_list.append(name)
        self.observer.end_expression_list()
        return exp_list
Exemplo n.º 5
0
class Parser:

    """Takes a list of tokens and performs semantic analysis
       to check if input adheres to grammar. Outputs to stdout
       a textual representation of the CST via the call stack.
       Parser is also capable of outputting graphical output
    """

    # initializes Parser instance and parses a list of tokens
    # cmd line arguments determine output type
    def __init__(self, observer = Observer(), token_list=[], print_symbol_table = 0, visitor = Visitor()):
        self.current = 0 # current position in token list
        self.token_list = token_list # token list received from scanner
        self.kind_map = Token.kind_map # dictionary of token kinds
        self.observer = observer # output class determined by cmd line arguments
        self.total_error_flag = 0 # detects if an error occurs anywhere in the program
        self.universe = Scope(None) # universe scope
        self.universe.insert("INTEGER", Integer()) # universe scope only holds integer
        self.program_scope = Scope(self.universe) # program scope to hold program names
        self.current_scope = self.program_scope # current scope for switching between scopes
        self.print_symbol_table = print_symbol_table # determines whether to print cst or st
        self.visitor = visitor

    # parse the token list
    def parse(self):
        self._program()
        # do not print any output if error occurs
        if self.total_error_flag == 0:
            if self.print_symbol_table == 0:
                self.observer.print_output()
            elif self.print_symbol_table == 1:
                self.visitor.visitScope(self.program_scope)
                self.visitor.end()

    # check if the currently parsed token is a token we are
    # expecting to find
    # kind = expected kind of token
    def match(self, kind):
        if self.token_list[self.current].kind == self.kind_map[kind]:
            self.observer.print_token(self.token_list[self.current])
            self.current += 1
            ### for returning identifier names
            return self.token_list[self.current-1].get_token_name()
        else:
            self.total_error_flag = 1
            sys.stderr.write("error: expected token kind \'{0}\', "
                             "received unexpected token \'{1}\'"
                             " @({2}, {3})".format(kind, self.token_list[self.current],
                                                   self.token_list[self.current].start_position,
                                                   self.token_list[self.current].end_position) + '\n')

    # set expectation of creating a program
    # by following the program production
    def _program(self):
        # print "Program"
        self.observer.begin_program()
        self.match("PROGRAM")
        name = self.match("IDENTIFIER")
        self.match(";")
        self._declarations()
        if self.token_list[self.current].kind == self.kind_map["BEGIN"]:
            self.match("BEGIN")
            self._instructions()
        self.match("END")
        end_name = self.match("IDENTIFIER")
        self.match(".")
        self.observer.end_program()
        if not name == end_name:
            self.total_error_flag = 1
            sys.stderr.write("error: program identifier does not match end identifier\n")
        if not self.token_list[self.current].kind == self.kind_map["EOF"]:
            self.total_error_flag = 1
            sys.stderr.write("error: trash detected after program end:\n"
                             "Token \'{0}\'".format(self.token_list[self.current]) + '\n')

    # set expectation of creating a declaration
    # by following the declaration production
    def _declarations(self):
        self.observer.begin_declarations()
        while (self.token_list[self.current].kind == self.kind_map["CONST"]) or \
            (self.token_list[self.current].kind == self.kind_map["TYPE"]) or \
            (self.token_list[self.current].kind == self.kind_map["VAR"]):
            if self.token_list[self.current].kind == self.kind_map["CONST"]:
                self._constdecl()
            elif self.token_list[self.current].kind == self.kind_map["TYPE"]:
                self._typedecl()
            elif self.token_list[self.current].kind == self.kind_map["VAR"]:
                self._vardecl()
            else:
                pass
        self.observer.end_declarations()

    # set expectation of creating a ConstDecl
    # by following the ConstDecl production
    def _constdecl(self):
        # print "ConstDecl"
        self.observer.begin_constdecl()
        self.match("CONST")
        while self.token_list[self.current].kind == self.kind_map["IDENTIFIER"]:
            name = self.match("IDENTIFIER")
            # check if const name in local scope
            if self.program_scope.local(name):
                self.total_error_flag = 1
                sys.stderr.write("error: attempted to redefine identifier\n")
            self.match("=")
            e = self._expression()
            self.match(";")
            # add it we formed a constant
            if isinstance(e, Constant): # is it constant object or constant name
                self.program_scope.insert(name, e)
            else:
                self.total_error_flag = 1
                sys.stderr.write("error: attempted to define const with nonconst object\n")
        self.observer.end_constdecl()

    # set expectation of creating a TypeDecl
    # by following the TypeDecl production
    def _typedecl(self):
        # print "TypeDecl"
        self.observer.begin_typedecl()
        self.match("TYPE")
        while self.token_list[self.current].kind == self.kind_map["IDENTIFIER"]:
            name = self.match("IDENTIFIER")
            self.match("=")
            # type of current Type
            return_type = self._type()
            self.match(";")
            if return_type is None:
                self.total_error_flag = 1
                sys.stderr.write("error: type not found\n")
                return None
            if not self.current_scope.local(name):
                self.current_scope.insert(name, return_type)
            else:
                self.total_error_flag = 1
                sys.stderr.write("error: attempting to redefine variable\n")
        self.observer.end_typedecl()

    # set expectation of creating a VarDecl
    # by following the VarDecl production
    def _vardecl(self):
        # print "VarDecl"
        self.observer.begin_vardecl()
        self.match("VAR")
        id_list = []
        return_type = None
        while self.token_list[self.current].kind == self.kind_map["IDENTIFIER"]:
            id_list = self._identifier_list()
            self.match(":")
            return_type = self._type()
            # type of current identifier
            if return_type is None:
                self.total_error_flag = 1
                sys.stderr.write("error: type not found\n")
                return None
            self.match(";")
            for name in id_list:
                if not self.current_scope.local(name):
                    self.current_scope.insert(name, Variable(return_type))
                else:
                    self.total_error_flag = 1
                    sys.stderr.write("error: attempting to redefine var\n")
        self.observer.end_vardecl()

    # set expectation of creating a Type
    # by following the Type production
    def _type(self):
        # print "Type"
        self.observer.begin_type()
        return_type = None
        if self.token_list[self.current].kind == self.kind_map["IDENTIFIER"]:
            name = self.match("IDENTIFIER")
            # get the name of an identifier
            return_type = self.current_scope.find(name)
            if return_type is None:
                self.total_error_flag = 1
                sys.stderr.write("error: indentifier not found. attempting to assign "
                                 "uncreated type\n")
                self.observer.end_type()
                return None
            if isinstance(return_type, Type):
                self.observer.end_type()
                return return_type
            else:
                self.total_error_flag = 1
                sys.stderr.write("error: found not Type object\n")
                return None
        elif self.token_list[self.current].kind == self.kind_map["ARRAY"]:
            self.match("ARRAY")
            length = None
            e = self._expression()
            # get length of array
            if isinstance(e, Constant):
                length = e
            else:
                self.total_error_flag = 1
                sys.stderr.write("error: not a valid type for array length\n")
            self.match("OF")
            array_type = self._type()
            # check if array_type is already defined
            if array_type is None:
                self.total_error_flag = 1
                sys.stderr.write("error: array type not found\n")
                return None
            return_type = Array(length, array_type)
            self.observer.end_type()
            return return_type
        elif self.token_list[self.current].kind == self.kind_map["RECORD"]:
            self.match("RECORD")
            id_list = []
            outer_scope = self.current_scope
            self.current_scope = Scope(outer_scope)
            while self.token_list[self.current].kind == self.kind_map["IDENTIFIER"]:
                id_list = self._identifier_list()
                self.match(":")
                # type of current identifier(s)
                record_field_type = self._type()
                if record_field_type is None:
                    self.total_error_flag = 1
                    sys.stderr.write("error: record field type nonexistent\n")
                self.match(";")
                for name in id_list:
                    if not self.current_scope.local(name):
                        self.current_scope.insert(name, Variable(record_field_type))
                    else:
                        self.total_error_flag = 1
                        sys.stderr.write("error: attempting to redefine field\n")
                        return None
            self.match("END")
            return_type = Record(self.current_scope)
            outer_scope = self.current_scope.outer_scope
            self.current_scope.outer_scope = None
            self.current_scope = outer_scope
            self.observer.end_type()
            return return_type
        else:
            self.total_error_flag = 1
            sys.stderr.out("error: expecting Identifier, ARRAY, or RECORD\n")
        self.observer.end_type()

    # set expectation of creating a Expression
    # by following the Expression production
    def _expression(self):
        self.observer.begin_expression()
        if self.token_list[self.current].kind == self.kind_map["+"]:
            self.match("+")
        elif self.token_list[self.current].kind == self.kind_map["-"]:
            self.match("-")
        self._term()
        while (self.token_list[self.current].kind == self.kind_map["+"]) or \
                (self.token_list[self.current].kind == self.kind_map["-"]):
            if self.token_list[self.current].kind == self.kind_map["+"]:
                self.match("+")
            elif self.token_list[self.current].kind == self.kind_map["-"]:
                self.match("-")
            else:
                self.total_error_flag = 1
                sys.stderr.write("error: expecting \'+\' or \'-\'\n")
            self._term()
        self.observer.end_expression()
        e = Constant(self.universe.find("INTEGER"), 5)
        return e

    # set expectation of creating a Term
    # by following the Term production
    def _term(self):
        self.observer.begin_term()
        self._factor()
        while (self.token_list[self.current].kind == self.kind_map["*"])or \
            (self.token_list[self.current].kind == self.kind_map["DIV"]) or \
            (self.token_list[self.current].kind == self.kind_map["MOD"]):
            if self.token_list[self.current].kind == self.kind_map["*"]:
                self.match("*")
            elif self.token_list[self.current].kind == self.kind_map["DIV"]:
                self.match("DIV")
            elif self.token_list[self.current].kind == self.kind_map["MOD"]:
                self.match("MOD")
            else:
                self.total_error_flag = 1
                sys.stderr.out("error: expecting \'*\', \'DIV\', or \'MOD\'\n")
            self._factor()
        self.observer.end_term()

    # set expectation of creating a Factor
    # by following the Factor production
    def _factor(self):
        self.observer.begin_factor()
        if self.token_list[self.current].kind == self.kind_map["INTEGER"]:
            self.match("INTEGER")
        elif self.token_list[self.current].kind == self.kind_map["IDENTIFIER"]:
            self._designator()
        elif self.token_list[self.current].kind == self.kind_map["("]:
            self.match("(")
            self._expression()
            self.match(")")
        else:
            self.total_error_flag = 1
            sys.stdout.error("error: expecting integer, identifier or \'(\'\n")
        self.observer.end_factor()

    # set expectation of creating a Instructions
    # by following the Instructions production
    def _instructions(self):
        self.observer.begin_instructions()
        self._instruction()
        while self.token_list[self.current].kind == self.kind_map[";"]:
            self.match(";")
            self._instruction()
        self.observer.end_instructions()

    # set expectation of creating a Instruction
    # by following the Instruction production
    def _instruction(self):
        # print "Instruction"
        self.observer.begin_instruction()
        if self.token_list[self.current].kind == self.kind_map["IDENTIFIER"]:
            self._assign()
        elif self.token_list[self.current].kind == self.kind_map["IF"]:
            self._if()
        elif self.token_list[self.current].kind == self.kind_map["REPEAT"]:
            self._repeat()
        elif self.token_list[self.current].kind == self.kind_map["WHILE"]:
            self._while()
        elif self.token_list[self.current].kind == self.kind_map["READ"]:
            self._read()
        elif self.token_list[self.current].kind == self.kind_map["WRITE"]:
            self._write()
        else:
            self.total_error_flag = 1
            sys.stderr.write("error: not a valid instruction\n"
                             "@({0}, {1})".format(self.token_list[self.current].start_position,
                                                  self.token_list[self.current].end_position))
        self.observer.end_instruction()

    # set expectation of creating a Assign
    # by following the Assign production
    def _assign(self):
        # print "Assign"
        self.observer.begin_assign()
        self._designator()
        self.match(":=")
        self._expression()
        self.observer.end_assign()

    # set expectation of creating a If
    # by following the If production
    def _if(self):
        # print "If"
        self.observer.begin_if()
        self.match("IF")
        self._condition()
        self.match("THEN")
        self._instructions()
        if self.token_list[self.current].kind == self.kind_map["ELSE"]:
            self.match("ELSE")
            self._instructions()
        self.match("END")
        self.observer.end_if()

    # set expectation of creating a Repeat
    # by following the Repeat production
    def _repeat(self):
        self.observer.begin_repeat()
        self.match("REPEAT")
        self._instructions()
        self.match("UNTIL")
        self._condition()
        self.match("END")
        self.observer.end_repeat()

    # set expectation of creating a While
    # by following the While production
    def _while(self):
        self.observer.begin_while()
        self.match("WHILE")
        self._condition()
        self.match("DO")
        self._instructions()
        self.match("END")
        self.observer.end_while()

    # set expectation of creating a Condition
    # by following the Condition production
    def _condition(self):
        self.observer.begin_condition()
        self._expression()
        if self.token_list[self.current].kind == self.kind_map["="]:
            self.match("=")
        elif self.token_list[self.current].kind == self.kind_map["#"]:
            self.match("#")
        elif self.token_list[self.current].kind == self.kind_map["<"]:
            self.match("<")
        elif self.token_list[self.current].kind == self.kind_map[">"]:
            self.match(">")
        elif self.token_list[self.current].kind == self.kind_map["<="]:
            self.match("<=")
        elif self.token_list[self.current].kind == self.kind_map[">="]:
            self.match(">=")
        else:
            self.total_error_flag = 1
            sys.stderr.write("error: not a valid condition\n"
                             "@({0}, {1})".format(self.token_list[self.current].start_position,
                                                  self.token_list[self.current].end_position))
        self._expression()
        self.observer.end_condition()

    # set expectation of creating a Write
    # by following the Write production
    def _write(self):
        self.observer.begin_write()
        self.match("WRITE")
        self._expression()
        self.observer.end_write()

    # set expectation of creating a Read
    # by following the Read production
    def _read(self):
        self.observer.begin_read()
        self.match("READ")
        self._designator()
        self.observer.end_read()

    # set expectation of creating a Designator
    # by following the Designator production
    def _designator(self):
        self.observer.begin_designator()
        self.match("IDENTIFIER")
        self._selector()
        self.observer.end_designator()

    # set expectation of creating a Selector
    # by following the Selector production
    def _selector(self):
        self.observer.begin_selector()
        while (self.token_list[self.current].kind == self.kind_map["["]) \
                or (self.token_list[self.current].kind == self.kind_map["."]):
            if self.token_list[self.current].kind == self.kind_map["["]:
                self.match("[")
                self._expression_list()
                self.match("]")
            elif self.token_list[self.current].kind == self.kind_map["."]:
                self.match(".")
                self.match("IDENTIFIER")
            else:
                self.total_error_flag = 1
                sys.stderr.write("error: not a valid selector\n"
                             "@({0}, {1})".format(self.token_list[self.current].start_position,
                                                  self.token_list[self.current].end_position))
        self.observer.end_selector()

    # set expectation of creating a IdentifierList
    # by following the IdentifierList production
    def _identifier_list(self):
        self.observer.begin_identifier_list()
        name = self.match("IDENTIFIER")
        id_list = []
        id_list.append(name)
        while self.token_list[self.current].kind == self.kind_map[","]:
            self.match(",")
            name = self.match("IDENTIFIER")
            id_list.append(name)
        self.observer.end_identifier_list()
        return id_list

    # set expectation of creating a ExpressionList
    # by following the ExpressionList production
    def _expression_list(self):
        self.observer.begin_expression_list()
        self._expression()
        while self.token_list[self.current].kind == self.kind_map[","]:
            self.match(",")
            self._expression()
        self.observer.end_expression_list()