elysium/validator.go

153 lines
3.7 KiB
Go
Raw Normal View History

2024-03-16 20:12:00 +01:00
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
}