package main import ( "errors" ) func createError(message string) error { // TODO: pass token and get actual token position return errors.New(message) } func validateImport(imp *Import) []error { // TODO return nil } func validateExpression(expr *Expression, block *Block) []error { var errors []error switch expr.Type { case Expression_Assignment: assignment := expr.Value.(AssignmentExpression) var local Local var ok bool if local, ok = block.Locals[assignment.Variable]; !ok { errors = append(errors, createError("Assignment to undeclared variable "+assignment.Variable)) return errors } valueErrors := validateExpression(&assignment.Value, block) if len(valueErrors) != 0 { errors = append(errors, valueErrors...) return errors } // TODO: check if assignment is valid expr.ValueType = local.Type case Expression_Literal: literal := expr.Value.(LiteralExpression) switch literal.Literal.Type { case Literal_Boolean: case Literal_Number: expr.ValueType = Type{Type: Type_Primitive, Value: literal.Literal.Primitive} case Literal_String: expr.ValueType = STRING_TYPE } case Expression_VariableReference: reference := expr.Value.(VariableReferenceExpression) var local Local var ok bool if local, ok = block.Locals[reference.Variable]; !ok { errors = append(errors, createError("Reference to undeclared variable "+reference.Variable)) return errors } expr.ValueType = local.Type case Expression_Arithmetic: arithmethic := expr.Value.(ArithmeticExpression) errors = append(errors, validateExpression(&arithmethic.Left, block)...) errors = append(errors, validateExpression(&arithmethic.Right, block)...) if len(errors) != 0 { return errors } // TODO: validate types compatible and determine result type case Expression_Tuple: tuple := expr.Value.(TupleExpression) var types []Type for _, member := range tuple.Members { memberErrors := validateExpression(&member, block) if len(memberErrors) != 0 { errors = append(errors, memberErrors...) continue } types = append(types, member.ValueType) } if len(errors) != 0 { return errors } expr.ValueType = Type{Type: Type_Tuple, Value: TupleType{Types: types}} } return errors } func validateStatement(stmt *Statement, block *Block) []error { var errors []error switch stmt.Type { case Statement_Expression: expression := stmt.Value.(ExpressionStatement) errors = append(errors, validateExpression(&expression.Expression, block)...) case Statement_Block: block := stmt.Value.(BlockStatement) errors = append(errors, validateBlock(&block.Block)...) case Statement_Return: ret := stmt.Value.(ReturnStatement) if ret.Value != nil { errors = append(errors, validateExpression(ret.Value, block)...) } case Statement_DeclareLocalVariable: dlv := stmt.Value.(DeclareLocalVariableStatement) if dlv.Initializer != nil { errors = append(errors, validateExpression(dlv.Initializer, block)...) } block.Locals[dlv.Variable] = Local{Name: dlv.Variable, Type: dlv.VariableType} } return errors } func validateBlock(block *Block) []error { var errors []error block.Locals = make(map[string]Local) for _, stmt := range block.Statements { errors = append(errors, validateStatement(&stmt, block)...) } return errors } func validateFunction(function *ParsedFunction) []error { var errors []error errors = append(errors, validateBlock(&function.Body)...) return errors } func validator(file *ParsedFile) []error { var errors []error for _, imp := range file.Imports { errors = append(errors, validateImport(&imp)...) } for _, function := range file.Functions { errors = append(errors, validateFunction(&function)...) } return errors }