diff --git a/examples/test.br b/examples/test.br index 7f58e07..7d68caa 100644 --- a/examples/test.br +++ b/examples/test.br @@ -1,58 +1,11 @@ -// main.br +// test.br -unsafe { - type println fn (String) -> Void; - type print fn (String) -> Void; -} +const name = "John Doe"; +let x = 20; +let y = x + 5; -trait Error { - fn error () -> String; -} +let conversion: Number = @Number("45.3"); +let conversion2: String = @String(x * y); -struct FooError { - fn error () -> String { - return "Error: Foo"; - } -} - -struct CustomError { - message: String; - - static fn new(message: String) -> Self { - return Self { - message: message, - }; - } - - fn error () -> String { - return self.message; - } -} - -fn main () { - const errors = []Error{ - CustomError::new("Something went wrong"), - FooError{} - }; - - const customErr = error[0]; - match customErr { - case CustomError { - customErr.message = "Custom Error Occured"; - println("CustomError: " + customErr.error()); - }, - - case FooError { - println("FooError: " + customErr.error()); - }, - } - - const e1 = errors[0]; - const e2 = errors[1]; - const message: String = e1.error() + e2.error(); - - const numStr = @string(45.21); // Converts Data to a String - const customErr = @assert(CustomError, errors[1]); // casts away a type explicitly. No conversion - - println(customErr.error()); -} \ No newline at end of file +const result = -x + y; +println(result); \ No newline at end of file diff --git a/examples/traits.br b/examples/traits.br index 3c4e925..b4b3258 100644 --- a/examples/traits.br +++ b/examples/traits.br @@ -1,15 +1,61 @@ // main.br -// impl Number { -// fn to_string() -> String { -// return as_str(self); -// } -// } +unsafe { + type println fn (String) -> Void; + type print fn (String) -> Void; +} -// unsafe { -// type println = fn (String) -> Void; -// type as_str = fn (Any) -> String; -// } +trait Error { + fn error () -> String; +} + +struct FooError { + fn error () -> String { + return "Error: Foo"; + } +} + +struct CustomError { + message: String; + + static fn new(message: String) -> Self { + return Self { + message: message, + }; + } + + fn error () -> String { + return self.message; + } +} + +fn main () { + const errors = []Error{ + CustomError::new("Something went wrong"), + FooError{} + }; + + const customErr = error[0]; + match customErr { + case CustomError { + customErr.message = "Custom Error Occured"; + println("CustomError: " + customErr.error()); + }, + + case FooError { + println("FooError: " + customErr.error()); + }, + } + + const e1 = errors[0]; + const e2 = errors[1]; + const message: String = e1.error() + e2.error(); + + const numStr = @string(45.21); // Converts Data to a String + const customErr = @assert(CustomError, errors[1]); // casts away a type explicitly. No conversion + + println(customErr.error()); +} trait Error { fn error () -> String; diff --git a/src/analysis/analysis.go b/src/analysis/analysis.go deleted file mode 100644 index 2b6584c..0000000 --- a/src/analysis/analysis.go +++ /dev/null @@ -1,273 +0,0 @@ -package analysis - -import ( - "fmt" - - "github.com/fatih/color" - "github.com/sanity-io/litter" - "github.com/tlaceby/bedrock/src/ast" - "github.com/tlaceby/bedrock/src/helpers" -) - -var numTables int = 0 - -type Type interface { - str() string // string representation of Abstract Type -} - -type SymbolInfo struct { - Type Type // Underlying type of symbol - IsConstant bool // Whether the symbol is defined as a constant or fn/module etc... - AssignmentCount int // Number of times this symbol is assigned/reassigned - AccessedCount int // Number of times symbol is accessed/referenced - ClosedOver bool // Whether this variable will be used inside a closure and needs heap allocation -} - -type SymbolTable struct { - TableName string // the chained name of the current environment - TableID int // ID assigned to table - - IsGlobal bool // whether this table exists in the global scope - IsModule bool // Represents the module which this table exists in - IsFunction bool // whether this table is the body of a function - IsStaticMethod bool // whether we are inside a static methods scope. Prevents access to instance methods/properties from inside - IsLoop bool // Whether the current env is a while/for loop - IsUnsafe bool // Inside a unsafe block - - Parent *SymbolTable // Reference to parent environment - DefinedTypes map[string]Type // All Alias/ Defined Types / Traits - Symbols map[string]SymbolInfo // All Variables/Functions/Modlues - - FoundReturnTypes []Type // Inside functions keep track of found return types -} - -func CreateSymbolTable(parent *SymbolTable, fn bool, isModule bool, isStruct bool, givenTableName string) *SymbolTable { - isGlobal := true - - if parent != nil { - isGlobal = false - } - - numTables += 1 - table := &SymbolTable{ - TableID: numTables, - Parent: parent, - IsGlobal: isGlobal, - IsModule: isModule, - IsFunction: fn, - Symbols: map[string]SymbolInfo{}, - DefinedTypes: map[string]Type{}, - } - - // Generate Table Name - if table.Parent != nil { - table.TableName = fmt.Sprintf("%s.%s", table.Parent.TableName, givenTableName) - } else { - table.TableName = givenTableName - } - - if table.IsGlobal { - defineGlobalDefaultTypes(table) - table.TableName = color.RedString("global") - } - - return table -} - -func typecheck_expr(expr ast.Expr, env *SymbolTable) Type { - switch e := expr.(type) { - case ast.NumberExpr: - return NumType{} - case ast.StringExpr: - return StrType{} - case ast.SymbolExpr: - return tc_symbol_expr(e, env) - case ast.BinaryExpr: - return tc_binary_expr(e, env) - case ast.MemberExpr: - return tc_member_expr(e, env) - case ast.AssignmentExpr: - return tc_assignment_expr(e, env) - case ast.StaticMemberExpr: - return tc_static_member_expr(e, env) - case ast.CallExpr: - return tc_call_expr(e, env) - case ast.StructInstantiationExpr: - return tc_struct_instantation_expr(e, env) - case ast.ArrayLiteral: - return tc_array_literal_expr(e, env) - case ast.ComputedExpr: - return tc_computed_expr(e, env) - - // macros - case ast.AssertExpr: - return tc_assert_expr(e, env) - case ast.CastStringExpr: - return tc_cast_string_expr(e, env) - default: - litter.Dump(expr) - panic("^^^^^^ Unknown ast.Expr encountered! ^^^^^^\n") - } -} - -func typecheck_type(_type ast.Type, env *SymbolTable) Type { - switch t := _type.(type) { - case ast.SymbolType: - return tc_symbol_type(t, env) - case ast.ListType: - return tc_list_type(t, env) - case ast.FnType: - return tc_fn_type(t, env) - case ast.StructType: - return tc_struct_generic_type(t, env) - default: - litter.Dump(t) - panic("^^^^^^ Unknown ast.Type encountered! ^^^^^^\n") - } -} - -func typecheck_stmt(stmt ast.Stmt, env *SymbolTable) Type { - switch s := stmt.(type) { - case ast.ModuleStmt: - return tc_module_stmt(s, env) - case ast.BlockStmt: - return tc_block_stmt(s, env) - case ast.VarDeclarationStmt: - return tc_var_declaration_stmt(s, env) - case ast.ExpressionStmt: - return typecheck_expr(s.Expression, env) - case ast.FunctionDeclarationStmt: - return tc_fn_declaration_stmt(s, env) - case ast.ReturnStmt: - return tc_return_stmt(s, env) - case ast.StructDeclarationStmt: - return tc_struct_declaration_stmt(s, env) - case ast.WhileStmt: - return tc_while_stmt(s, env) - case ast.TraitStmt: - return tc_trait_stmt(s, env) - default: - litter.Dump(stmt) - panic("^^^^^^ Unknown ast.Stmt encountered! ^^^^^^\n") - } -} - -func Typecheck(module ast.ModuleStmt, globalEnv *SymbolTable) Type { - t := typecheck_stmt(module, globalEnv) - return t -} - -// ------------------------ -// Helpers for symbol table -// ------------------------ - -func (table *SymbolTable) findNearestFunctionEnv() *SymbolTable { - if table.IsFunction { - return table - } - - if table.Parent != nil { - return table.Parent.findNearestFunctionEnv() - } - - return nil -} - -func (table *SymbolTable) findNearestSymbolEnv(symbolName string) (*SymbolInfo, *SymbolTable) { - t, existsInCurrentTable := table.Symbols[symbolName] - - if existsInCurrentTable { - return &t, table - } - - if table.Parent != nil { - return table.Parent.findNearestSymbolEnv(symbolName) - } - - return nil, nil -} - -func (table *SymbolTable) findNearestTypeEnv(typeName string) (Type, *SymbolTable) { - t, existsInCurrentTable := table.DefinedTypes[typeName] - - if existsInCurrentTable { - return t, table - } - - if table.Parent != nil { - return table.Parent.findNearestTypeEnv(typeName) - } - - return ErrType(fmt.Sprintf("Type %s does not exist in the current env", typeName)), nil -} - -func (table *SymbolTable) debugTable(printParent bool) { - - if printParent && table.Parent != nil { - table.Parent.debugTable(printParent) - } - - println(fmt.Sprintf("\n------------ %s ------------ ", table.TableName)) - if len(table.DefinedTypes) > 0 { - color.Blue("types:") - - for typename, typevalue := range table.DefinedTypes { - println(fmt.Sprintf(" %s -> %s", typename, typevalue.str())) - } - } - - count := 0 - for typename, typevalue := range table.DefinedTypes { - if count != 0 { - println() - } else { - color.Blue("\nstructs:") - } - - count++ - - if helpers.TypesMatchT[StructType](typevalue) { - println(fmt.Sprintf(" %s ", typename)) - - structVal := helpers.ExpectType[StructType](typevalue) - - if len(structVal.Properties) > 0 { - for propertyName, propertyType := range structVal.Properties { - println(fmt.Sprintf(" %s : %s", propertyName, propertyType.str())) - } - } - - if len(structVal.Methods) > 0 { - for propertyName, methodType := range structVal.Methods { - println(fmt.Sprintf(" %s : %s", propertyName, methodType.str())) - } - } - - if len(structVal.StaticMethods) > 0 { - for propertyName, staticMethod := range structVal.StaticMethods { - println(fmt.Sprintf(" %s : %s %s", propertyName, color.YellowString("static"), staticMethod.str())) - } - } - } - - if helpers.TypesMatchT[GenericStructType](typevalue) { - structVal := helpers.ExpectType[GenericStructType](typevalue) - fmt.Printf(" %s\n", structVal.str()) - } - } - - color.Blue("\nsymbols:") - for symbol, symbolInfo := range table.Symbols { - println(fmt.Sprintf(" %s: ", color.BlueString(symbol))) - println(fmt.Sprintf(" type : %s", symbolInfo.Type.str())) - - if symbolInfo.IsConstant { - println(" const : " + color.BlueString("true")) - } else { - println(" const : " + color.BlueString("false")) - } - - println(fmt.Sprintf(" accessed : %s", color.BlueString("%d", symbolInfo.AccessedCount))) - println(fmt.Sprintf(" assigned : %s\n", color.BlueString("%d", symbolInfo.AccessedCount))) - } -} diff --git a/src/analysis/errors.go b/src/analysis/errors.go new file mode 100644 index 0000000..3c92065 --- /dev/null +++ b/src/analysis/errors.go @@ -0,0 +1,8 @@ +package analysis + +func typeError(kind string, message string) TypeError { + return TypeError{ + Kind: kind, + Message: message, + } +} diff --git a/src/analysis/helpers.go b/src/analysis/helpers.go deleted file mode 100644 index 462b0da..0000000 --- a/src/analysis/helpers.go +++ /dev/null @@ -1,155 +0,0 @@ -package analysis - -import "github.com/fatih/color" - -func createGenericListStr(structName string, generics []string) string { - str := color.MagentaString(structName) + "<" - - for indx, name := range generics { - str += name - if indx != len(generics)-1 { - str += ", " - } - } - - str += ">" - return str -} - -func typesSame(t Type, expected Type) bool { - if t.str() == expected.str() { - return true - } - - switch other := expected.(type) { - case TraitType: - return implements(t, other) - } - - return false -} - -func CastableToBool(t Type) bool { - switch t.(type) { - case BoolType: - return true - case NumType: - return true - case StrType: - return true - case ArrayType: - return true - default: - return false - } -} - -func CastableToString(t Type) bool { - // For now everything can be cast to a string functions, structs and traits included. - switch t.(type) { - case BoolType: - return true - case NumType: - return true - case StrType: - return true - case ArrayType: - return true - default: - return true - } -} - -func IsAFnType(t Type) bool { - switch t.(type) { - case FnType: - return true - case GenericFnType: - return true - } - - return false -} - -func IsAStructType(t Type) bool { - switch t.(type) { - case StructType: - return true - case GenericStructType: - return true - } - - return false -} - -func IsNormalFnType(t Type) bool { - switch t.(type) { - case FnType: - return true - } - - return false -} - -func IsNormalStructType(t Type) bool { - switch t.(type) { - case StructType: - return true - } - - return false -} - -func IsGenericFnType(t Type) bool { - switch t.(type) { - case GenericFnType: - return true - } - - return false -} - -func IsGenericStructType(t Type) bool { - switch t.(type) { - case GenericStructType: - return true - } - - return false -} - -func implements(t Type, trait TraitType) bool { - switch v := t.(type) { - case StructType: - for methodName, fnType := range trait.Methods { - method, exists := v.Methods[methodName] - if !exists || !isFnTypesEqual(method, fnType) { - return false - } - } - return true - // Add cases for other types that can implement traits - default: - return false - } -} - -func isFnTypesEqual(fn1, fn2 FnType) bool { - if fn1.Variadic != fn2.Variadic || len(fn1.ParamTypes) != len(fn2.ParamTypes) { - return false - } - - for i := range fn1.ParamTypes { - if !isTypesEqual(fn1.ParamTypes[i], fn2.ParamTypes[i]) { - return false - } - } - - return isTypesEqual(fn1.ReturnType, fn2.ReturnType) -} - -func isTypesEqual(t1, t2 Type) bool { - // Implement type equality check based on your type system - // You can compare the string representation of types for simplicity - return t1.str() == t2.str() -} diff --git a/src/analysis/macros.go b/src/analysis/macros.go deleted file mode 100644 index 57c1eaf..0000000 --- a/src/analysis/macros.go +++ /dev/null @@ -1,24 +0,0 @@ -package analysis - -import ( - "fmt" - - "github.com/tlaceby/bedrock/src/ast" -) - -func tc_cast_string_expr(m ast.CastStringExpr, env *SymbolTable) Type { - expr := typecheck_expr(m.Expr, env) - - if !CastableToString(expr) { - panic(fmt.Sprintf("Cannot cast type to a string @string(%s)", expr.str())) - } - - return StrType{} -} - -func tc_assert_expr(m ast.AssertExpr, env *SymbolTable) Type { - var assertType = typecheck_type(m.TypeAssertion, env) - typecheck_expr(m.Expr, env) - - return assertType -} diff --git a/src/analysis/tc_expr.go b/src/analysis/tc_expr.go new file mode 100644 index 0000000..cc79a0e --- /dev/null +++ b/src/analysis/tc_expr.go @@ -0,0 +1,30 @@ +package analysis + +import ( + "github.com/sanity-io/litter" + "github.com/tlaceby/bedrock/src/ast" +) + +func (tc *Typechecker) expr(node ast.Expr, scope *ast.Scope) ast.Type { + switch expr := node.(type) { + case ast.StringExpr: + return String() + case ast.SymbolExpr: + return tc.symbol_expr(expr, scope) + case ast.NumberExpr: + return Number() + case ast.BinaryExpr: + return tc.binary_expr(expr, scope) + } + + litter.Dump(node) + panic("Unknown type inside typecheck_expr()") +} + +func (tc *Typechecker) binary_expr(node ast.BinaryExpr, scope *ast.Scope) ast.Type { + return nil +} + +func (tc *Typechecker) symbol_expr(node ast.SymbolExpr, scope *ast.Scope) ast.Type { + return nil +} diff --git a/src/analysis/tc_exprs.go b/src/analysis/tc_exprs.go deleted file mode 100644 index 0404fa5..0000000 --- a/src/analysis/tc_exprs.go +++ /dev/null @@ -1,439 +0,0 @@ -package analysis - -import ( - "fmt" - - "github.com/sanity-io/litter" - "github.com/tlaceby/bedrock/src/ast" - "github.com/tlaceby/bedrock/src/helpers" - "github.com/tlaceby/bedrock/src/lexer" -) - -func tc_symbol_expr(e ast.SymbolExpr, env *SymbolTable) Type { - typeInfo, table := env.findNearestSymbolEnv(e.Value) - - if typeInfo != nil { - typeInfo.AccessedCount += 1 - table.Symbols[e.Value] = *typeInfo - return typeInfo.Type - } - - panic(ErrType(fmt.Sprintf("Symbol %s does not exist in the current env", e.Value)).str()) -} - -func tc_binary_expr(e ast.BinaryExpr, env *SymbolTable) Type { - leftType := typecheck_expr(e.Left, env) - rightType := typecheck_expr(e.Right, env) - - // Default error if all checks fail - err := ErrType("Invalid use of operator " + e.Operator.Value + " with " + leftType.str() + " and " + rightType.str()) - - isComparisonOp := e.Operator.IsOneOfMany(lexer.LESS, lexer.LESS_EQUALS, lexer.GREATER, lexer.GREATER_EQUALS) - isArithmeticOp := e.Operator.IsOneOfMany(lexer.DASH, lexer.SLASH, lexer.STAR, lexer.PERCENT) - isLogical := e.Operator.IsOneOfMany(lexer.AND, lexer.OR) - - // Both operands must be numbers for arithmetic - if isArithmeticOp && helpers.TypesMatchT[NumType](leftType, rightType) { - return NumType{} - } - - // Both types must be a number for < <= > >= :-> bool - if isComparisonOp && helpers.TypesMatchT[NumType](leftType) && helpers.TypesMatch(leftType, rightType) { - return BoolType{} - } - - // for == or != both types must be the same on either side :-> bool - if e.Operator.IsOneOfMany(lexer.EQUALS, lexer.NOT_EQUALS) && helpers.TypesMatch(leftType, rightType) { - return BoolType{} - } - - // && || must both be booleans on left/right side :-> bool - if isLogical && CastableToBool(leftType) && CastableToBool(rightType) { - return BoolType{} - } - - // Check String Concatenation vs Addition - if e.Operator.Kind == lexer.PLUS { - // number + number - if helpers.TypesMatchT[NumType](leftType) { - if helpers.TypesMatchT[NumType](rightType) { // number + number - return NumType{} - } - } - - // string + string - if helpers.TypesMatchT[StrType](leftType) { // string + string - if helpers.TypesMatchT[StrType](rightType) { - return StrType{} - } - } - } - - panic(err.str()) -} - -func tc_struct_instantation_expr(e ast.StructInstantiationExpr, env *SymbolTable) Type { - var structName = e.StructName - var initialValues = e.Objects - var structType StructType - - // Verify the struct has been defined. - T, definedScope := env.findNearestTypeEnv(structName) - - if definedScope == nil { - panic(ErrType(fmt.Sprintf("Cannot instantiate struct %s as it has not yet been defined.", structName))) - } - - // Validate struct type is indeed a struct type - if !IsAStructType(T) { - panic(ErrType(fmt.Sprintf("Cannot perform struct inbstantiation on %s{} as it is not a struct.", structName))) - } - - if IsNormalStructType(T) { - structType = helpers.ExpectType[StructType](T) - } - - if IsGenericStructType(T) { - var genericStruct = helpers.ExpectType[GenericStructType](T) - var expectedGenericArity = len(e.Generics) - var recievedGenericArity = len(genericStruct.Generics) - var structEnv = CreateSymbolTable(genericStruct.Closure, false, false, true, genericStruct.str()) - var recievedGenericsTypeStrings = []string{} - - // Verify Generics Arity - if expectedGenericArity != recievedGenericArity { - panic(fmt.Sprintf("Expected arity for generic struct instantiation of %d but recieved %d instead. \nGeneric structs must have explicit generic parameters.", expectedGenericArity, recievedGenericArity)) - } - - // Install Generic Types For Respective Names - for genericIndex, recievedGenericType := range e.Generics { - genericName := genericStruct.Generics[genericIndex] - genericType := typecheck_type(recievedGenericType, structEnv) - - recievedGenericsTypeStrings = append(recievedGenericsTypeStrings, genericType.str()) - structEnv.DefinedTypes[genericName] = genericType - } - var structSignature = createGenericListStr(structName, recievedGenericsTypeStrings) - - structType = validate_struct_body(structEnv, structSignature, genericStruct.Properties, genericStruct.StaticMethods, genericStruct.InstanceMethods) - definedScope.DefinedTypes[structName] = genericStruct - definedScope.DefinedTypes[structSignature] = structType - } - - // Validate each field is instantiated which is expected in the struct - var errors = []ErrorType{} // array of error messages to display - - for expectedPropertyName, expectedProperty := range structType.Properties { - var foundProperty ast.ObjectField - var found = false - - for _, val := range initialValues { - if expectedPropertyName == val.PropertyName { - foundProperty = val - found = true - break - } - } - - if !found { - errors = append(errors, ErrType(fmt.Sprintf("Missing value for %s in struct instantiation of %s{}", expectedPropertyName, structName))) - continue - } - - // Validate the given type against the expected one for that propertyname - givenType := typecheck_expr(foundProperty.PropertyValue, env) - if !helpers.TypesMatch(givenType, expectedProperty) { - errors = append(errors, ErrType(fmt.Sprintf("Property %s of struct %s{} expects type %s but was given %s instead.", expectedPropertyName, structName, expectedProperty.str(), givenType.str()))) - } - } - - if len(errors) > 0 { - for _, err := range errors { - println(err.Message) - } - - panic("Typechecking failed at struct instantiation") - } - - return structType -} - -func tc_member_expr(e ast.MemberExpr, env *SymbolTable) Type { - memberType := typecheck_expr(e.Member, env) - propertyName := e.Property - - switch member := memberType.(type) { - case TraitType: - var propertyType Type - - propertyType, propertyExists := member.Methods[propertyName] - if propertyExists { - return propertyType - } - - panic(fmt.Sprintf("Member %s does not contain property %s. Attempted to access %s but it does not exist on object\n", memberType.str(), propertyName, propertyName)) - - case StructType: - var propertyType Type - - // Check struct.properties for propertyname - propertyType, propertyExists := member.Properties[propertyName] - if propertyExists { - return propertyType - } - - // then check struct.methods for propertyname - propertyType, propertyExists = member.Methods[propertyName] - if propertyExists { - return propertyType - } - - panic(fmt.Sprintf("Member %s does not contain property %s. Attempted to access %s but it does not exist on object\n", memberType.str(), propertyName, propertyName)) - - } - - panic(fmt.Sprintf("Member expression with %s.%s not handled\n", memberType.str(), propertyName)) -} - -func tc_static_member_expr(e ast.StaticMemberExpr, env *SymbolTable) Type { - structName := e.StructName - propertyName := e.MethodName - memberType, memberEnv := env.findNearestTypeEnv(structName) - - if memberEnv == nil { - panic(ErrType(fmt.Sprintf("Invalid member access using resolution operator on %s::%s. Type %s Cannot be resolved.", structName, propertyName, structName)).str()) - } - - switch member := memberType.(type) { - case StructType: - // Check struct.properties for propertyname - propertyType, propertyExists := member.StaticMethods[propertyName] - if propertyExists { - return propertyType - } - - panic(fmt.Sprintf("Member %s does not contain static property %s. Attempted to access static member %s but it does not exist on object\n", memberType.str(), propertyName, propertyName)) - - case GenericStructType: - // Create a temporary ast node to validate that this generic type is valid. - genericType := ast.StructType{ - GenericList: e.StructGenerics, - StructName: e.StructName, - } - - // Validate Struct Instance - genericStructInstance := helpers.ExpectType[StructType](tc_struct_generic_type(genericType, env)) - propertyType, propertyExists := genericStructInstance.StaticMethods[propertyName] - if propertyExists { - return propertyType - } - - panic(fmt.Sprintf("Member %s does not contain static property %s. Attempted to access static member %s but it does not exist on object\n", memberType.str(), propertyName, propertyName)) - - } - - panic(fmt.Sprintf("Static member expression with %s::%s not handled\n", memberType.str(), propertyName)) -} - -func tc_call_expr(e ast.CallExpr, env *SymbolTable) Type { - var method = typecheck_expr(e.Method, env) - - // Verify method is indeed a function - if !IsAFnType(method) { - panic("Attempted to call function in call expression with non function.") - } - - var fnInstanceType FnType - var recievedArgs = []Type{} - - // If the method is not generic then simply cast type away - if IsNormalFnType(method) { - fnInstanceType = helpers.ExpectType[FnType](method) - } - - // If the function is generic then we need to validate body aswell. - if IsGenericFnType(method) { - var genericFn = helpers.ExpectType[GenericFnType](method) - var fnEnv = CreateSymbolTable(genericFn.Closure, true, false, false, genericFn.str()) - var expectedReturnType Type = VoidType{} - var params = []Type{} - - // Validate Arity of Generic Call - expectedGenericArity := len(genericFn.Generics) - recievedGenericArity := len(e.Generics) - - if expectedGenericArity != recievedGenericArity { - panic(fmt.Sprintf("Expected %d generic args but recieved %d instead. You must suply the explit generic types in a generic fn call.", expectedGenericArity, recievedGenericArity)) - } - - // Since we know the arity is correct, assign each generic type parameter with the ones recieved - for index, genericName := range genericFn.Generics { - genericType := typecheck_type(e.Generics[index], env) - fnEnv.DefinedTypes[genericName] = genericType - } - - // Validate and Install Parameters - for _, param := range genericFn.Parameters { - paramName := param.Name - paramType := typecheck_type(param.Type, fnEnv) - - fnEnv.Symbols[paramName] = SymbolInfo{ - Type: paramType, - IsConstant: true, - AssignmentCount: 1, - } - - params = append(params, paramType) - } - - // Return Type - if genericFn.ReturnType != nil { - expectedReturnType = typecheck_type(genericFn.ReturnType, fnEnv) - } - - // Evaluate Body - for _, stmt := range genericFn.Body { - typecheck_stmt(stmt, fnEnv) - } - - // Validate Returns Match - for _, foundReturn := range fnEnv.FoundReturnTypes { - if !typesSame(foundReturn, expectedReturnType) { - panic(fmt.Sprintf("In generic function call %s, found %s as a return type, however expected to find %s", genericFn.str(), foundReturn.str(), expectedReturnType.str())) - } - } - - // If we expect a return type but recieve none - if len(fnEnv.FoundReturnTypes) == 0 && !typesSame(expectedReturnType, VoidType{}) { - panic(fmt.Sprintf("In generic function call %s , expected to find return type of %s but found none.", genericFn.str(), expectedReturnType.str())) - } - - fnInstanceType = FnType{ - Variadic: false, - ParamTypes: params, - ReturnType: expectedReturnType, - } - } - - // Create Arg Types - for _, recievedArg := range e.Arguments { - recievedArgs = append(recievedArgs, typecheck_expr(recievedArg, env)) - } - - expectedArity := len(fnInstanceType.ParamTypes) - recievedArity := len(recievedArgs) - - // TODO: Variadic functions ??? - if expectedArity != recievedArity { - panic(fmt.Sprintf("Expected arity of call expression %s, is %d but recieved %d instead.", fnInstanceType.str(), expectedArity, recievedArity)) - } - - for argIndx, expectedArg := range fnInstanceType.ParamTypes { - recievedArg := recievedArgs[argIndx] - if !typesSame(expectedArg, recievedArg) { - panic(fmt.Sprintf("In function call %s: Arg at position %d expected to be a type %s but recieved %s instead.", fnInstanceType.str(), argIndx, expectedArg.str(), recievedArg.str())) - } - } - - return fnInstanceType.ReturnType -} - -func tc_assignment_expr(e ast.AssignmentExpr, env *SymbolTable) Type { - assigne := typecheck_expr(e.Assigne, env) - value := typecheck_expr(e.AssignedValue, env) - // Make sure we are assigning valid things. - switch lhs := e.Assigne.(type) { - case ast.SymbolExpr: - varname := lhs.Value - // make sure symbol exists and is not constant - varInfo, varEnv := env.findNearestSymbolEnv(varname) - - // Make sure variable exists - if varInfo == nil || varEnv == nil { - panic(fmt.Sprintf("Cannot perform assignment on %s as it does not exist.", varname)) - } - - // Avoid Constant Assignment - if varInfo.IsConstant { - panic(fmt.Sprintf("Cannot perform assignment on %s as it declared as constant.", varname)) - } - - if !typesSame(value, assigne) { - panic(fmt.Sprintf("Invalid assignment of %s with %s = %s\n", varname, varInfo.Type.str(), value.str())) - } - - // Update assignment and access count - varInfo.AccessedCount-- // When performing the lookup it adds an access. Remove it. - varInfo.AssignmentCount++ - - varEnv.Symbols[varname] = *varInfo - - return value - - case ast.MemberExpr: - memberExpr := typecheck_expr(lhs.Member, env) - - switch member := memberExpr.(type) { - case StructType: - propertyName := lhs.Property - propertyValue := member.getPropertyByName(propertyName) - - // Make sure types match - if !typesSame(propertyValue, value) { - panic(fmt.Sprintf("Mismatched types. Invalid assignment of %s.%s with %s. Expected type is %s.\n", member.StructName, propertyName, value.str(), propertyValue.str())) - } - - return value - } - } - - litter.Dump(e) - panic(fmt.Sprintf("Invalid assignment expression. Cannot assign %s to %s\n", assigne.str(), value.str())) -} - -func tc_computed_expr(e ast.ComputedExpr, env *SymbolTable) Type { - var member Type = typecheck_expr(e.Member, env) - var computedExpr = typecheck_expr(e.Property, env) - var arr ArrayType - - if !helpers.TypesMatchT[ArrayType](member) { - panic(fmt.Sprintf("Cannot perform computed expression on %s. Expected an array instead.", member.str())) - } - - if !typesSame(computedExpr, NumType{}) { - panic(fmt.Sprintf("Computed member must be a number. Recieved %s indtead in computed expression of %s", computedExpr.str(), member.str())) - } - - arr = helpers.ExpectType[ArrayType](member) - return arr.Underlying -} - -func tc_array_literal_expr(e ast.ArrayLiteral, env *SymbolTable) Type { - var expectedUnderlyingType = typecheck_type(e.UnderlyingType, env) - var capacity = e.Capacity - var initialValues = []Type{} - - // Verify all components have the same type - for _, val := range e.Contents { - valType := typecheck_expr(val, env) - - if !typesSame(valType, expectedUnderlyingType) { - panic(fmt.Sprintf("Expected array of %s but recived %s inside array instatiation.", expectedUnderlyingType.str(), valType.str())) - } - - initialValues = append(initialValues, valType) - } - - if capacity == -1 { - capacity = len(initialValues) - } - - if capacity < len(initialValues) { - panic(fmt.Sprintf("Capacity of %d is smaller then length of array literal (%d).", capacity, len(initialValues))) - } - - return ArrayType{ - Underlying: expectedUnderlyingType, - Capacity: uint(capacity), - } -} diff --git a/src/analysis/tc_stmt.go b/src/analysis/tc_stmt.go new file mode 100644 index 0000000..4c22d78 --- /dev/null +++ b/src/analysis/tc_stmt.go @@ -0,0 +1,34 @@ +package analysis + +import ( + "github.com/sanity-io/litter" + "github.com/tlaceby/bedrock/src/ast" +) + +func (tc *Typechecker) stmt(node ast.Stmt, scope *ast.Scope) ast.Type { + switch stmt := node.(type) { + case ast.ModuleStmt: + return tc.Typecheck(stmt) + case ast.BlockStmt: + return tc.block_stmt(stmt, scope) + case ast.VarDeclarationStmt: + return tc.var_decl_stmt(stmt, scope) + case ast.TraitStmt: + return tc.trait_stmt(stmt, scope) + } + + litter.Dump(node) + panic("Unknown type inside typecheck_stmt()") +} + +func (tc *Typechecker) block_stmt(node ast.BlockStmt, scope *ast.Scope) ast.Type { + return nil +} + +func (tc *Typechecker) var_decl_stmt(node ast.VarDeclarationStmt, scope *ast.Scope) ast.Type { + return nil +} + +func (tc *Typechecker) trait_stmt(node ast.TraitStmt, scope *ast.Scope) ast.Type { + return nil +} diff --git a/src/analysis/tc_stmts.go b/src/analysis/tc_stmts.go deleted file mode 100644 index 3ca3648..0000000 --- a/src/analysis/tc_stmts.go +++ /dev/null @@ -1,479 +0,0 @@ -package analysis - -import ( - "fmt" - - "github.com/tlaceby/bedrock/src/ast" - "github.com/tlaceby/bedrock/src/helpers" -) - -func tc_block_stmt(s ast.BlockStmt, env *SymbolTable) Type { - var last Type - env = CreateSymbolTable(env, false, false, false, "block") - for _, stmt := range s.Body { - last = typecheck_stmt(stmt, env) - } - - env.debugTable(false) - return last -} - -func tc_var_declaration_stmt(s ast.VarDeclarationStmt, env *SymbolTable) Type { - // Check that variable does not already exist in current scope - e := ErrType(fmt.Sprintf("A variable with the name %s already exists in the current scope. Did you mean to reassign instead?", s.Identifier)) - _, exists := env.Symbols[s.Identifier] - - if !exists { - var expectedType Type - var rhsValue Type - - // If a given explicit type is there then need to check it - if s.ExplicitType != nil { - expectedType = typecheck_type(s.ExplicitType, env) - - // If nothing was defined but a type was given mark it. - if s.AssignedValue == nil { - env.Symbols[s.Identifier] = SymbolInfo{ - Type: expectedType, - IsConstant: s.Constant, // should be false - AssignmentCount: 0, - AccessedCount: 0, - } - - return expectedType - } - } - - rhsValue = typecheck_expr(s.AssignedValue, env) - - // This means implicit type from rhs no explicit given - if expectedType == nil { - env.Symbols[s.Identifier] = SymbolInfo{ - Type: rhsValue, - IsConstant: s.Constant, - AssignmentCount: 1, - AccessedCount: 0, - } - - return rhsValue - } - - // Both types match so were good. - if helpers.TypesMatch(expectedType, rhsValue) { - env.Symbols[s.Identifier] = SymbolInfo{ - Type: rhsValue, - IsConstant: s.Constant, - AssignmentCount: 1, - AccessedCount: 0, - } - - return expectedType - } - - e = ErrType(fmt.Sprintf("Mismatched types inside variable declaration of %s. Expected %s but recieved %s instead.", s.Identifier, expectedType.str(), rhsValue.str())) - } - - panic(e.str()) -} - -func tc_fn_declaration_stmt(s ast.FunctionDeclarationStmt, env *SymbolTable) Type { - var functionName = s.Name - var body = s.Body - var generics = s.Generics - var isGeneric = len(generics) > 0 - - // Make sure inside global/module scope when definining functions - // TODO: Create Module AST node which will represent the file. - // All Modules have the same global env. - // A env which is a module or global are allowed for function declarations - - if !env.IsGlobal && !env.IsModule { - panic(fmt.Sprintf("Cannot define function %s outside module scope.", functionName)) - } - - _, alreadyExists := env.Symbols[functionName] - if alreadyExists { - return ErrType(fmt.Sprintf("Cannot define function %s as a symbol with the same name already exists.", functionName)) - } - - if isGeneric { - genericFn := GenericFnType{ - Body: body, - Generics: generics, - Parameters: s.Parameters, - ReturnType: s.ReturnType, - Closure: env, - } - - env.Symbols[functionName] = SymbolInfo{ - Type: genericFn, - IsConstant: true, - AssignmentCount: 1, - } - - return genericFn - } - - var fnBodyEnv = CreateSymbolTable(env, true, false, false, functionName) - var parameters = []Type{} - var returns Type = VoidType{} - - if s.ReturnType != nil { - returns = typecheck_type(s.ReturnType, env) - } - - // Install Parameters - for _, param := range s.Parameters { - paramName := param.Name - paramType := typecheck_type(param.Type, env) - - fnBodyEnv.Symbols[paramName] = SymbolInfo{ - Type: paramType, - IsConstant: true, - AssignmentCount: 1, - } - - parameters = append(parameters, paramType) - } - - var fnType = FnType{ - ParamTypes: parameters, - ReturnType: returns, - Variadic: false, - } - - env.Symbols[functionName] = SymbolInfo{ - Type: fnType, - AssignmentCount: 1, - IsConstant: true, - } - - // Verify Function Body - for _, stmt := range s.Body { - typecheck_stmt(stmt, fnBodyEnv) - } - - // Validate Return Types - for _, foundReturn := range fnBodyEnv.FoundReturnTypes { - if !typesSame(foundReturn, returns) { - panic(fmt.Sprintf("In function declaration for %s, Found %s as a return type, however expected to find %s", functionName, foundReturn.str(), returns.str())) - } - } - - // If function expects a return but none is provided. - if len(fnBodyEnv.FoundReturnTypes) == 0 && !typesSame(returns, VoidType{}) { - panic(fmt.Sprintf("In function declaration for %s, Expected to find return type of %s but found none.", functionName, returns.str())) - } - - fnBodyEnv.debugTable(false) - return fnType -} - -func tc_return_stmt(s ast.ReturnStmt, env *SymbolTable) Type { - var returnType Type = VoidType{} - nearestFnScope := env.findNearestFunctionEnv() - - if s.ReturnValue != nil { - returnType = typecheck_expr(s.ReturnValue, env) - } - - if nearestFnScope == nil { - return ErrType("Cannot use return statement outside a function") - } - - nearestFnScope.FoundReturnTypes = append(nearestFnScope.FoundReturnTypes, returnType) - return returnType -} - -func tc_module_stmt(s ast.ModuleStmt, env *SymbolTable) Type { - moduleEnv := CreateSymbolTable(env, false, true, false, s.ModuleName) - - for _, stmt := range s.Body { - typecheck_stmt(stmt, moduleEnv) - } - - moduleEnv.debugTable(false) - - return ModuleType{ - ModuleName: s.ModuleName, - } -} - -func tc_struct_declaration_stmt(s ast.StructDeclarationStmt, env *SymbolTable) Type { - var structName = s.Name - var isGeneric = len(s.Generics) > 0 - - // Make sure struct is only defined inside global/module scope - if !env.IsGlobal && !env.IsModule { - panic(fmt.Sprintf("Cannot define struct %s in current scope. Structs can only be defined inside a module.", structName)) - } - - // Make sure struct does not already exist in current module/global scopes - _, foundEnv := env.findNearestTypeEnv(structName) - if foundEnv != nil { - panic(fmt.Sprintf("Cannot define struct as a type with this name (%s) already exists", structName)) - } - - if isGeneric { - genericType := GenericStructType{ - Name: s.Name, - Properties: s.Properties, - InstanceMethods: s.InstanceMethods, - StaticMethods: s.StaticMethods, - Closure: env, - Generics: s.Generics, - } - - env.DefinedTypes[structName] = genericType - return genericType - } - - var structEnv = CreateSymbolTable(env, false, false, true, structName) - var structType = validate_struct_body(structEnv, structName, s.Properties, s.StaticMethods, s.InstanceMethods) - - env.DefinedTypes[structName] = structType - return structType -} - -func validate_struct_body(structEnv *SymbolTable, structName string, Properties []ast.StructProperty, StaticMethods []ast.FunctionDeclarationStmt, InstanceMethods []ast.FunctionDeclarationStmt) StructType { - var staticMethods = map[string]FnType{} - var methods = map[string]FnType{} - var properties = map[string]Type{} - var structType = StructType{ - StructName: structName, - StaticMethods: staticMethods, - Properties: properties, - Methods: methods, - } - - structEnv.DefinedTypes[structName] = structType - structEnv.DefinedTypes["Self"] = structType - - // Generate Property Types - for _, prop := range Properties { - foundType := typecheck_type(prop.Type, structEnv) - - structEnv.Symbols[prop.Name] = SymbolInfo{ - Type: foundType, - IsConstant: false, - AssignmentCount: 0, - AccessedCount: 0, - } - - properties[prop.Name] = foundType - } - - // Generate Static Method Types (NO BODY EVAL) - // This will just generate the type definitions for each of the static methods. - for _, staticNode := range StaticMethods { - var returnType Type = VoidType{} - - if staticNode.ReturnType != nil { - returnType = typecheck_type(staticNode.ReturnType, structEnv) - } - - params := []Type{} - - for _, param := range staticNode.Parameters { - params = append(params, typecheck_type(param.Type, structEnv)) - } - - staticMethods[staticNode.Name] = FnType{ - ReturnType: returnType, - ParamTypes: params, - } - } - - // Generate Instance Method Types (NO BODY EVAL) - // This will just generate the type definitions for each of the instance methods. - for _, instanceNode := range InstanceMethods { - var returnType Type = VoidType{} - - if instanceNode.ReturnType != nil { - returnType = typecheck_type(instanceNode.ReturnType, structEnv) - } - - params := []Type{} - - for _, param := range instanceNode.Parameters { - paramType := typecheck_type(param.Type, structEnv) - params = append(params, paramType) - } - - fnType := FnType{ - ReturnType: returnType, - ParamTypes: params, - } - - methods[instanceNode.Name] = fnType - - structEnv.Symbols[instanceNode.Name] = SymbolInfo{ - Type: fnType, - IsConstant: true, - AssignmentCount: 1, - } - } - - // Generate Instance Method Types (NO BODY EVAL) - // This will just generate the type definitions for each of the instance methods. - for _, instanceNode := range InstanceMethods { - var returnType Type = VoidType{} - - if instanceNode.ReturnType != nil { - returnType = typecheck_type(instanceNode.ReturnType, structEnv) - } - - params := []Type{} - - for _, param := range instanceNode.Parameters { - params = append(params, typecheck_type(param.Type, structEnv)) - } - - fnType := FnType{ - ReturnType: returnType, - ParamTypes: params, - } - - methods[instanceNode.Name] = fnType - - structEnv.Symbols[instanceNode.Name] = SymbolInfo{ - Type: fnType, - IsConstant: true, - AssignmentCount: 1, - } - } - - // Eval Body for static/instance methods - // Now that all of the methods/instace/static/member properties are known we can re run through each - // method/static method and make sure they return the proper types and dont access things they are not supposed to. - - for _, instanceMethod := range InstanceMethods { - methodName := instanceMethod.Name - methodEnv := CreateSymbolTable(structEnv, true, false, false, methodName+"()") - - methodEnv.Symbols["self"] = SymbolInfo{ - IsConstant: true, - AssignmentCount: 1, - Type: structType, - } - - // foreach parameter populate the simulated env - for _, param := range instanceMethod.Parameters { - paramName := param.Name - paramType := typecheck_type(param.Type, structEnv) - - methodEnv.Symbols[paramName] = SymbolInfo{ - Type: paramType, - IsConstant: true, - AssignmentCount: 1, - } - } - - for _, stmt := range instanceMethod.Body { - typecheck_stmt(stmt, methodEnv) - } - - // Validate return matches what is needs to for each instance method - var expectedReturnType Type = VoidType{} - if instanceMethod.ReturnType != nil { - expectedReturnType = typecheck_type(instanceMethod.ReturnType, methodEnv) - } - - for _, recievedReturnType := range methodEnv.FoundReturnTypes { - if !helpers.TypesMatch(expectedReturnType, recievedReturnType) { - err := fmt.Sprintf("Mismatch return types for method %s on struct %s. Expected to return %s but recieved %s instead.", instanceMethod.Name, structName, expectedReturnType.str(), recievedReturnType.str()) - panic(ErrType(err).str()) - } - } - - methodEnv.debugTable(false) - } - - // Eval static methods to verify the integrigity of the body. - for _, staticMethod := range StaticMethods { - methodName := staticMethod.Name - methodEnv := CreateSymbolTable(structEnv, true, false, false, methodName+"()") - - // this will prevent us from accessing non static methods when inside static methods - // Check the tc_member_expr() for examples - methodEnv.IsStaticMethod = true - - // Before evaluating this environment make sure to remove all instance methods so they cannot be called. - // This prevents static methods from calling or knowing about properties or instance methods - for instanceMethodName := range structType.Methods { - delete(methodEnv.Symbols, instanceMethodName) - } - - for instanceProperty := range structType.Properties { - delete(methodEnv.Symbols, instanceProperty) - } - - // foreach parameter populate the simulated env - for _, param := range staticMethod.Parameters { - paramName := param.Name - paramType := typecheck_type(param.Type, methodEnv) - - methodEnv.Symbols[paramName] = SymbolInfo{ - Type: paramType, - IsConstant: true, - AssignmentCount: 1, - } - - } - - for _, stmt := range staticMethod.Body { - typecheck_stmt(stmt, methodEnv) - } - - // Validate return matches what is needs to for static method - var expectedReturnType Type = VoidType{} - if staticMethod.ReturnType != nil { - expectedReturnType = typecheck_type(staticMethod.ReturnType, methodEnv) - } - - for _, recievedReturnType := range methodEnv.FoundReturnTypes { - if !helpers.TypesMatch(expectedReturnType, recievedReturnType) { - err := fmt.Sprintf("Mismatch return types for method %s on struct %s. Expected to return %s but recieved %s instead.", staticMethod.Name, structName, expectedReturnType.str(), recievedReturnType.str()) - panic(ErrType(err)) - } - } - - methodEnv.debugTable(false) - } - - return structType -} - -func tc_while_stmt(s ast.WhileStmt, env *SymbolTable) Type { - var condition = typecheck_expr(s.Condition, env) - var whileEnv = CreateSymbolTable(env, false, false, false, "while-loop") - whileEnv.IsLoop = true - - if !typesSame(condition, BoolType{}) { - panic(fmt.Sprintf("Expected bool in condition for while loop. Recieved %s instead.", condition.str())) - } - - for _, stmt := range s.Body { - typecheck_stmt(stmt, whileEnv) - } - - whileEnv.debugTable(false) - return VoidType{} -} - -func tc_trait_stmt(n ast.TraitStmt, env *SymbolTable) Type { - var methods = map[string]FnType{} - var trait TraitType = TraitType{ - TraitName: n.Name, - Methods: methods, - } - - for _, method := range n.Methods { - methodName := method.MethodName - fnType := helpers.ExpectType[FnType](tc_fn_type(method.MethodType, env)) - methods[methodName] = fnType - } - - env.DefinedTypes[trait.TraitName] = trait - return trait -} diff --git a/src/analysis/tc_types.go b/src/analysis/tc_types.go deleted file mode 100644 index 23e7579..0000000 --- a/src/analysis/tc_types.go +++ /dev/null @@ -1,86 +0,0 @@ -package analysis - -import ( - "fmt" - - "github.com/tlaceby/bedrock/src/ast" - "github.com/tlaceby/bedrock/src/helpers" -) - -func tc_symbol_type(t ast.SymbolType, env *SymbolTable) Type { - _type, _ := env.findNearestTypeEnv(t.Value) - return _type -} - -func tc_list_type(t ast.ListType, env *SymbolTable) Type { - return ArrayType{ - Underlying: typecheck_type(t.Underlying, env), - } -} - -func tc_fn_type(t ast.FnType, env *SymbolTable) Type { - var params = []Type{} - var returns = typecheck_type(t.ReturnType, env) - - for _, param := range t.Parameters { - params = append(params, typecheck_type(param, env)) - } - - return FnType{ - Variadic: false, - ParamTypes: params, - ReturnType: returns, - } -} - -func tc_struct_generic_type(t ast.StructType, env *SymbolTable) Type { - var genericEnv *SymbolTable - var structSignature string // StructName{} - var structName = t.StructName - var genStruct GenericStructType - var expectedGenericArity int - var expectedGenericNames []string - var recievedGenericArity = len(t.GenericList) - var recievedGenerics = []Type{} - var recievedGenericsTypeStrings = []string{} - var T, definedEnv = env.findNearestTypeEnv(structName) - - if !IsAStructType(T) { - panic(fmt.Sprintf("Could not resolve struct with name %s.", structName)) - } - - if IsNormalStructType(T) { - panic(fmt.Sprintf("Struct %s is not a generic struct. No need for type parameters.", structName)) - } else { - genStruct = helpers.ExpectType[GenericStructType](T) - expectedGenericArity = len(genStruct.Generics) - expectedGenericNames = genStruct.Generics - } - - // Verify Arity Matches Expected - if expectedGenericArity != recievedGenericArity { - panic(fmt.Sprintf("Generic %s expected arity of %d but recieved %d instead.", structName, expectedGenericArity, recievedGenericArity)) - } - - // Calculate Generics Recieved - for _, recievedGeneric := range t.GenericList { - genericType := typecheck_type(recievedGeneric, env) - recievedGenerics = append(recievedGenerics, genericType) - recievedGenericsTypeStrings = append(recievedGenericsTypeStrings, genericType.str()) - } - - structSignature = createGenericListStr(structName, recievedGenericsTypeStrings) - genericEnv = CreateSymbolTable(env, false, false, true, structSignature) - - // Install Generic Types - for indx, genericName := range expectedGenericNames { - recieved := recievedGenerics[indx] - genericEnv.DefinedTypes[genericName] = recieved - } - - // Generate Valid Struct Instance Which is NOT Generic - // This can Be installed in the environment which it was defined. - structInstanceType := validate_struct_body(genericEnv, structSignature, genStruct.Properties, genStruct.StaticMethods, genStruct.InstanceMethods) - definedEnv.DefinedTypes[structSignature] = structInstanceType - return structInstanceType -} diff --git a/src/analysis/typechecker.go b/src/analysis/typechecker.go new file mode 100644 index 0000000..e818514 --- /dev/null +++ b/src/analysis/typechecker.go @@ -0,0 +1,53 @@ +package analysis + +import ( + "fmt" + + "github.com/tlaceby/bedrock/src/ast" +) + +func CreateTypechecker(defaultGlobalScope *ast.Scope) Typechecker { + if defaultGlobalScope == nil { + return Typechecker{Global: &ast.Scope{ + IsGlobal: true, + ScopeName: "global", + }} + } + + return Typechecker{ + Global: defaultGlobalScope, + } +} + +type TypeError struct { + Kind string + Message string +} + +type Typechecker struct { + Errors []TypeError + Global *ast.Scope + Modules map[string]*ast.ModuleType +} + +func (checker *Typechecker) Typecheck(node ast.ModuleStmt) ast.Type { + module, exists := checker.Modules[node.ModuleName] + + if exists { + fmt.Printf("module %s already typechecked\n", node.ModuleName) + return module // todo get the already generated module and return it + } + + module = &ast.ModuleType{ + ModuleName: node.ModuleName, + } + + var scope = ast.CreateScope(checker.Global, node.ModuleName).SetIsModule() + checker.Modules[node.ModuleName] = module + + for _, stmt := range node.Body { + checker.stmt(stmt, scope) + } + + return module +} diff --git a/src/analysis/types.go b/src/analysis/types.go index 83c31eb..fddf534 100644 --- a/src/analysis/types.go +++ b/src/analysis/types.go @@ -1,194 +1,19 @@ package analysis -import ( - "fmt" +import "github.com/tlaceby/bedrock/src/ast" - "github.com/fatih/color" - "github.com/tlaceby/bedrock/src/ast" -) - -type TraitType struct { - TraitName string - Methods map[string]FnType -} - -func (t TraitType) str() string { - return fmt.Sprintf("%s {}", color.MagentaString(t.TraitName)) -} - -type ModuleType struct { - ModuleName string - PublicSymbols map[string]SymbolInfo - PublicTypes map[string]Type // TODO: .... -} - -func (t ModuleType) str() string { - return fmt.Sprintf("%s %s", color.RedString("module"), t.ModuleName) -} - -type StructType struct { - StructName string - Properties map[string]Type - Methods map[string]FnType - StaticMethods map[string]FnType -} - -func (t StructType) getPropertyByName(propertyName string) Type { - value, exists := t.Properties[propertyName] - if exists { - return value - } - - return nil -} - -func (t StructType) str() string { - return fmt.Sprintf("%s {}", t.StructName) +func Number() ast.NumType { + return ast.NumType{} } -type GenericStructType struct { - Generics []string - Closure *SymbolTable - Name string - Properties []ast.StructProperty - StaticMethods []ast.FunctionDeclarationStmt - InstanceMethods []ast.FunctionDeclarationStmt +func String() ast.StrType { + return ast.StrType{} } -func (t GenericStructType) str() string { - return createGenericListStr(t.Name, t.Generics) +func Bool() ast.BoolType { + return ast.BoolType{} } -type ErrorType struct { - Message string -} - -func (t ErrorType) str() string { - return fmt.Sprintf("%s (%s)", color.RedString("Error"), t.Message) -} - -func ErrType(message string) ErrorType { - return ErrorType{Message: message} -} - -type NumType struct{} - -func (t NumType) str() string { - return color.BlueString("Number") -} - -type VoidType struct{} - -func (t VoidType) str() string { - return color.RedString("Void") -} - -type StrType struct{} - -func (t StrType) str() string { - return color.BlueString("String") -} - -type BoolType struct{} - -func (t BoolType) str() string { - return color.RedString("Bool") -} - -type ArrayType struct { - Capacity uint - Underlying Type -} - -func (t ArrayType) str() string { - return fmt.Sprintf("[]%s", t.Underlying.str()) -} - -type FnType struct { - Variadic bool - ParamTypes []Type - ReturnType Type -} - -func (t FnType) str() string { - var paramsStr string = "" - - for idx, param := range t.ParamTypes { - paramsStr += param.str() - - if idx < len(t.ParamTypes)-1 { - paramsStr += ", " - } - } - - return fmt.Sprintf("%s (%s) -> %s", color.RedString("fn"), paramsStr, t.ReturnType.str()) -} - -type GenericFnType struct { - Body []ast.Stmt - Parameters []ast.Parameter - Generics []string - ReturnType ast.Type - Closure *SymbolTable -} - -func (t GenericFnType) str() string { - var genericStr string = "" - - genericStr += "<" - for idx, genericType := range t.Generics { - genericStr += genericType - - if idx < len(t.Generics)-1 { - genericStr += ", " - } - } - - genericStr += ">" - - return fmt.Sprintf("%s %s", color.RedString("fn"), genericStr) -} - -func defineGlobalDefaultTypes(table *SymbolTable) { - table.DefinedTypes["String"] = StrType{} - table.DefinedTypes["Number"] = NumType{} - table.DefinedTypes["Void"] = VoidType{} - table.DefinedTypes["Bool"] = BoolType{} - - table.Symbols["true"] = SymbolInfo{ - Type: BoolType{}, - IsConstant: true, - AssignmentCount: 1, - AccessedCount: 1, - } - - table.Symbols["false"] = SymbolInfo{ - Type: BoolType{}, - IsConstant: true, - AssignmentCount: 1, - AccessedCount: 1, - } - - // Create default builtin global methods - table.Symbols["println"] = SymbolInfo{ - IsConstant: true, AssignmentCount: 1, - AccessedCount: 1, Type: FnType{ - Variadic: false, - ReturnType: VoidType{}, - ParamTypes: []Type{ - StrType{}, - }, - }, - } - - table.Symbols["print"] = SymbolInfo{ - IsConstant: true, AssignmentCount: 1, - AccessedCount: 1, Type: FnType{ - Variadic: false, - ReturnType: VoidType{}, - ParamTypes: []Type{ - StrType{}, - }, - }, - } +func Void() ast.VoidType { + return ast.VoidType{} } diff --git a/src/ast/ast.go b/src/ast/ast.go index 35c50e2..8fb6fc8 100644 --- a/src/ast/ast.go +++ b/src/ast/ast.go @@ -10,7 +10,7 @@ type Expr interface { expr() } -type Type interface { +type ASTType interface { _type() } @@ -22,6 +22,6 @@ func ExpectStmt[T Stmt](expr Stmt) T { return helpers.ExpectType[T](expr) } -func ExpectType[T Type](_type Type) T { +func ExpectType[T ASTType](_type ASTType) T { return helpers.ExpectType[T](_type) } diff --git a/src/ast/expressions.go b/src/ast/expressions.go index 3cad3d4..22ba52f 100644 --- a/src/ast/expressions.go +++ b/src/ast/expressions.go @@ -62,7 +62,7 @@ func (n MemberExpr) expr() {} type StaticMemberExpr struct { StructName string MethodName string - StructGenerics []Type + StructGenerics []ASTType } func (n StaticMemberExpr) expr() {} @@ -70,7 +70,7 @@ func (n StaticMemberExpr) expr() {} type CallExpr struct { Method Expr Arguments []Expr - Generics []Type + Generics []ASTType } func (n CallExpr) expr() {} @@ -92,13 +92,13 @@ func (n RangeExpr) expr() {} type FunctionExpr struct { Parameters []Parameter Body []Stmt - ReturnType Type + ReturnType ASTType } func (n FunctionExpr) expr() {} type ArrayLiteral struct { - UnderlyingType Type + UnderlyingType ASTType Capacity int // -1 represents none a inferable size Contents []Expr } @@ -112,7 +112,7 @@ type ObjectField struct { type StructInstantiationExpr struct { StructName string - Generics []Type + Generics []ASTType Objects []ObjectField } @@ -122,7 +122,7 @@ func (n StructInstantiationExpr) expr() {} // @assert(Type, Expr) type AssertExpr struct { - TypeAssertion Type + TypeAssertion ASTType Expr Expr } diff --git a/src/ast/statements.go b/src/ast/statements.go index 5833987..7cb7a04 100644 --- a/src/ast/statements.go +++ b/src/ast/statements.go @@ -1,6 +1,7 @@ package ast type ModuleStmt struct { + Scope *Scope FilePath string ModuleName string Body []Stmt @@ -9,7 +10,8 @@ type ModuleStmt struct { func (s ModuleStmt) stmt() {} type BlockStmt struct { - Body []Stmt + Scope *Scope + Body []Stmt } func (b BlockStmt) stmt() {} @@ -18,7 +20,7 @@ type VarDeclarationStmt struct { Identifier string Constant bool AssignedValue Expr - ExplicitType Type + ExplicitType ASTType } func (n VarDeclarationStmt) stmt() {} @@ -31,15 +33,16 @@ func (n ExpressionStmt) stmt() {} type Parameter struct { Name string - Type Type + Type ASTType } type FunctionDeclarationStmt struct { + Scope *Scope Parameters []Parameter Generics []string Name string Body []Stmt - ReturnType Type + ReturnType ASTType } func (n FunctionDeclarationStmt) stmt() {} @@ -70,6 +73,7 @@ type ForStmt struct { func (n ForStmt) stmt() {} type WhileStmt struct { + Scope *Scope Condition Expr Body []Stmt } @@ -78,7 +82,7 @@ func (n WhileStmt) stmt() {} type StructProperty struct { Name string - Type Type + Type ASTType } type StructMethod struct { @@ -98,7 +102,7 @@ func (n StructDeclarationStmt) stmt() {} type TraitMethod struct { MethodName string - MethodType FnType + MethodType ASTFnType } type TraitStmt struct { @@ -115,7 +119,8 @@ type ReturnStmt struct { func (n ReturnStmt) stmt() {} type MatchCase struct { - Type Type + Scope *Scope + Type ASTType Block []Stmt IsElseCase bool } @@ -128,14 +133,15 @@ type MatchStmt struct { func (n MatchStmt) stmt() {} type UnsafeStmt struct { - Body []Stmt + Scope *Scope + Body []Stmt } func (n UnsafeStmt) stmt() {} type TypedefStmt struct { Typename string - Type Type + Type ASTType } func (n TypedefStmt) stmt() {} diff --git a/src/ast/tc_scope.go b/src/ast/tc_scope.go new file mode 100644 index 0000000..ecdddbd --- /dev/null +++ b/src/ast/tc_scope.go @@ -0,0 +1,62 @@ +package ast + +type Symbol struct { + Type Type // Underlying type of symbol + IsConstant bool // Whether the symbol is defined as a constant or fn/module etc... + ClosedOver bool // Whether this variable will be used inside a closure and needs heap allocation + AssignmentCount int // Number of times this symbol is assigned/reassigned + AccessedCount int // Number of times symbol is accessed/referenced +} + +type Scope struct { + ScopeName string + Parent *Scope + + IsGlobal bool // whether this table exists in the global scope + IsModule bool // Represents the module which this table exists in + IsFunction bool // whether this table is the body of a function + IsStaticMethod bool // whether we are inside a static methods scope. Prevents access to instance methods/properties from inside + IsLoop bool // Whether the current env is a while/for loop + IsUnsafe bool // Inside a unsafe block + DefinedTypes map[string]Type // All Alias/ Defined Types / Traits + Symbols map[string]Symbol // All Variables/Functions/Modlues + + FoundReturnTypes []Type // Inside functions keep track of found return types +} + +func CreateScope(parent *Scope, name string) *Scope { + return &Scope{ + ScopeName: name, + Parent: parent, + } +} + +func (scope *Scope) SetIsGlobal() *Scope { + scope.IsGlobal = true + return scope +} + +func (scope *Scope) SetIsModule() *Scope { + scope.IsModule = true + return scope +} + +func (scope *Scope) SetIsFunction() *Scope { + scope.IsFunction = true + return scope +} + +func (scope *Scope) SetIsStatic() *Scope { + scope.IsStaticMethod = true + return scope +} + +func (scope *Scope) SetIsLoop() *Scope { + scope.IsLoop = true + return scope +} + +func (scope *Scope) SetIsUnsafe() *Scope { + scope.IsUnsafe = true + return scope +} diff --git a/src/ast/tc_type.go b/src/ast/tc_type.go new file mode 100644 index 0000000..a98ec29 --- /dev/null +++ b/src/ast/tc_type.go @@ -0,0 +1,337 @@ +package ast + +import ( + "fmt" + + "github.com/fatih/color" +) + +type Type interface { + str() string +} + +type TraitType struct { + TraitName string + Methods map[string]FnType +} + +func (t TraitType) str() string { + return fmt.Sprintf("%s {}", color.MagentaString(t.TraitName)) +} + +type ModuleType struct { + ModuleName string + PublicSymbols map[string]Symbol + PublicTypes map[string]Type +} + +func (t ModuleType) str() string { + return fmt.Sprintf("%s %s", color.RedString("module"), t.ModuleName) +} + +type StructType struct { + StructName string + Properties map[string]Type + Methods map[string]FnType + StaticMethods map[string]FnType +} + +func (t StructType) getPropertyByName(propertyName string) Type { + value, exists := t.Properties[propertyName] + if exists { + return value + } + + return nil +} + +func (t StructType) str() string { + return fmt.Sprintf("%s {}", t.StructName) +} + +type GenericStructType struct { + Generics []string + Closure *Scope + Name string + Properties []StructProperty + StaticMethods []FunctionDeclarationStmt + InstanceMethods []FunctionDeclarationStmt +} + +func (t GenericStructType) str() string { + return createGenericListStr(t.Name, t.Generics) +} + +type NumType struct{} + +func (t NumType) str() string { + return color.BlueString("Number") +} + +type VoidType struct{} + +func (t VoidType) str() string { + return color.RedString("Void") +} + +type StrType struct{} + +func (t StrType) str() string { + return color.BlueString("String") +} + +type BoolType struct{} + +func (t BoolType) str() string { + return color.RedString("Bool") +} + +type ArrayType struct { + Capacity uint + Underlying Type +} + +func (t ArrayType) str() string { + return fmt.Sprintf("[]%s", t.Underlying.str()) +} + +type FnType struct { + Variadic bool + ParamTypes []Type + ReturnType Type +} + +func (t FnType) str() string { + var paramsStr string = "" + + for idx, param := range t.ParamTypes { + paramsStr += param.str() + + if idx < len(t.ParamTypes)-1 { + paramsStr += ", " + } + } + + return fmt.Sprintf("%s (%s) -> %s", color.RedString("fn"), paramsStr, t.ReturnType.str()) +} + +type GenericFnType struct { + Body []Stmt + Parameters []Parameter + Generics []string + ReturnType Type + Closure *Scope +} + +func (t GenericFnType) str() string { + var genericStr string = "" + + genericStr += "<" + for idx, genericType := range t.Generics { + genericStr += genericType + + if idx < len(t.Generics)-1 { + genericStr += ", " + } + } + + genericStr += ">" + + return fmt.Sprintf("%s %s", color.RedString("fn"), genericStr) +} + +func defineGlobalDefaultTypes(table *Scope) { + table.DefinedTypes["String"] = StrType{} + table.DefinedTypes["Number"] = NumType{} + table.DefinedTypes["Void"] = VoidType{} + table.DefinedTypes["Bool"] = BoolType{} + + table.Symbols["true"] = Symbol{ + Type: BoolType{}, + IsConstant: true, + AssignmentCount: 1, + AccessedCount: 1, + } + + table.Symbols["false"] = Symbol{ + Type: BoolType{}, + IsConstant: true, + AssignmentCount: 1, + AccessedCount: 1, + } + + // Create default builtin global methods + table.Symbols["println"] = Symbol{ + IsConstant: true, AssignmentCount: 1, + AccessedCount: 1, Type: FnType{ + Variadic: false, + ReturnType: VoidType{}, + ParamTypes: []Type{ + StrType{}, + }, + }, + } + + table.Symbols["print"] = Symbol{ + IsConstant: true, AssignmentCount: 1, + AccessedCount: 1, Type: FnType{ + Variadic: false, + ReturnType: VoidType{}, + ParamTypes: []Type{ + StrType{}, + }, + }, + } +} + +func createGenericListStr(structName string, generics []string) string { + str := color.MagentaString(structName) + "<" + + for indx, name := range generics { + str += name + if indx != len(generics)-1 { + str += ", " + } + } + + str += ">" + return str +} + +func typesSame(t Type, expected Type) bool { + if t.str() == expected.str() { + return true + } + + switch other := expected.(type) { + case TraitType: + return implements(t, other) + } + + return false +} + +func CastableToBool(t Type) bool { + switch t.(type) { + case BoolType: + return true + case NumType: + return true + case StrType: + return true + case ArrayType: + return true + default: + return false + } +} + +func CastableToString(t Type) bool { + // For now everything can be cast to a string functions, structs and traits included. + switch t.(type) { + case BoolType: + return true + case NumType: + return true + case StrType: + return true + case ArrayType: + return true + default: + return true + } +} + +func IsAFnType(t Type) bool { + switch t.(type) { + case FnType: + return true + case GenericFnType: + return true + } + + return false +} + +func IsAStructType(t Type) bool { + switch t.(type) { + case StructType: + return true + case GenericStructType: + return true + } + + return false +} + +func IsNormalFnType(t Type) bool { + switch t.(type) { + case FnType: + return true + } + + return false +} + +func IsNormalStructType(t Type) bool { + switch t.(type) { + case StructType: + return true + } + + return false +} + +func IsGenericFnType(t Type) bool { + switch t.(type) { + case GenericFnType: + return true + } + + return false +} + +func IsGenericStructType(t Type) bool { + switch t.(type) { + case GenericStructType: + return true + } + + return false +} + +func implements(t Type, trait TraitType) bool { + switch v := t.(type) { + case StructType: + for methodName, fnType := range trait.Methods { + method, exists := v.Methods[methodName] + if !exists || !isFnTypesEqual(method, fnType) { + return false + } + } + return true + // Add cases for other types that can implement traits + default: + return false + } +} + +func isFnTypesEqual(fn1, fn2 FnType) bool { + if fn1.Variadic != fn2.Variadic || len(fn1.ParamTypes) != len(fn2.ParamTypes) { + return false + } + + for i := range fn1.ParamTypes { + if !isTypesEqual(fn1.ParamTypes[i], fn2.ParamTypes[i]) { + return false + } + } + + return isTypesEqual(fn1.ReturnType, fn2.ReturnType) +} + +func isTypesEqual(t1, t2 Type) bool { + // Implement type equality check based on your type system + // You can compare the string representation of types for simplicity + return t1.str() == t2.str() +} diff --git a/src/ast/types.go b/src/ast/types.go index bc74dd0..a4bce1c 100644 --- a/src/ast/types.go +++ b/src/ast/types.go @@ -1,27 +1,27 @@ package ast -type StructType struct { - GenericList []Type +type ASTStructType struct { + GenericList []ASTType StructName string } -func (t StructType) _type() {} +func (t ASTStructType) _type() {} -type SymbolType struct { +type ASTSymbolType struct { Value string } -func (t SymbolType) _type() {} +func (t ASTSymbolType) _type() {} -type ListType struct { - Underlying Type +type ASTListType struct { + Underlying ASTType } -func (t ListType) _type() {} +func (t ASTListType) _type() {} -type FnType struct { - Parameters []Type - ReturnType Type +type ASTFnType struct { + Parameters []ASTType + ReturnType ASTType } -func (t FnType) _type() {} +func (t ASTFnType) _type() {} diff --git a/src/bedrock.go b/src/bedrock.go index 648500c..226ae35 100644 --- a/src/bedrock.go +++ b/src/bedrock.go @@ -5,6 +5,7 @@ import ( "os" "time" + "github.com/sanity-io/litter" "github.com/tlaceby/bedrock/src/analysis" "github.com/tlaceby/bedrock/src/parser" ) @@ -15,9 +16,10 @@ func main() { ast := parser.Parse(string(sourceBytes), "test.br") duration := time.Since(start) - // litter.Dump(ast) - println() - globalEnv := analysis.CreateSymbolTable(nil, false, false, false, "global") - analysis.Typecheck(ast, globalEnv) + litter.Dump(ast) + + checker := analysis.CreateTypechecker(nil) + checker.Typecheck(ast) + fmt.Printf("\nDuration: %v\n", duration) } diff --git a/src/parser/expr.go b/src/parser/expr.go index e2e63d0..f1f2ec2 100644 --- a/src/parser/expr.go +++ b/src/parser/expr.go @@ -168,7 +168,7 @@ func parse_static_member_expr(p *parser, left ast.Expr, bp binding_power) ast.Ex } func parse_array_literal_expr(p *parser) ast.Expr { - var underlyingType ast.Type + var underlyingType ast.ASTType var capacity = -1 var arrayContents = make([]ast.Expr, 0) p.expect(lexer.OPEN_BRACKET) @@ -282,7 +282,7 @@ func parse_struct_instantiation(p *parser, left ast.Expr, bp binding_power) ast. } func parse_generic_list_instantiation(p *parser, left ast.Expr, bp binding_power) ast.Expr { - var genericLists = []ast.Type{} + var genericLists = []ast.ASTType{} p.expect(lexer.OPEN_GENERIC) for p.hasTokens() && p.currentTokenKind() != lexer.CLOSE_GENERIC { diff --git a/src/parser/stmt.go b/src/parser/stmt.go index 97b5c7d..4721257 100644 --- a/src/parser/stmt.go +++ b/src/parser/stmt.go @@ -42,7 +42,7 @@ func parse_block_stmt(p *parser) ast.Stmt { } func parse_var_decl_stmt(p *parser) ast.Stmt { - var explicitType ast.Type + var explicitType ast.ASTType startToken := p.advance().Kind isConstant := startToken == lexer.CONST symbolName := p.expectError(lexer.IDENTIFIER, @@ -76,7 +76,7 @@ func parse_var_decl_stmt(p *parser) ast.Stmt { } } -func parse_fn_params_and_body(p *parser) ([]ast.Parameter, ast.Type, []ast.Stmt) { +func parse_fn_params_and_body(p *parser) ([]ast.Parameter, ast.ASTType, []ast.Stmt) { functionParams := make([]ast.Parameter, 0) p.expect(lexer.OPEN_PAREN) @@ -96,7 +96,7 @@ func parse_fn_params_and_body(p *parser) ([]ast.Parameter, ast.Type, []ast.Stmt) } p.expect(lexer.CLOSE_PAREN) - var returnType ast.Type + var returnType ast.ASTType if p.currentTokenKind() == lexer.ARROW { p.advance() @@ -275,7 +275,7 @@ func parse_trait_stmt(p *parser) ast.Stmt { methodName := p.expect(lexer.IDENTIFIER).Value p.pos-- // set a new tmp token for the fn keywors p.tokens[p.pos] = lexer.Token{Kind: lexer.FN} - methodType := ast.ExpectType[ast.FnType](parse_type(p, defalt_bp)) + methodType := ast.ExpectType[ast.ASTFnType](parse_type(p, defalt_bp)) methods = append(methods, ast.TraitMethod{ MethodName: methodName, @@ -354,7 +354,7 @@ func parse_unsafe_stmt(p *parser) ast.Stmt { func parse_typedef_stmt(p *parser) ast.Stmt { var typeName string - var typeType ast.Type + var typeType ast.ASTType p.expect(lexer.TYPE) typeName = p.expect(lexer.IDENTIFIER).Value diff --git a/src/parser/types.go b/src/parser/types.go index 84cf9ca..3a46094 100644 --- a/src/parser/types.go +++ b/src/parser/types.go @@ -8,8 +8,8 @@ import ( "github.com/tlaceby/bedrock/src/lexer" ) -type type_nud_handler func(p *parser) ast.Type -type type_led_handler func(p *parser, left ast.Type, bp binding_power) ast.Type +type type_nud_handler func(p *parser) ast.ASTType +type type_led_handler func(p *parser, left ast.ASTType, bp binding_power) ast.ASTType type type_nud_lookup map[lexer.TokenKind]type_nud_handler type type_led_lookup map[lexer.TokenKind]type_led_handler @@ -37,7 +37,7 @@ func createTypeTokenLookups() { type_led(lexer.LESS, primary, parse_generic_type) // StructName } -func parse_type(p *parser, bp binding_power) ast.Type { +func parse_type(p *parser, bp binding_power) ast.ASTType { tokenKind := p.currentTokenKind() nud_fn, exists := type_nud_lu[tokenKind] @@ -61,9 +61,9 @@ func parse_type(p *parser, bp binding_power) ast.Type { return left } -func parse_generic_type(p *parser, left ast.Type, bp binding_power) ast.Type { +func parse_generic_type(p *parser, left ast.ASTType, bp binding_power) ast.ASTType { p.expect(lexer.LESS) - generics := make([]ast.Type, 0) + generics := make([]ast.ASTType, 0) for p.hasTokens() && p.currentTokenKind() != lexer.GREATER { genericType := parse_type(p, defalt_bp) @@ -75,31 +75,31 @@ func parse_generic_type(p *parser, left ast.Type, bp binding_power) ast.Type { } p.expect(lexer.GREATER) - return ast.StructType{ + return ast.ASTStructType{ GenericList: generics, - StructName: helpers.ExpectType[ast.SymbolType](left).Value, + StructName: helpers.ExpectType[ast.ASTSymbolType](left).Value, } } -func parse_symbol_type(p *parser) ast.Type { - return ast.SymbolType{ +func parse_symbol_type(p *parser) ast.ASTType { + return ast.ASTSymbolType{ Value: p.advance().Value, } } -func parse_list_type(p *parser) ast.Type { +func parse_list_type(p *parser) ast.ASTType { p.advance() p.expect(lexer.CLOSE_BRACKET) insideType := parse_type(p, defalt_bp) - return ast.ListType{ + return ast.ASTListType{ Underlying: insideType, } } -func parse_fn_type(p *parser) ast.Type { - var params = []ast.Type{} - var returns ast.Type +func parse_fn_type(p *parser) ast.ASTType { + var params = []ast.ASTType{} + var returns ast.ASTType p.expect(lexer.FN) p.expect(lexer.OPEN_PAREN) @@ -115,7 +115,7 @@ func parse_fn_type(p *parser) ast.Type { p.expect(lexer.ARROW) returns = parse_type(p, defalt_bp) - return ast.FnType{ + return ast.ASTFnType{ Parameters: params, ReturnType: returns, }