740 lines
25 KiB
Go
740 lines
25 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
|
|
|
|
// Constant returns true if the expression's value can be computed at
|
|
// compile time.
|
|
IsConstant () bool
|
|
|
|
// Description returns a human-readable description of the expression.
|
|
Description () string
|
|
|
|
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) IsConstant () bool { return false }
|
|
func (this *Variable) Description () string { return "variable" }
|
|
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) IsConstant () bool { return false }
|
|
func (this *Declaration) Description () string { return "declaration" }
|
|
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) IsConstant () bool { return false }
|
|
func (this *Call) Description () string { return "function call" }
|
|
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) IsConstant () bool { return false }
|
|
func (this *MethodCall) Description () string { return "method call" }
|
|
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) IsConstant () bool { return false }
|
|
func (this *Subscript) Description () string { return "subscript operation" }
|
|
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) IsConstant () bool {
|
|
return this.Slice.IsConstant() &&
|
|
this.Start.IsConstant() &&
|
|
this.End.IsConstant()
|
|
}
|
|
func (this *Slice) Description () string { return "slice operation" }
|
|
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) IsConstant () bool { return this.Slice.IsConstant() }
|
|
func (this *Length) Description () string { return "length operation" }
|
|
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) IsConstant () bool { return false }
|
|
func (this *Dereference) Description () string { return "dereference operation" }
|
|
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) IsConstant () bool { return false }
|
|
func (this *Reference) Description () string { return "reference operation" }
|
|
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) IsConstant () bool { return this.Value.IsConstant() }
|
|
func (this *ValueCast) Description () string { return "value cast" }
|
|
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) IsConstant () bool { return this.Value.IsConstant() }
|
|
func (this *BitCast) Description () string { return "bit cast" }
|
|
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) IsConstant () bool {
|
|
for _, argument := range this.Arguments {
|
|
if !argument.IsConstant() { return false }
|
|
}
|
|
return true
|
|
}
|
|
func (this *Operation) Description () string {
|
|
return fmt.Sprintf("%v operation", this.Operator)
|
|
}
|
|
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) IsConstant () bool {
|
|
if len(this.Steps) == 0 { return false }
|
|
return this.Steps[len(this.Steps) - 1].IsConstant()
|
|
}
|
|
func (this *Block) Description () string { return "block" }
|
|
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) IsConstant () bool { return this.Source.IsConstant() }
|
|
func (this *MemberAccess) Description () string { return "member access" }
|
|
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) IsConstant () bool {
|
|
return this.Condition.IsConstant() &&
|
|
this.True.IsConstant() &&
|
|
this.False.IsConstant()
|
|
}
|
|
func (this *IfElse) Description () string { return "if/else" }
|
|
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) IsConstant () bool {
|
|
if !this.Value.IsConstant() { return false }
|
|
if !this.Default.IsConstant() { return false }
|
|
for _, cas := range this.Cases {
|
|
if !cas.IsConstant () { return false }
|
|
}
|
|
return true
|
|
}
|
|
func (this *Match) Description () string { return "match" }
|
|
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) IsConstant () bool {
|
|
if !this.Value.IsConstant() { return false }
|
|
if !this.Default.IsConstant() { return false }
|
|
for _, cas := range this.Cases {
|
|
if !cas.IsConstant () { return false }
|
|
}
|
|
return true
|
|
}
|
|
func (this *Switch) Description () string { return "switch" }
|
|
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) IsConstant () bool { return false /* TODO this may be decidable */ }
|
|
func (this *Loop) Description () string { return "loop" }
|
|
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) IsConstant () bool { return false /* TODO this may be decidable */ }
|
|
func (this *For) Description () string { return "for loop" }
|
|
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) IsConstant () bool { return false }
|
|
func (this *Break) Description () string { return "break" }
|
|
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) IsConstant () bool { return false }
|
|
func (this *Return) Description () string { return "return" }
|
|
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) IsConstant () bool { return false }
|
|
func (this *Assignment) Description () string { return "assignment" }
|
|
func (this *Assignment) String () string {
|
|
return fmt.Sprint(this.Location, "=", this.Value)
|
|
}
|
|
|
|
var _ Expression = &Constant { }
|
|
// Constant access allows accessing an enumerated constant defined as part of a
|
|
// type. It can be assigned to a type matching the constant declaration's type.
|
|
// Since it contains inherent type information, it may be directly assigned to
|
|
// an interface.
|
|
type Constant struct {
|
|
// Syntax
|
|
Pos errors.Position
|
|
UnitNickname string
|
|
TypeName string
|
|
Name string
|
|
|
|
// Semantics
|
|
Ty Type
|
|
Unit uuid.UUID
|
|
Declaration *ConstantDeclaration
|
|
}
|
|
func (*Constant) expression(){}
|
|
func (this *Constant) Position () errors.Position { return this.Pos }
|
|
func (this *Constant) Type () Type { return this.Ty }
|
|
func (this *Constant) HasExplicitType () bool { return true }
|
|
func (this *Constant) IsConstant () bool { return true }
|
|
func (this *Constant) Description () string { return "constant" }
|
|
func (this *Constant) String () string {
|
|
output := ""
|
|
if this.UnitNickname != "" {
|
|
output += fmt.Sprint(this.UnitNickname, "::")
|
|
}
|
|
output += fmt.Sprint(this.TypeName, ".", this.Name)
|
|
return output
|
|
}
|