scpt/ast.go

159 lines
4.2 KiB
Go
Raw Normal View History

2021-03-02 01:07:35 +00:00
/*
Copyright (c) 2021 Arsen Musayelyan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
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 `@@`
}