fspl/entity/expression.go

625 lines
20 KiB
Go

package entity
import "fmt"
import "github.com/google/uuid"
import "git.tebibyte.media/fspl/fspl/errors"
// Expression is any construct that can be evaluated.
type Expression interface {
fmt.Stringer
// Position returns the position of the expression within its source
// file.
Position () errors.Position
// Type returns the type of the expression. The return value of this is
// only considered valid if the expression has its semantic information
// fields filled.
Type () Type
// HasExplicitType returns true if the expression carries inherent type
// information. If it returns false, the expression must be given a type
// to conform to. The value of this does not depend on semantic
// information fields.
HasExplicitType () bool
expression ()
}
var _ Expression = &Variable { }
// Variable specifies a named variable. It can be assigned to a type matching
// the variable declaration's type. Since it contains inherent type information,
// it may be directly assigned to an interface. A variable is always a valid
// location expression.
type Variable struct {
// Syntax
Pos errors.Position
Name string
// Semantics
Declaration *Declaration
}
func (*Variable) expression(){}
func (this *Variable) Position () errors.Position { return this.Pos }
func (this *Variable) Type () Type { return this.Declaration.Type() }
func (this *Variable) HasExplicitType () bool { return true }
func (this *Variable) String () string {
return this.Name
}
var _ Expression = &Declaration { }
// Declaration binds a local identifier to a typed variable, but also acts as a
// variable expression allowing the variable to be used the moment it is
// defined. Since it contains inherent type information, it may be directly
// assigned to an interface. A declaration is always a valid location
// expression.
type Declaration struct {
Pos errors.Position
Name string
Ty Type
}
func (*Declaration) expression(){}
func (this *Declaration) Position () errors.Position { return this.Pos }
func (this *Declaration) Type () Type { return this.Ty }
func (this *Declaration) HasExplicitType () bool { return true }
func (this *Declaration) String () string {
return fmt.Sprint(this.Name, ":", this.Ty)
}
var _ Expression = &Call { }
// Call calls upon the function specified by the first argument, passing the
// rest of the arguments to that function. The first argument must be a function
// name. The result of a call may be assigned to any type matching the
// function's return type. Since it contains inherent type information, it may
// be directly assigned to an interface. A call is never a valid location
// expression.
type Call struct {
// Syntax
Pos errors.Position
UnitNickname string
Name string
Arguments []Expression
// Semantics
Function *Function
Unit uuid.UUID
}
func (*Call) expression(){}
func (this *Call) Position () errors.Position { return this.Pos }
func (this *Call) Type () Type { return this.Function.Signature.Return }
func (this *Call) HasExplicitType () bool { return true }
func (this *Call) String () string {
out := ""
if this.UnitNickname != "" {
out += fmt.Sprint(this.UnitNickname, "::")
}
out += fmt.Sprint("[", this.Name)
for _, argument := range this.Arguments {
out += fmt.Sprint(" ", argument)
}
return out + "]"
}
var _ Expression = &MethodCall { }
// MethodCall calls upon the method of the variable before the dot that is
// specified by the first argument, passing the rest of the arguments to the
// method. The first argument must be a method name. The result of a call may be
// assigned to any type matching the method's return type. Since it contains
// inherent type information, it may be directly assigned to an interface.
// A method call is never a valid location expression.
type MethodCall struct {
// Syntax
Pos errors.Position
Source Expression
Name string
Arguments []Expression
// Semantics
Method *Method
Behavior *Signature
Ty Type
}
func (*MethodCall) expression(){}
func (this *MethodCall) Position () errors.Position { return this.Pos }
func (this *MethodCall) Type () Type {
if this.Method != nil {
return this.Method.Signature.Return
} else {
return this.Behavior.Return
}
}
func (this *MethodCall) HasExplicitType () bool { return true }
func (this *MethodCall) String () string {
out := fmt.Sprint(this.Source, ".[", this.Name)
for _, argument := range this.Arguments {
out += fmt.Sprint(" ", argument)
}
return out + "]"
}
var _ Expression = &Subscript { }
// Slice subscripting allows referring to a specific element of a slice. It
// accepts any slice, and any offset of type Size. It may be assigned to any
// type matching the slice's element type. Since it contains inherent type
// 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 {
// Syntax
Pos errors.Position
Slice Expression
Offset Expression
// Semantics
Ty Type
}
func (*Subscript) expression(){}
func (this *Subscript) Position () errors.Position { return this.Pos }
func (this *Subscript) Type () Type { return this.Ty }
func (this *Subscript) HasExplicitType () bool { return true }
func (this *Subscript) String () string {
return fmt.Sprint("[.", this.Slice, " ", this.Offset, "]")
}
var _ Expression = &Slice { }
// Slice adjusts the start and end points of a slice relative to its current
// starting index, and returns an adjusted copy pointing to the same data. Any
// 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 {
// Syntax
Pos errors.Position
Slice Expression
Start Expression
End Expression
}
func (*Slice) expression(){}
func (this *Slice) Position () errors.Position { return this.Pos }
func (this *Slice) Type () Type { return this.Slice.Type() }
func (this *Slice) HasExplicitType () bool { return true }
func (this *Slice) String () string {
out := fmt.Sprint("[\\", this.Slice, " ")
if this.Start != nil {
out += fmt.Sprint(this.Start)
}
out += "/" // TODO have trailing decimals pop off of numbers, replace this with ..
if this.End != nil {
out += fmt.Sprint(this.End)
}
return out + "]"
}
var _ Expression = &Length { }
// Length returns the length of an array or a slice. It always returns a value
// of type Index. A length is never a valid location expression.
type Length struct {
// Syntax
Pos errors.Position
Slice Expression
// Semantics
Ty Type
}
func (*Length) expression(){}
func (this *Length) Position () errors.Position { return this.Pos }
func (this *Length) Type () Type { return this.Ty }
func (this *Length) HasExplicitType () bool { return true }
func (this *Length) String () string {
return fmt.Sprint("[#", this.Slice, "]")
}
var _ Expression = &Dereference { }
// Pointer dereferencing allows retrieving the value of a pointer. It accepts
// any pointer. It may be assigned to any type matching the pointer's pointed
// type. Since it contains inherent type information, it may be directly
// assigned to an interface. A dereference is a valid location expression only
// if the pointer being dereferenced is.
type Dereference struct {
// Syntax
Pos errors.Position
Pointer Expression
// Semantics
Ty Type
}
func (*Dereference) expression(){}
func (this *Dereference) Position () errors.Position { return this.Pos }
func (this *Dereference) Type () Type { return this.Ty }
func (this *Dereference) HasExplicitType () bool { return true }
func (this *Dereference) String () string {
return fmt.Sprint("[.", this.Pointer, "]")
}
var _ Expression = &Reference { }
// Value referencing allows retrieving the location of a value in memory. It
// accepts any location expression, and can be assigned to any type that is a
// pointer to the location expression's type. Since it contains inherent type
// information, it can be directly assigned to an interface, although it doesn't
// make a whole lot of sense to do so because assigning a value to an interface
// automatically references it anyway. A reference is never a valid location
// expression.
type Reference struct {
// Syntax
Pos errors.Position
Value Expression
// Semantics
Ty Type
}
func (*Reference) expression(){}
func (this *Reference) Position () errors.Position { return this.Pos }
func (this *Reference) Type () Type { return this.Ty }
func (this *Reference) HasExplicitType () bool { return true }
func (this *Reference) String () string {
return fmt.Sprint("[@", this.Value, "]")
}
var _ Expression = &ValueCast { }
// Vaue casting converts a value of a certain type to another type. Since it
// 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 errors.Position
Ty Type
Value Expression
}
func (*ValueCast) expression(){}
func (this *ValueCast) Position () errors.Position { return this.Pos }
func (this *ValueCast) Type () Type { return this.Ty }
func (this *ValueCast) HasExplicitType () bool { return true }
func (this *ValueCast) String () string {
return fmt.Sprint("[~ ", this.Ty, this.Value, "]")
}
var _ Expression = &BitCast { }
// Bit casting takes the raw data in memory of a certain value and re-interprets
// it as a value of another type. Since it contains inherent type information,
// it may be directly assigned to an interface. A bit cast is never a valid
// location expression.
type BitCast struct {
Pos errors.Position
Ty Type
Value Expression
}
func (*BitCast) expression(){}
func (this *BitCast) Position () errors.Position { return this.Pos }
func (this *BitCast) Type () Type { return this.Ty }
func (this *BitCast) HasExplicitType () bool { return true }
func (this *BitCast) String () string {
return fmt.Sprint("[~~ ", this.Ty, this.Value, "]")
}
var _ Expression = &Operation { }
// Operations perform math, logic, or bit manipulation on values. They accept
// values of the same type as the type they are being assigned to, except in
// 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 errors.Position
Operator Operator
Arguments []Expression
// Semantics
Ty Type
}
func (*Operation) expression(){}
func (this *Operation) Position () errors.Position { return this.Pos }
func (this *Operation) Type () Type { return this.Ty }
func (this *Operation) HasExplicitType () bool {
return this.Operator.ResultsInBoolean()
}
func (this *Operation) String () string {
out := fmt.Sprint("[", this.Operator)
for _, argument := range this.Arguments {
out += fmt.Sprint(" ", argument)
}
return out + "]"
}
var _ Expression = &Block { }
// Block is an ordered collection of expressions that are evaluated
// sequentially. It has its own scope. The last expression in the block
// specifies the block's value, and any assignment rules of the block are
// equivalent to those of its last expression. A block is never a valid location
// expression.
type Block struct {
// Syntax
Pos errors.Position
Steps []Expression
// Semantics
Ty Type
Scope
}
func (*Block) expression(){}
func (this *Block) Position () errors.Position { return this.Pos }
func (this *Block) Type () Type { return this.Ty }
func (this *Block) HasExplicitType () bool {
if len(this.Steps) == 0 { return false }
return this.Steps[len(this.Steps) - 1].HasExplicitType()
}
func (this *Block) String () string {
out := "{"
for index, step := range this.Steps {
if index > 0 { out += " " }
out += fmt.Sprint(step)
}
return out + "}"
}
var _ Expression = &MemberAccess { }
// Member access allows referring to a specific member of a value with a struct
// type. It accepts any struct type that contains the specified member name, and
// may be assigned to any type that matches the type of the selected member.
// Since it contains inherent type information, it may be directly assigned to
// an interface. A member access is a valid location expression only when the
// struct being accessed is.
type MemberAccess struct {
// Syntax
Pos errors.Position
Source Expression
Member string
// Semantics
Ty Type
}
func (*MemberAccess) expression(){}
func (this *MemberAccess) Position () errors.Position { return this.Pos }
func (this *MemberAccess) Type () Type { return this.Ty }
func (this *MemberAccess) HasExplicitType () bool { return true }
func (this *MemberAccess) String () string {
return fmt.Sprint(this.Source, ".", this.Member)
}
var _ Expression = &IfElse { }
// If/else is a control flow branching expression that executes one of two
// expressions depending on a boolean value. If the value of the if/else is
// unused, the else expression need not be specified. It may be assigned to any
// 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 errors.Position
Condition Expression
True Expression
False Expression
// Semantics
Ty Type
}
func (*IfElse) expression(){}
func (this *IfElse) Position () errors.Position { return this.Pos }
func (this *IfElse) Type () Type { return this.Ty }
func (this *IfElse) HasExplicitType () bool {
return this.True.HasExplicitType()
}
func (this *IfElse) String () string {
out := fmt.Sprint("if ", this.Condition, " then ", this.True)
if this.False != nil {
out += fmt.Sprint(" else ", this.False)
}
return out
}
var _ Expression = &Match { }
// Match is a control flow branching expression that executes one of several
// case expressions depending on the input. It can be used to check the type of
// a union value. Each case takes the form of a declaration, and an associated
// expression. If the type of the union matches the type of the declaration in
// the case, the expression is executed and the value of the union is made
// available to it through the declaration. If the value of the match expression
// is used, all possible types in the union must be accounted for. It may be
// assigned to any type that satisfies the assignment rules of its first case.
type Match struct {
// Syntax
Pos errors.Position
Value Expression
Cases []*MatchCase
Default *DefaultCase
// Semantics
Ty Type
CaseOrder []Hash
CaseMap map[Hash] *MatchCase
}
func (*Match) expression(){}
func (this *Match) Position () errors.Position { return this.Pos }
func (this *Match) Type () Type { return this.Ty }
func (this *Match) HasExplicitType () bool {
if len(this.Cases) == 0 {
return true
} else {
return this.Cases[0].HasExplicitType()
}
}
func (this *Match) String () string {
out := fmt.Sprint("match ", this.Value)
for _, cas := range this.Cases {
out += fmt.Sprint(" ", cas)
}
if this.Default != nil {
out += fmt.Sprint(" ", this.Default)
}
return out
}
var _ Expression = &Switch { }
// Switch is a control flow branching expression that executes one of several
// case expressions depending on the value of the input. It accepts any pointer
// or integer type. If the value of the switch expression is used, a default
// case must be present. It may be assigned to any type that satisfies the
// assignment rules of its first case.
type Switch struct {
// Syntax
Pos errors.Position
Value Expression
Cases []*SwitchCase
Default *DefaultCase
// Semantics
Ty Type
CaseOrder []int64
CaseMap map[int64] *SwitchCase
}
func (*Switch) expression(){}
func (this *Switch) Position () errors.Position { return this.Pos }
func (this *Switch) Type () Type { return this.Ty }
func (this *Switch) HasExplicitType () bool {
if len(this.Cases) == 0 {
return true
} else {
return this.Cases[0].HasExplicitType()
}
}
func (this *Switch) String () string {
out := fmt.Sprint("switch ", this.Value)
for _, cas := range this.Cases {
out += fmt.Sprint(" ", cas)
}
if this.Default != nil {
out += fmt.Sprint(" ", this.Default)
}
return out
}
// Breakable is any expression that can be halted using a break.
type Breakable interface {
Expression
breakable()
}
var _ Expression = &Loop { }
var _ Breakable = &Loop { }
// Loop is a control flow expression that repeats an expression until a break
// statement is called from within it. The break statement must be given a value
// if the value of the loop is used. Otherwise, it need not even have a break
// statement. The result of the loop may be assigned to any type that satisfies
// the assignment rules of all of its break statements. Loops may be nested, and
// 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 errors.Position
Body Expression
// Semantics
Ty Type
}
func (*Loop) expression(){}
func (*Loop) breakable(){}
func (this *Loop) Position () errors.Position { return this.Pos }
func (this *Loop) Type () Type { return this.Ty }
func (this *Loop) HasExplicitType () bool {
// this is as accurate as possible without doing a full analysis of the
// loop
return false
}
func (this *Loop) String () string {
return fmt.Sprint("loop ", this.Body)
}
var _ Expression = &For { }
var _ Breakable = &For { }
// For is a special kind of loop that evaluates an expression for each element
// of an array or slice. It accepts an index declaration and an element
// declaration, which are scoped to the loop's body and are set to the index of
// the current element and the element itself respectively at the beginning of
// each iteration. The assignment rules of a for statement are identical to that
// of a normal loop.
type For struct {
// Syntax
Pos errors.Position
Index *Declaration
Element *Declaration
Over Expression
Body Expression
// Semantics
Ty Type
Scope
}
func (*For) expression(){}
func (*For) breakable(){}
func (this *For) Position () errors.Position { return this.Pos }
func (this *For) Type () Type { return this.Ty }
func (this *For) HasExplicitType () bool {
// this is as accurate as possible without doing a full analysis of the
// loop
return false
}
func (this *For) String () string {
out := "for"
if this.Index != nil {
out += " " + this.Index.String()
}
out += fmt.Sprintf(" %v in %v %v", this.Element, this.Over, this.Body)
return out
}
var _ Expression = &Break { }
// Break allows breaking out of loops. It has no value and may not be assigned
// to anything. It is never a valid location expression.
type Break struct {
// Syntax
Pos errors.Position
Value Expression
// Semantics
Loop Breakable
}
func (*Break) expression(){}
func (this *Break) Position () errors.Position { return this.Pos }
func (this *Break) Type () Type { return nil }
func (this *Break) HasExplicitType () bool { return false }
func (this *Break) String () string {
if this.Value == nil {
return "[break]"
} else {
return fmt.Sprint("[break ", this.Value, "]")
}
}
var _ Expression = &Return { }
// Return allows terminating functions before they have reached their end. It
// accepts values that may be assigned to the function's return type. If a
// function does not return anything, the return statement does not accept a
// value. In all cases, return statements have no value and may not be assigned
// to anything. A return statement is never a valid location expression.
type Return struct {
// Syntax
Pos errors.Position
Value Expression
// Semantics
Declaration TopLevel
}
func (*Return) expression(){}
func (this *Return) Position () errors.Position { return this.Pos }
func (this *Return) Type () Type { return nil }
func (this *Return) HasExplicitType () bool { return false }
func (this *Return) String () string {
if this.Value == nil {
return "[return]"
} else {
return fmt.Sprint("[return ", this.Value, "]")
}
}
var _ Expression = &Assignment { }
// Assignment allows assigning the result of one expression to one or more
// location expressions. The assignment statement itself has no value and may
// not be assigned to anything. An assignment statement is never a valid
// location expression.
type Assignment struct {
Pos errors.Position
Location Expression
Value Expression
}
func (*Assignment) expression(){}
func (this *Assignment) Position () errors.Position { return this.Pos }
func (this *Assignment) Type () Type { return nil }
func (this *Assignment) HasExplicitType () bool { return false }
func (this *Assignment) String () string {
return fmt.Sprint(this.Location, "=", this.Value)
}