Esempio n. 1
0
def _make_module_parser(*, global_variable, function, class_, module_name):

    global_stmt = Any(
        global_variable.map(lambda x: ('var', x)),
        function.map(lambda x: ('func', x)),
        class_.map(lambda x: ('cls', x)),
    )

    module_pattern = All(
        import_stmt.map(lambda x: ('import', x)).repeat(),
        global_stmt.repeat(),
    ).flatten()

    def module_callback(m):
        imports = []
        vars = []
        funcs = []
        clss = []
        for kind, node in m.value:
            if kind == 'import':
                imports.append(node)
            elif kind == 'var':
                vars.append(node)
            elif kind == 'func':
                funcs.append(node)
            elif kind == 'cls':
                clss.append(node)
            else:
                raise base.Error([node.mark], f'FUBAR: {kind}: {node}')
        return ast.Module(
            mark=m.mark,
            name=module_name,
            imports=tuple(imports),
            vars=tuple(vars),
            funcs=tuple(funcs),
            clss=tuple(clss),
        )

    return module_pattern.fatmap(module_callback)
Esempio n. 2
0
def _make_combined_parser(*, module_name, importable_id, exportable_id, type_,
                          expression, block, global_dict, module_scope):

    # We can perform some basic validations if the global_dict
    # is available to us.
    validate = global_dict is not None

    global_variable = All(
        type_,
        exportable_id,
        '=',
        expression.required(),
        Any(';').required(),
    ).fatmap(lambda m: ast.GlobalVariable(
        mark=m.mark,
        type=m.value[0],
        name=m.value[1],
        expr=m.value[3] if global_dict is None else m.value[3](global_dict),
    ))

    parameter = All(type_, 'ID').fatmap(lambda m: ast.Parameter(
        mark=m.mark,
        type=m.value[0],
        name=m.value[1],
    ))

    parameters = All(
        '(',
        parameter.join(',').map(tuple),
        ')',
    ).map(lambda args: tuple(args[1]))

    ############
    # Function
    ############
    function_pattern = All(
        native,  # 0: native
        type_,  # 1: return type
        exportable_id,  # 2: name
        parameters,  # 3: parameters
        Any(
            block,
            Any(';').map(lambda x: None),
        ),  # 4: body
    )

    def function_callback(m):
        native = m.value[0]
        rtype = m.value[1]
        name = m.value[2]
        params = m.value[3]
        body_thunk = m.value[4]

        if body_thunk is None:
            body = None
        else:
            scope = Scope(module_scope)
            for param in params:
                scope[param.name] = param
            body = body_thunk(scope)

        if validate:
            if native and body:
                raise base.Error([m.mark],
                                 'Native functions cannot have a body')

            if not native and not body:
                raise base.Error([m.mark],
                                 'Non-native functions must have a body')

        return ast.Function(
            mark=m.mark,
            rtype=rtype,
            name=name,
            params=params,
            body=body,
        )

    function = function_pattern.fatmap(function_callback)

    ############
    # Field
    ############

    field_thunk = (All(type_, 'ID',
                       ';').fatmap(lambda m: lambda scope: ast.Field(
                           mark=m.mark,
                           type=m.value[0],
                           name=f'{scope["@class_name"]}.{m.value[1]}',
                       )))

    #################
    # Method (thunk)
    #################

    method_pattern = All(
        type_,  # 0: return type
        'ID',  # 1: name
        parameters,  # 2: parameters
        Any(
            block,
            Any(';').map(lambda x: None),
        ),  # 3: body
    )

    def method_callback(m):
        def inner_callback(outer_scope):
            scope = Scope(outer_scope)
            rtype = m.value[0]
            name = f'{scope["@class_name"]}#{m.value[1]}'
            params = m.value[2]
            body_thunk = m.value[3]
            if body_thunk is None:
                body = None
            else:
                for param in params:
                    scope[param.name] = param
                body = body_thunk(scope)
            return ast.Method(
                mark=m.mark,
                rtype=rtype,
                name=name,
                params=params,
                body=body,
            )

        return inner_callback

    method_thunk = method_pattern.fatmap(method_callback)

    ############
    # Class
    ############

    base_class = Any(
        All(':', importable_id).map(lambda args: args[1]),
        All().map(lambda args: None),
    )

    class_pattern = All(
        native,  # 0: native
        'class',  # 1
        exportable_id,  # 2: name
        base_class,  # 3: base/super class
        '{',  # 4
        field_thunk.repeat(),  # 5: fields
        method_thunk.repeat(),  # 6: methods
        '}',  # 7
    )

    def class_callback(m):
        native = m.value[0]
        class_name = m.value[2]
        declared_base = m.value[3]
        field_thunks = m.value[5]
        method_thunks = m.value[6]
        base = (ast.OBJECT if declared_base is None
                and class_name != ast.OBJECT else declared_base)
        scope = Scope(module_scope)
        scope['@class_name'] = class_name
        fields = tuple(ft(scope) for ft in field_thunks)
        methods = tuple(mt(scope) for mt in method_thunks)
        if validate:
            for method in methods:
                if native and method.body:
                    raise base.Error(
                        [m.mark, method.mark],
                        'Native classes cannot have method bodies',
                    )
                if not native and not method.body:
                    raise base.Error(
                        [m.mark, method.mark],
                        'Non-native classes cannot have native methods',
                    )
            if native and fields:
                raise base.Error(
                    [m.mark, fields[0].mark],
                    'Native classes cannot have fields',
                )
        return ast.Class(
            mark=m.mark,
            native=native,
            name=class_name,
            base=base,
            fields=fields,
            methods=methods,
        )

    class_ = class_pattern.fatmap(class_callback)

    module = _make_module_parser(
        global_variable=global_variable,
        function=function,
        class_=class_,
        module_name=module_name,
    )

    return module