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 []*Case // Semantics Ty Type CaseOrder []Hash CaseMap map[Hash] *Case } 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) } 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) }