Implement return, implement instant break, add exit function, implement dumping and loading of AST via encoding/json

This commit is contained in:
Arsen Musayelyan 2021-03-07 17:25:16 -08:00
parent f7a34b3da4
commit 1c99345af1
6 changed files with 138 additions and 62 deletions

64
ast.go
View File

@ -15,6 +15,8 @@
package scpt
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/alecthomas/participle/lexer"
@ -22,6 +24,8 @@ import (
var loopRunning bool
var breakLoop bool
var funcRunning bool
var retValue interface{} = nil
// AST stores the root of the Abstract Syntax Tree for scpt
type AST struct {
@ -44,6 +48,30 @@ func (ast *AST) Execute() error {
return nil
}
func (ast *AST) Dump() ([]byte, error) {
return json.Marshal(ast)
}
func (ast *AST) DumpPretty() ([]byte, error) {
buf := bytes.NewBuffer([]byte{})
enc := json.NewEncoder(buf)
enc.SetIndent("", " ")
err := enc.Encode(ast)
if err != nil {
return buf.Bytes(), err
}
return buf.Bytes(), nil
}
func LoadAST(data []byte) (*AST, error) {
var ast AST
err := json.Unmarshal(data, &ast)
if err != nil {
return nil, err
}
return &ast, nil
}
// Execute a variable declaration
func executeVarCmd(Var *Var) error {
// Parse value of variable
@ -124,13 +152,8 @@ func executeRptLoop(rptLoop *RptLoop) error {
// Set loopRunning to true to allow break
loopRunning = true
// Run for loop with correct amount of iterations
rpt:
for i := 0; i < *rptLoop.Times; i++ {
// If breakLoop set to true
if breakLoop {
// Reset breakLoop
breakLoop = false
break
}
// If user requested index variable via "{ var in ... }"
if rptLoop.IndexVar != nil {
// Set requested variable name to index
@ -143,6 +166,12 @@ func executeRptLoop(rptLoop *RptLoop) error {
if err != nil {
return fmt.Errorf("%s: %s", InnerCmd.Pos, err)
}
// If breakLoop set to true
if breakLoop {
// Reset breakLoop
breakLoop = false
break rpt
}
}
}
// Remove index variable if existent
@ -166,13 +195,8 @@ func executeWhlLoop(whlLoop *WhlLoop) error {
return errors.New("condition must be a boolean")
}
// Run for loop if condition is true
whl:
for condBool {
// If breakLoop set to true
if breakLoop {
// Reset breakLoop
breakLoop = false
break
}
// For each inner command
for _, InnerCmd := range whlLoop.InnerCmds {
// Execute command recursively
@ -180,6 +204,12 @@ func executeWhlLoop(whlLoop *WhlLoop) error {
if err != nil {
return fmt.Errorf("%s: %s", InnerCmd.Pos, err)
}
// If breakLoop set to true
if breakLoop {
// Reset breakLoop
breakLoop = false
break whl
}
// Get condition value
condVal, err = callIfFunc(ParseValue(whlLoop.Condition))
if err != nil {
@ -200,6 +230,7 @@ func executeWhlLoop(whlLoop *WhlLoop) error {
func executeFuncDef(def *FuncDef) error {
// Set requested function name in Funcs
Funcs[*def.Name] = func(args map[string]interface{}) (interface{}, error) {
funcRunning = true
// Create new empty map[interface{}]interface{}
argIMap := map[interface{}]interface{}{}
// Convert args map[string]interface{} to map[interface{}]interface{}
@ -215,9 +246,16 @@ func executeFuncDef(def *FuncDef) error {
if err != nil {
return nil, fmt.Errorf("%s: %s", InnerCmd.Pos, err)
}
if retValue != nil {
ret := retValue
retValue = nil
funcRunning = false
return ret, nil
}
}
// Remove args variable from Vars
delete(Vars, "_args")
funcRunning = false
return nil, nil
}
return nil
@ -395,4 +433,4 @@ type WhlLoop struct {
Pos lexer.Position
Condition *Value `"loop" "while" @@ "{"`
InnerCmds []*Command `@@* "}"`
}
}

View File

@ -5,32 +5,65 @@ import (
"flag"
"fmt"
"gitea.arsenm.dev/Arsen6331/scpt"
"io/ioutil"
"log"
"os"
)
func main() {
filePath := flag.String("file", "", "File to parse")
useStdin := flag.Bool("stdin", false, "Parse STDIN")
dumpAST := flag.Bool("dump-ast", false, "Dump the AST as JSON to STDOUT and quit")
loadAST := flag.String("load-ast", "", "Load JSON AST from specified file and execute it")
flag.Parse()
if *filePath == "" {
log.Fatalln("Use --file to specify a file to parse")
var ast *scpt.AST
if *loadAST != "" {
data, err := ioutil.ReadFile(*loadAST)
if err != nil {
log.Fatalln("Error opening specified file:", err)
}
ast, err = scpt.LoadAST(data)
if err != nil {
log.Fatalln("Error loading AST:", err)
}
} else if *useStdin {
var err error
ast, err = scpt.Parse(os.Stdin)
if err != nil {
log.Fatalln("Error parsing STDIN:", err)
}
} else {
if flag.NArg() < 1 {
log.Fatalln("Filepath or --stdin required")
}
filePath := flag.Args()[0]
file, err := os.Open(filePath)
if err != nil {
log.Fatalln("Error opening specified file:", err)
}
ast, err = scpt.Parse(file)
if err != nil {
log.Fatalln("Error parsing file:", err)
}
}
file, err := os.Open(*filePath)
if err != nil {
log.Fatalln("Error opening specified file:", err)
}
ast, err := scpt.Parse(file)
if err != nil {
log.Fatalln("Error parsing file:", err)
if *dumpAST {
data, err := ast.DumpPretty()
if err != nil {
log.Fatalln("Error dumping AST:", err)
}
fmt.Println(string(data))
os.Exit(0)
}
scpt.AddFuncs(scpt.FuncMap{
"print": scptPrint,
"display-dialog": displayDialog,
"do-shell-script": doShellScript,
})
err = ast.Execute()
err := ast.Execute()
if err != nil {
log.Fatalln("Error executing script:", err)
}

View File

@ -17,6 +17,7 @@ package scpt
import (
"errors"
"fmt"
"os"
"strconv"
)
@ -59,6 +60,27 @@ func setBreakLoop(_ map[string]interface{}) (interface{}, error) {
return nil, nil
}
// Default function to set the breakLoop variable to true, breaking any loops that may be running
func setReturn(args map[string]interface{}) (interface{}, error) {
// If a loop is running
if funcRunning {
// Set breakLoop to true, breaking the loop on next cycle
retValue = args[""]
} else {
return nil, errors.New("return not inside function")
}
return nil, nil
}
func scptExit(args map[string]interface{}) (interface{}, error) {
exitCode, ok := args[""].(float64)
if !ok {
return nil, errors.New("exit requires an unnamed number argument")
}
os.Exit(int(exitCode))
return nil, nil
}
// Default function that returns an array with an appended element
func appendArray(args map[string]interface{}) (interface{}, error) {
// Attempt to get unnamed argument and assert as []interface{}

33
go.sum
View File

@ -1,4 +1,3 @@
github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/alecthomas/participle v0.7.1 h1:2bN7reTw//5f0cugJcTOnY/NYZcWQOaajW+BwZB5xWs=
github.com/alecthomas/participle v0.7.1/go.mod h1:HfdmEuwvr12HXQN44HPWXR0lHmVolVYe4dyL6lQ3duY=
@ -6,66 +5,38 @@ github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 h1:GDQdwm/gAcJcLAK
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/antonmedv/expr v1.8.9 h1:O9stiHmHHww9b4ozhPx7T6BK7fXfOCHJ8ybxf0833zw=
github.com/antonmedv/expr v1.8.9/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmHhwGEk8=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.3.0 h1:r35w0JBADPZCVQijYebl6YMWWtHRqVEGt7kL2eBADRM=
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
github.com/gen2brain/dlgs v0.0.0-20210222160047-2f436553172f h1:HPKrg4xeWLWOGAJGVJIYXWhVSdy2kaihuoSy7kBP7S4=
github.com/gen2brain/dlgs v0.0.0-20210222160047-2f436553172f/go.mod h1:/eFcjDXaU2THSOOqLxOPETIbHETnamk8FA/hMjhg/gU=
github.com/gopherjs/gopherjs v0.0.0-20210202160940-bed99a852dfe h1:rcf1P0fm+1l0EjG16p06mYLj9gW9X36KgdHJ/88hS4g=
github.com/gopherjs/gopherjs v0.0.0-20210202160940-bed99a852dfe/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498 h1:4CFNy7/q7P06AsIONZzuWy7jcdqEmYQvOZ9FAFZdbls=
github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84=
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
github.com/sanity-io/litter v1.2.0 h1:DGJO0bxH/+C2EukzOSBmAlxmkhVMGqzvcx/rvySYw9M=
github.com/sanity-io/litter v1.2.0/go.mod h1:JF6pZUFgu2Q0sBZ+HSV35P8TVPI1TTzEwyu9FXAw2W4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74 h1:4cFkmztxtMslUX2SctSl+blCyXfpzhGOy9LhKAqSMA4=
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -39,6 +39,8 @@ var Funcs = FuncMap{
"bool": parseBool,
"break": setBreakLoop,
"append": appendArray,
"exit": scptExit,
"return": setReturn,
}
// AddFuncs adds all functions from the provided FuncMap into
@ -300,10 +302,7 @@ func UnwrapArgs(args []*Arg) (map[string]interface{}, error) {
// IsFuncCall checks if val is a FuncCall struct
func IsFuncCall(val interface{}) bool {
// If type of val is a pointer to FuncCall, return true
if reflect.TypeOf(val) == reflect.TypeOf(&FuncCall{}) {
return true
}
return false
return reflect.TypeOf(val) == reflect.TypeOf(&FuncCall{})
}
// CallFunction executes a given function call in the form of

19
test.scpt Normal file → Executable file
View File

@ -1,3 +1,5 @@
#!/usr/bin/env scpt
set y to (display-dialog "Hello" with title 12 with type "yesno")
display-dialog "Goodbye" with title 21 with type "error"
do-shell-script "notify-send Test Notification"
@ -30,9 +32,9 @@ This is a multiline comment
# This is a single line comment
set hi to ["testovich", 3]
set hi to ["test", 3]
set hi[0] to "testo"
set hi[0] to "test2"
print $hi[0]
@ -55,9 +57,20 @@ loop while $f {
print {"iter: " + (str $c)}
}
repeat 6 times { i in
print {"brktest: " + (str $i)}
if {$i == 3} {
break
}
print {"brktest: " + (str $i) + " (2)"}
}
define hi {
print {"Hello, " + $_args[""]}
return {"Hello, " + $_args[""]}
print "Something isn't right"
}
hi "Function"
hi "World"
set f to (hi "World")
print {"Returned: " + $f}