git @ Cat's Eye Technologies Unlikely / master src / unlikely / parser.py
master

Tree @master (Download .tar.gz)

parser.py @masterraw · history · blame

# -*- coding: utf-8 -*-

# (c)2010-2012 Chris Pressey, Cat's Eye Technologies.
# All rights reserved.  Released under a BSD-style license (see LICENSE).

"""
Parser for the Unlikely programming language.

(c)2010-2012 Chris Pressey, Cat's Eye Technologies.
All rights reserved.  Released under a BSD-style license (see LICENSE).

Based on the following EBNF grammar:

ClassBase   ::= {ClassDefn}.
ClassDefn   ::= "class" ClassName<NEW> "(" [ClassName {"," ClassName}] ")"
                "extends" ClassName ["{" {PropDefn} {MethodDefn} "}"]
                ["is" ClassMod {"and" ClassMod}].
ClassMod    ::= "final" | "saturated" | "abstract".
PropDefn    ::= ClassName PropName<NEW> ";".
MethodDefn  ::= "method" MethodName<NEW> "(" [ParamDecl {"," ParamDecl}] ")"
                ("{" {Assignment} Continue "}" | "is" "abstract").
ParamDecl   ::= ClassName PropName.
Assignment  ::= QualName "=" Expr ";".
Continue    ::= "goto" PropName "." MethodName "(" [Expr {"," Expr}] ")" ";".
Expr        ::= ConstrExpr | QualName.
ConstrExpr  ::= "new" (ClassName) "(" [ClassName {"," ClassName}] ")".
QualName    ::= PropName {"." PropName}.
Constant    ::= <<sequence of decimal digits>>
              | <<sequence of arbitrary characters between double quotes>>.
"""


class Parser(object):
    """A recursive-descent parser for Unlikely.
    """

    def __init__(self, scanner):
        """
        Creates a new Parser object.  The passed-in scanner is expected
        to be compatible with a Scanner object.
        """
        self.scanner = scanner

    def parse(self):
        raise NotImplementedError


class ClassBaseParser(Parser):
    # ClassBase ::= {ClassDefn}.

    def __init__(self, scanner, classbase=None):
        Parser.__init__(self, scanner)
        self.classbase = classbase

    def parse(self):
        class_defn_parser = ClassDefnParser(self.scanner, self.classbase)
        while self.scanner.token == "class":
            class_defn_parser.parse()


class ClassDefnParser(Parser):
    def __init__(self, scanner, classbase):
        Parser.__init__(self, scanner)
        self.classbase = classbase

    def parse(self):
        self.scanner.expect("class")
        class_name = self.scanner.grab()
        class_defn = self.classbase.add_class_defn_by_name(class_name)
        self.scanner.expect("(")
        dependant_names = []
        if self.scanner.token != ")":
            dependant_names.append(self.scanner.grab())
            while self.scanner.token == ",":
                self.scanner.expect(",")
                dependant_names.append(self.scanner.grab())
        self.scanner.expect(")")
        self.scanner.expect("extends")
        class_defn.set_superclass_by_name(self.scanner.grab())
        for dependant_name in dependant_names:
            class_defn.add_dependant_by_name(dependant_name)
        if self.scanner.token == "{":
            self.scanner.expect("{")
            while self.scanner.token != "}":
                if self.scanner.token == "method":
                    parser = MethodDefnParser(self.scanner, class_defn)
                else:
                    parser = PropDefnParser(self.scanner, class_defn)
                parser.parse()
            self.scanner.expect("}")
            is_forward_decl = False
        else:
            is_forward_decl = True
        if self.scanner.token == "is":
            self.scanner.expect("is")
            class_defn.add_modifier(self.scanner.grab())
            while self.scanner.token == "and":
                self.scanner.expect("and")
                class_defn.add_modifier(self.scanner.grab())
        if not is_forward_decl:
            class_defn.typecheck()


class PropDefnParser(Parser):
    def __init__(self, scanner, class_defn):
        Parser.__init__(self, scanner)
        self.class_defn = class_defn

    def parse(self):
        class_name = self.scanner.grab()
        prop_name = self.scanner.grab()
        self.class_defn.add_prop_defn_by_name(prop_name, class_name)
        self.scanner.expect(";")


class MethodDefnParser(Parser):
    def __init__(self, scanner, class_defn):
        Parser.__init__(self, scanner)
        self.class_defn = class_defn

    def parse(self):
        self.scanner.expect("method")
        method_name = self.scanner.grab()
        method_defn = self.class_defn.add_method_defn_by_name(method_name)
        self.scanner.expect("(")
        if self.scanner.token != ")":
            param_decl_parser = ParamDeclParser(self.scanner, method_defn)
            param_decl_parser.parse()
            while self.scanner.token == ",":
                param_decl_parser.parse()
        self.scanner.expect(")")
        if self.scanner.token == "{":
            self.scanner.expect("{")
            assignment_parser = AssignmentParser(self.scanner, method_defn)
            while self.scanner.token != "goto":
                assignment_parser.parse()
            continue_parser = ContinueParser(self.scanner, method_defn)
            continue_parser.parse()
            self.scanner.expect("}")
        elif self.scanner.token == "is":
            self.scanner.expect("is")
            method_defn.add_modifier(self.scanner.grab())
            while self.scanner.token == "and":
                self.scanner.expect("and")
                method_defn.add_modifier(self.scanner.grab())
        else:
            self.scanner.error("expected '{' or 'is', but found " +
                               self.scanner.token)


class ParamDeclParser(Parser):
    def __init__(self, scanner, method_defn):
        Parser.__init__(self, scanner)
        self.method_defn = method_defn

    def parse(self):
        type_class_name = self.scanner.grab()
        prop_name = self.scanner.grab()
        self.method_defn.add_param_decl_by_name(prop_name, type_class_name)


class AssignmentParser(Parser):
    def __init__(self, scanner, method_defn):
        Parser.__init__(self, scanner)
        self.method_defn = method_defn

    def parse(self):
        assignment = self.method_defn.add_assignment()
        qual_name_parser = QualNameParser(self.scanner, assignment)
        qual_name_parser.parse()
        self.scanner.expect("=")
        expr_parser = ExprParser(self.scanner, assignment)
        expr_parser.parse()
        self.scanner.expect(";")


class ContinueParser(Parser):
    def __init__(self, scanner, method_defn):
        Parser.__init__(self, scanner)
        self.method_defn = method_defn

    def parse(self):
        continue_ = self.method_defn.add_continue()
        self.scanner.expect("goto")
        prop_name = self.scanner.grab()
        self.scanner.expect(".")
        method_name = self.scanner.grab()
        continue_.set_prop_defn_by_name(prop_name)
        continue_.set_method_defn_by_name(method_name)
        self.scanner.expect("(")
        expr_parser = ExprParser(self.scanner, continue_)
        if self.scanner.token != ")":
            expr_parser.parse()
            while self.scanner.token == ",":
                self.scanner.expect(",")
                expr_parser.parse()
        self.scanner.expect(")")
        self.scanner.expect(";")
        continue_.typecheck()
        return continue_


class ExprParser(Parser):
    def __init__(self, scanner, parent):
        Parser.__init__(self, scanner)
        self.parent = parent

    def parse(self):
        if self.scanner.token == "new":
            parser = ConstructionParser(self.scanner, self.parent)
        else:
            parser = QualNameParser(self.scanner, self.parent)
        parser.parse()


class ConstructionParser(Parser):
    def __init__(self, scanner, parent):
        Parser.__init__(self, scanner)
        self.parent = parent

    def parse(self):
        self.scanner.expect("new")
        class_name = self.scanner.grab()
        construction = self.parent.add_construction(class_name)
        self.scanner.expect("(")
        if self.scanner.token != ")":
            construction.add_dependency_by_name(self.scanner.grab())
            while self.scanner.token == ",":
                self.scanner.expect(",")
                construction.add_dependency_by_name(self.scanner.grab())
        self.scanner.expect(")")
        construction.typecheck()


class QualNameParser(Parser):
    def __init__(self, scanner, parent):
        Parser.__init__(self, scanner)
        self.parent = parent

    def parse(self):
        qual_name = self.parent.add_qual_name()
        qual_name.add_prop_defn_by_name(self.scanner.grab())
        while self.scanner.token == ".":
            self.scanner.expect(".")
            qual_name.add_prop_defn_by_name(self.scanner.grab())