Rework analyzer code to fit new parser

This commit is contained in:
Sasha Koshka 2024-02-08 03:51:21 -05:00
parent 090d52b8f8
commit 5bb9508a76
10 changed files with 194 additions and 222 deletions

View File

@ -2,8 +2,7 @@ package analyzer
import "fmt" import "fmt"
import "math" import "math"
import "github.com/alecthomas/participle/v2" import "git.tebibyte.media/sashakoshka/fspl/errors"
import "github.com/alecthomas/participle/v2/lexer"
import "git.tebibyte.media/sashakoshka/fspl/entity" import "git.tebibyte.media/sashakoshka/fspl/entity"
import "git.tebibyte.media/sashakoshka/fspl/integer" import "git.tebibyte.media/sashakoshka/fspl/integer"
@ -24,7 +23,7 @@ type strictness int; const (
// canAssign takes in an analyzed destination type and an analyzed source type, // canAssign takes in an analyzed destination type and an analyzed source type,
// and determines whether source can be assigned to destination. // and determines whether source can be assigned to destination.
func (this *Tree) canAssign ( func (this *Tree) canAssign (
pos lexer.Position, pos errors.Position,
// assuming both are analyzed already // assuming both are analyzed already
destination entity.Type, destination entity.Type,
mode strictness, mode strictness,
@ -33,13 +32,13 @@ func (this *Tree) canAssign (
fail := func () error { fail := func () error {
switch mode { switch mode {
case strict: case strict:
return participle.Errorf(pos, "expected %v", destination) return errors.Errorf(pos, "expected %v", destination)
case weak: case weak:
return participle.Errorf ( return errors.Errorf (
pos, "cannot use %v as %v", pos, "cannot use %v as %v",
source, destination) source, destination)
case structural: case structural:
return participle.Errorf ( return errors.Errorf (
pos, "cannot convert from %v to %v", pos, "cannot convert from %v to %v",
source, destination) source, destination)
default: default:
@ -142,12 +141,12 @@ func (this *Tree) canAssign (
// canAssignCoerce determines if data of an analyzed source type can be // canAssignCoerce determines if data of an analyzed source type can be
// converted into data of an analyzed destination type. // converted into data of an analyzed destination type.
func (this *Tree) canAssignCoerce ( func (this *Tree) canAssignCoerce (
pos lexer.Position, pos errors.Position,
destination entity.Type, destination entity.Type,
source entity.Type, source entity.Type,
) error { ) error {
fail := func () error { fail := func () error {
return participle.Errorf ( return errors.Errorf (
pos, "cannot convert from %v to %v", pos, "cannot convert from %v to %v",
source, destination) source, destination)
} }
@ -213,13 +212,13 @@ func (this *Tree) canAssignCoerce (
// canAssignSliceArray takes in an analyzed slice type and an analyzed array // canAssignSliceArray takes in an analyzed slice type and an analyzed array
// type, and determines whether the array can be assigned to the slice. // type, and determines whether the array can be assigned to the slice.
func (this *Tree) canAssignSliceArray ( func (this *Tree) canAssignSliceArray (
pos lexer.Position, pos errors.Position,
destination *entity.TypeSlice, destination *entity.TypeSlice,
source *entity.TypeArray, source *entity.TypeArray,
) error { ) error {
err := this.canAssign(pos, destination.Element, strict, source.Element) err := this.canAssign(pos, destination.Element, strict, source.Element)
if err != nil { if err != nil {
return participle.Errorf(pos, "expected %v", destination) return errors.Errorf(pos, "expected %v", destination)
} else { } else {
return nil return nil
} }
@ -229,7 +228,7 @@ func (this *Tree) canAssignSliceArray (
// analyzed source type, and determines whether source can be assigned to // analyzed source type, and determines whether source can be assigned to
// destination. // destination.
func (this *Tree) canAssignInterface ( func (this *Tree) canAssignInterface (
pos lexer.Position, pos errors.Position,
destination *entity.TypeInterface, destination *entity.TypeInterface,
source entity.Type, source entity.Type,
) error { ) error {
@ -254,7 +253,7 @@ func (this *Tree) canAssignInterface (
// check equivalence // check equivalence
if !signature.Equals(behavior) { if !signature.Equals(behavior) {
return participle.Errorf ( return errors.Errorf (
pos, "%v has wrong signature for method %v", pos, "%v has wrong signature for method %v",
source, name) source, name)
} }
@ -326,60 +325,60 @@ func (this *Tree) isLocationExpression (expression entity.Expression) error {
case *entity.Declaration: case *entity.Declaration:
return nil return nil
case *entity.Call: case *entity.Call:
return participle.Errorf ( return errors.Errorf (
expression.Pos, expression.Position,
"cannot assign to function call") "cannot assign to function call")
case *entity.MethodCall: case *entity.MethodCall:
return participle.Errorf ( return errors.Errorf (
expression.Pos, expression.Position,
"cannot assign to method call") "cannot assign to method call")
case *entity.Subscript: case *entity.Subscript:
return this.isLocationExpression(expression.Slice) return this.isLocationExpression(expression.Slice)
case *entity.Slice: case *entity.Slice:
return participle.Errorf ( return errors.Errorf (
expression.Pos, expression.Position,
"cannot assign to slice operation") "cannot assign to slice operation")
case *entity.Dereference: case *entity.Dereference:
return this.isLocationExpression(expression.Pointer) return this.isLocationExpression(expression.Pointer)
case *entity.Reference: case *entity.Reference:
return participle.Errorf ( return errors.Errorf (
expression.Pos, expression.Position,
"cannot assign to reference operation") "cannot assign to reference operation")
case *entity.ValueCast: case *entity.ValueCast:
return participle.Errorf ( return errors.Errorf (
expression.Pos, expression.Position,
"cannot assign to value cast") "cannot assign to value cast")
case *entity.BitCast: case *entity.BitCast:
return participle.Errorf ( return errors.Errorf (
expression.Pos, expression.Position,
"cannot assign to bit cast") "cannot assign to bit cast")
case *entity.Operation: case *entity.Operation:
return participle.Errorf ( return errors.Errorf (
expression.Pos, expression.Position,
"cannot assign to %v operation", "cannot assign to %v operation",
expression.Operator) expression.Operator)
case *entity.Block: case *entity.Block:
return participle.Errorf ( return errors.Errorf (
expression.Pos, expression.Position,
"cannot assign to block") "cannot assign to block")
case *entity.MemberAccess: case *entity.MemberAccess:
return this.isLocationExpression ( return this.isLocationExpression (
expression.Source) expression.Source)
case *entity.IfElse: case *entity.IfElse:
return participle.Errorf ( return errors.Errorf (
expression.Pos, expression.Position,
"cannot assign to if/else") "cannot assign to if/else")
case *entity.Loop: case *entity.Loop:
return participle.Errorf ( return errors.Errorf (
expression.Pos, expression.Position,
"cannot assign to loop") "cannot assign to loop")
case *entity.Break: case *entity.Break:
return participle.Errorf ( return errors.Errorf (
expression.Pos, expression.Position,
"cannot assign to break statement") "cannot assign to break statement")
case *entity.Return: case *entity.Return:
return participle.Errorf ( return errors.Errorf (
expression.Pos, expression.Position,
"cannot assign to return statement") "cannot assign to return statement")
default: default:
panic(fmt.Sprint ( panic(fmt.Sprint (

View File

@ -12,6 +12,8 @@ func (this *Tree) analyzeExpression (
error, error,
) { ) {
switch expression := expression.(type) { switch expression := expression.(type) {
case *entity.Assignment:
return this.analyzeAssignment(expression)
case *entity.Variable: case *entity.Variable:
return this.analyzeVariable(into, mode, expression) return this.analyzeVariable(into, mode, expression)
case *entity.Declaration: case *entity.Declaration:
@ -69,22 +71,3 @@ func (this *Tree) analyzeExpression (
expression, expression)) expression, expression))
} }
} }
func (this *Tree) analyzeStatement (
statement entity.Statement,
) (
entity.Statement,
error,
) {
if assignment, ok := statement.(*entity.Assignment); ok {
return this.analyzeAssignment(assignment)
} else if expression, ok := statement.(entity.Expression); ok {
expression, err := this.analyzeExpression(nil, strict, expression)
if err != nil { return nil, err }
return expression.(entity.Statement), nil
} else {
panic(fmt.Sprintf (
"BUG: analyzer doesnt know about statement %v, ty: %T",
statement, statement))
}
}

View File

@ -1,7 +1,7 @@
package analyzer package analyzer
import "fmt" import "fmt"
import "github.com/alecthomas/participle/v2" import "git.tebibyte.media/sashakoshka/fspl/errors"
import "git.tebibyte.media/sashakoshka/fspl/entity" import "git.tebibyte.media/sashakoshka/fspl/entity"
// All expression analysis routines must take in the type they are being // All expression analysis routines must take in the type they are being
@ -9,6 +9,29 @@ import "git.tebibyte.media/sashakoshka/fspl/entity"
// is nil, the expression must ignore it unless it can do upwards type // is nil, the expression must ignore it unless it can do upwards type
// inference. // inference.
func (this *Tree) analyzeAssignment (
assignment *entity.Assignment,
) (
entity.Expression,
error,
) {
// analyze location
location, err := this.analyzeExpression(nil, strict, assignment.Location)
if err != nil { return nil, err }
assignment.Location = location
// ensure location is location expression
err = this.isLocationExpression(location)
if err != nil { return nil, err }
// analyze value
value, err := this.analyzeExpression(location.Type(), strict, assignment.Value)
if err != nil { return nil, err }
assignment.Value = value
return assignment, nil
}
func (this *Tree) analyzeVariable ( func (this *Tree) analyzeVariable (
into entity.Type, into entity.Type,
mode strictness, mode strictness,
@ -19,12 +42,12 @@ func (this *Tree) analyzeVariable (
) { ) {
declaration := this.variable(variable.Name) declaration := this.variable(variable.Name)
if declaration == nil { if declaration == nil {
return nil, participle.Errorf ( return nil, errors.Errorf (
variable.Pos, "no variable named %s", variable.Position, "no variable named %s",
variable.Name) variable.Name)
} }
err := this.canAssign(variable.Pos, into, mode, declaration.Type()) err := this.canAssign(variable.Position, into, mode, declaration.Type())
if err != nil { return nil, err } if err != nil { return nil, err }
variable.Declaration = declaration variable.Declaration = declaration
@ -42,17 +65,17 @@ func (this *Tree) analyzeDeclaration (
scope, _ := this.topScope() scope, _ := this.topScope()
existing := scope.Variable(declaration.Name) existing := scope.Variable(declaration.Name)
if existing != nil { if existing != nil {
return nil, participle.Errorf ( return nil, errors.Errorf (
declaration.Pos, declaration.Position,
"%s already declared in block at %v", "%s already declared in block at %v",
declaration.Name, existing.Pos) declaration.Name, existing.Position)
} }
ty, err := this.analyzeType(declaration.Ty, false) ty, err := this.analyzeType(declaration.Ty, false)
declaration.Ty = ty declaration.Ty = ty
if err != nil { return nil, err } if err != nil { return nil, err }
err = this.canAssign(declaration.Pos, into, mode, declaration.Type()) err = this.canAssign(declaration.Position, into, mode, declaration.Type())
if err != nil { return nil, err } if err != nil { return nil, err }
this.addVariable(declaration) this.addVariable(declaration)
@ -68,22 +91,22 @@ func (this *Tree) analyzeCall (
error, error,
) { ) {
// get function // get function
function, err := this.analyzeFunction(call.Pos, call.Name) function, err := this.analyzeFunction(call.Position, call.Name)
call.Function = function call.Function = function
if err != nil { return nil, err } if err != nil { return nil, err }
// check return result // check return result
err = this.canAssign(call.Pos, into, mode, function.Signature.Return) err = this.canAssign(call.Position, into, mode, function.Signature.Return)
if err != nil { return nil, err } if err != nil { return nil, err }
// check arg count // check arg count
if len(call.Arguments) > len(function.Signature.Arguments) { if len(call.Arguments) > len(function.Signature.Arguments) {
return nil, participle.Errorf ( return nil, errors.Errorf (
call.Pos, "too many arguments in call to %s", call.Position, "too many arguments in call to %s",
call.Name) call.Name)
} else if len(call.Arguments) < len(function.Signature.Arguments) { } else if len(call.Arguments) < len(function.Signature.Arguments) {
return nil, participle.Errorf ( return nil, errors.Errorf (
call.Pos, "too few arguments in call to %s", call.Position, "too few arguments in call to %s",
call.Name) call.Name)
} }
@ -108,11 +131,11 @@ func (this *Tree) analyzeMethodCall (
error, error,
) { ) {
// get method // get method
sourceExpr, err := this.analyzeVariable(nil, strict, call.Source) sourceExpr, err := this.analyzeExpression(nil, strict, call.Source)
source := sourceExpr.(*entity.Variable) source := sourceExpr.(*entity.Variable)
if err != nil { return nil, err } if err != nil { return nil, err }
method, err := this.analyzeMethodOrBehavior ( method, err := this.analyzeMethodOrBehavior (
call.Pos, source.Declaration.Type(), call.Name) call.Position, source.Declaration.Type(), call.Name)
if err != nil { return nil, err } if err != nil { return nil, err }
// extract signature // extract signature
@ -131,17 +154,17 @@ func (this *Tree) analyzeMethodCall (
} }
// check return result // check return result
err = this.canAssign(call.Pos, into, mode, signature.Return) err = this.canAssign(call.Position, into, mode, signature.Return)
if err != nil { return nil, err } if err != nil { return nil, err }
// check arg count // check arg count
if len(call.Arguments) > len(signature.Arguments) { if len(call.Arguments) > len(signature.Arguments) {
return nil, participle.Errorf ( return nil, errors.Errorf (
call.Pos, "too many arguments in call to %s", call.Position, "too many arguments in call to %s",
call.Name) call.Name)
} else if len(call.Arguments) < len(signature.Arguments) { } else if len(call.Arguments) < len(signature.Arguments) {
return nil, participle.Errorf ( return nil, errors.Errorf (
call.Pos, "too few arguments in call to %s", call.Position, "too few arguments in call to %s",
call.Name) call.Name)
} }
@ -166,8 +189,8 @@ func (this *Tree) analyzeSubscript (
) { ) {
slice, err := this.analyzeExpression ( slice, err := this.analyzeExpression (
&entity.TypeSlice { &entity.TypeSlice {
Pos: subscript.Pos, Position: subscript.Position,
Element: into, Element: into,
}, weak, }, weak,
subscript.Slice) subscript.Slice)
if err != nil { return nil, err } if err != nil { return nil, err }
@ -254,7 +277,7 @@ func (this *Tree) analyzeDereference (
) { ) {
pointer, err := this.analyzeExpression ( pointer, err := this.analyzeExpression (
&entity.TypePointer { &entity.TypePointer {
Pos: dereference.Pos, Position: dereference.Position,
Referenced: into, Referenced: into,
}, weak, }, weak,
dereference.Pointer) dereference.Pointer)
@ -288,7 +311,7 @@ func (this *Tree) analyzeReference (
) { ) {
referenced, ok := into.(*entity.TypePointer) referenced, ok := into.(*entity.TypePointer)
if !ok { if !ok {
return nil, participle.Errorf(reference.Pos, "expected %v", into) return nil, errors.Errorf(reference.Position, "expected %v", into)
} }
value, err := this.analyzeExpression ( value, err := this.analyzeExpression (
@ -315,7 +338,7 @@ func (this *Tree) analyzeValueCast (
if err != nil { return nil, err } if err != nil { return nil, err }
cast.Ty = ty cast.Ty = ty
err = this.canAssign(cast.Pos, into, mode, cast.Type()) err = this.canAssign(cast.Position, into, mode, cast.Type())
if err != nil { return nil, err } if err != nil { return nil, err }
value, err := this.analyzeExpression(cast.Ty, coerce, cast.Value) value, err := this.analyzeExpression(cast.Ty, coerce, cast.Value)
@ -337,7 +360,7 @@ func (this *Tree) analyzeBitCast (
if err != nil { return nil, err } if err != nil { return nil, err }
cast.Ty = ty cast.Ty = ty
err = this.canAssign(cast.Pos, into, mode, cast.Type()) err = this.canAssign(cast.Position, into, mode, cast.Type())
if err != nil { return nil, err } if err != nil { return nil, err }
value, err := this.analyzeExpression(cast.Ty, force, cast.Value) value, err := this.analyzeExpression(cast.Ty, force, cast.Value)
@ -356,12 +379,12 @@ func (this *Tree) analyzeOperation (
error, error,
) { ) {
wrongInto := func () (entity.Expression, error) { wrongInto := func () (entity.Expression, error) {
return nil, participle.Errorf(operation.Pos, "expected %v", into) return nil, errors.Errorf(operation.Position, "expected %v", into)
} }
wrongArgCount := func () (entity.Expression, error) { wrongArgCount := func () (entity.Expression, error) {
return nil, participle.Errorf ( return nil, errors.Errorf (
operation.Pos, "wrong argument count for %v", operation.Position, "wrong argument count for %v",
operation.Operator) operation.Operator)
} }
@ -413,8 +436,8 @@ func (this *Tree) analyzeOperation (
} }
} }
if argumentType == nil { if argumentType == nil {
return nil, participle.Errorf ( return nil, errors.Errorf (
operation.Pos, operation.Position,
"operation arguments have ambiguous type") "operation arguments have ambiguous type")
} }
@ -510,8 +533,8 @@ func (this *Tree) analyzeBlock (
defer this.popScope() defer this.popScope()
if len(block.Steps) == 0 && into != nil { if len(block.Steps) == 0 && into != nil {
return nil, participle.Errorf ( return nil, errors.Errorf (
block.Pos, "block must have at least one statement") block.Position, "block must have at least one statement")
} }
final := len(block.Steps) - 1 final := len(block.Steps) - 1
@ -519,8 +542,8 @@ func (this *Tree) analyzeBlock (
if index == final && into != nil { if index == final && into != nil {
expression, ok := step.(entity.Expression) expression, ok := step.(entity.Expression)
if !ok { if !ok {
return nil, participle.Errorf ( return nil, errors.Errorf (
block.Pos, "expected expression") block.Position, "expected expression")
} }
step, err := this.analyzeExpression(into, strict, expression) step, err := this.analyzeExpression(into, strict, expression)
@ -528,7 +551,7 @@ func (this *Tree) analyzeBlock (
block.Steps[index] = step block.Steps[index] = step
block.Ty = step.Type() block.Ty = step.Type()
} else { } else {
step, err := this.analyzeStatement(step) step, err := this.analyzeExpression(nil, strict, step)
if err != nil { return nil, err } if err != nil { return nil, err }
block.Steps[index] = step block.Steps[index] = step
} }
@ -555,22 +578,22 @@ func (this *Tree) analyzeMemberAccess (
case *entity.TypePointer: case *entity.TypePointer:
referenced, ok := ReduceToBase(sourceTypeAny.Referenced).(*entity.TypeStruct) referenced, ok := ReduceToBase(sourceTypeAny.Referenced).(*entity.TypeStruct)
if !ok { if !ok {
return nil, participle.Errorf ( return nil, errors.Errorf (
access.Pos, "cannot access members of %v", source) access.Position, "cannot access members of %v", source)
} }
sourceType = referenced sourceType = referenced
default: default:
return nil, participle.Errorf ( return nil, errors.Errorf (
access.Pos, "cannot access members of %v", source) access.Position, "cannot access members of %v", source)
} }
member, ok := sourceType.MemberMap[access.Member] member, ok := sourceType.MemberMap[access.Member]
if !ok { if !ok {
return nil, participle.Errorf ( return nil, errors.Errorf (
access.Pos, "no member %v", access) access.Position, "no member %v", access)
} }
err = this.canAssign(access.Pos, into, mode, member.Type()) err = this.canAssign(access.Position, into, mode, member.Type())
if err != nil { return nil, err } if err != nil { return nil, err }
access.Ty = member.Type() access.Ty = member.Type()
@ -600,8 +623,8 @@ func (this *Tree) analyzeIfElse (
if ifelse.False == nil { if ifelse.False == nil {
if into != nil { if into != nil {
return nil, participle.Errorf ( return nil, errors.Errorf (
ifelse.Pos, ifelse.Position,
"else case required when using value of if ") "else case required when using value of if ")
} }
} else { } else {
@ -643,20 +666,20 @@ func (this *Tree) analyzeBreak (
error, error,
) { ) {
if into != nil { if into != nil {
return nil, participle.Errorf(brk.Pos, "expected %v", into) return nil, errors.Errorf(brk.Position, "expected %v", into)
} }
loop, ok := this.topLoop() loop, ok := this.topLoop()
if !ok { if !ok {
return nil, participle.Errorf ( return nil, errors.Errorf (
brk.Pos, brk.Position,
"break statement must be within loop") "break statement must be within loop")
} }
brk.Loop = loop brk.Loop = loop
if loop.Type() != nil && brk.Value == nil { if loop.Type() != nil && brk.Value == nil {
return nil, participle.Errorf ( return nil, errors.Errorf (
brk.Pos, brk.Position,
"break statement must have value") "break statement must have value")
} }
@ -678,7 +701,7 @@ func (this *Tree) analyzeReturn (
error, error,
) { ) {
if into != nil { if into != nil {
return nil, participle.Errorf(ret.Pos, "expected %v", into) return nil, errors.Errorf(ret.Position, "expected %v", into)
} }
ret.Declaration, _ = this.topDeclaration() ret.Declaration, _ = this.topDeclaration()
@ -691,8 +714,8 @@ func (this *Tree) analyzeReturn (
} }
if ty != nil && ret.Value == nil { if ty != nil && ret.Value == nil {
return nil, participle.Errorf ( return nil, errors.Errorf (
ret.Pos, ret.Position,
"break statement must have value") "break statement must have value")
} }

View File

@ -1,11 +1,10 @@
package analyzer package analyzer
import "github.com/alecthomas/participle/v2" import "git.tebibyte.media/sashakoshka/fspl/errors"
import "github.com/alecthomas/participle/v2/lexer"
import "git.tebibyte.media/sashakoshka/fspl/entity" import "git.tebibyte.media/sashakoshka/fspl/entity"
func (this *Tree) analyzeFunction ( func (this *Tree) analyzeFunction (
pos lexer.Position, pos errors.Position,
name string, name string,
) ( ) (
*entity.Function, *entity.Function,
@ -21,7 +20,7 @@ func (this *Tree) analyzeFunction (
// error if function is missing // error if function is missing
function, exists := this.rawFunctions[name] function, exists := this.rawFunctions[name]
if !exists { if !exists {
return nil, participle.Errorf(pos, "no function named %s", name) return nil, errors.Errorf(pos, "no function named %s", name)
} }
// create a new scope context for this function // create a new scope context for this function

View File

@ -1,7 +1,7 @@
package analyzer package analyzer
import "unicode/utf16" import "unicode/utf16"
import "github.com/alecthomas/participle/v2" import "git.tebibyte.media/sashakoshka/fspl/errors"
import "git.tebibyte.media/sashakoshka/fspl/entity" import "git.tebibyte.media/sashakoshka/fspl/entity"
func (this *Tree) analyzeLiteralInt ( func (this *Tree) analyzeLiteralInt (
@ -13,14 +13,14 @@ func (this *Tree) analyzeLiteralInt (
error, error,
) { ) {
if !isNumeric(into) { if !isNumeric(into) {
return nil, participle.Errorf ( return nil, errors.Errorf (
literal.Pos, "cannot use integer literal as %v", literal.Position, "cannot use integer literal as %v",
into) into)
} }
if isInteger(into) && !inRange(into, int64(literal.Value)) { if isInteger(into) && !inRange(into, int64(literal.Value)) {
return nil, participle.Errorf ( return nil, errors.Errorf (
literal.Pos, "integer literal out of range for type %v", literal.Position, "integer literal out of range for type %v",
into) into)
} }
@ -37,8 +37,8 @@ func (this *Tree) analyzeLiteralFloat (
error, error,
) { ) {
if !isFloat(into) { if !isFloat(into) {
return nil, participle.Errorf ( return nil, errors.Errorf (
literal.Pos, "cannot use float literal as %v", literal.Position, "cannot use float literal as %v",
into) into)
} }
@ -57,8 +57,8 @@ func (this *Tree) analyzeLiteralString (
base := ReduceToBase(into) base := ReduceToBase(into)
errCantUse := func () error { errCantUse := func () error {
return participle.Errorf ( return errors.Errorf (
literal.Pos, "cannot use string literal as %v", literal.Position, "cannot use string literal as %v",
into) into)
} }
@ -82,14 +82,14 @@ func (this *Tree) analyzeLiteralString (
length = len(literal.ValueUTF8) length = len(literal.ValueUTF8)
} }
if length > 1 { if length > 1 {
return nil, participle.Errorf ( return nil, errors.Errorf (
literal.Pos, literal.Position,
"cannot fit string literal of length " + "cannot fit string literal of length " +
"%v into %v", "%v into %v",
length, into) length, into)
} else if length < 1 { } else if length < 1 {
return nil, participle.Errorf ( return nil, errors.Errorf (
literal.Pos, literal.Position,
"string literal must have data when " + "string literal must have data when " +
"assigning to %v", "assigning to %v",
into) into)
@ -108,8 +108,8 @@ func (this *Tree) analyzeLiteralString (
length = len(literal.ValueUTF8) length = len(literal.ValueUTF8)
} }
if length > base.Length { if length > base.Length {
return nil, participle.Errorf ( return nil, errors.Errorf (
literal.Pos, literal.Position,
"cannot fit string literal of length " + "cannot fit string literal of length " +
"%v into array of length %v", "%v into array of length %v",
length, base.Length) length, base.Length)
@ -160,8 +160,8 @@ func (this *Tree) analyzeLiteralArray (
case *entity.TypeArray: case *entity.TypeArray:
base := base.(*entity.TypeArray) base := base.(*entity.TypeArray)
if base.Length < len(literal.Elements) { if base.Length < len(literal.Elements) {
return nil, participle.Errorf ( return nil, errors.Errorf (
literal.Pos, "expected %v elements or less", literal.Position, "expected %v elements or less",
base.Length) base.Length)
} }
elementType = base.Element elementType = base.Element
@ -174,8 +174,8 @@ func (this *Tree) analyzeLiteralArray (
base := base.(*entity.TypePointer) base := base.(*entity.TypePointer)
elementType = base.Referenced elementType = base.Referenced
default: default:
return nil, participle.Errorf ( return nil, errors.Errorf (
literal.Pos, "cannot use array literal as %v", literal.Position, "cannot use array literal as %v",
into) into)
} }
@ -199,8 +199,8 @@ func (this *Tree) analyzeLiteralStruct (
) { ) {
base, ok := ReduceToBase(into).(*entity.TypeStruct) base, ok := ReduceToBase(into).(*entity.TypeStruct)
if !ok { if !ok {
return nil, participle.Errorf ( return nil, errors.Errorf (
literal.Pos, "cannot use struct literal as %v", literal.Position, "cannot use struct literal as %v",
into) into)
} }
@ -210,8 +210,8 @@ func (this *Tree) analyzeLiteralStruct (
for name, member := range literal.MemberMap { for name, member := range literal.MemberMap {
correct, ok := base.MemberMap[name] correct, ok := base.MemberMap[name]
if !ok { if !ok {
return nil, participle.Errorf ( return nil, errors.Errorf (
literal.Pos, "no member %v.%s", literal.Position, "no member %v.%s",
into, name) into, name)
} }
@ -233,8 +233,8 @@ func (this *Tree) analyzeLiteralBoolean (
error, error,
) { ) {
if !isBoolean(into) { if !isBoolean(into) {
return nil, participle.Errorf ( return nil, errors.Errorf (
literal.Pos, "cannot use boolean literal as %v", literal.Position, "cannot use boolean literal as %v",
into) into)
} }
@ -264,9 +264,9 @@ func (this *Tree) assembleStructLiteralMap (
literal.MemberOrder = make([]string, len(literal.Members)) literal.MemberOrder = make([]string, len(literal.Members))
for index, member := range literal.Members { for index, member := range literal.Members {
if previous, exists := literal.MemberMap[member.Name]; exists { if previous, exists := literal.MemberMap[member.Name]; exists {
return literal, participle.Errorf ( return literal, errors.Errorf (
member.Pos, "%s already listed in struct literal at %v", member.Position, "%s already listed in struct literal at %v",
member.Name, previous.Pos) member.Name, previous.Position)
} }
literal.MemberMap [member.Name] = member literal.MemberMap [member.Name] = member
literal.MemberOrder[index] = member.Name literal.MemberOrder[index] = member.Name

View File

@ -1,13 +1,12 @@
package analyzer package analyzer
import "fmt" import "fmt"
import "github.com/alecthomas/participle/v2" import "git.tebibyte.media/sashakoshka/fspl/errors"
import "github.com/alecthomas/participle/v2/lexer"
import "git.tebibyte.media/sashakoshka/fspl/entity" import "git.tebibyte.media/sashakoshka/fspl/entity"
// analyzeMethod analyzes a method that is directly owned by the specified type. // analyzeMethod analyzes a method that is directly owned by the specified type.
func (this *Tree) analyzeMethod ( func (this *Tree) analyzeMethod (
pos lexer.Position, pos errors.Position,
typeName string, typeName string,
name string, name string,
) ( ) (
@ -26,7 +25,7 @@ func (this *Tree) analyzeMethod (
// error if method is missing // error if method is missing
method, exists := this.rawMethods[typeName + "." + name] method, exists := this.rawMethods[typeName + "." + name]
if !exists { if !exists {
return nil, participle.Errorf ( return nil, errors.Errorf (
pos, "no method named %s.%s", pos, "no method named %s.%s",
typeName, name) typeName, name)
} }
@ -52,12 +51,12 @@ func (this *Tree) analyzeMethod (
// add owner to method // add owner to method
method.This = &entity.Declaration { method.This = &entity.Declaration {
Pos: method.Pos, Position: method.Position,
Name: "this", Name: "this",
Ty: &entity.TypePointer { Ty: &entity.TypePointer {
Pos: method.Pos, Position: method.Position,
Referenced: &entity.TypeNamed { Referenced: &entity.TypeNamed {
Pos: method.Pos, Position: method.Position,
Name: typeName, Name: typeName,
Type: owner.Type, Type: owner.Type,
}, },
@ -88,7 +87,7 @@ func (this *Tree) methodExists (typeName, name string) bool {
// analyzeMethodOrBehavior returns *entity.Signature if it found an interface // analyzeMethodOrBehavior returns *entity.Signature if it found an interface
// behavior, and *entity.Method if it found a method. // behavior, and *entity.Method if it found a method.
func (this *Tree) analyzeMethodOrBehavior ( func (this *Tree) analyzeMethodOrBehavior (
pos lexer.Position, pos errors.Position,
ty entity.Type, ty entity.Type,
name string, name string,
) ( ) (
@ -99,7 +98,7 @@ func (this *Tree) analyzeMethodOrBehavior (
} }
func (this *Tree) analyzeMethodOrBehaviorInternal ( func (this *Tree) analyzeMethodOrBehaviorInternal (
pos lexer.Position, pos errors.Position,
ty entity.Type, ty entity.Type,
name string, name string,
pierceReference bool, pierceReference bool,
@ -124,7 +123,7 @@ func (this *Tree) analyzeMethodOrBehaviorInternal (
if behavior, ok := ty.BehaviorMap[name]; ok { if behavior, ok := ty.BehaviorMap[name]; ok {
return behavior, nil return behavior, nil
} else { } else {
return nil, participle.Errorf ( return nil, errors.Errorf (
pos, "no behavior or method named %s", pos, "no behavior or method named %s",
name) name)
} }
@ -135,7 +134,7 @@ func (this *Tree) analyzeMethodOrBehaviorInternal (
return this.analyzeMethodOrBehaviorInternal ( return this.analyzeMethodOrBehaviorInternal (
pos, ty.Referenced, name, false) pos, ty.Referenced, name, false)
} else { } else {
return nil, participle.Errorf ( return nil, errors.Errorf (
pos, "no method named %s defined on this type", pos, "no method named %s defined on this type",
name) name)
} }
@ -147,7 +146,7 @@ func (this *Tree) analyzeMethodOrBehaviorInternal (
*entity.TypeFloat, *entity.TypeFloat,
*entity.TypeWord: *entity.TypeWord:
return nil, participle.Errorf ( return nil, errors.Errorf (
pos, "no method named %s defined on this type", pos, "no method named %s defined on this type",
name) name)

View File

@ -1,6 +1,6 @@
package analyzer package analyzer
import "github.com/alecthomas/participle/v2" import "git.tebibyte.media/sashakoshka/fspl/errors"
import "git.tebibyte.media/sashakoshka/fspl/entity" import "git.tebibyte.media/sashakoshka/fspl/entity"
func (this *Tree) assembleSignatureMap (signature *entity.Signature) (*entity.Signature, error) { func (this *Tree) assembleSignatureMap (signature *entity.Signature) (*entity.Signature, error) {
@ -8,9 +8,9 @@ func (this *Tree) assembleSignatureMap (signature *entity.Signature) (*entity.Si
signature.ArgumentOrder = make([]string, len(signature.Arguments)) signature.ArgumentOrder = make([]string, len(signature.Arguments))
for index, member := range signature.Arguments { for index, member := range signature.Arguments {
if previous, exists := signature.ArgumentMap[member.Name]; exists { if previous, exists := signature.ArgumentMap[member.Name]; exists {
return signature, participle.Errorf ( return signature, errors.Errorf (
member.Pos, "%s already listed as argument at %v", member.Position, "%s already listed as argument at %v",
member.Name, previous.Pos) member.Name, previous.Position)
} }
signature.ArgumentMap [member.Name] = member signature.ArgumentMap [member.Name] = member
signature.ArgumentOrder[index] = member.Name signature.ArgumentOrder[index] = member.Name

View File

@ -1,28 +0,0 @@
package analyzer
// import "fmt"
//import "github.com/alecthomas/participle/v2"
import "git.tebibyte.media/sashakoshka/fspl/entity"
func (this *Tree) analyzeAssignment (
assignment *entity.Assignment,
) (
entity.Statement,
error,
) {
// analyze location
location, err := this.analyzeExpression(nil, strict, assignment.Location)
if err != nil { return nil, err }
assignment.Location = location
// ensure location is location expression
err = this.isLocationExpression(location)
if err != nil { return nil, err }
// analyze value
value, err := this.analyzeExpression(location.Type(), strict, assignment.Value)
if err != nil { return nil, err }
assignment.Value = value
return assignment, nil
}

View File

@ -1,7 +1,6 @@
package analyzer package analyzer
import "github.com/alecthomas/participle/v2" import "git.tebibyte.media/sashakoshka/fspl/errors"
import "github.com/alecthomas/participle/v2/lexer"
import "git.tebibyte.media/sashakoshka/fspl/entity" import "git.tebibyte.media/sashakoshka/fspl/entity"
import "git.tebibyte.media/sashakoshka/fspl/parser" import "git.tebibyte.media/sashakoshka/fspl/parser"
@ -36,21 +35,21 @@ func (this *Tree) assembleRawMaps () error {
switch declaration.(type) { switch declaration.(type) {
case *entity.Typedef: case *entity.Typedef:
ty := declaration.(*entity.Typedef) ty := declaration.(*entity.Typedef)
err := this.topLevelNameAvailable(ty.Pos, ty.Name) err := this.topLevelNameAvailable(ty.Position, ty.Name)
if err != nil { return err } if err != nil { return err }
ty.Methods = make(map[string] *entity.Method) ty.Methods = make(map[string] *entity.Method)
this.rawTypes[ty.Name] = ty this.rawTypes[ty.Name] = ty
case *entity.Function: case *entity.Function:
function := declaration.(*entity.Function) function := declaration.(*entity.Function)
err := this.topLevelNameAvailable ( err := this.topLevelNameAvailable (
function.Pos, function.Position,
function.Signature.Name) function.Signature.Name)
if err != nil { return err } if err != nil { return err }
this.rawFunctions[function.Signature.Name] = function this.rawFunctions[function.Signature.Name] = function
case *entity.Method: case *entity.Method:
method := declaration.(*entity.Method) method := declaration.(*entity.Method)
name := method.TypeName + "." + method.Signature.Name name := method.TypeName + "." + method.Signature.Name
err := this.topLevelNameAvailable(method.Pos, name) err := this.topLevelNameAvailable(method.Position, name)
if err != nil { return err } if err != nil { return err }
this.rawMethods[name] = method this.rawMethods[name] = method
} }
@ -58,48 +57,48 @@ func (this *Tree) assembleRawMaps () error {
return nil return nil
} }
func (this *Tree) topLevelNameAvailable (currentPos lexer.Position, name string) error { func (this *Tree) topLevelNameAvailable (currentPos errors.Position, name string) error {
if _, isPrimitive := primitiveTypes[name]; isPrimitive { if _, isPrimitive := primitiveTypes[name]; isPrimitive {
return participle.Errorf ( return errors.Errorf (
currentPos, "cannot shadow primitive %s", currentPos, "cannot shadow primitive %s",
name) name)
} }
if _, isBuiltin := builtinTypes[name]; isBuiltin { if _, isBuiltin := builtinTypes[name]; isBuiltin {
return participle.Errorf ( return errors.Errorf (
currentPos, "cannot shadow builtin %s", currentPos, "cannot shadow builtin %s",
name) name)
} }
if ty, isType := this.rawTypes[name]; isType { if ty, isType := this.rawTypes[name]; isType {
return participle.Errorf ( return errors.Errorf (
currentPos, "%s already declared at %v", currentPos, "%s already declared at %v",
name, ty.Pos) name, ty.Position)
} }
if function, isFunction := this.rawFunctions[name]; isFunction { if function, isFunction := this.rawFunctions[name]; isFunction {
return participle.Errorf ( return errors.Errorf (
currentPos, "%s already declared at %v", currentPos, "%s already declared at %v",
name, function.Pos) name, function.Position)
} }
if method, isMethod := this.rawMethods[name]; isMethod { if method, isMethod := this.rawMethods[name]; isMethod {
return participle.Errorf ( return errors.Errorf (
currentPos, "%s already declared at %v", currentPos, "%s already declared at %v",
name, method.Pos) name, method.Position)
} }
return nil return nil
} }
func (this *Tree) analyzeDeclarations () error { func (this *Tree) analyzeDeclarations () error {
for name, rawType := range this.rawTypes { for name, rawType := range this.rawTypes {
ty, err := this.analyzeTypedef(rawType.Pos, name, false) ty, err := this.analyzeTypedef(rawType.Position, name, false)
if err != nil { return err } if err != nil { return err }
this.Types[name] = ty this.Types[name] = ty
} }
for name, rawFunction := range this.rawFunctions { for name, rawFunction := range this.rawFunctions {
_, err := this.analyzeFunction(rawFunction.Pos, name) _, err := this.analyzeFunction(rawFunction.Position, name)
if err != nil { return err } if err != nil { return err }
} }
for _, rawMethod := range this.rawMethods { for _, rawMethod := range this.rawMethods {
_, err := this.analyzeMethod ( _, err := this.analyzeMethod (
rawMethod.Pos, rawMethod.Position,
rawMethod.TypeName, rawMethod.TypeName,
rawMethod.Signature.Name) rawMethod.Signature.Name)
if err != nil { return err } if err != nil { return err }
@ -117,9 +116,8 @@ func (this *Tree) ensure () {
this.Functions = make(map[string] *entity.Function) this.Functions = make(map[string] *entity.Function)
for name, ty := range builtinTypes { for name, ty := range builtinTypes {
access := entity.AccessPublic
this.Types[name] = &entity.Typedef { this.Types[name] = &entity.Typedef {
Acc: &access, Acc: entity.AccessPublic,
Name: name, Name: name,
Type: ty, Type: ty,
} }

View File

@ -1,12 +1,11 @@
package analyzer package analyzer
import "fmt" import "fmt"
import "github.com/alecthomas/participle/v2" import "git.tebibyte.media/sashakoshka/fspl/errors"
import "github.com/alecthomas/participle/v2/lexer"
import "git.tebibyte.media/sashakoshka/fspl/entity" import "git.tebibyte.media/sashakoshka/fspl/entity"
func (this *Tree) analyzeTypedef ( func (this *Tree) analyzeTypedef (
pos lexer.Position, pos errors.Position,
name string, name string,
acceptIncomplete bool, acceptIncomplete bool,
) ( ) (
@ -23,7 +22,7 @@ func (this *Tree) analyzeTypedef (
if acceptIncomplete { if acceptIncomplete {
return definition, nil return definition, nil
} else { } else {
return nil, participle.Errorf ( return nil, errors.Errorf (
pos, "type %s cannot be used in this context", pos, "type %s cannot be used in this context",
name) name)
} }
@ -32,7 +31,7 @@ func (this *Tree) analyzeTypedef (
// error if type is missing // error if type is missing
definition, exists := this.rawTypes[name] definition, exists := this.rawTypes[name]
if !exists { if !exists {
return nil, participle.Errorf(pos, "no type named %s", name) return nil, errors.Errorf(pos, "no type named %s", name)
} }
var err error var err error
@ -83,7 +82,7 @@ func (this *Tree) analyzeTypeInternal (
return primitive, nil return primitive, nil
} }
var def *entity.Typedef var def *entity.Typedef
def, err = this.analyzeTypedef(ty.Pos, ty.Name, acceptIncomplete) def, err = this.analyzeTypedef(ty.Position, ty.Name, acceptIncomplete)
if err == nil { if err == nil {
ty.Type = def.Type ty.Type = def.Type
} }
@ -108,8 +107,8 @@ func (this *Tree) analyzeTypeInternal (
ty := ty.(*entity.TypeArray) ty := ty.(*entity.TypeArray)
updateIncompleteInfo() updateIncompleteInfo()
if ty.Length < 1 { if ty.Length < 1 {
return ty, participle.Errorf ( return ty, errors.Errorf (
ty.Pos, "array length must be > 0") ty.Position, "array length must be > 0")
} }
ty.Element, err = this.analyzeType(ty.Element, false) ty.Element, err = this.analyzeType(ty.Element, false)
return ty, err return ty, err
@ -144,8 +143,8 @@ func (this *Tree) analyzeTypeInternal (
ty := ty.(*entity.TypeInt) ty := ty.(*entity.TypeInt)
updateIncompleteInfo() updateIncompleteInfo()
if ty.Width < 1 { if ty.Width < 1 {
return ty, participle.Errorf ( return ty, errors.Errorf (
ty.Pos, "integer width must be > 0") ty.Position, "integer width must be > 0")
} }
return ty, nil return ty, nil
@ -162,9 +161,9 @@ func (this *Tree) assembleStructMap (ty *entity.TypeStruct) (*entity.TypeStruct,
ty.MemberOrder = make([]string, len(ty.Members)) ty.MemberOrder = make([]string, len(ty.Members))
for index, member := range ty.Members { for index, member := range ty.Members {
if previous, exists := ty.MemberMap[member.Name]; exists { if previous, exists := ty.MemberMap[member.Name]; exists {
return ty, participle.Errorf ( return ty, errors.Errorf (
member.Pos, "%s already listed in struct at %v", member.Position, "%s already listed in struct at %v",
member.Name, previous.Pos) member.Name, previous.Position)
} }
ty.MemberMap [member.Name] = member ty.MemberMap [member.Name] = member
ty.MemberOrder[index] = member.Name ty.MemberOrder[index] = member.Name
@ -178,9 +177,9 @@ func (this *Tree) assembleInterfaceMap (ty *entity.TypeInterface) (*entity.TypeI
ty.BehaviorOrder = make([]string, len(ty.Behaviors)) ty.BehaviorOrder = make([]string, len(ty.Behaviors))
for index, method := range ty.Behaviors { for index, method := range ty.Behaviors {
if previous, exists := ty.BehaviorMap[method.Name]; exists { if previous, exists := ty.BehaviorMap[method.Name]; exists {
return ty, participle.Errorf ( return ty, errors.Errorf (
method.Pos, "%s already listed in interface at %v", method.Position, "%s already listed in interface at %v",
method.Name, previous.Pos) method.Name, previous.Position)
} }
ty.BehaviorMap [method.Name] = method ty.BehaviorMap [method.Name] = method
ty.BehaviorOrder[index] = method.Name ty.BehaviorOrder[index] = method.Name