Basic validation
This commit is contained in:
parent
cf0307c164
commit
a8d5c7d479
11
main.go
11
main.go
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
20
parser.go
20
parser.go
@ -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
152
validator.go
Normal 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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user