Improve error messages
This commit is contained in:
parent
0a5fdf71a4
commit
1d79aeaa0b
@ -333,13 +333,12 @@ func compileFunctionWAT(function ParsedFunction) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: tuples
|
// TODO: tuples
|
||||||
returnTypes := []Type{function.ReturnType}
|
returnTypes := []Type{}
|
||||||
if function.ReturnType.Type == Type_Tuple {
|
if function.ReturnType != nil {
|
||||||
returnTypes = function.ReturnType.Value.(TupleType).Types
|
returnTypes = []Type{*function.ReturnType}
|
||||||
}
|
if function.ReturnType.Type == Type_Tuple {
|
||||||
|
returnTypes = function.ReturnType.Value.(TupleType).Types
|
||||||
if function.ReturnType.Type == Type_Void {
|
}
|
||||||
returnTypes = []Type{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range returnTypes {
|
for _, t := range returnTypes {
|
||||||
|
26
lexer.go
26
lexer.go
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -82,12 +81,13 @@ type Literal struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Lexer struct {
|
type Lexer struct {
|
||||||
Runes []rune
|
Runes []rune
|
||||||
Position uint64
|
LastTokenPosition uint64
|
||||||
|
Position uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Lexer) error(message string) error {
|
func (l *Lexer) error(message string) error {
|
||||||
return CompilerError{Position: l.Position, Message: message}
|
return CompilerError{Position: l.LastTokenPosition, Message: message}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Lexer) peekRune() *rune {
|
func (l *Lexer) peekRune() *rune {
|
||||||
@ -110,6 +110,8 @@ func (l *Lexer) nextRune() *rune {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *Lexer) stringLiteral() (string, error) {
|
func (l *Lexer) stringLiteral() (string, error) {
|
||||||
|
l.LastTokenPosition = l.Position
|
||||||
|
|
||||||
openQuote := l.nextRune()
|
openQuote := l.nextRune()
|
||||||
if openQuote == nil || *openQuote != '"' {
|
if openQuote == nil || *openQuote != '"' {
|
||||||
return "", l.error("expected \"")
|
return "", l.error("expected \"")
|
||||||
@ -158,6 +160,8 @@ func (l *Lexer) nextToken() (string, error) {
|
|||||||
l.nextRune()
|
l.nextRune()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
l.LastTokenPosition = l.Position
|
||||||
|
|
||||||
r := l.peekRune()
|
r := l.peekRune()
|
||||||
if r == nil {
|
if r == nil {
|
||||||
return "", nil
|
return "", nil
|
||||||
@ -208,7 +212,7 @@ func parseNumber(raw string, numberType PrimitiveType) (any, error) {
|
|||||||
|
|
||||||
func (l *Lexer) parseToken(token string) (*LexToken, error) {
|
func (l *Lexer) parseToken(token string) (*LexToken, error) {
|
||||||
if strings.HasPrefix(token, "\"") {
|
if strings.HasPrefix(token, "\"") {
|
||||||
return &LexToken{Type: Type_Literal, Position: l.Position, Value: Literal{Type: Literal_String, Primitive: InvalidValue, Value: token[1 : len(token)-1]}}, nil
|
return &LexToken{Type: Type_Literal, Position: l.LastTokenPosition, Value: Literal{Type: Literal_String, Primitive: InvalidValue, Value: token[1 : len(token)-1]}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
runes := []rune(token)
|
runes := []rune(token)
|
||||||
@ -235,7 +239,7 @@ func (l *Lexer) parseToken(token string) (*LexToken, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if containsDot && !isFloatingPoint(numberType) {
|
if containsDot && !isFloatingPoint(numberType) {
|
||||||
return nil, errors.New("dot in non floating-point constant")
|
return nil, l.error("dot in non floating-point constant")
|
||||||
}
|
}
|
||||||
|
|
||||||
number, err := parseNumber(rawNumber, numberType)
|
number, err := parseNumber(rawNumber, numberType)
|
||||||
@ -243,24 +247,24 @@ func (l *Lexer) parseToken(token string) (*LexToken, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &LexToken{Type: Type_Literal, Position: l.Position, Value: Literal{Type: Literal_Number, Primitive: numberType, Value: number}}, nil
|
return &LexToken{Type: Type_Literal, Position: l.LastTokenPosition, Value: Literal{Type: Literal_Number, Primitive: numberType, Value: number}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(runes) == 1 {
|
if len(runes) == 1 {
|
||||||
if idx := slices.Index(Separators, runes[0]); idx != -1 {
|
if idx := slices.Index(Separators, runes[0]); idx != -1 {
|
||||||
return &LexToken{Type: Type_Separator, Position: l.Position, Value: Separator(idx)}, nil
|
return &LexToken{Type: Type_Separator, Position: l.LastTokenPosition, Value: Separator(idx)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if idx := slices.Index(Operators, runes[0]); idx != -1 {
|
if idx := slices.Index(Operators, runes[0]); idx != -1 {
|
||||||
return &LexToken{Type: Type_Operator, Position: l.Position, Value: Operator(idx)}, nil
|
return &LexToken{Type: Type_Operator, Position: l.LastTokenPosition, Value: Operator(idx)}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if idx := slices.Index(Keywords, token); idx != -1 {
|
if idx := slices.Index(Keywords, token); idx != -1 {
|
||||||
return &LexToken{Type: Type_Keyword, Position: l.Position, Value: Keyword(idx)}, nil
|
return &LexToken{Type: Type_Keyword, Position: l.LastTokenPosition, Value: Keyword(idx)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return &LexToken{Type: Type_Identifier, Position: l.Position, Value: token}, nil
|
return &LexToken{Type: Type_Identifier, Position: l.LastTokenPosition, Value: token}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lexer(program string) ([]LexToken, error) {
|
func lexer(program string) ([]LexToken, error) {
|
||||||
|
69
parser.go
69
parser.go
@ -9,15 +9,15 @@ type TypeType uint32
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
Type_Primitive TypeType = iota
|
Type_Primitive TypeType = iota
|
||||||
Type_Void
|
|
||||||
Type_Named
|
Type_Named
|
||||||
Type_Array
|
Type_Array
|
||||||
Type_Tuple
|
Type_Tuple
|
||||||
)
|
)
|
||||||
|
|
||||||
type Type struct {
|
type Type struct {
|
||||||
Type TypeType
|
Type TypeType
|
||||||
Value any
|
Value any
|
||||||
|
Position uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type NamedType struct {
|
type NamedType struct {
|
||||||
@ -42,8 +42,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Statement struct {
|
type Statement struct {
|
||||||
Type StatementType
|
Type StatementType
|
||||||
Value any
|
Value any
|
||||||
|
Position uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExpressionStatement struct {
|
type ExpressionStatement struct {
|
||||||
@ -79,7 +80,8 @@ const (
|
|||||||
type Expression struct {
|
type Expression struct {
|
||||||
Type ExpressionType
|
Type ExpressionType
|
||||||
Value any
|
Value any
|
||||||
ValueType Type
|
ValueType *Type
|
||||||
|
Position uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type AssignmentExpression struct {
|
type AssignmentExpression struct {
|
||||||
@ -145,7 +147,7 @@ type ParsedParameter struct {
|
|||||||
type ParsedFunction struct {
|
type ParsedFunction struct {
|
||||||
Name string
|
Name string
|
||||||
Parameters []ParsedParameter
|
Parameters []ParsedParameter
|
||||||
ReturnType Type
|
ReturnType *Type
|
||||||
Body Block
|
Body Block
|
||||||
Locals []Local // All of the locals of the function, ordered by their index
|
Locals []Local // All of the locals of the function, ordered by their index
|
||||||
}
|
}
|
||||||
@ -273,22 +275,17 @@ func (p *Parser) tryType() (*Type, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if tok.Type == Type_Keyword && tok.Value.(Keyword) == Keyword_Void {
|
|
||||||
*p = pCopy
|
|
||||||
return &Type{Type: Type_Void, Value: nil}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if tok.Type == Type_Identifier {
|
if tok.Type == Type_Identifier {
|
||||||
// TODO: array type
|
// TODO: array type
|
||||||
|
|
||||||
index := slices.Index(PRIMITIVE_TYPE_NAMES, tok.Value.(string))
|
index := slices.Index(PRIMITIVE_TYPE_NAMES, tok.Value.(string))
|
||||||
if index != -1 {
|
if index != -1 {
|
||||||
*p = pCopy
|
*p = pCopy
|
||||||
return &Type{Type: Type_Primitive, Value: PrimitiveType(index)}, nil
|
return &Type{Type: Type_Primitive, Value: PrimitiveType(index), Position: tok.Position}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
*p = pCopy
|
*p = pCopy
|
||||||
return &Type{Type: Type_Named, Value: tok.Value}, nil
|
return &Type{Type: Type_Named, Value: tok.Value, Position: tok.Position}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -342,7 +339,7 @@ func (p *Parser) expectTypeOrTupleType() (*Type, error) {
|
|||||||
return nil, p.error("empty tuple")
|
return nil, p.error("empty tuple")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Type{Type: Type_Tuple, Value: TupleType{Types: types}}, nil
|
return &Type{Type: Type_Tuple, Value: TupleType{Types: types}, Position: tok.Position}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := p.tryType()
|
t, err := p.tryType()
|
||||||
@ -412,7 +409,7 @@ func (p *Parser) tryUnaryExpression() (*Expression, error) {
|
|||||||
if token.Type == Type_Literal {
|
if token.Type == Type_Literal {
|
||||||
pCopy.nextToken()
|
pCopy.nextToken()
|
||||||
*p = pCopy
|
*p = pCopy
|
||||||
return &Expression{Type: Expression_Literal, Value: LiteralExpression{Literal: token.Value.(Literal)}}, nil
|
return &Expression{Type: Expression_Literal, Value: LiteralExpression{Literal: token.Value.(Literal)}, Position: token.Position}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if token.Type == Type_Keyword {
|
if token.Type == Type_Keyword {
|
||||||
@ -420,7 +417,7 @@ func (p *Parser) tryUnaryExpression() (*Expression, error) {
|
|||||||
if keyword == Keyword_True || keyword == KeyWord_False {
|
if keyword == Keyword_True || keyword == KeyWord_False {
|
||||||
pCopy.nextToken()
|
pCopy.nextToken()
|
||||||
*p = pCopy
|
*p = pCopy
|
||||||
return &Expression{Type: Expression_Literal, Value: LiteralExpression{Literal: Literal{Type: Literal_Boolean, Primitive: Primitive_Bool, Value: keyword == Keyword_True}}}, nil
|
return &Expression{Type: Expression_Literal, Value: LiteralExpression{Literal: Literal{Type: Literal_Boolean, Primitive: Primitive_Bool, Value: keyword == Keyword_True}}, Position: token.Position}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,7 +435,7 @@ func (p *Parser) tryUnaryExpression() (*Expression, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if op == Operator_Minus {
|
if op == Operator_Minus {
|
||||||
expr = &Expression{Type: Expression_Negate, Value: NegateExpression{Value: *expr}}
|
expr = &Expression{Type: Expression_Negate, Value: NegateExpression{Value: *expr}, Position: token.Position}
|
||||||
}
|
}
|
||||||
|
|
||||||
return expr, nil
|
return expr, nil
|
||||||
@ -466,11 +463,11 @@ func (p *Parser) tryUnaryExpression() (*Expression, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
*p = pCopy
|
*p = pCopy
|
||||||
return &Expression{Type: Expression_FunctionCall, Value: FunctionCallExpression{Function: token.Value.(string), Parameters: params}}, nil
|
return &Expression{Type: Expression_FunctionCall, Value: FunctionCallExpression{Function: token.Value.(string), Parameters: params}, Position: token.Position}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
*p = pCopy
|
*p = pCopy
|
||||||
return &Expression{Type: Expression_VariableReference, Value: VariableReferenceExpression{Variable: token.Value.(string)}}, nil
|
return &Expression{Type: Expression_VariableReference, Value: VariableReferenceExpression{Variable: token.Value.(string)}, Position: token.Position}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -521,7 +518,7 @@ func (p *Parser) tryMultiplicativeExpression() (*Expression, error) {
|
|||||||
operation = Arithmetic_Mod
|
operation = Arithmetic_Mod
|
||||||
}
|
}
|
||||||
|
|
||||||
left = &Expression{Type: Expression_Arithmetic, Value: ArithmeticExpression{Operation: operation, Left: *left, Right: *right}}
|
left = &Expression{Type: Expression_Arithmetic, Value: ArithmeticExpression{Operation: operation, Left: *left, Right: *right}, Position: left.Position}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,7 +558,7 @@ func (p *Parser) tryAdditiveExpression() (*Expression, error) {
|
|||||||
operation = Arithmetic_Sub
|
operation = Arithmetic_Sub
|
||||||
}
|
}
|
||||||
|
|
||||||
left = &Expression{Type: Expression_Arithmetic, Value: ArithmeticExpression{Operation: operation, Left: *left, Right: *right}}
|
left = &Expression{Type: Expression_Arithmetic, Value: ArithmeticExpression{Operation: operation, Left: *left, Right: *right}, Position: left.Position}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,7 +600,7 @@ func (p *Parser) tryTupleExpression() (*Expression, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
*p = pCopy
|
*p = pCopy
|
||||||
return &Expression{Type: Expression_Tuple, Value: TupleExpression{Members: members}}, nil
|
return &Expression{Type: Expression_Tuple, Value: TupleExpression{Members: members}, Position: members[0].Position}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) expectTupleExpression() (*Expression, error) {
|
func (p *Parser) expectTupleExpression() (*Expression, error) {
|
||||||
@ -646,7 +643,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 {
|
||||||
*p = pCopy
|
*p = pCopy
|
||||||
return &Statement{Type: Statement_DeclareLocalVariable, Value: DeclareLocalVariableStatement{Variable: variableName, VariableType: *variableType, Initializer: nil}}, nil
|
return &Statement{Type: Statement_DeclareLocalVariable, Value: DeclareLocalVariableStatement{Variable: variableName, VariableType: *variableType, Initializer: nil}, Position: variableType.Position}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if token.Type != Type_Operator || token.Value.(Operator) != Operator_Equals {
|
if token.Type != Type_Operator || token.Value.(Operator) != Operator_Equals {
|
||||||
@ -666,7 +663,7 @@ func (p *Parser) tryDeclareLocalVariableStatement() (*Statement, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
*p = pCopy
|
*p = pCopy
|
||||||
return &Statement{Type: Statement_DeclareLocalVariable, Value: DeclareLocalVariableStatement{Variable: variableName, VariableType: *variableType, Initializer: initializer}}, nil
|
return &Statement{Type: Statement_DeclareLocalVariable, Value: DeclareLocalVariableStatement{Variable: variableName, VariableType: *variableType, Initializer: initializer}, Position: variableType.Position}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) expectStatement() (*Statement, error) {
|
func (p *Parser) expectStatement() (*Statement, error) {
|
||||||
@ -685,7 +682,7 @@ func (p *Parser) expectStatement() (*Statement, error) {
|
|||||||
|
|
||||||
if token.Type == Type_Separator && token.Value.(Separator) == Separator_Semicolon {
|
if token.Type == Type_Separator && token.Value.(Separator) == Separator_Semicolon {
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
return &Statement{Type: Statement_Return, Value: ReturnStatement{Value: nil}}, nil
|
return &Statement{Type: Statement_Return, Value: ReturnStatement{Value: nil}, Position: token.Position}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
expr, err := p.expectTupleExpression()
|
expr, err := p.expectTupleExpression()
|
||||||
@ -698,7 +695,7 @@ func (p *Parser) expectStatement() (*Statement, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Statement{Type: Statement_Return, Value: ReturnStatement{Value: expr}}, nil
|
return &Statement{Type: Statement_Return, Value: ReturnStatement{Value: expr}, Position: token.Position}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if token.Type == Type_Separator && token.Value.(Separator) == Separator_OpenCurly {
|
if token.Type == Type_Separator && token.Value.(Separator) == Separator_OpenCurly {
|
||||||
@ -707,7 +704,7 @@ func (p *Parser) expectStatement() (*Statement, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Statement{Type: Statement_Block, Value: BlockStatement{Block: *block}}, nil
|
return &Statement{Type: Statement_Block, Value: BlockStatement{Block: *block}, Position: token.Position}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt, err := p.tryDeclareLocalVariableStatement()
|
stmt, err := p.tryDeclareLocalVariableStatement()
|
||||||
@ -730,7 +727,7 @@ func (p *Parser) expectStatement() (*Statement, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Statement{Type: Statement_Expression, Value: ExpressionStatement{Expression: *expr}}, nil
|
return &Statement{Type: Statement_Expression, Value: ExpressionStatement{Expression: *expr}, Position: expr.Position}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, p.error("expected statement")
|
return nil, p.error("expected statement")
|
||||||
@ -773,9 +770,15 @@ func (p *Parser) expectFunction() (*ParsedFunction, error) {
|
|||||||
var returnType *Type
|
var returnType *Type
|
||||||
var body *Block
|
var body *Block
|
||||||
|
|
||||||
returnType, err = p.expectTypeOrTupleType()
|
tok := p.peekToken()
|
||||||
if err != nil {
|
if tok.Type == Type_Keyword && tok.Value.(Keyword) == Keyword_Void {
|
||||||
return nil, err
|
p.nextToken()
|
||||||
|
returnType = nil
|
||||||
|
} else {
|
||||||
|
returnType, err = p.expectTypeOrTupleType()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
name, err = p.expectIdentifier()
|
name, err = p.expectIdentifier()
|
||||||
@ -826,7 +829,7 @@ func (p *Parser) expectFunction() (*ParsedFunction, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ParsedFunction{Name: name, Parameters: parameters, ReturnType: *returnType, Body: *body}, nil
|
return &ParsedFunction{Name: name, Parameters: parameters, ReturnType: returnType, Body: *body}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseFile() (*ParsedFile, error) {
|
func (p *Parser) parseFile() (*ParsedFile, error) {
|
||||||
|
75
validator.go
75
validator.go
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -49,9 +48,9 @@ func isPrimitiveTypeExpandableTo(from PrimitiveType, to PrimitiveType) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Validator) createError(message string) error {
|
func (v *Validator) createError(message string, position uint64) error {
|
||||||
// TODO: pass token and get actual token position
|
// TODO: pass token and get actual token position
|
||||||
return errors.New(message)
|
return CompilerError{Position: position, Message: message}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Validator) validateImport(imp *Import) []error {
|
func (v *Validator) validateImport(imp *Import) []error {
|
||||||
@ -59,9 +58,9 @@ func (v *Validator) validateImport(imp *Import) []error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Validator) getArithmeticResultType(left PrimitiveType, right PrimitiveType, operation ArithmeticOperation) (PrimitiveType, error) {
|
func (v *Validator) getArithmeticResultType(expr *Expression, left PrimitiveType, right PrimitiveType, operation ArithmeticOperation) (PrimitiveType, error) {
|
||||||
if left == Primitive_Bool || right == Primitive_Bool {
|
if left == Primitive_Bool || right == Primitive_Bool {
|
||||||
return InvalidValue, v.createError("bool type cannot be used in arithmetic expressions")
|
return InvalidValue, v.createError("bool type cannot be used in arithmetic expressions", expr.Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isPrimitiveTypeExpandableTo(left, right) {
|
if isPrimitiveTypeExpandableTo(left, right) {
|
||||||
@ -74,10 +73,10 @@ func (v *Validator) getArithmeticResultType(left PrimitiveType, right PrimitiveT
|
|||||||
|
|
||||||
// TODO: boolean expressions etc.
|
// TODO: boolean expressions etc.
|
||||||
|
|
||||||
return InvalidValue, v.createError("cannot use these types in an arithmetic expression without an explicit cast") // TODO: include type names in error
|
return InvalidValue, v.createError("cannot use these types in an arithmetic expression without an explicit cast", expr.Position) // TODO: include type names in error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Validator) validateExpression(expr *Expression, block *Block) []error {
|
func (v *Validator) validatePotentiallyVoidExpression(expr *Expression, block *Block) []error {
|
||||||
var errors []error
|
var errors []error
|
||||||
|
|
||||||
switch expr.Type {
|
switch expr.Type {
|
||||||
@ -86,7 +85,7 @@ func (v *Validator) validateExpression(expr *Expression, block *Block) []error {
|
|||||||
var local Local
|
var local Local
|
||||||
var ok bool
|
var ok bool
|
||||||
if local, ok = block.Locals[assignment.Variable]; !ok {
|
if local, ok = block.Locals[assignment.Variable]; !ok {
|
||||||
errors = append(errors, v.createError("Assignment to undeclared variable "+assignment.Variable))
|
errors = append(errors, v.createError("Assignment to undeclared variable "+assignment.Variable, expr.Position))
|
||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,27 +96,27 @@ func (v *Validator) validateExpression(expr *Expression, block *Block) []error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check if assignment is valid
|
// TODO: check if assignment is valid
|
||||||
expr.ValueType = local.Type
|
expr.ValueType = &local.Type
|
||||||
expr.Value = assignment
|
expr.Value = assignment
|
||||||
case Expression_Literal:
|
case Expression_Literal:
|
||||||
literal := expr.Value.(LiteralExpression)
|
literal := expr.Value.(LiteralExpression)
|
||||||
|
|
||||||
switch literal.Literal.Type {
|
switch literal.Literal.Type {
|
||||||
case Literal_Boolean, Literal_Number:
|
case Literal_Boolean, Literal_Number:
|
||||||
expr.ValueType = Type{Type: Type_Primitive, Value: literal.Literal.Primitive}
|
expr.ValueType = &Type{Type: Type_Primitive, Value: literal.Literal.Primitive}
|
||||||
case Literal_String:
|
case Literal_String:
|
||||||
expr.ValueType = STRING_TYPE
|
expr.ValueType = &STRING_TYPE
|
||||||
}
|
}
|
||||||
case Expression_VariableReference:
|
case Expression_VariableReference:
|
||||||
reference := expr.Value.(VariableReferenceExpression)
|
reference := expr.Value.(VariableReferenceExpression)
|
||||||
var local Local
|
var local Local
|
||||||
var ok bool
|
var ok bool
|
||||||
if local, ok = block.Locals[reference.Variable]; !ok {
|
if local, ok = block.Locals[reference.Variable]; !ok {
|
||||||
errors = append(errors, v.createError("Reference to undeclared variable "+reference.Variable))
|
errors = append(errors, v.createError("Reference to undeclared variable "+reference.Variable, expr.Position))
|
||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
expr.ValueType = local.Type
|
expr.ValueType = &local.Type
|
||||||
case Expression_Arithmetic:
|
case Expression_Arithmetic:
|
||||||
arithmethic := expr.Value.(ArithmeticExpression)
|
arithmethic := expr.Value.(ArithmeticExpression)
|
||||||
|
|
||||||
@ -129,19 +128,19 @@ func (v *Validator) validateExpression(expr *Expression, block *Block) []error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if arithmethic.Left.ValueType.Type != Type_Primitive || arithmethic.Right.ValueType.Type != Type_Primitive {
|
if arithmethic.Left.ValueType.Type != Type_Primitive || arithmethic.Right.ValueType.Type != Type_Primitive {
|
||||||
errors = append(errors, v.createError("both sides of an arithmetic expression must be a primitive type"))
|
errors = append(errors, v.createError("both sides of an arithmetic expression must be a primitive type", expr.Position))
|
||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
leftType := arithmethic.Left.ValueType.Value.(PrimitiveType)
|
leftType := arithmethic.Left.ValueType.Value.(PrimitiveType)
|
||||||
rightType := arithmethic.Right.ValueType.Value.(PrimitiveType)
|
rightType := arithmethic.Right.ValueType.Value.(PrimitiveType)
|
||||||
result, err := v.getArithmeticResultType(leftType, rightType, arithmethic.Operation)
|
result, err := v.getArithmeticResultType(expr, leftType, rightType, arithmethic.Operation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors = append(errors, err)
|
errors = append(errors, err)
|
||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
expr.ValueType = Type{Type: Type_Primitive, Value: result}
|
expr.ValueType = &Type{Type: Type_Primitive, Value: result}
|
||||||
expr.Value = arithmethic
|
expr.Value = arithmethic
|
||||||
case Expression_Tuple:
|
case Expression_Tuple:
|
||||||
tuple := expr.Value.(TupleExpression)
|
tuple := expr.Value.(TupleExpression)
|
||||||
@ -156,14 +155,14 @@ func (v *Validator) validateExpression(expr *Expression, block *Block) []error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
types = append(types, member.ValueType)
|
types = append(types, *member.ValueType)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errors) != 0 {
|
if len(errors) != 0 {
|
||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
expr.ValueType = Type{Type: Type_Tuple, Value: TupleType{Types: types}}
|
expr.ValueType = &Type{Type: Type_Tuple, Value: TupleType{Types: types}}
|
||||||
expr.Value = tuple
|
expr.Value = tuple
|
||||||
case Expression_FunctionCall:
|
case Expression_FunctionCall:
|
||||||
fc := expr.Value.(FunctionCallExpression)
|
fc := expr.Value.(FunctionCallExpression)
|
||||||
@ -177,12 +176,16 @@ func (v *Validator) validateExpression(expr *Expression, block *Block) []error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if calledFunc == nil {
|
if calledFunc == nil {
|
||||||
errors = append(errors, v.createError("call to undefined function '"+fc.Function+"'"))
|
errors = append(errors, v.createError("call to undefined function '"+fc.Function+"'", expr.Position))
|
||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
if fc.Parameters != nil {
|
if fc.Parameters != nil {
|
||||||
errors = append(errors, v.validateExpression(fc.Parameters, block)...)
|
paramsErrors := v.validateExpression(fc.Parameters, block)
|
||||||
|
if len(paramsErrors) > 0 {
|
||||||
|
errors = append(errors, paramsErrors...)
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
params := []Expression{*fc.Parameters}
|
params := []Expression{*fc.Parameters}
|
||||||
if fc.Parameters.Type == Expression_Tuple {
|
if fc.Parameters.Type == Expression_Tuple {
|
||||||
@ -190,14 +193,14 @@ func (v *Validator) validateExpression(expr *Expression, block *Block) []error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(params) != len(calledFunc.Parameters) {
|
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))))
|
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))
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < min(len(params), len(calledFunc.Parameters)); i++ {
|
for i := 0; i < min(len(params), len(calledFunc.Parameters)); i++ {
|
||||||
typeGiven := params[i]
|
typeGiven := params[i]
|
||||||
typeExpected := calledFunc.Parameters[i]
|
typeExpected := calledFunc.Parameters[i]
|
||||||
if !isTypeExpandableTo(typeGiven.ValueType, typeExpected.Type) {
|
if !isTypeExpandableTo(*typeGiven.ValueType, typeExpected.Type) {
|
||||||
errors = append(errors, v.createError("invalid type for parameter "+strconv.Itoa(i)))
|
errors = append(errors, v.createError("invalid type for parameter "+strconv.Itoa(i), expr.Position))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -208,10 +211,14 @@ func (v *Validator) validateExpression(expr *Expression, block *Block) []error {
|
|||||||
case Expression_Negate:
|
case Expression_Negate:
|
||||||
neg := expr.Value.(NegateExpression)
|
neg := expr.Value.(NegateExpression)
|
||||||
|
|
||||||
errors = append(errors, v.validateExpression(&neg.Value, block)...)
|
valErrors := v.validateExpression(&neg.Value, block)
|
||||||
|
if len(valErrors) > 0 {
|
||||||
|
errors = append(errors, valErrors...)
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
if neg.Value.ValueType.Type != Type_Primitive {
|
if neg.Value.ValueType.Type != Type_Primitive {
|
||||||
errors = append(errors, v.createError("cannot negate non-number types"))
|
errors = append(errors, v.createError("cannot negate non-number types", expr.Position))
|
||||||
}
|
}
|
||||||
|
|
||||||
expr.ValueType = neg.Value.ValueType
|
expr.ValueType = neg.Value.ValueType
|
||||||
@ -221,6 +228,16 @@ func (v *Validator) validateExpression(expr *Expression, block *Block) []error {
|
|||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Validator) validateExpression(expr *Expression, block *Block) []error {
|
||||||
|
errors := v.validatePotentiallyVoidExpression(expr, block)
|
||||||
|
|
||||||
|
if expr.ValueType == nil {
|
||||||
|
errors = append(errors, v.createError("expression must not evaluate to void", expr.Position))
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Validator) validateStatement(stmt *Statement, block *Block, functionLocals *[]Local) []error {
|
func (v *Validator) validateStatement(stmt *Statement, block *Block, functionLocals *[]Local) []error {
|
||||||
var errors []error
|
var errors []error
|
||||||
|
|
||||||
@ -229,12 +246,12 @@ func (v *Validator) validateStatement(stmt *Statement, block *Block, functionLoc
|
|||||||
switch stmt.Type {
|
switch stmt.Type {
|
||||||
case Statement_Expression:
|
case Statement_Expression:
|
||||||
expression := stmt.Value.(ExpressionStatement)
|
expression := stmt.Value.(ExpressionStatement)
|
||||||
errors = append(errors, v.validateExpression(&expression.Expression, block)...)
|
errors = append(errors, v.validatePotentiallyVoidExpression(&expression.Expression, block)...)
|
||||||
*stmt = Statement{Type: Statement_Expression, Value: expression}
|
stmt.Value = expression
|
||||||
case Statement_Block:
|
case Statement_Block:
|
||||||
block := stmt.Value.(BlockStatement)
|
block := stmt.Value.(BlockStatement)
|
||||||
errors = append(errors, v.validateBlock(&block.Block, functionLocals)...)
|
errors = append(errors, v.validateBlock(&block.Block, functionLocals)...)
|
||||||
*stmt = Statement{Type: Statement_Block, Value: block}
|
stmt.Value = block
|
||||||
case Statement_Return:
|
case Statement_Return:
|
||||||
ret := stmt.Value.(ReturnStatement)
|
ret := stmt.Value.(ReturnStatement)
|
||||||
if ret.Value != nil {
|
if ret.Value != nil {
|
||||||
@ -247,7 +264,7 @@ func (v *Validator) validateStatement(stmt *Statement, block *Block, functionLoc
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := block.Locals[dlv.Variable]; ok {
|
if _, ok := block.Locals[dlv.Variable]; ok {
|
||||||
errors = append(errors, v.createError("redeclaration of variable '"+dlv.Variable+"'"))
|
errors = append(errors, v.createError("redeclaration of variable '"+dlv.Variable+"'", stmt.Position))
|
||||||
}
|
}
|
||||||
|
|
||||||
local := Local{Name: dlv.Variable, Type: dlv.VariableType, IsParameter: false, Index: len(*functionLocals)}
|
local := Local{Name: dlv.Variable, Type: dlv.VariableType, IsParameter: false, Index: len(*functionLocals)}
|
||||||
|
Loading…
Reference in New Issue
Block a user