Basic validation

This commit is contained in:
MrLetsplay 2024-03-16 20:12:00 +01:00
parent cf0307c164
commit a8d5c7d479
Signed by: mr
SSH Key Fingerprint: SHA256:92jBH80vpXyaZHjaIl47pjRq+Yt7XGTArqQg1V7hSqg
3 changed files with 185 additions and 6 deletions

11
main.go
View File

@ -38,4 +38,15 @@ func main() {
} }
log.Printf("Parsed:\n%+#v\n\n", parsed) log.Printf("Parsed:\n%+#v\n\n", parsed)
errors := validator(parsed)
if len(errors) != 0 {
for _, err = range errors {
if c, ok := err.(CompilerError); ok {
log.Fatalln(err, "\nv- here\n"+string([]rune(string(content))[c.Position:]))
}
log.Fatalln(err)
}
}
} }

View File

@ -20,6 +20,10 @@ type Type struct {
Value any Value any
} }
const STRING_TYPE_NAME = "string"
var STRING_TYPE = Type{Type: Type_Named, Value: STRING_TYPE_NAME}
type NamedType struct { type NamedType struct {
TypeName string TypeName string
} }
@ -46,6 +50,10 @@ type Statement struct {
Value any Value any
} }
type ExpressionStatement struct {
Expression Expression
}
type BlockStatement struct { type BlockStatement struct {
Block Block Block Block
} }
@ -56,6 +64,7 @@ type ReturnStatement struct {
type DeclareLocalVariableStatement struct { type DeclareLocalVariableStatement struct {
Variable string Variable string
VariableType Type
Initializer *Expression Initializer *Expression
} }
@ -72,6 +81,7 @@ const (
type Expression struct { type Expression struct {
Type ExpressionType Type ExpressionType
Value any Value any
ValueType Type
} }
type AssignmentExpression struct { type AssignmentExpression struct {
@ -107,8 +117,14 @@ type TupleExpression struct {
Members []Expression Members []Expression
} }
type Local struct {
Name string
Type Type
}
type Block struct { type Block struct {
Statements []Statement Statements []Statement
Locals map[string]Local
} }
type ParsedParameter struct { type ParsedParameter struct {
@ -474,7 +490,7 @@ func (p *Parser) tryDeclareLocalVariableStatement() (*Statement, error) {
token := pCopy.nextToken() token := pCopy.nextToken()
if token.Type == Type_Separator && token.Value.(Separator) == Separator_Semicolon { if token.Type == Type_Separator && token.Value.(Separator) == Separator_Semicolon {
return &Statement{Type: Statement_DeclareLocalVariable, Value: DeclareLocalVariableStatement{Variable: variableName, Initializer: nil}}, nil return &Statement{Type: Statement_DeclareLocalVariable, Value: DeclareLocalVariableStatement{Variable: variableName, VariableType: *variableType, Initializer: nil}}, nil
} }
if token.Type != Type_Operator || token.Value.(Operator) != Operator_Equals { if token.Type != Type_Operator || token.Value.(Operator) != Operator_Equals {
@ -494,7 +510,7 @@ func (p *Parser) tryDeclareLocalVariableStatement() (*Statement, error) {
} }
*p = pCopy *p = pCopy
return &Statement{Type: Statement_DeclareLocalVariable, Value: DeclareLocalVariableStatement{Variable: variableName, Initializer: initializer}}, nil return &Statement{Type: Statement_DeclareLocalVariable, Value: DeclareLocalVariableStatement{Variable: variableName, VariableType: *variableType, Initializer: initializer}}, nil
} }
func (p *Parser) expectStatement() (*Statement, error) { func (p *Parser) expectStatement() (*Statement, error) {

152
validator.go Normal file
View File

@ -0,0 +1,152 @@
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
}