2024-03-16 20:12:00 +01:00
package main
import (
2024-03-28 15:48:50 +01:00
"fmt"
2024-03-20 19:26:48 +01:00
"strconv"
2024-03-16 20:12:00 +01:00
)
2024-03-20 19:26:48 +01:00
type Validator struct {
2024-03-30 21:57:38 +01:00
Files [ ] * ParsedFile
2024-04-02 19:43:05 +02:00
Wasm64 bool
2024-03-30 21:57:38 +01:00
AllFunctions map [ string ] * ParsedFunction
2024-03-24 15:19:45 +01:00
2024-03-30 21:57:38 +01:00
CurrentBlock * Block
CurrentFunction * ParsedFunction
}
2024-10-27 00:07:14 +02:00
const (
BUILTIN_MEMORY_GROW = "__builtin_memory_grow"
BUILTIN_MEMORY_SIZE = "__builtin_memory_size"
)
var builtinFunctions map [ string ] * ParsedFunction = map [ string ] * ParsedFunction {
BUILTIN_MEMORY_GROW : {
Name : BUILTIN_MEMORY_GROW ,
FullName : "builtin." + BUILTIN_MEMORY_GROW ,
Parameters : [ ] ParsedParameter { { Name : "memory" , Type : Type { Type : Type_Primitive , Value : Primitive_U64 , Position : unknownPosition ( ) } } } ,
ReturnType : & Type { Type : Type_Primitive , Value : Primitive_I64 , Position : unknownPosition ( ) } ,
} ,
BUILTIN_MEMORY_SIZE : {
Name : BUILTIN_MEMORY_SIZE ,
FullName : "builtin." + BUILTIN_MEMORY_SIZE ,
Parameters : [ ] ParsedParameter { } ,
ReturnType : & Type { Type : Type_Primitive , Value : Primitive_U64 , Position : unknownPosition ( ) } ,
} ,
}
2024-03-30 21:57:38 +01:00
func isSameType ( a Type , b Type ) bool {
if a . Type != b . Type {
return false
}
switch a . Type {
case Type_Primitive :
return a . Value . ( PrimitiveType ) == b . Value . ( PrimitiveType )
case Type_Named :
return a . Value . ( NamedType ) . TypeName == b . Value . ( NamedType ) . TypeName
case Type_Array :
return isSameType ( a . Value . ( ArrayType ) . ElementType , b . Value . ( ArrayType ) . ElementType )
case Type_Tuple :
aTuple := a . Value . ( TupleType )
bTuple := b . Value . ( TupleType )
if len ( aTuple . Types ) != len ( bTuple . Types ) {
return false
}
for i := 0 ; i < len ( aTuple . Types ) ; i ++ {
if ! isSameType ( aTuple . Types [ i ] , bTuple . Types [ i ] ) {
return false
}
}
return true
}
panic ( "type not implemented" )
2024-03-16 20:12:00 +01:00
}
2024-03-20 19:26:48 +01:00
func isTypeExpandableTo ( from Type , to Type ) bool {
2024-03-24 15:19:45 +01:00
if from . Type != to . Type {
// cannot convert between primitive, named, array and tuple types
return false
}
if from . Type == Type_Primitive {
2024-03-20 19:26:48 +01:00
return isPrimitiveTypeExpandableTo ( from . Value . ( PrimitiveType ) , to . Value . ( PrimitiveType ) )
}
2024-03-24 15:19:45 +01:00
if from . Type == Type_Tuple {
fromT := from . Value . ( TupleType )
toT := to . Value . ( TupleType )
if len ( fromT . Types ) != len ( toT . Types ) {
return false
}
for i := 0 ; i < len ( fromT . Types ) ; i ++ {
if ! isTypeExpandableTo ( fromT . Types [ i ] , toT . Types [ i ] ) {
return false
}
}
return true
}
2024-03-30 21:57:38 +01:00
if from . Type == Type_Array {
return isSameType ( from . Value . ( ArrayType ) . ElementType , to . Value . ( ArrayType ) . ElementType )
}
2024-03-20 19:26:48 +01:00
panic ( "not implemented" )
2024-03-16 20:12:00 +01:00
}
2024-03-30 21:57:38 +01:00
func expandExpressionToType ( expr * Expression , to Type ) {
// TODO: merge with isTypeExpandableTo?
if isSameType ( * expr . ValueType , to ) {
return
}
if expr . Type == Expression_Tuple {
tupleExpr := expr . Value . ( TupleExpression )
tupleType := to . Value . ( TupleType )
for i := 0 ; i < len ( tupleType . Types ) ; i ++ {
expandExpressionToType ( & tupleExpr . Members [ i ] , tupleType . Types [ i ] )
}
expr . Value = tupleExpr
return
}
* expr = Expression { Type : Expression_Cast , Value : CastExpression { Type : to , Value : * expr } , ValueType : & to , Position : expr . Position }
}
2024-03-20 19:26:48 +01:00
func isPrimitiveTypeExpandableTo ( from PrimitiveType , to PrimitiveType ) bool {
2024-03-17 19:55:28 +01:00
if from == to {
return true
}
2024-10-27 16:21:46 +01:00
if isSignedInt ( from ) && ! isSignedInt ( to ) {
return false
}
2024-03-17 19:55:28 +01:00
switch from {
2024-03-19 12:19:19 +01:00
case Primitive_I8 , Primitive_U8 :
2024-03-17 19:55:28 +01:00
if to == Primitive_I16 || to == Primitive_U16 {
return true
}
fallthrough
2024-03-19 12:19:19 +01:00
case Primitive_I16 , Primitive_U16 :
2024-03-17 19:55:28 +01:00
if to == Primitive_I32 || to == Primitive_U32 {
return true
}
fallthrough
2024-03-19 12:19:19 +01:00
case Primitive_I32 , Primitive_U32 :
2024-03-17 19:55:28 +01:00
if to == Primitive_I64 || to == Primitive_U64 {
return true
}
case Primitive_F32 :
if to == Primitive_F64 {
return true
}
}
return false
}
2024-03-29 15:12:37 +01:00
func ( v * Validator ) createError ( message string , position TokenPosition ) error {
2024-03-21 19:55:05 +01:00
return CompilerError { Position : position , Message : message }
2024-03-20 19:26:48 +01:00
}
func ( v * Validator ) validateImport ( imp * Import ) [ ] error {
2024-03-28 15:48:50 +01:00
// TODO imports
2024-03-20 19:26:48 +01:00
return nil
}
2024-03-24 14:01:23 +01:00
func ( v * Validator ) getArithmeticResultType ( expr * Expression , left PrimitiveType , right PrimitiveType , operation Operation ) ( PrimitiveType , error ) {
2024-03-17 19:55:28 +01:00
if left == Primitive_Bool || right == Primitive_Bool {
2024-03-21 19:55:05 +01:00
return InvalidValue , v . createError ( "bool type cannot be used in arithmetic expressions" , expr . Position )
2024-03-17 19:55:28 +01:00
}
2024-03-20 19:26:48 +01:00
if isPrimitiveTypeExpandableTo ( left , right ) {
2024-03-17 19:55:28 +01:00
return right , nil
}
2024-03-20 19:26:48 +01:00
if isPrimitiveTypeExpandableTo ( right , left ) {
2024-03-17 19:55:28 +01:00
return left , nil
}
2024-04-09 13:57:17 +02:00
return InvalidValue , v . createError ( fmt . Sprintf ( "cannot use the types [%s, %s] in an arithmetic expression without an explicit cast" , left , right ) , expr . Position )
2024-03-17 19:55:28 +01:00
}
2024-03-24 20:53:34 +01:00
func getLocal ( block * Block , variable string ) * Local {
if local , ok := block . Locals [ variable ] ; ok {
return & local
}
if block . Parent == nil {
return nil
}
return getLocal ( block . Parent , variable )
}
2024-03-24 15:19:45 +01:00
func ( v * Validator ) validatePotentiallyVoidExpression ( expr * Expression ) [ ] error {
2024-03-16 20:12:00 +01:00
var errors [ ] error
switch expr . Type {
case Expression_Assignment :
assignment := expr . Value . ( AssignmentExpression )
2024-04-02 19:43:05 +02:00
errors = append ( errors , v . validateExpression ( & assignment . Lhs ) ... )
2024-03-16 20:12:00 +01:00
2024-03-24 15:19:45 +01:00
valueErrors := v . validateExpression ( & assignment . Value )
2024-03-16 20:12:00 +01:00
if len ( valueErrors ) != 0 {
errors = append ( errors , valueErrors ... )
return errors
}
2024-04-20 13:56:41 +02:00
if assignment . Operation != Operation_Equals && ( assignment . Lhs . ValueType . Type != Type_Primitive || assignment . Value . ValueType . Type != Type_Primitive ) {
errors = append ( errors , v . createError ( "both sides of an arithmetic expression must evaluate to a primitive type" , expr . Position ) )
return errors
}
2024-04-02 19:43:05 +02:00
if ! isSameType ( * assignment . Value . ValueType , * assignment . Lhs . ValueType ) {
if ! isTypeExpandableTo ( * assignment . Value . ValueType , * assignment . Lhs . ValueType ) {
errors = append ( errors , v . createError ( fmt . Sprintf ( "cannot assign expression of type %s to variable of type %s" , * assignment . Value . ValueType , * assignment . Lhs . ValueType ) , expr . Position ) )
2024-03-30 21:57:38 +01:00
}
2024-04-02 19:43:05 +02:00
expandExpressionToType ( & assignment . Value , * assignment . Lhs . ValueType )
2024-03-24 21:36:34 +01:00
}
2024-04-02 19:43:05 +02:00
expr . ValueType = assignment . Lhs . ValueType
2024-03-19 10:54:21 +01:00
expr . Value = assignment
2024-03-16 20:12:00 +01:00
case Expression_Literal :
literal := expr . Value . ( LiteralExpression )
switch literal . Literal . Type {
2024-03-19 12:48:06 +01:00
case Literal_Boolean , Literal_Number :
2024-03-21 19:55:05 +01:00
expr . ValueType = & Type { Type : Type_Primitive , Value : literal . Literal . Primitive }
2024-03-16 20:12:00 +01:00
case Literal_String :
2024-03-21 19:55:05 +01:00
expr . ValueType = & STRING_TYPE
2024-03-16 20:12:00 +01:00
}
case Expression_VariableReference :
reference := expr . Value . ( VariableReferenceExpression )
2024-03-30 21:57:38 +01:00
local := getLocal ( v . CurrentBlock , reference . Variable )
2024-03-24 20:53:34 +01:00
if local == nil {
2024-03-21 19:55:05 +01:00
errors = append ( errors , v . createError ( "Reference to undeclared variable " + reference . Variable , expr . Position ) )
2024-03-16 20:12:00 +01:00
return errors
}
2024-03-21 19:55:05 +01:00
expr . ValueType = & local . Type
2024-03-23 14:03:20 +01:00
case Expression_Binary :
2024-03-24 14:01:23 +01:00
binary := expr . Value . ( BinaryExpression )
2024-03-16 20:12:00 +01:00
2024-03-24 15:19:45 +01:00
errors = append ( errors , v . validateExpression ( & binary . Left ) ... )
errors = append ( errors , v . validateExpression ( & binary . Right ) ... )
2024-03-16 20:12:00 +01:00
if len ( errors ) != 0 {
return errors
}
2024-03-24 14:01:23 +01:00
if isBooleanOperation ( binary . Operation ) {
if binary . Left . ValueType . Type != Type_Primitive || binary . Right . ValueType . Type != Type_Primitive {
errors = append ( errors , v . createError ( "cannot compare non-primitive types" , expr . Position ) )
return errors
}
2024-03-30 21:57:38 +01:00
var operandType Type
if isTypeExpandableTo ( * binary . Left . ValueType , * binary . Right . ValueType ) {
operandType = * binary . Right . ValueType
} else if isTypeExpandableTo ( * binary . Right . ValueType , * binary . Left . ValueType ) {
operandType = * binary . Left . ValueType
} else {
errors = append ( errors , v . createError ( fmt . Sprintf ( "cannot compare the types %s and %s without an explicit cast" , binary . Left . ValueType . Value . ( PrimitiveType ) , binary . Right . ValueType . Value . ( PrimitiveType ) ) , expr . Position ) )
2024-03-24 14:01:23 +01:00
return errors
}
2024-03-30 21:57:38 +01:00
expandExpressionToType ( & binary . Left , operandType )
expandExpressionToType ( & binary . Right , operandType )
2024-03-24 14:01:23 +01:00
expr . ValueType = & Type { Type : Type_Primitive , Value : Primitive_Bool }
2024-03-17 19:55:28 +01:00
}
2024-03-24 14:01:23 +01:00
if isArithmeticOperation ( binary . Operation ) {
if binary . Left . ValueType . Type != Type_Primitive || binary . Right . ValueType . Type != Type_Primitive {
2024-03-28 15:48:50 +01:00
errors = append ( errors , v . createError ( "both sides of an arithmetic expression must evaluate to a primitive type" , expr . Position ) )
2024-03-24 14:01:23 +01:00
return errors
}
leftType := binary . Left . ValueType . Value . ( PrimitiveType )
rightType := binary . Right . ValueType . Value . ( PrimitiveType )
result , err := v . getArithmeticResultType ( expr , leftType , rightType , binary . Operation )
if err != nil {
errors = append ( errors , err )
return errors
}
expr . ValueType = & Type { Type : Type_Primitive , Value : result }
2024-03-17 19:55:28 +01:00
}
2024-03-24 14:01:23 +01:00
expr . Value = binary
2024-03-16 20:12:00 +01:00
case Expression_Tuple :
tuple := expr . Value . ( TupleExpression )
var types [ ] Type
2024-03-17 19:55:28 +01:00
for i := range tuple . Members {
member := & tuple . Members [ i ]
2024-03-24 15:19:45 +01:00
memberErrors := v . validateExpression ( member )
2024-03-16 20:12:00 +01:00
if len ( memberErrors ) != 0 {
errors = append ( errors , memberErrors ... )
continue
}
2024-03-21 19:55:05 +01:00
types = append ( types , * member . ValueType )
2024-03-16 20:12:00 +01:00
}
if len ( errors ) != 0 {
return errors
}
2024-03-21 19:55:05 +01:00
expr . ValueType = & Type { Type : Type_Tuple , Value : TupleType { Types : types } }
2024-03-19 10:54:21 +01:00
expr . Value = tuple
2024-03-20 19:26:48 +01:00
case Expression_FunctionCall :
fc := expr . Value . ( FunctionCallExpression )
2024-10-27 00:07:14 +02:00
calledFunc , ok := builtinFunctions [ fc . Function ]
2024-03-29 15:12:37 +01:00
if ! ok {
2024-10-27 00:07:14 +02:00
calledFunc , ok = v . AllFunctions [ fc . Function ]
if ! ok {
errors = append ( errors , v . createError ( "call to undefined function '" + fc . Function + "'" , expr . Position ) )
return errors
}
2024-03-20 19:26:48 +01:00
}
if fc . Parameters != nil {
2024-03-24 15:19:45 +01:00
paramsErrors := v . validateExpression ( fc . Parameters )
2024-03-21 20:37:21 +01:00
if len ( paramsErrors ) != 0 {
2024-03-21 19:55:05 +01:00
errors = append ( errors , paramsErrors ... )
return errors
}
2024-10-27 00:07:14 +02:00
}
2024-03-20 19:26:48 +01:00
2024-10-27 00:07:14 +02:00
var params [ ] * Expression
if fc . Parameters == nil {
params = [ ] * Expression { }
} else if fc . Parameters . Type == Expression_Tuple {
params = make ( [ ] * Expression , len ( fc . Parameters . Value . ( TupleExpression ) . Members ) )
for i := 0 ; i < len ( fc . Parameters . Value . ( TupleExpression ) . Members ) ; i ++ {
params [ i ] = & fc . Parameters . Value . ( TupleExpression ) . Members [ i ]
2024-03-20 19:26:48 +01:00
}
2024-10-27 00:07:14 +02:00
} else {
params = [ ] * Expression { fc . Parameters }
}
2024-03-20 19:26:48 +01:00
2024-10-27 00:07:14 +02:00
if len ( params ) != len ( calledFunc . Parameters ) {
errors = append ( errors , v . createError ( "wrong number of arguments in function call: expected " + strconv . Itoa ( len ( calledFunc . Parameters ) ) + ", got " + strconv . Itoa ( len ( params ) ) , expr . Position ) )
}
2024-03-30 21:57:38 +01:00
2024-10-27 00:07:14 +02:00
for i := 0 ; i < min ( len ( params ) , len ( calledFunc . Parameters ) ) ; i ++ {
typeGiven := params [ i ]
typeExpected := calledFunc . Parameters [ i ]
if ! isTypeExpandableTo ( * typeGiven . ValueType , typeExpected . Type ) {
errors = append ( errors , v . createError ( "invalid type for parameter " + strconv . Itoa ( i ) + ": expected " + typeExpected . Type . String ( ) + ", got " + typeGiven . ValueType . String ( ) , expr . Position ) )
2024-03-20 19:26:48 +01:00
}
2024-10-27 00:07:14 +02:00
expandExpressionToType ( typeGiven , typeExpected . Type )
2024-03-20 19:26:48 +01:00
}
expr . ValueType = calledFunc . ReturnType
expr . Value = fc
2024-04-09 13:57:17 +02:00
case Expression_Unary :
unary := expr . Value . ( UnaryExpression )
2024-03-20 19:26:48 +01:00
2024-04-09 13:57:17 +02:00
valErrors := v . validateExpression ( & unary . Value )
2024-03-21 20:37:21 +01:00
if len ( valErrors ) != 0 {
2024-03-21 19:55:05 +01:00
errors = append ( errors , valErrors ... )
return errors
}
2024-03-20 19:26:48 +01:00
2024-04-09 13:57:17 +02:00
if unary . Value . ValueType . Type != Type_Primitive {
errors = append ( errors , v . createError ( "cannot operate on non-primitive types" , expr . Position ) )
} else {
primitive := unary . Value . ValueType . Value . ( PrimitiveType )
if ( unary . Operation == UnaryOperation_Negate || unary . Operation == UnaryOperation_Nop /* + sign */ ) && ! isSignedInt ( primitive ) && ! isFloatingPoint ( primitive ) {
errors = append ( errors , v . createError ( "can only perform negation/unary plus on signed types" , expr . Position ) )
}
if unary . Operation == UnaryOperation_LogicalNot && primitive != Primitive_Bool {
errors = append ( errors , v . createError ( "cannot perform logical not on non-bool type" , expr . Position ) )
}
if unary . Operation == UnaryOperation_BitwiseNot && ! isUnsignedInt ( primitive ) && ! isSignedInt ( primitive ) {
errors = append ( errors , v . createError ( "cannot perform bitwise not on non-integer type" , expr . Position ) )
}
2024-03-20 19:26:48 +01:00
}
2024-04-09 13:57:17 +02:00
expr . ValueType = unary . Value . ValueType
expr . Value = unary
2024-04-02 19:43:05 +02:00
case Expression_RawMemoryReference :
raw := expr . Value . ( RawMemoryReferenceExpression )
addrErrors := v . validateExpression ( & raw . Address )
if len ( addrErrors ) != 0 {
errors = append ( errors , addrErrors ... )
return errors
}
2024-10-27 16:21:46 +01:00
typeU64 := Type { Type : Type_Primitive , Value : Primitive_U64 , Position : unknownPosition ( ) }
if ! isTypeExpandableTo ( * raw . Address . ValueType , typeU64 ) {
errors = append ( errors , v . createError ( "address must be expandable to a u64 value" , expr . Position ) )
2024-04-02 19:43:05 +02:00
return errors
}
2024-10-27 16:21:46 +01:00
expandExpressionToType ( & raw . Address , typeU64 )
2024-04-02 19:43:05 +02:00
if ! v . Wasm64 {
castTo := Type { Type : Type_Primitive , Value : Primitive_U32 }
raw . Address = Expression { Type : Expression_Cast , Value : CastExpression { Type : castTo , Value : raw . Address } , ValueType : & castTo , Position : raw . Address . Position }
}
expr . ValueType = & raw . Type
expr . Value = raw
2024-10-27 16:21:46 +01:00
case Expression_ArrayAccess :
array := expr . Value . ( ArrayAccessExpression )
arrayErrors := v . validateExpression ( & array . Array )
if len ( arrayErrors ) != 0 {
errors = append ( errors , arrayErrors ... )
}
indexErrors := v . validateExpression ( & array . Index )
if len ( indexErrors ) != 0 {
errors = append ( errors , indexErrors ... )
return errors
}
if len ( errors ) != 0 {
return errors
}
if array . Array . ValueType . Type != Type_Array {
errors = append ( errors , v . createError ( "trying to access non-array type as an array" , array . Array . Position ) )
}
typeI64 := Type { Type : Type_Primitive , Value : Primitive_I64 , Position : unknownPosition ( ) }
if ! isTypeExpandableTo ( * array . Index . ValueType , typeI64 ) {
errors = append ( errors , v . createError ( "array index must be expandable to an i64 value" , array . Index . Position ) )
return errors
}
expandExpressionToType ( & array . Index , typeI64 )
if len ( errors ) != 0 {
return errors
}
elementType := array . Array . ValueType . Value . ( ArrayType ) . ElementType
expr . ValueType = & elementType
expr . Value = array
2024-03-24 14:01:23 +01:00
default :
panic ( "expr not implemented" )
2024-03-16 20:12:00 +01:00
}
return errors
}
2024-03-24 15:19:45 +01:00
func ( v * Validator ) validateExpression ( expr * Expression ) [ ] error {
errors := v . validatePotentiallyVoidExpression ( expr )
2024-03-21 20:37:21 +01:00
if len ( errors ) != 0 {
return errors
}
2024-03-21 19:55:05 +01:00
if expr . ValueType == nil {
errors = append ( errors , v . createError ( "expression must not evaluate to void" , expr . Position ) )
}
return errors
}
2024-03-24 15:19:45 +01:00
func ( v * Validator ) validateStatement ( stmt * Statement , functionLocals * [ ] Local ) [ ] error {
2024-03-16 20:12:00 +01:00
var errors [ ] error
switch stmt . Type {
case Statement_Expression :
expression := stmt . Value . ( ExpressionStatement )
2024-03-24 15:19:45 +01:00
errors = append ( errors , v . validatePotentiallyVoidExpression ( & expression . Expression ) ... )
2024-03-21 19:55:05 +01:00
stmt . Value = expression
2024-03-16 20:12:00 +01:00
case Statement_Block :
block := stmt . Value . ( BlockStatement )
2024-03-24 20:53:34 +01:00
errors = append ( errors , v . validateBlock ( block . Block , functionLocals ) ... )
2024-04-20 14:50:20 +02:00
stmt . Returns = block . Block . Returns
2024-03-21 19:55:05 +01:00
stmt . Value = block
2024-03-16 20:12:00 +01:00
case Statement_Return :
ret := stmt . Value . ( ReturnStatement )
2024-04-20 14:50:20 +02:00
stmt . Returns = true
2024-03-16 20:12:00 +01:00
if ret . Value != nil {
2024-03-30 21:57:38 +01:00
if v . CurrentFunction . ReturnType == nil {
2024-03-24 15:19:45 +01:00
errors = append ( errors , v . createError ( "cannot return value from void function" , stmt . Position ) )
return errors
}
errors = append ( errors , v . validateExpression ( ret . Value ) ... )
if len ( errors ) != 0 {
return errors
}
2024-03-30 21:57:38 +01:00
if ! isTypeExpandableTo ( * ret . Value . ValueType , * v . CurrentFunction . ReturnType ) {
errors = append ( errors , v . createError ( fmt . Sprintf ( "cannot return value of type %s from function returning %s" , * ret . Value . ValueType , * v . CurrentFunction . ReturnType ) , ret . Value . Position ) )
2024-03-24 15:19:45 +01:00
}
2024-03-30 21:57:38 +01:00
expandExpressionToType ( ret . Value , * v . CurrentFunction . ReturnType )
} else if v . CurrentFunction . ReturnType != nil {
2024-03-24 15:19:45 +01:00
errors = append ( errors , v . createError ( "missing return value" , stmt . Position ) )
2024-03-16 20:12:00 +01:00
}
2024-03-24 20:53:34 +01:00
stmt . Value = ret
2024-03-16 20:12:00 +01:00
case Statement_DeclareLocalVariable :
dlv := stmt . Value . ( DeclareLocalVariableStatement )
if dlv . Initializer != nil {
2024-03-24 15:19:45 +01:00
errors = append ( errors , v . validateExpression ( dlv . Initializer ) ... )
2024-03-24 21:36:34 +01:00
if errors != nil {
return errors
}
if ! isTypeExpandableTo ( * dlv . Initializer . ValueType , dlv . VariableType ) {
2024-03-28 15:48:50 +01:00
errors = append ( errors , v . createError ( fmt . Sprintf ( "cannot assign expression of type %s to variable of type %s" , * dlv . Initializer . ValueType , dlv . VariableType ) , stmt . Position ) )
2024-03-24 21:36:34 +01:00
}
2024-03-30 21:57:38 +01:00
expandExpressionToType ( dlv . Initializer , dlv . VariableType )
2024-03-16 20:12:00 +01:00
}
2024-03-30 21:57:38 +01:00
if getLocal ( v . CurrentBlock , dlv . Variable ) != nil {
2024-03-21 19:55:05 +01:00
errors = append ( errors , v . createError ( "redeclaration of variable '" + dlv . Variable + "'" , stmt . Position ) )
2024-03-17 19:55:28 +01:00
}
2024-03-18 21:14:28 +01:00
local := Local { Name : dlv . Variable , Type : dlv . VariableType , IsParameter : false , Index : len ( * functionLocals ) }
2024-03-30 21:57:38 +01:00
v . CurrentBlock . Locals [ dlv . Variable ] = local
2024-03-18 21:14:28 +01:00
* functionLocals = append ( * functionLocals , local )
2024-03-19 12:48:06 +01:00
2024-03-24 14:01:23 +01:00
stmt . Value = dlv
case Statement_If :
ifS := stmt . Value . ( IfStatement )
2024-03-24 15:19:45 +01:00
errors = append ( errors , v . validateExpression ( & ifS . Condition ) ... )
2024-03-24 20:53:34 +01:00
errors = append ( errors , v . validateBlock ( ifS . ConditionalBlock , functionLocals ) ... )
2024-03-24 14:01:23 +01:00
if ifS . ElseBlock != nil {
errors = append ( errors , v . validateBlock ( ifS . ElseBlock , functionLocals ) ... )
}
2024-04-20 14:50:20 +02:00
stmt . Returns = ifS . ConditionalBlock . Returns && ifS . ElseBlock != nil && ifS . ElseBlock . Returns
2024-03-24 14:01:23 +01:00
if len ( errors ) != 0 {
return errors
}
if ifS . Condition . ValueType . Type != Type_Primitive || ifS . Condition . ValueType . Value . ( PrimitiveType ) != Primitive_Bool {
errors = append ( errors , v . createError ( "condition must evaluate to boolean" , ifS . Condition . Position ) )
}
stmt . Value = ifS
2024-04-04 20:05:57 +02:00
case Statement_WhileLoop :
while := stmt . Value . ( WhileLoopStatement )
errors = append ( errors , v . validateExpression ( & while . Condition ) ... )
errors = append ( errors , v . validateBlock ( while . Body , functionLocals ) ... )
if len ( errors ) != 0 {
return errors
}
if while . Condition . ValueType . Type != Type_Primitive || while . Condition . ValueType . Value . ( PrimitiveType ) != Primitive_Bool {
errors = append ( errors , v . createError ( "condition must evaluate to boolean" , while . Condition . Position ) )
}
stmt . Value = while
2024-03-24 14:01:23 +01:00
default :
panic ( "stmt not implemented" )
2024-03-16 20:12:00 +01:00
}
return errors
}
2024-03-20 19:26:48 +01:00
func ( v * Validator ) validateBlock ( block * Block , functionLocals * [ ] Local ) [ ] error {
2024-03-16 20:12:00 +01:00
var errors [ ] error
2024-03-17 19:55:28 +01:00
if block . Locals == nil {
block . Locals = make ( map [ string ] Local )
}
2024-03-16 20:12:00 +01:00
2024-03-17 19:55:28 +01:00
for i := range block . Statements {
2024-03-30 21:57:38 +01:00
v . CurrentBlock = block
2024-03-17 19:55:28 +01:00
stmt := & block . Statements [ i ]
2024-03-24 15:19:45 +01:00
errors = append ( errors , v . validateStatement ( stmt , functionLocals ) ... )
2024-04-20 14:50:20 +02:00
if stmt . Returns {
block . Returns = true
}
2024-03-16 20:12:00 +01:00
}
return errors
}
2024-03-20 19:26:48 +01:00
func ( v * Validator ) validateFunction ( function * ParsedFunction ) [ ] error {
2024-03-16 20:12:00 +01:00
var errors [ ] error
2024-03-18 21:14:28 +01:00
var locals [ ] Local
2024-03-30 21:57:38 +01:00
v . CurrentFunction = function
2024-03-24 15:19:45 +01:00
2024-03-24 20:53:34 +01:00
body := function . Body
2024-03-17 19:55:28 +01:00
body . Locals = make ( map [ string ] Local )
for _ , param := range function . Parameters {
2024-03-18 21:14:28 +01:00
local := Local { Name : param . Name , Type : param . Type , IsParameter : true , Index : len ( locals ) }
locals = append ( locals , local )
body . Locals [ param . Name ] = local
2024-03-17 19:55:28 +01:00
}
2024-03-20 19:26:48 +01:00
errors = append ( errors , v . validateBlock ( body , & locals ) ... )
2024-03-17 19:55:28 +01:00
2024-04-20 14:50:20 +02:00
if function . ReturnType != nil && ! body . Returns {
errors = append ( errors , v . createError ( "function must return a value" , function . ReturnType . Position ) )
}
2024-03-24 21:36:34 +01:00
2024-03-18 21:14:28 +01:00
function . Locals = locals
2024-03-16 20:12:00 +01:00
return errors
}
2024-03-20 19:26:48 +01:00
func ( v * Validator ) validate ( ) [ ] error {
2024-03-16 20:12:00 +01:00
var errors [ ] error
2024-03-30 21:57:38 +01:00
v . AllFunctions = make ( map [ string ] * ParsedFunction )
for _ , file := range v . Files {
2024-03-29 15:12:37 +01:00
for i := range file . Functions {
function := & file . Functions [ i ]
fullFunctionName := function . Name
if file . Module != "" {
fullFunctionName = file . Module + "." + fullFunctionName
}
function . FullName = fullFunctionName
2024-03-30 21:57:38 +01:00
if _ , exists := v . AllFunctions [ fullFunctionName ] ; exists {
2024-03-29 15:12:37 +01:00
errors = append ( errors , v . createError ( "duplicate function " + fullFunctionName , function . ReturnType . Position ) )
}
2024-03-30 21:57:38 +01:00
v . AllFunctions [ fullFunctionName ] = function
2024-03-29 15:12:37 +01:00
}
2024-03-16 20:12:00 +01:00
}
2024-03-30 21:57:38 +01:00
for _ , file := range v . Files {
2024-03-29 15:12:37 +01:00
for i := range file . Imports {
errors = append ( errors , v . validateImport ( & file . Imports [ i ] ) ... )
}
for i := range file . Functions {
errors = append ( errors , v . validateFunction ( & file . Functions [ i ] ) ... )
}
2024-03-16 20:12:00 +01:00
}
return errors
}