diff --git a/README.md b/README.md index bfa99d1..07e4e56 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ This language is under active development, progress will be marked here as the l - [x] Getter - [x] Setter - [x] Type Aliases -- [ ] Methods +- [x] Methods - [x] Declaration - - [ ] Use + - [x] Use - [ ] Traits - [ ] Generics - [ ] Basic diff --git a/boring/main.py b/boring/main.py index c46b298..e6c471d 100644 --- a/boring/main.py +++ b/boring/main.py @@ -33,7 +33,7 @@ if __name__ == "__main__": alias_resolver.with_module(AliasContex([]), result) # pretty_print(result) type_checker = TypeChecker() - while type_checker.with_module(Context(builtins, None), result): + while type_checker.with_module(Context(builtins, None, result), result): print("loop") # type_checker.with_module({}, result) pretty_print(result) diff --git a/boring/parse.py b/boring/parse.py index 5f62e4b..6e586a4 100644 --- a/boring/parse.py +++ b/boring/parse.py @@ -178,7 +178,10 @@ class AliasTypeDeclaration: old: TypeUsage -TypeDeclaration = Union[StructTypeDeclaration, PrimitiveTypeDeclaration, AliasTypeDeclaration] +TypeDeclaration = Union[ + StructTypeDeclaration, PrimitiveTypeDeclaration, AliasTypeDeclaration +] + @dataclass class Impl: diff --git a/boring/type_alias_resolution.py b/boring/type_alias_resolution.py index 5508d8c..e430650 100644 --- a/boring/type_alias_resolution.py +++ b/boring/type_alias_resolution.py @@ -18,7 +18,7 @@ def resolve_type(ctx: Context, type: parse.DataTypeUsage) -> parse.TypeUsage: changed = False if isinstance(result, parse.DataTypeUsage): for type_alias in ctx.type_aliases: - if type_alias.new.name == result.name: # type: ignore + if type_alias.new.name == result.name: # type: ignore result = type_alias.old changed = True else: @@ -32,13 +32,12 @@ def process_type(ctx: Context, type: parse.TypeUsage) -> parse.TypeUsage: elif isinstance(type, parse.FunctionTypeUsage): return parse.FunctionTypeUsage( return_type=process_type(ctx, type.return_type), - arguments=[process_type(ctx, argument) for argument in type.arguments] + arguments=[process_type(ctx, argument) for argument in type.arguments], ) else: return type - class TypeAliasResolver: def with_module(self, ctx: Context, module: parse.Module): for type_declaration in module.types: @@ -48,11 +47,18 @@ class TypeAliasResolver: for type_declaration in module.types: if isinstance(type_declaration, parse.StructTypeDeclaration): for field in type_declaration.fields: - type_declaration.fields[field] = process_type(ctx, type_declaration.fields[field]) + type_declaration.fields[field] = process_type( + ctx, type_declaration.fields[field] + ) for impl in module.impls: impl_ctx = ctx.copy() - impl_ctx.type_aliases.append(parse.AliasTypeDeclaration(new=parse.DataTypeUsage("Self"), old=parse.DataTypeUsage(impl.struct))) + impl_ctx.type_aliases.append( + parse.AliasTypeDeclaration( + new=parse.DataTypeUsage("Self"), + old=parse.DataTypeUsage(impl.struct), + ) + ) for function in impl.functions: self.with_function(impl_ctx, function) @@ -88,9 +94,7 @@ class TypeAliasResolver: else: assert False - def with_let_statement( - self, ctx: Context, let_statement: parse.LetStatement - ): + def with_let_statement(self, ctx: Context, let_statement: parse.LetStatement): self.with_expression(ctx, let_statement.expression) let_statement.type = process_type(ctx, let_statement.type) @@ -140,9 +144,7 @@ class TypeAliasResolver: assert False return - def with_variable_usage( - self, ctx: Context, variable_usage: parse.VariableUsage - ): + def with_variable_usage(self, ctx: Context, variable_usage: parse.VariableUsage): variable_usage.type = process_type(ctx, variable_usage.type) def with_operation(self, ctx: Context, operation: parse.Operation): @@ -151,25 +153,19 @@ class TypeAliasResolver: operation.type = process_type(ctx, operation.type) return - def with_function_call( - self, ctx: Context, function_call: parse.FunctionCall - ): + def with_function_call(self, ctx: Context, function_call: parse.FunctionCall): self.with_expression(ctx, function_call.source) for argument in function_call.arguments: self.with_expression(ctx, argument) function_call.type = process_type(ctx, function_call.type) return - def with_struct_getter( - self, ctx: Context, struct_getter: parse.StructGetter - ): + def with_struct_getter(self, ctx: Context, struct_getter: parse.StructGetter): self.with_expression(ctx, struct_getter.source) struct_getter.type = process_type(ctx, struct_getter.type) return - def with_literal_float( - self, ctx: Context, literal_float: parse.LiteralFloat - ): + def with_literal_float(self, ctx: Context, literal_float: parse.LiteralFloat): literal_float.type = process_type(ctx, literal_float.type) return @@ -177,9 +173,7 @@ class TypeAliasResolver: literal_int.type = process_type(ctx, literal_int.type) return - def with_literal_struct( - self, ctx: Context, literal_struct: parse.LiteralStruct - ): + def with_literal_struct(self, ctx: Context, literal_struct: parse.LiteralStruct): for name, expression in literal_struct.fields.items(): self.with_expression(ctx, expression) literal_struct.type = process_type(ctx, literal_struct.type) diff --git a/boring/type_checking.py b/boring/type_checking.py index ed4c047..954c135 100644 --- a/boring/type_checking.py +++ b/boring/type_checking.py @@ -15,9 +15,12 @@ Environment = Dict[str, Identified] class Context: environment: Environment current_function: Optional[parse.Function] + current_module: parse.Module def copy(self): - return Context(self.environment.copy(), self.current_function) + return Context( + self.environment.copy(), self.current_function, self.current_module + ) def unify(ctx: Context, first, second) -> bool: @@ -28,6 +31,10 @@ def unify(ctx: Context, first, second) -> bool: return changed +def function_to_method(type: parse.FunctionTypeUsage) -> parse.FunctionTypeUsage: + return parse.FunctionTypeUsage(type.arguments[1:], type.return_type) + + def type_compare( ctx: Context, first: parse.TypeUsage, second: parse.TypeUsage ) -> Tuple[parse.TypeUsage, bool]: @@ -334,10 +341,30 @@ class TypeChecker: assert isinstance(struct_getter.source.type, parse.DataTypeUsage) struct_declaration = ctx.environment[struct_getter.source.type.name] assert isinstance(struct_declaration, parse.StructTypeDeclaration) - assert struct_getter.attribute in struct_declaration.fields - result_type, changed_getter = type_compare( - ctx, struct_getter.type, struct_declaration.fields[struct_getter.attribute] - ) + if struct_getter.attribute in struct_declaration.fields: + result_type, changed_getter = type_compare( + ctx, + struct_getter.type, + struct_declaration.fields[struct_getter.attribute], + ) + else: # check methods and traits + impls = [] + for module_impl in ctx.current_module.impls: + if module_impl.struct == struct_getter.source.type.name: + impls.append(module_impl) + assert len(impls) != 0 + found = False + for impl in impls: + for function in impl.functions: + if function.name == struct_getter.attribute: + assert isinstance(function.type, parse.FunctionTypeUsage) + result_type, changed_getter = type_compare( + ctx, struct_getter.type, function_to_method(function.type) + ) + found = True + break + assert found, "Method not found" + if changed_getter: changed = True struct_getter.type = result_type diff --git a/examples/math/main.bl b/examples/math/main.bl index 6f48b44..e4e4454 100644 --- a/examples/math/main.bl +++ b/examples/math/main.bl @@ -45,6 +45,10 @@ fn get_user_id(): U64 { return user.id; } +fn use_method(user: User): U64 { + return user.get_id(); +} + type User struct { id: U64, }