fspl/entity/expression.go

413 lines
13 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 {
Type () Type
expression ()
}
// 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
Position errors.Position
Name string
// Semantics
Declaration *Declaration
}
func (*Variable) expression(){}
func (this *Variable) Type () Type { return this.Declaration.Type() }
func (this *Variable) String () string {
return this.Name
}
// 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 {
Position errors.Position
Name string
Ty Type
}
func (*Declaration) expression(){}
func (this *Declaration) Type () Type { return this.Ty }
func (this *Declaration) String () string {
return fmt.Sprint(this.Name, ":", this.Ty)
}
// 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
Position errors.Position
UnitNickname string
Name string
Arguments []Expression
// Semantics
Function *Function
Unit uuid.UUID
}
func (*Call) expression(){}
func (this *Call) Type () Type { return this.Function.Signature.Return }
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 + "]"
}
// 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
Position errors.Position
Source Expression
Name string
Arguments []Expression
// Semantics
Method *Method
Behavior *Signature
Ty Type
}
func (*MethodCall) expression(){}
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 {
out += fmt.Sprint(" ", argument)
}
return out + "]"
}
// 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
Position errors.Position
Slice Expression
Offset Expression
// Semantics
Ty Type
}
func (*Subscript) expression(){}
func (this *Subscript) Type () Type { return this.Ty }
func (this *Subscript) String () string {
return fmt.Sprint("[.", this.Slice, " ", this.Offset, "]")
}
// 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
Position errors.Position
Slice Expression
Start Expression
End Expression
}
func (*Slice) expression(){}
func (this *Slice) Type () Type { return this.Slice.Type() }
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 + "]"
}
// 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
Position errors.Position
Slice Expression
// Semantics
Ty Type
}
func (*Length) expression(){}
func (this *Length) Type () Type { return this.Ty }
func (this *Length) String () string {
return fmt.Sprint("[#", this.Slice, "]")
}
// 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
Position errors.Position
Pointer Expression
// Semantics
Ty Type
}
func (*Dereference) expression(){}
func (this *Dereference) Type () Type { return this.Ty }
func (this *Dereference) String () string {
return fmt.Sprint("[.", this.Pointer, "]")
}
// 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
Position errors.Position
Value Expression
// Semantics
Ty Type
}
func (*Reference) expression(){}
func (this *Reference) Type () Type { return this.Ty }
func (this *Reference) String () string {
return fmt.Sprint("[@", this.Value, "]")
}
// 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 {
Position errors.Position
Ty Type
Value Expression
}
func (*ValueCast) expression(){}
func (this *ValueCast) Type () Type { return this.Ty }
func (this *ValueCast) String () string {
return fmt.Sprint("[~ ", this.Ty, this.Value, "]")
}
// 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 {
Position errors.Position
Ty Type
Value Expression
}
func (*BitCast) expression(){}
func (this *BitCast) Type () Type { return this.Ty }
func (this *BitCast) String () string {
return fmt.Sprint("[~~ ", this.Ty, this.Value, "]")
}
// 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
Position errors.Position
Operator Operator
Arguments []Expression
// Semantics
Ty Type
}
func (*Operation) expression(){}
func (this *Operation) Type () Type { return this.Ty }
func (this *Operation) String () string {
out := fmt.Sprint("[", this.Operator)
for _, argument := range this.Arguments {
out += fmt.Sprint(" ", argument)
}
return out + "]"
}
// 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
Position errors.Position
Steps []Expression
// Semantics
Ty Type
Scope
}
func (*Block) expression(){}
func (this *Block) Type () Type { return this.Ty }
func (this *Block) String () string {
out := "{"
for index, step := range this.Steps {
if index > 0 { out += " " }
out += fmt.Sprint(step)
}
return out + "}"
}
// 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
Position errors.Position
Source Expression
Member string
// Semantics
Ty Type
}
func (*MemberAccess) expression(){}
func (this *MemberAccess) Type () Type { return this.Ty }
func (this *MemberAccess) String () string {
return fmt.Sprint(this.Source, ".", this.Member)
}
// 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
Position errors.Position
Condition Expression
True Expression
False Expression
// Semantics
Ty Type
}
func (*IfElse) expression(){}
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 {
out += fmt.Sprint(" else ", this.False)
}
return out
}
// 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
Position errors.Position
Body Expression
// Semantics
Ty Type
}
func (*Loop) expression(){}
func (this *Loop) Type () Type { return this.Ty }
func (this *Loop) String () string {
return fmt.Sprint("loop ", this.Body)
}
// 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
Position errors.Position
Value Expression
// Semantics
Loop *Loop
}
func (*Break) expression(){}
func (this *Break) Type () Type { return nil }
func (this *Break) String () string {
if this.Value == nil {
return "[break]"
} else {
return fmt.Sprint("[break ", this.Value, "]")
}
}
// 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
Position errors.Position
Value Expression
// Semantics
Declaration TopLevel
}
func (*Return) expression(){}
func (this *Return) Type () Type { return nil }
func (this *Return) String () string {
if this.Value == nil {
return "[return]"
} else {
return fmt.Sprint("[return ", this.Value, "]")
}
}
// 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 {
Position errors.Position
Location Expression
Value Expression
}
func (*Assignment) expression(){}
func (this *Assignment) Type () Type { return nil }
func (this *Assignment) String () string {
return fmt.Sprint(this.Location, "=", this.Value)
}