In January 2021 we will introduce a 10 GB quota for project repositories. Higher limits for individual projects will be available on request. Please see https://doku.lrz.de/display/PUBLIC/GitLab for more information.

ArithmeticCompiler.py 7.99 KB
Newer Older
1
#!/usr/bin/python3
eckhart's avatar
eckhart committed
2 3 4 5 6 7 8 9 10 11 12 13 14

#######################################################################
#
# SYMBOLS SECTION - Can be edited. Changes will be preserved.
#
#######################################################################


import collections
from functools import partial
import os
import sys

15
sys.path.extend(['../../', '../', './'])
eckhart's avatar
eckhart committed
16 17 18 19 20 21 22 23 24 25 26 27

try:
    import regex as re
except ImportError:
    import re
from DHParser import logging, is_filename, load_if_file, \
    Grammar, Compiler, nil_preprocessor, PreprocessorToken, Whitespace, DropWhitespace, \
    Lookbehind, Lookahead, Alternative, Pop, Token, DropToken, Synonym, AllOf, SomeOf, \
    Unordered, Option, NegativeLookbehind, OneOrMore, RegExp, Retrieve, Series, Capture, \
    ZeroOrMore, Forward, NegativeLookahead, Required, mixin_comment, compile_source, \
    grammar_changed, last_value, counterpart, accumulate, PreprocessorFunc, \
    Node, TransformationFunc, TransformationDict, transformation_factory, traverse, \
28
    remove_children_if, move_adjacent, normalize_whitespace, is_anonymous, matches_re, \
eckhart's avatar
eckhart committed
29
    reduce_single_child, replace_by_single_child, replace_or_reduce, remove_whitespace, \
Eckhart Arnold's avatar
Eckhart Arnold committed
30 31 32
    remove_empty, remove_tokens, flatten, is_insignificant_whitespace, is_empty, \
    collapse, collapse_if, replace_content, WHITESPACE_PTYPE, TOKEN_PTYPE, \
    remove_nodes, remove_content, remove_brackets, change_tag_name, remove_anonymous_tokens, \
eckhart's avatar
eckhart committed
33 34 35
    keep_children, is_one_of, not_one_of, has_content, apply_if, remove_first, remove_last, \
    remove_anonymous_empty, keep_nodes, traverse_locally, strip, lstrip, rstrip, \
    replace_content, replace_content_by, forbid, assert_content, remove_infix_operator, \
36
    error_on, recompile_grammar, left_associative, GLOBALS
eckhart's avatar
eckhart committed
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61


#######################################################################
#
# PREPROCESSOR SECTION - Can be edited. Changes will be preserved.
#
#######################################################################

def ArithmeticPreprocessor(text):
    return text, lambda i: i

def get_preprocessor() -> PreprocessorFunc:
    return ArithmeticPreprocessor


#######################################################################
#
# PARSER SECTION - Don't edit! CHANGES WILL BE OVERWRITTEN!
#
#######################################################################

class ArithmeticGrammar(Grammar):
    r"""Parser for an Arithmetic source file.
    """
    expression = Forward()
eckhart's avatar
eckhart committed
62
    source_hash__ = "b75119067b29e37cd0bfe66facbcad22"
63
    static_analysis_pending__ = [True]
eckhart's avatar
eckhart committed
64 65 66 67 68 69 70 71
    parser_initialization__ = ["upon instantiation"]
    resume_rules__ = {}
    COMMENT__ = r'#.*'
    WHITESPACE__ = r'\s*'
    WSP_RE__ = mixin_comment(whitespace=WHITESPACE__, comment=COMMENT__)
    dwsp__ = DropWhitespace(WSP_RE__)
    VARIABLE = Series(RegExp('[A-Za-z]'), dwsp__)
    NUMBER = Series(RegExp('(?:0|(?:[1-9]\\d*))(?:\\.\\d+)?'), dwsp__)
Eckhart Arnold's avatar
Eckhart Arnold committed
72 73 74
    NEGATIVE = RegExp('[-]')
    POSITIVE = RegExp('[+]')
    DIV = Series(Token("/"), dwsp__)
Eckhart Arnold's avatar
Eckhart Arnold committed
75
    MUL = Series(Token("*"), dwsp__)
Eckhart Arnold's avatar
Eckhart Arnold committed
76 77 78 79
    MINUS = Series(Token("-"), dwsp__)
    PLUS = Series(Token("+"), dwsp__)
    group = Series(Series(DropToken("("), dwsp__), expression, Series(DropToken(")"), dwsp__))
    sign = Alternative(POSITIVE, NEGATIVE)
Eckhart Arnold's avatar
Eckhart Arnold committed
80
    factor = Series(Option(sign), Alternative(NUMBER, VARIABLE, group), ZeroOrMore(Alternative(VARIABLE, group)))
81
    term = Series(factor, ZeroOrMore(Series(Alternative(DIV, MUL), factor)))
Eckhart Arnold's avatar
Eckhart Arnold committed
82
    expression.set(Series(term, ZeroOrMore(Series(Alternative(PLUS, MINUS), term))))
eckhart's avatar
eckhart committed
83 84 85
    root__ = expression
    
def get_grammar() -> ArithmeticGrammar:
86
    """Returns a thread/process-exclusive ArithmeticGrammar-singleton."""
eckhart's avatar
eckhart committed
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
    try:
        grammar = GLOBALS.Arithmetic_00000001_grammar_singleton
    except AttributeError:
        GLOBALS.Arithmetic_00000001_grammar_singleton = ArithmeticGrammar()
        if hasattr(get_grammar, 'python_src__'):
            GLOBALS.Arithmetic_00000001_grammar_singleton.python_src__ = get_grammar.python_src__
        grammar = GLOBALS.Arithmetic_00000001_grammar_singleton
    return grammar


#######################################################################
#
# AST SECTION - Can be edited. Changes will be preserved.
#
#######################################################################

103
def group_no_asterix_mul(context):
104 105 106
    pass
    # TODO: Find an algorithm, here

eckhart's avatar
eckhart committed
107 108
Arithmetic_AST_transformation_table = {
    # AST Transformations for the Arithmetic-grammar
Eckhart Arnold's avatar
Eckhart Arnold committed
109 110
    "expression, term": [left_associative, replace_by_single_child],
    "factor, sign": replace_by_single_child,
Eckhart Arnold's avatar
Eckhart Arnold committed
111
    "group": [remove_tokens('(', ')'), replace_by_single_child],
eckhart's avatar
eckhart committed
112 113 114
}


115
def ArithmeticTransform() -> TransformationFunc:
eckhart's avatar
eckhart committed
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
    return partial(traverse, processing_table=Arithmetic_AST_transformation_table.copy())

def get_transformer() -> TransformationFunc:
    try:
        transformer = GLOBALS.Arithmetic_00000001_transformer_singleton
    except AttributeError:
        GLOBALS.Arithmetic_00000001_transformer_singleton = ArithmeticTransform()
        transformer = GLOBALS.Arithmetic_00000001_transformer_singleton
    return transformer


#######################################################################
#
# COMPILER SECTION - Can be edited. Changes will be preserved.
#
#######################################################################

class ArithmeticCompiler(Compiler):
    """Compiler for the abstract-syntax-tree of a Arithmetic source file.
    """

    def __init__(self):
        super(ArithmeticCompiler, self).__init__()

140 141
    def reset(self):
        super().reset()
eckhart's avatar
eckhart committed
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
        # initialize your variables here, not in the constructor!
    def on_expression(self, node):
        return self.fallback_compiler(node)

    # def on_term(self, node):
    #     return node

    # def on_factor(self, node):
    #     return node

    # def on_NUMBER(self, node):
    #     return node

    # def on_VARIABLE(self, node):
    #     return node


def get_compiler() -> ArithmeticCompiler:
    try:
        compiler = GLOBALS.Arithmetic_00000001_compiler_singleton
    except AttributeError:
        GLOBALS.Arithmetic_00000001_compiler_singleton = ArithmeticCompiler()
        compiler = GLOBALS.Arithmetic_00000001_compiler_singleton
    return compiler


#######################################################################
#
# END OF DHPARSER-SECTIONS
#
#######################################################################


def compile_src(source, log_dir=''):
    """Compiles ``source`` and returns (result, errors, ast).
    """
    with logging(log_dir):
        compiler = get_compiler()
        cname = compiler.__class__.__name__
        result_tuple = compile_source(source, get_preprocessor(),
                                      get_grammar(),
                                      get_transformer(), compiler)
    return result_tuple


if __name__ == "__main__":
    # recompile grammar if needed
    grammar_path = os.path.abspath(__file__).replace('Compiler.py', '.ebnf')
    if os.path.exists(grammar_path):
        if not recompile_grammar(grammar_path, force=False,
                                  notify=lambda:print('recompiling ' + grammar_path)):
            error_file = os.path.basename(__file__).replace('Compiler.py', '_ebnf_ERRORS.txt')
            with open(error_file, encoding="utf-8") as f:
                print(f.read())
            sys.exit(1)
    else:
        print('Could not check whether grammar requires recompiling, '
              'because grammar was not found at: ' + grammar_path)

    if len(sys.argv) > 1:
        # compile file
        file_name, log_dir = sys.argv[1], ''
        if file_name in ['-d', '--debug'] and len(sys.argv) > 2:
            file_name, log_dir = sys.argv[2], 'LOGS'
        result, errors, ast = compile_src(file_name, log_dir)
        if errors:
            cwd = os.getcwd()
            rel_path = file_name[len(cwd):] if file_name.startswith(cwd) else file_name
            for error in errors:
                print(rel_path + ':' + str(error))
            sys.exit(1)
        else:
            print(result.as_xml() if isinstance(result, Node) else result)
    else:
        print("Usage: ArithmeticCompiler.py [FILENAME]")