Merge pull request 'remove-particple-refit-analyzer' (#8) from remove-particple-refit-analyzer into remove-participle

Reviewed-on: sashakoshka/fspl#8
This commit is contained in:
Sasha Koshka 2024-02-08 18:08:31 +00:00
commit 16317a9eb8
23 changed files with 298 additions and 309 deletions

View File

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

View File

@ -27,7 +27,7 @@ testStringErr (test,
"cannot use struct literal as Int", 3, 10,
`
[main] = {
x:Int = (x: 5)
x:Int = (. x: 5)
}
`)
}
@ -64,7 +64,7 @@ testStringErr (test,
func TestAssignmentLiteralErrArrayWrongLength (test *testing.T) {
testStringErr (test,
"expected 3 elements", 3, 12,
"expected 3 elements or less", 3, 12,
`
[main] = {
x:3:Int = (* 1 2 3 4)
@ -94,19 +94,19 @@ testStringErr (test,
func TestAssignmentLiteralErrStructWrongType (test *testing.T) {
testStringErr (test,
"cannot use float literal as Int", 3, 24,
"cannot use float literal as Int", 3, 28,
`
[main] = {
x:(x:Int y:Int) = (x: 5.5 y: 3)
x:(. x:Int y:Int) = (. x: 5.5 y: 3)
}
`)
}
func TestAssignmentLiteralErrStructInteger (test *testing.T) {
testStringErr (test,
"cannot use integer literal as (x:Int y:Int)", 3, 20,
"cannot use integer literal as (. x:Int y:Int)", 3, 22,
`
[main] = {
x:(x:Int y:Int) = 5
x:(. x:Int y:Int) = 5
}
`)
}
@ -115,7 +115,7 @@ func TestAssignmentLiteralErrInterfaceInt (test *testing.T) {
testStringErr (test,
"cannot use integer literal as Bird", 4, 11,
`
Bird: ([fly distance:F64] [land])
Bird: (~ [fly distance:F64] [land])
[main] = {
b:Bird = 5
}
@ -126,7 +126,7 @@ func TestAssignmentLiteralErrInterfaceFloat (test *testing.T) {
testStringErr (test,
"cannot use float literal as Bird", 4, 11,
`
Bird: ([fly distance:F64] [land])
Bird: (~ [fly distance:F64] [land])
[main] = {
b:Bird = 5.5
}
@ -137,7 +137,7 @@ func TestAssignmentLiteralErrInterfaceArray (test *testing.T) {
testStringErr (test,
"cannot use array literal as Bird", 4, 11,
`
Bird: ([fly distance:F64] [land])
Bird: (~ [fly distance:F64] [land])
[main] = {
b:Bird = (* 1 2 3 4)
}
@ -148,9 +148,9 @@ func TestAssignmentLiteralErrInterfaceStruct (test *testing.T) {
testStringErr (test,
"cannot use struct literal as Bird", 4, 11,
`
Bird: ([fly distance:F64] [land])
Bird: (~ [fly distance:F64] [land])
[main] = {
b:Bird = (x: 5 y: 6)
b:Bird = (. x: 5 y: 6)
}
`)
}
@ -183,7 +183,7 @@ testString (test,
(* 5 6)
(* 7 8))
slice:*:Int = (* 3 1 2 3)
struct:(x:Int y:Int) = (
struct:(. x:Int y:Int) = (.
x: 9
y: 10)
@ -197,7 +197,7 @@ func TestAssignmentInterfaceErrBadSignature (test *testing.T) {
testStringErr (test,
"BlueJay has wrong signature for method fly", 7, 11,
`
Bird: ([fly distance:F64] [land])
Bird: (~ [fly distance:F64] [land])
BlueJay: Int
BlueJay.[fly] = { }
BlueJay.[land] = { }
@ -211,7 +211,7 @@ func TestAssignmentInterfaceErrMissingMethod (test *testing.T) {
testStringErr (test,
"no method named fly defined on this type", 6, 11,
`
Bird: ([fly distance:F64] [land])
Bird: (~ [fly distance:F64] [land])
BlueJay: Int
BlueJay.[land] = { }
[main] = {
@ -224,7 +224,7 @@ func TestAssignmentInterfaceErrBadLayer (test *testing.T) {
testStringErr (test,
"no method named fly defined on this type", 7, 11,
`
Bird: ([fly distance:F64])
Bird: (~ [fly distance:F64])
BlueJay: Int
BlueJay.[fly distance:F64] = { }
BlueJayRef: *BlueJay
@ -237,7 +237,7 @@ BlueJayRef: *BlueJay
func TestAssignmentInterface (test *testing.T) {
testString (test,
`
Bird: ([fly distance:F64] [land])
Bird: (~ [fly distance:F64] [land])
BlueJay: Int
BlueJay.[fly distance:F64] = { }
BlueJay.[land] = { }
@ -267,15 +267,15 @@ testString (test,
[f]:Byte = 5
A:Int
A.[g]:Int = 2
B:([g]:Int)
B:(~ [g]:Int)
[main] = {
a:Int
b:Int = a
c:Int = d:Int
d = { a:F64 b }
e:Byte = [f]
g:(x:Int y:(w:F64 z:F64)) = (x: 1 y: (w: 1.2 z: 78.5))
gx:(w:F64 z:F64) = g.y
g:(. x:Int y:(. w:F64 z:F64)) = (. x: 1 y: (. w: 1.2 z: 78.5))
gx:(. w:F64 z:F64) = g.y
h:F64 = gx.z
i:A
j:Int = i.[g]

View File

@ -15,9 +15,9 @@ testStringErr (test,
func TestCastErrIntStruct (test *testing.T) {
testStringErr (test,
"cannot convert from (x:Int y:Int) to Int", 2, 21,
"cannot convert from (. x:Int y:Int) to Int", 2, 21,
`
[main]:Int = [~ Int a:(x:Int y:Int)]
[main]:Int = [~ Int a:(. x:Int y:Int)]
`)
}
@ -47,9 +47,9 @@ testStringErr (test,
func TestCastErrStructInt (test *testing.T) {
testStringErr (test,
"cannot convert from Int to (x:Int y:Int)", 2, 41,
"cannot convert from Int to (. x:Int y:Int)", 2, 45,
`
[main]:(x:Int y:Int) = [~ (x:Int y:Int) a:Int]
[main]:(. x:Int y:Int) = [~ (. x:Int y:Int) a:Int]
`)
}
@ -72,7 +72,7 @@ testStringErr (test,
func TestCast (test *testing.T) {
testString (test,
`
Bird: ([fly distance:F64] [land])
Bird: (~ [fly distance:F64] [land])
BlueJay: Int
BlueJay.[fly distance:F64] = { }
BlueJay.[land] = { }
@ -81,8 +81,8 @@ IntDerived: Int
a:IntDerived = 5
b:Int [~ Int [~ F64 [~ Byte a]]]
c:Int [~~ Int [~~ F64 [~~ Byte a]]]
d:(x:Int y:Int) = (x: 1 y: 2)
e:(z:Int a:Int) = [~~ (z:Int a:Int) d]
d:(. x:Int y:Int) = (. x: 1 y: 2)
e:(. z:Int a:Int) = [~~ (. z:Int a:Int) d]
f:Bird = [~~ BlueJay 0]
g:String = 'hello'
h:*:Byte = [~ *:Byte g]

View File

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

View File

@ -1,11 +1,10 @@
package analyzer
import "github.com/alecthomas/participle/v2"
import "github.com/alecthomas/participle/v2/lexer"
import "git.tebibyte.media/sashakoshka/fspl/errors"
import "git.tebibyte.media/sashakoshka/fspl/entity"
func (this *Tree) analyzeFunction (
pos lexer.Position,
pos errors.Position,
name string,
) (
*entity.Function,
@ -21,7 +20,7 @@ func (this *Tree) analyzeFunction (
// error if function is missing
function, exists := this.rawFunctions[name]
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

View File

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

View File

@ -6,9 +6,9 @@ func TestLiteralStructMemberUniqueErr (test *testing.T) {
testStringErr (test,
"x already listed in struct literal at stream0.fspl:5:3", 7, 3,
`
Point: (x:Int y:Int z:Int)
Point: (. x:Int y:Int z:Int)
[main] = {
point:Point = (
point:Point = (.
x: 5
y: 0
x: 32)
@ -19,9 +19,9 @@ Point: (x:Int y:Int z:Int)
func TestLiteralStructMemberUnique (test *testing.T) {
testString (test,
`
Point: (x:Int y:Int z:Int)
Point: (. x:Int y:Int z:Int)
[main] = {
point:Point = (
point:Point = (.
x: 5
y: 0
z: 32)
@ -33,9 +33,9 @@ func TestLiteralStructNested (test *testing.T) {
testString (test,
`
[main] = {
g:(x:Int y:(w:F64 z:F64)) = (
g:(. x:Int y:(. w:F64 z:F64)) = (.
x: 1
y: (
y: (.
w: 1.2
z: 78.5))
}

View File

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

View File

@ -47,7 +47,7 @@ testString (test,
`
Number: Int
Number.[add x:Number]:Number = [++[.this] x]
StringHolder: (string:String)
StringHolder: (. string:String)
StringHolder.[setString string:String] = {
this.string = string
}

View File

@ -1,6 +1,6 @@
package analyzer
import "github.com/alecthomas/participle/v2"
import "git.tebibyte.media/sashakoshka/fspl/errors"
import "git.tebibyte.media/sashakoshka/fspl/entity"
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))
for index, member := range signature.Arguments {
if previous, exists := signature.ArgumentMap[member.Name]; exists {
return signature, participle.Errorf (
member.Pos, "%s already listed as argument at %v",
member.Name, previous.Pos)
return signature, errors.Errorf (
member.Position, "%s already listed as argument at %v",
member.Name, previous.Position)
}
signature.ArgumentMap [member.Name] = member
signature.ArgumentOrder[index] = member.Name

View File

@ -4,7 +4,7 @@ import "testing"
func TestMethodNameErrMissing (test *testing.T) {
testStringErr (test,
"no method named world defined on this type", 6, 2,
"no method named world defined on this type", 6, 10,
`
Type: Int
Type.[something] = { }
@ -17,7 +17,7 @@ Type.[something] = { }
func TestMethodNameErrBadLayer (test *testing.T) {
testStringErr (test,
"no method named something defined on this type", 7, 2,
"no method named something defined on this type", 7, 10,
`
Type: Int
Type.[something] = { }
@ -121,7 +121,6 @@ testString (test,
example = 3
}
}
}
`)
}
@ -155,9 +154,9 @@ testString (test,
func TestInterfaceBehaviorNameErrMissing (test *testing.T) {
testStringErr (test,
"no behavior or method named swim", 8, 2,
"no behavior or method named swim", 8, 6,
`
Bird: ([fly] [land])
Bird: (~ [fly] [land])
BirdImpl: Int
BirdImpl.[fly] = { }
BirdImpl.[land] = { }
@ -171,7 +170,7 @@ BirdImpl.[land] = { }
func TestInterfaceBehaviorName (test *testing.T) {
testString (test,
`
Bird: ([fly] [land])
Bird: (~ [fly] [land])
BirdImpl: Int
BirdImpl.[fly] = { }
BirdImpl.[land] = { }
@ -184,9 +183,9 @@ BirdImpl.[land] = { }
func TestStructMemberNameErrMissing (test *testing.T) {
testStringErr (test,
"no member instance.world", 5, 2,
"no member instance.world", 5, 10,
`
Type: (something:Int)
Type: (. something:Int)
[main] = {
instance:Type
instance.world = 5
@ -197,7 +196,7 @@ Type: (something:Int)
func TestStructMemberName (test *testing.T) {
testString (test,
`
Type: (world:Int)
Type: (. world:Int)
[main] = {
instance:Type
instance.world = 5

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

@ -4,7 +4,7 @@ import "io"
import "fmt"
import "testing"
import "strings"
import "github.com/alecthomas/participle/v2"
import "git.tebibyte.media/sashakoshka/fspl/errors"
import "git.tebibyte.media/sashakoshka/fspl/parser"
func testStringErr (
@ -23,8 +23,8 @@ func testStringErr (
func testReaderErr (
test *testing.T,
errMessage string,
errLine int,
errColumn int,
errRow int,
errStart int,
inputs ...io.Reader,
) {
ast := parser.Tree { }
@ -42,20 +42,20 @@ func testReaderErr (
test.Error("analyzer did not return error")
return
}
got := err.(participle.Error)
gotMessage := got.Message()
gotLine := got.Position().Line
gotColumn := got.Position().Column
got := err.(*errors.Error)
gotMessage := got.Message
gotRow := got.Position.Row + 1
gotStart := got.Position.Start + 1
correct :=
gotMessage == errMessage &&
gotLine == errLine &&
gotColumn == errColumn
gotRow == errRow &&
gotStart == errStart
if !correct {
test.Log("errors do not match")
test.Logf("got:\n%v:%v: %v", gotLine, gotColumn, gotMessage)
test.Logf("correct:\n%v:%v: %v", errLine, errColumn, errMessage)
test.Log("got:\n" + got.Format())
test.Logf("correct:\n%v:%v: %v", errRow, errStart, errMessage)
test.Fail()
return
}
}

View File

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

View File

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

View File

@ -7,7 +7,7 @@ testStringErr (test,
"Hello already declared at stream0.fspl:2:1", 3, 1,
`
Hello: *Int
Hello: (x:Int y:Int)
Hello: (. x:Int y:Int)
`)
}
@ -31,7 +31,7 @@ func TestTypedefUnique (test *testing.T) {
testString (test,
`
Hello: *Int
World: (x:Int y:Int)
World: (. x:Int y:Int)
`)
}
@ -39,7 +39,7 @@ func TestTypedefRecursiveErr (test *testing.T) {
testStringErr (test,
"type List cannot be used in this context", 4, 9,
`
List: (
List: (.
value: Int
next: List)
`)
@ -48,7 +48,7 @@ List: (
func TestTypedefRecursive (test *testing.T) {
testString (test,
`
List: (
List: (.
value: Int
next: *List)
`)
@ -66,7 +66,7 @@ func TestTypeNamed (test *testing.T) {
testString (test,
`
Example: Int
AllBuiltin: (
AllBuiltin: (.
int: Int
uint: UInt
byte: Byte
@ -129,7 +129,7 @@ func TestTypeStructMemberUniqueErr (test *testing.T) {
testStringErr (test,
"x already listed in struct at stream0.fspl:3:2", 6, 2,
`
Bird: (
Bird: (.
x:Int
y:Int
z:Int
@ -140,7 +140,7 @@ Bird: (
func TestTypeStructMemberUnique (test *testing.T) {
testString (test,
`
Bird: (
Bird: (.
x:Int
y:Int
z:Int
@ -150,15 +150,15 @@ Bird: (
func TestTypeInterfaceBehaviorUniqueErr (test *testing.T) {
testStringErr (test,
"fly already listed in interface at stream0.fspl:2:8", 2, 14,
"fly already listed in interface at stream0.fspl:2:10", 2, 16,
`
Bird: ([fly] [fly])
Bird: (~ [fly] [fly])
`)
}
func TestTypeInterfaceBehaviorUnique (test *testing.T) {
testString (test,
`
Bird: ([fly] [land])
Bird: (~ [fly] [land])
`)
}

View File

@ -130,7 +130,7 @@ type TypeInterface struct {
}
func (*TypeInterface) ty(){}
func (this *TypeInterface) String () string {
out := "(?"
out := "(~"
for _, behavior := range this.Behaviors {
out += fmt.Sprint(" ", behavior)
}

View File

@ -25,6 +25,8 @@ import "git.tebibyte.media/sashakoshka/fspl/entity"
// | |
// | | +Dot +Expression =Dereference
// | | | +Expression =Subscript
// | | +Star =Operation
// | |
// | | +Symbol X
// | | '\' =Slice
// | | '#' =Length
@ -154,7 +156,6 @@ func (this *Parser) parseExpressionRootIdent () (entity.Expression, error) {
case "if": return this.parseIfElse()
case "loop": return this.parseLoop()
default:
println(this.token.String())
this.next()
switch this.kind() {
case lexer.Colon:
@ -196,11 +197,12 @@ func (this *Parser) parseExpressionRootLBracket () (entity.Expression, error) {
if err != nil { return nil, err }
pos := this.pos()
err = this.expectNext(lexer.Ident, lexer.Dot, lexer.Symbol)
err = this.expectNext(lexer.Ident, lexer.Dot, lexer.Star, lexer.Symbol)
if err != nil { return nil, err }
switch this.kind() {
case lexer.Ident: return this.parseExpressionRootLBracketIdent(pos)
case lexer.Dot: return this.parseDereferenceOrSubscriptCore(pos)
case lexer.Star: return this.parseOperationCore(pos)
case lexer.Symbol: return this.parseExpressionRootLBracketSymbol(pos)
}
panic(this.bug())
@ -487,8 +489,18 @@ var valuesOperator = []string {
}
func (this *Parser) parseOperationCore (pos errors.Position) (*entity.Operation, error) {
err := this.expectValue(lexer.Symbol, valuesOperator...)
if err != nil { return nil, err }
// TODO: maybe come up with a more elegant way of writing this?
// possibly make a verion of expectValue that matches a list of kinds
// and a list of values. could also make a version that accepts any kind
// of token, and always supplement that with a normal expect.
if this.token.Is(lexer.Star) {
err := this.expectValue(lexer.Star, valuesOperator...)
if err != nil { return nil, err }
} else {
err := this.expectValue(lexer.Symbol, valuesOperator...)
if err != nil { return nil, err }
}
operation := &entity.Operation {
Position: pos,
Operator: entity.OperatorFromString(this.value()),
@ -496,7 +508,7 @@ func (this *Parser) parseOperationCore (pos errors.Position) (*entity.Operation,
this.next()
for {
err = this.expectDesc (
err := this.expectDesc (
"expression or operation end",
appendCopy(startTokensExpression, lexer.RBracket)...)
if err != nil { return nil, err }

View File

@ -10,12 +10,12 @@ var startTokensSignature = []lexer.TokenKind { lexer.LBracket }
func (this *Parser) parseSignature () (*entity.Signature, error) {
err := this.expectDesc(descriptionSignature, startTokensSignature...)
if err != nil { return nil, err }
pos := this.pos()
err = this.expectNext(lexer.Ident)
if err != nil { return nil, err }
signature := &entity.Signature{
Position: this.pos(),
Position: pos,
Name: this.value(),
}
this.next()

View File

@ -7,7 +7,7 @@ testString (test,
// correct
`- BasicInt: Int
- Structure: (. x:Int y:Int)
- Interface: (? [aMethod x:Int]:*U8 [otherMethod arg:5:Int]:*U8)
- Interface: (~ [aMethod x:Int]:*U8 [otherMethod arg:5:Int]:*U8)
- Array: 16:16:16:Int
- StructArray: 31:24:340920:(. x:Int y:Int)
- String: *:U8`,
@ -17,7 +17,7 @@ BasicInt: Int
Structure: (.
x:Int
y:Int)
Interface: (?
Interface: (~
[aMethod x:Int]:*U8
[otherMethod arg:5:Int]:*U8)
Array: 16:16:16:Int

View File

@ -1,6 +1,7 @@
package parser
import "git.tebibyte.media/sashakoshka/fspl/lexer"
import "git.tebibyte.media/sashakoshka/fspl/errors"
import "git.tebibyte.media/sashakoshka/fspl/entity"
var descriptionTopLevel = "typedef, function, or method"
@ -17,6 +18,7 @@ func (this *Parser) parseTopLevel () error {
startTokensTopLevel...)
if err != nil { return err }
if this.token.EOF() { return nil }
pos := this.pos()
access := entity.AccessPrivate
if this.token.Kind == lexer.Symbol {
@ -33,7 +35,7 @@ func (this *Parser) parseTopLevel () error {
// LBracket: Function
if this.token.Is(lexer.LBracket) {
function, err := this.parseFunctionCore(access)
function, err := this.parseFunctionCore(pos, access)
if err != nil { return err }
this.tree.AddDeclaration(function)
return nil
@ -49,14 +51,14 @@ func (this *Parser) parseTopLevel () error {
// Dot: Method
err = this.expectNextDesc(descriptionSignature, startTokensSignature...)
if err != nil { return err }
method, err := this.parseMethodCore(access, typeName)
method, err := this.parseMethodCore(pos, access, typeName)
if err != nil { return err }
this.tree.AddDeclaration(method)
case lexer.Colon:
// Colon: Typedef
err = this.expectNextDesc(descriptionType, startTokensType...)
if err != nil { return err }
typedef, err := this.parseTypedefCore(access, typeName)
typedef, err := this.parseTypedefCore(pos, access, typeName)
if err != nil { return err }
this.tree.AddDeclaration(typedef)
default: this.bug()
@ -79,11 +81,12 @@ func (this *Parser) parseAccess () (entity.Access, error) {
}
}
func (this *Parser) parseFunctionCore (access entity.Access) (*entity.Function, error) {
func (this *Parser) parseFunctionCore (pos errors.Position, access entity.Access) (*entity.Function, error) {
signature, err := this.parseSignature()
if err != nil { return nil, err }
function := &entity.Function {
Position: pos.Union(signature.Position),
Acc: access,
Signature: signature,
}
@ -121,10 +124,11 @@ func (this *Parser) parseFunctionCore (access entity.Access) (*entity.Function,
return function, nil
}
func (this *Parser) parseMethodCore (access entity.Access, typeName string) (*entity.Method, error) {
function, err := this.parseFunctionCore(access)
func (this *Parser) parseMethodCore (pos errors.Position, access entity.Access, typeName string) (*entity.Method, error) {
function, err := this.parseFunctionCore(pos, access)
if err != nil { return nil, err }
return &entity.Method {
Position: function.Position,
Acc: function.Acc,
TypeName: typeName,
Signature: function.Signature,
@ -133,12 +137,14 @@ func (this *Parser) parseMethodCore (access entity.Access, typeName string) (*en
}, nil
}
func (this *Parser) parseTypedefCore (access entity.Access, typeName string) (*entity.Typedef, error) {
func (this *Parser) parseTypedefCore (pos errors.Position, access entity.Access, typeName string) (*entity.Typedef, error) {
pos = pos.Union(this.pos())
ty, err := this.parseType()
if err != nil { return nil, err }
return &entity.Typedef {
Acc: access,
Name: typeName,
Type: ty,
Position: pos,
Acc: access,
Name: typeName,
Type: ty,
}, nil
}

View File

@ -57,12 +57,12 @@ func (this *Parser) parseType () (entity.Type, error) {
case lexer.LParen:
err := this.expectNext(lexer.Dot, lexer.Symbol)
if err != nil { return nil, err }
err = this.expectValue(0, ".", "?", "|")
err = this.expectValue(0, ".", "~", "|")
if err != nil { return nil, err }
switch this.value() {
case ".": return this.parseTypeStructCore()
case "?": return this.parseTypeInterfaceCore()
case "~": return this.parseTypeInterfaceCore()
}
panic(this.bug())
}
@ -155,7 +155,7 @@ func (this *Parser) parseTypeStructCore () (entity.Type, error) {
}
func (this *Parser) parseTypeInterfaceCore () (entity.Type, error) {
err := this.expectValue(lexer.Symbol, "?")
err := this.expectValue(lexer.Symbol, "~")
if err != nil { return nil, err }
ty := &entity.TypeInterface {
Position: this.pos(),