Location expressions

This commit is contained in:
Sasha Koshka 2023-10-24 21:54:59 -04:00
parent bec91323b9
commit 2a5ffb12a1
10 changed files with 214 additions and 44 deletions

View File

@ -127,3 +127,77 @@ func (this *Tree) reduceToBase (ty entity.Type) entity.Type {
return ty
}
}
// isLocationExpression returns whether or not an expression is a valid location
// expression.
func (this *Tree) isLocationExpression (expression entity.Expression) error {
switch expression.(type) {
case *entity.Variable:
return nil
case *entity.Declaration:
return nil
case *entity.Call:
return participle.Errorf (
expression.(*entity.Call).Pos,
"cannot assign to function call")
case *entity.MethodCall:
return participle.Errorf (
expression.(*entity.MethodCall).Pos,
"cannot assign to method call")
case *entity.Subscript:
return this.isLocationExpression (
expression.(*entity.Subscript).Slice)
case *entity.Slice:
return participle.Errorf (
expression.(*entity.MethodCall).Pos,
"cannot assign to slice operation")
case *entity.Dereference:
return this.isLocationExpression (
expression.(*entity.Dereference).Pointer)
case *entity.Reference:
return participle.Errorf (
expression.(*entity.Reference).Pos,
"cannot assign to reference operation")
case *entity.ValueCast:
return participle.Errorf (
expression.(*entity.ValueCast).Pos,
"cannot assign to value cast")
case *entity.BitCast:
return participle.Errorf (
expression.(*entity.BitCast).Pos,
"cannot assign to bit cast")
case *entity.Operation:
expression := expression.(*entity.Operation)
return participle.Errorf (
expression.Pos,
"cannot assign to %v operation",
expression.Operator)
case *entity.Block:
return participle.Errorf (
expression.(*entity.Block).Pos,
"cannot assign to block")
case *entity.MemberAccess:
return this.isLocationExpression (
expression.(*entity.MemberAccess).Source)
case *entity.IfElse:
return participle.Errorf (
expression.(*entity.Block).Pos,
"cannot assign to if/else")
case *entity.Loop:
return participle.Errorf (
expression.(*entity.Block).Pos,
"cannot assign to loop")
case *entity.Break:
return participle.Errorf (
expression.(*entity.Block).Pos,
"cannot assign to break statement")
case *entity.Return:
return participle.Errorf (
expression.(*entity.Block).Pos,
"cannot assign to return statement")
default:
panic(fmt.Sprint (
"BUG: analyzer doesnt know about expression ",
expression))
}
}

View File

@ -24,7 +24,7 @@ func (this *Tree) analyzeVariable (
variable.Name)
}
err := this.canAssign(variable.Pos, into, mode, declaration.Type)
err := this.canAssign(variable.Pos, into, mode, declaration.Type())
if err != nil { return nil, err }
variable.Declaration = declaration
@ -47,11 +47,11 @@ func (this *Tree) analyzeDeclaration (
declaration.Name, existing.Pos)
}
ty, err := this.analyzeType(declaration.Type, false)
declaration.Type = ty
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.Pos, into, mode, declaration.Type())
if err != nil { return nil, err }
this.AddVariable(declaration)
@ -94,7 +94,7 @@ func (this *Tree) analyzeCall (
for index, argument := range call.Arguments {
signature := function.Signature
correct := signature.ArgumentMap[signature.ArgumentOrder[index]]
argument, err := this.analyzeExpression(correct.Type, strict, argument)
argument, err := this.analyzeExpression(correct.Type(), strict, argument)
if err != nil { return nil, err }
call.Arguments[index] = argument
}
@ -115,7 +115,7 @@ func (this *Tree) analyzeMethodCall (
source := sourceExpr.(*entity.Variable)
if err != nil { return nil, err }
method, err := this.analyzeMethodOrBehavior (
call.Pos, source.Declaration.Type, call.Name)
call.Pos, source.Declaration.Type(), call.Name)
// extract signature
var signature *entity.Signature
@ -150,7 +150,7 @@ func (this *Tree) analyzeMethodCall (
// check arg types
for index, argument := range call.Arguments {
correct := signature.ArgumentMap[signature.ArgumentOrder[index]]
argument, err := this.analyzeExpression(correct.Type, strict, argument)
argument, err := this.analyzeExpression(correct.Type(), strict, argument)
if err != nil { return nil, err }
call.Arguments[index] = argument
}
@ -174,6 +174,7 @@ func (this *Tree) analyzeSubscript (
subscript.Slice)
if err != nil { return nil, err }
subscript.Slice = slice
subscript.Ty = into
offset, err := this.analyzeExpression (
builtinType("index"), weak,
@ -192,9 +193,9 @@ func (this *Tree) analyzeSlice (
entity.Expression,
error,
) {
slice, err := this.analyzeExpression(into, weak, slice.Slice)
value, err := this.analyzeExpression(into, weak, slice.Slice)
if err != nil { return nil, err }
slice.Slice = slice
slice.Slice = value
if slice.Start != nil {
start, err := this.analyzeExpression (
@ -225,12 +226,13 @@ func (this *Tree) analyzeDereference (
) {
pointer, err := this.analyzeExpression (
&entity.TypePointer {
Pos: subscript.Pos,
Pos: dereference.Pos,
Referenced: into,
}, weak,
dereference.Pointer)
if err != nil { return nil, err }
dereference.Pointer = pointer
dereference.Ty = into
return pointer, nil
}
@ -243,15 +245,19 @@ func (this *Tree) analyzeReference (
entity.Expression,
error,
) {
reference, ok := into.(*entity.TypePointer)
if !ok { return nil, participle.Errorf(pos, "expected %v", into) }
referenced, ok := into.(*entity.TypePointer)
if !ok {
return nil, participle.Errorf(reference.Pos, "expected %v", into)
}
value, err := this.analyzeExpression (
referenced.Referenced,
reference.Pointer)
referenced.Referenced, weak,
reference.Value)
if err != nil { return nil, err }
err = this.isLocationExpression(reference.Value)
if err != nil { return nil, err }
// TODO error if value is not a location expression
reference.Value = value
reference.Ty = into
return pointer, nil
return reference, nil
}

View File

@ -34,7 +34,7 @@ func (this *Tree) analyzeFunction (
function.Signature, err = this.assembleSignatureMap(function.Signature)
if err != nil { return function, err }
for name, argument := range function.Signature.ArgumentMap {
argument.Type, err = this.analyzeType(argument.Type, false)
argument.Ty, err = this.analyzeType(argument.Ty, false)
this.AddVariable(argument)
function.Signature.ArgumentMap[name] = argument
if err != nil { return function, err }

View File

@ -41,7 +41,7 @@ func (this *Tree) analyzeMethod (
method.Signature, err = this.assembleSignatureMap(method.Signature)
if err != nil { return method, err }
for name, argument := range method.Signature.ArgumentMap {
argument.Type, err = this.analyzeType(argument.Type, false)
argument.Ty, err = this.analyzeType(argument.Ty, false)
this.AddVariable(argument)
method.Signature.ArgumentMap[name] = argument
if err != nil { return method, err }

View File

@ -5,15 +5,24 @@ package analyzer
import "git.tebibyte.media/sashakoshka/fspl/entity"
func (this *Tree) analyzeAssignment (
variable *entity.Assignment,
assignment *entity.Assignment,
) (
entity.Statement,
error,
) {
// TODO
// 1. analyze location
// 2. ensure location is location expression
// 3. analyze value
return nil, nil
}
// 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

@ -123,8 +123,8 @@ func (this *Tree) analyzeTypeInternal (
updateIncompleteInfo()
if err != nil { return ty, err }
for name, member := range ty.MemberMap {
ty.MemberMap[name].Type,
err = this.analyzeType(member.Type, false)
ty.MemberMap[name].Ty,
err = this.analyzeType(member.Ty, false)
if err != nil { return ty, err }
}
return ty, nil
@ -195,7 +195,7 @@ func (this *Tree) analyzeBehavior (behavior *entity.Signature) (*entity.Signatur
behavior, err := this.assembleSignatureMap(behavior)
if err != nil { return behavior, nil }
for name, argument := range behavior.ArgumentMap {
behavior.ArgumentMap[name].Type, err = this.analyzeType(argument.Type, false)
behavior.ArgumentMap[name].Ty, err = this.analyzeType(argument.Ty, false)
if err != nil { return behavior, err }
}
behavior.Return, err = this.analyzeType(behavior.Return, false)

View File

@ -5,8 +5,10 @@ import "github.com/alecthomas/participle/v2/lexer"
// Expression is any construct that can be evaluated.
type Expression interface {
expression ()
Statement
Type () Type
expression ()
}
// Statement is any construct that can be placed inside of a block expression.
@ -28,6 +30,7 @@ type Variable struct {
}
func (*Variable) expression(){}
func (*Variable) statement(){}
func (this *Variable) Type () Type { return this.Declaration.Type() }
func (this *Variable) String () string {
return this.Name
}
@ -40,10 +43,11 @@ func (this *Variable) String () string {
type Declaration struct {
Pos lexer.Position
Name string `parser:" @Ident "`
Type Type `parser:" ':' @@ "`
Ty Type `parser:" ':' @@ "`
}
func (*Declaration) expression(){}
func (*Declaration) statement(){}
func (this *Declaration) Type () Type { return this.Ty }
func (this *Declaration) String () string {
return fmt.Sprint(this.Name, ":", this.Type)
}
@ -56,7 +60,7 @@ func (this *Declaration) String () string {
// expression.
type Call struct {
// Syntax
Pos lexer.Position
Pos lexer.Position
Name string `parser:" '[' @Ident "`
Arguments []Expression `parser:" @@* ']' "`
@ -65,6 +69,7 @@ type Call struct {
}
func (*Call) expression(){}
func (*Call) statement(){}
func (this *Call) Type () Type { return this.Function.Signature.Return }
func (this *Call) String () string {
out := fmt.Sprint("[", this.Name)
for _, argument := range this.Arguments {
@ -81,7 +86,7 @@ func (this *Call) String () string {
// A method call is never a valid location expression.
type MethodCall struct {
// Syntax
Pos lexer.Position
Pos lexer.Position
Source *Variable `parser:" @@ '.' "`
Name string `parser:" '[' @Ident "`
Arguments []Expression `parser:" @@* ']' "`
@ -89,9 +94,17 @@ type MethodCall struct {
// Semantics
Method *Method
Behavior *Signature
Ty Type
}
func (*MethodCall) expression(){}
func (*MethodCall) statement(){}
func (this *MethodCall) Type () Type {
if this.Method != nil {
return this.Method.Signature.Return
} else {
return this.Behavior.Return
}
}
func (this *MethodCall) String () string {
out := fmt.Sprint(this.Source, ".[", this.Name)
for _, argument := range this.Arguments {
@ -106,12 +119,17 @@ func (this *MethodCall) String () string {
// information, it may be directly assigned to an interface. A subscript is a
// valid location expression only if the array being subscripted is.
type Subscript struct {
Pos lexer.Position
Slice Expression `parser:" '[' '.' @@ "`
Offset Expression `parser:" @@ ']' "`
// Syntax
Pos lexer.Position
Slice Expression `parser:" '[' '.' @@ "`
Offset Expression `parser:" @@ ']' "`
// Semantics
Ty Type
}
func (*Subscript) expression(){}
func (*Subscript) statement(){}
func (this *Subscript) Type () Type { return this.Ty }
func (this *Subscript) String () string {
return fmt.Sprint("[.", this.Slice, " ", this.Offset, "]")
}
@ -121,13 +139,15 @@ func (this *Subscript) String () string {
// assignment rules of this expression are equivalent to those of the slice it
// is operating on. A slice is never a valid location expression.
type Slice struct {
Pos lexer.Position
// Syntax
Pos lexer.Position
Slice Expression `parser:" '[' '\\\\' @@ "`
Start Expression `parser:" @@? "`
End Expression `parser:" ':' @@? ']' "`
}
func (*Slice) expression(){}
func (*Slice) statement(){}
func (this *Slice) Type () Type { return this.Slice.Type() }
func (this *Slice) String () string {
out := fmt.Sprint("[\\", this.Slice, " ")
if this.Start != nil {
@ -146,11 +166,16 @@ func (this *Slice) String () string {
// assigned to an interface. A dereference is a valid location expression only
// if the pointer being dereferenced is.
type Dereference struct {
Pos lexer.Position
// Syntax
Pos lexer.Position
Pointer Expression `parser:" '[' '.' @@ ']' "`
// Semantics
Ty Type
}
func (*Dereference) expression(){}
func (*Dereference) statement(){}
func (this *Dereference) Type () Type { return this.Ty }
func (this *Dereference) String () string {
return fmt.Sprint("[.", this.Pointer, "]")
}
@ -163,11 +188,16 @@ func (this *Dereference) String () string {
// automatically references it anyway. A reference is never a valid location
// expression.
type Reference struct {
Pos lexer.Position
// Syntax
Pos lexer.Position
Value Expression `parser:" '[' '@' @@ ']' "`
// Semantics
Ty Type
}
func (*Reference) expression(){}
func (*Reference) statement(){}
func (this *Reference) Type () Type { return this.Ty }
func (this *Reference) String () string {
return fmt.Sprint("[@", this.Value, "]")
}
@ -176,12 +206,13 @@ func (this *Reference) String () string {
// contains inherent type information, it may be directly assigned to an
// interface. A value cast is never a valid location expression.
type ValueCast struct {
Pos lexer.Position
Type Type `parser:" '[' '~' @@ "`
Pos lexer.Position
Ty Type `parser:" '[' '~' @@ "`
Value Expression `parser:" @@ ']' "`
}
func (*ValueCast) expression(){}
func (*ValueCast) statement(){}
func (this *ValueCast) Type () Type { return this.Ty }
func (this *ValueCast) String () string {
return fmt.Sprint("[~ ", this.Type, this.Value, "]")
}
@ -191,12 +222,13 @@ func (this *ValueCast) String () string {
// it may be directly assigned to an interface. A bit cast is never a valid
// location expression.
type BitCast struct {
Pos lexer.Position
Type Type `parser:" '[' '~~' @@ "`
Pos lexer.Position
Ty Type `parser:" '[' '~~' @@ "`
Value Expression `parser:" @@ ']' "`
}
func (*BitCast) expression(){}
func (*BitCast) statement(){}
func (this *BitCast) Type () Type { return this.Ty }
func (this *BitCast) String () string {
return fmt.Sprint("[~~ ", this.Type, this.Value, "]")
}
@ -206,12 +238,17 @@ func (this *BitCast) String () string {
// special cases. Since they contain no inherent type information, they may not
// be assigned to interfaces. An operation is never a valid location expression.
type Operation struct {
// Syntax
Pos lexer.Position
Operator Operator `parser:" '[' @('++' | '+' | '--' | '-' | '*' | '/' | '%' | '!!' | '||' | '&&' | '^^' | '!' | '|' | '&' | '^' | '<<' | '>>' | '<' | '>' | '<=' | '>=' | '=') "`
Arguments []Expression `parser:" @@+ ']' "`
// Semantics
Ty Type
}
func (*Operation) expression(){}
func (*Operation) statement(){}
func (this *Operation) Type () Type { return this.Ty }
func (this *Operation) String () string {
out := fmt.Sprint("[", this.Operator)
for _, argument := range this.Arguments {
@ -231,10 +268,12 @@ type Block struct {
Steps []Statement `parser:" '{' @@* '}' "`
// Semantics
Ty Type
Scope
}
func (*Block) expression(){}
func (*Block) statement(){}
func (this *Block) Type () Type { return this.Ty }
func (this *Block) String () string {
out := "{"
for index, step := range this.Steps {
@ -251,12 +290,17 @@ func (this *Block) String () string {
// an interface. A member access is a valid location expression only when the
// struct being accessed is.
type MemberAccess struct {
// Syntax
Pos lexer.Position
Source *Variable `parser:" @@ "`
Member string `parser:" '.' @Ident "`
// Semantics
Ty Type
}
func (*MemberAccess) expression(){}
func (*MemberAccess) statement(){}
func (this *MemberAccess) Type () Type { return this.Ty }
func (this *MemberAccess) String () string {
return fmt.Sprint(this.Source, ".", this.Member)
}
@ -267,13 +311,18 @@ func (this *MemberAccess) String () string {
// type that satisfies the assignment rules of both the true and false
// expressions. An If/else is never a valid location expression.
type IfElse struct {
// Syntax
Pos lexer.Position
Condition Expression `parser:" 'if' @@ "`
True Expression `parser:" 'then' @@ "`
False Expression `parser:" ('else' @@)? "`
// Semantics
Ty Type
}
func (*IfElse) expression(){}
func (*IfElse) statement(){}
func (this *IfElse) Type () Type { return this.Ty }
func (this *IfElse) String () string {
out := fmt.Sprint("if ", this.Condition, " then ", this.True)
if this.False != nil {
@ -290,11 +339,16 @@ func (this *IfElse) String () string {
// break statements only apply to the closest containing loop. The value of the
// loop's expression is never used. A loop is never a valid location expression.
type Loop struct {
// Syntax
Pos lexer.Position
Body Expression `parser:" 'loop' @@ "`
// Semantics
Ty Type
}
func (*Loop) expression(){}
func (*Loop) statement(){}
func (this *Loop) Type () Type { return this.Ty }
func (this *Loop) String () string {
return fmt.Sprint("loop ", this.Body)
}
@ -307,6 +361,7 @@ type Break struct {
}
func (*Break) expression(){}
func (*Break) statement(){}
func (this *Break) Type () Type { return &TypeVoid { } }
func (this *Break) String () string {
if this.Value == nil {
return "[break]"
@ -325,6 +380,7 @@ type Return struct {
}
func (*Return) expression(){}
func (*Return) statement(){}
func (this *Return) Type () Type { return &TypeVoid { } }
func (this *Return) String () string {
if this.Value == nil {
return "[return]"

View File

@ -8,11 +8,16 @@ import "github.com/alecthomas/participle/v2/lexer"
// interface because it contains no inherent type information. A value cast may
// be used for this purpose.
type LiteralInt struct {
// Syntax
Pos lexer.Position
Value int `parser:" @Int "`
// Semantics
Ty Type
}
func (*LiteralInt) expression(){}
func (*LiteralInt) statement(){}
func (this *LiteralInt) Type () Type { return this.Ty }
func (this *LiteralInt) String () string {
return fmt.Sprint(this.Value)
}
@ -22,11 +27,16 @@ func (this *LiteralInt) String () string {
// because it contains no inherent type information. A value cast may be used
// for this purpose.
type LiteralFloat struct {
// Syntax
Pos lexer.Position
Value float64 `parser:" @Float "`
// Semantics
Ty Type
}
func (*LiteralFloat) expression(){}
func (*LiteralFloat) statement(){}
func (this *LiteralFloat) Type () Type { return this.Ty }
func (this *LiteralFloat) String () string {
return fmt.Sprint(this.Value)
}
@ -39,11 +49,16 @@ func (this *LiteralFloat) String () string {
// It cannot be directly assigned to an interface because it contains no
// inherent type information. A value cast may be used for this purpose.
type LiteralArray struct {
// Syntax
Pos lexer.Position
Elements []Expression `parser:" '(' '*' @@* ')' "`
// Semantics
Ty Type
}
func (*LiteralArray) expression(){}
func (*LiteralArray) statement(){}
func (this *LiteralArray) Type () Type { return this.Ty }
func (this *LiteralArray) String () string {
out := "(*"
for _, element := range this.Elements {
@ -60,11 +75,16 @@ func (this *LiteralArray) String () string {
// It cannot be directly assigned to an interface because it contains no
// inherent type information. A value cast may be used for this purpose.
type LiteralStruct struct {
// Syntax
Pos lexer.Position
Members []Member `parser:" '(' @@* ')' "`
// Semantics
Ty Type
}
func (*LiteralStruct) expression(){}
func (*LiteralStruct) statement(){}
func (this *LiteralStruct) Type () Type { return this.Ty }
func (this *LiteralStruct) String () string {
out := "("
for index, member := range this.Members {
@ -79,11 +99,16 @@ func (this *LiteralStruct) String () string {
// an interface because it contains no inherent type information. A value cast
// may be used for this purpose.
type LiteralBoolean struct {
// Syntax
Pos lexer.Position
Value *Boolean `parser:" @('true' | 'false') "`
// Semantics
Ty Type
}
func (*LiteralBoolean) expression(){}
func (*LiteralBoolean) statement(){}
func (this *LiteralBoolean) Type () Type { return this.Ty }
func (this *LiteralBoolean) String () string {
if *this.Value {
return "true"

View File

@ -38,7 +38,7 @@ func (this *Signature) Equals (ty Type) bool {
real.Name != this.Name { return false }
for index, argument := range this.Arguments {
if !argument.Type.Equals(real.Arguments[index].Type) {
if !argument.Type().Equals(real.Arguments[index].Type()) {
return false
}
}

View File

@ -108,7 +108,7 @@ func (this *TypeStruct) Equals (ty Type) bool {
if !ok || len(real.Members) != len(this.Members) { return false }
for index, member := range this.Members {
if !member.Type.Equals(real.Members[index].Type) {
if !member.Type().Equals(real.Members[index].Type()) {
return false
}
}