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)
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