301 lines
11 KiB
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)
|
|
}
|