scpt/ast.go

145 lines
3.6 KiB
Go
Raw Normal View History

2021-03-01 17:11:22 +00:00
package scpt
2021-03-01 17:22:00 +00:00
import (
"errors"
"fmt"
"github.com/alecthomas/participle/lexer"
2021-03-01 17:22:00 +00:00
)
// AST stores the root of the Abstract Syntax Tree for scpt
2021-03-01 17:11:22 +00:00
type AST struct {
Pos lexer.Position
2021-03-01 17:11:22 +00:00
Commands []*Command `@@*`
}
// Execute traverses the AST and executes any commands, it returns an error
// containing the position at which the error was encountered and the error
// itself
func (ast *AST) Execute() error {
2021-03-01 23:01:21 +00:00
// For each command in AST
2021-03-01 17:22:00 +00:00
for _, cmd := range ast.Commands {
2021-03-01 23:01:21 +00:00
// If parsing variables
2021-03-01 17:22:00 +00:00
if cmd.Vars != nil {
2021-03-01 23:01:21 +00:00
// For each variable
2021-03-01 17:22:00 +00:00
for _, Var := range cmd.Vars {
2021-03-01 23:01:21 +00:00
// Parse value of variable
val, err := ParseValue(Var.Value)
if err != nil {
return fmt.Errorf("%s: %s", Var.Value.Pos, err)
}
2021-03-01 23:01:21 +00:00
// If value of variable is a function call
if IsFuncCall(val) {
2021-03-01 23:01:21 +00:00
// Assert type of val as *FuncCall
2021-03-01 17:22:00 +00:00
Call := val.(*FuncCall)
2021-03-01 23:01:21 +00:00
// Set variable value to function return value
Vars[Var.Key], err = CallFunction(Call)
if err != nil {
return fmt.Errorf("%s: %s", Var.Value.Pos, err)
}
2021-03-01 17:22:00 +00:00
} else {
2021-03-01 23:01:21 +00:00
// If value is not a function call, set variable value to parsed value
2021-03-01 17:22:00 +00:00
Vars[Var.Key] = val
}
}
// If parsing function calls
2021-03-01 17:22:00 +00:00
} else if cmd.Calls != nil {
2021-03-01 23:01:21 +00:00
// For each function call
2021-03-01 17:22:00 +00:00
for _, Call := range cmd.Calls {
2021-03-01 23:01:21 +00:00
// Attempt to call function
_, err := CallFunction(Call)
if err != nil {
return fmt.Errorf("%s: %s", Call.Pos, err)
}
2021-03-01 17:22:00 +00:00
}
} else if cmd.Ifs != nil {
for _, If := range cmd.Ifs {
condVal, err := callIfFunc(ParseValue(If.Condition))
if err != nil {
return err
}
condBool, ok := condVal.(bool)
if !ok {
return errors.New("condition must be a boolean")
}
if condBool {
_, err := CallFunction(If.Action)
return err
}
}
2021-03-01 17:22:00 +00:00
}
}
return nil
2021-03-01 17:22:00 +00:00
}
// Command stores any commands encountered while parsing a script
2021-03-01 17:11:22 +00:00
type Command struct {
Pos lexer.Position
2021-03-01 17:11:22 +00:00
Vars []*Var `( @@`
Ifs []*If `| @@`
2021-03-01 17:11:22 +00:00
Calls []*FuncCall `| @@ )`
}
type If struct {
Pos lexer.Position
Condition *Value `"if" @@`
Action *FuncCall `"then" @@`
}
// FuncCall stores any function calls encountered while parsing a script
2021-03-01 17:11:22 +00:00
type FuncCall struct {
Pos lexer.Position
Name string `@Ident @("-" Ident)*`
Args []*Arg `@@*`
2021-03-01 17:11:22 +00:00
}
// Arg stores arguments for function calls
2021-03-01 17:11:22 +00:00
type Arg struct {
Pos lexer.Position
Key string `("with" @Ident)?`
Value *Value `@@`
2021-03-01 17:11:22 +00:00
}
// Var stores any variables encountered while parsing a script
2021-03-01 17:11:22 +00:00
type Var struct {
Pos lexer.Position
Key string `"set" @Ident "to"`
Value *Value `@@`
2021-03-01 17:11:22 +00:00
}
// Value stores any literal values encountered while parsing a script
2021-03-01 17:11:22 +00:00
type Value struct {
Pos lexer.Position
String *string ` @String`
Float *float64 `| @Float`
Integer *int64 `| @Int`
Bool *Bool `| @("true" | "false")`
SubCmd *FuncCall `| "(" @@ ")"`
VarVal *string `| "$" @Ident`
Expr *Expression `| "{" @@ "}"`
2021-03-01 17:11:22 +00:00
}
// Bool stores boolean values encountered while parsing a script.
// It is required for the Capture method
2021-03-01 17:11:22 +00:00
type Bool bool
// Capture parses a boolean literal encountered in the script into
// a Go boolean value
2021-03-01 17:11:22 +00:00
func (b *Bool) Capture(values []string) error {
2021-03-01 23:01:21 +00:00
// Convert string to boolean
2021-03-01 17:11:22 +00:00
*b = values[0] == "true"
return nil
}
// Expression stores any expressions encountered while parsing a
// script for later evaluation
type Expression struct {
Pos lexer.Position
Left *Value `@@`
RightSegs []*ExprRightSeg `@@*`
}
type ExprRightSeg struct {
Op string `@( ">" | ">" "=" | "<" | "<" "=" | "!" "=" | "=" "=" | "+" | "-" | "*" | "/" | "^" | "%")`
Right *Value `@@`
}