fspl/entity/expression.go

301 lines
11 KiB
Go

package entity
import "fmt"
import "github.com/alecthomas/participle/v2/lexer"
// Expression is any construct that can be evaluated.
type Expression interface {
expression ()
Statement
}
// Statement is any construct that can be placed inside of a block expression.
type Statement interface {
statement()
}
// 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 {
Pos lexer.Position
Name string `parser:" @Ident "`
}
func (*Variable) expression(){}
func (*Variable) statement(){}
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 {
Pos lexer.Position
Name string `parser:" @Ident "`
Type Type `parser:" ':' @@ "`
}
func (*Declaration) expression(){}
func (*Declaration) statement(){}
func (this *Declaration) String () string {
return fmt.Sprint(this.Name, ":", this.Type)
}
// Call calls upon the function specified by the first argument, and passes the
// rest of that argument to the function. The first argument must be a function
// type, usually the name of a function. 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 {
Pos lexer.Position
Function Expression `parser:" '[' @@ "`
Arguments []Expression `parser:" @@* ']' "`
}
func (*Call) expression(){}
func (*Call) statement(){}
func (this *Call) String () string {
out := fmt.Sprint("[", this.Function)
for _, argument := range this.Arguments {
out += fmt.Sprint(" ", argument)
}
return out + "]"
}
// Array subscripting allows referring to a specific element of an array. It
// accepts any array, and any offset of type Size. It may be assigned to any
// type matching the array'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 {
Pos lexer.Position
Array Expression `parser:" '[' '.' @@ "`
Offset Expression `parser:" @@ ']' "`
}
func (*Subscript) expression(){}
func (*Subscript) statement(){}
func (this *Subscript) String () string {
return fmt.Sprint("[.", this.Array, this.Offset, "]")
}
// 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 {
Pos lexer.Position
Pointer Expression `parser:" '[' '.' @@ ']' "`
}
func (*Dereference) expression(){}
func (*Dereference) statement(){}
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 {
Pos lexer.Position
Value Expression `parser:" '[' '@' @@ ']' "`
}
func (*Reference) expression(){}
func (*Reference) statement(){}
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 {
Pos lexer.Position
Type Type `parser:" '[' 'cast' @@ "`
Value Expression `parser:" @@ ']' "`
}
func (*ValueCast) expression(){}
func (*ValueCast) statement(){}
func (this *ValueCast) String () string {
return fmt.Sprint("[cast ", this.Type, 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 {
Pos lexer.Position
Type Type `parser:" '[' 'bitcast' @@ "`
Value Expression `parser:" @@ ']' "`
}
func (*BitCast) expression(){}
func (*BitCast) statement(){}
func (this *BitCast) String () string {
return fmt.Sprint("[bitcast ", this.Type, 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 {
Pos lexer.Position
// FIXME super janky, need to make a custom lexer at some point
Operator Operator `parser:" '[' @('+''+' | '+' | '-''-' | '-' | '*' | '/' | '%' | '!''!' | '|''|' | '&''&' | '^''^' | '!' | '|' | '&' | '^' | '<''<' | '>''>' | '<' | '>' | '<''=' | '>''=' | '=') "`
Arguments []Expression `parser:" @@+ ']' "`
}
func (*Operation) expression(){}
func (*Operation) statement(){}
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 {
Pos lexer.Position
Steps []Statement `parser:" '{' @@* '}' "`
}
func (*Block) expression(){}
func (*Block) statement(){}
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 {
Pos lexer.Position
Source *Variable `parser:" @@ "`
Member string `parser:" '.' @Ident "`
}
func (*MemberAccess) expression(){}
func (*MemberAccess) statement(){}
func (this *MemberAccess) String () string {
return fmt.Sprint(this.Source, ".", this.Member)
}
// Method access allows referring to a specific method of a type, or a behavior
// of an interface. It can only be assigned to the first argument of a call. A
// method access is never a valid location expression.
type MethodAccess struct {
Pos lexer.Position
Source *Variable `parser:" @@ "`
Member string `parser:" ':' ':' @Ident "`
}
func (*MethodAccess) expression(){}
func (*MethodAccess) statement(){}
func (this *MethodAccess) 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 {
Pos lexer.Position
Condition Expression `parser:" 'if' @@ "`
True Expression `parser:" 'then' @@ "`
False Expression `parser:" ('else' @@)? "`
}
func (*IfElse) expression(){}
func (*IfElse) statement(){}
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 {
Pos lexer.Position
Body Expression `parser:" 'loop' @@ "`
}
func (*Loop) expression(){}
func (*Loop) statement(){}
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 {
Pos lexer.Position
Value Expression `parser:" '[' 'break' @@? ']' "`
}
func (*Break) expression(){}
func (*Break) statement(){}
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 {
Pos lexer.Position
Value Expression `parser:" '[' 'return' @@? ']' "`
}
func (*Return) expression(){}
func (*Return) statement(){}
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 {
Pos lexer.Position
Location Expression `parser:" @@ "`
Value Expression `parser:" '=' @@ "`
}
func (*Assignment) statement(){}
func (this *Assignment) String () string {
return fmt.Sprint(this.Location, "=", this.Value)
}