Implement proper error handling, add godoc comments, and implement expressions
This commit is contained in:
		
							
								
								
									
										77
									
								
								ast.go
									
									
									
									
									
								
							
							
						
						
									
										77
									
								
								ast.go
									
									
									
									
									
								
							@@ -1,66 +1,105 @@
 | 
				
			|||||||
package scpt
 | 
					package scpt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"reflect"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"github.com/alecthomas/participle/lexer"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AST stores the root of the Abstract Syntax Tree for scpt
 | 
				
			||||||
type AST struct {
 | 
					type AST struct {
 | 
				
			||||||
 | 
						Pos      lexer.Position
 | 
				
			||||||
	Commands []*Command `@@*`
 | 
						Commands []*Command `@@*`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ast *AST) Execute() {
 | 
					// 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 {
 | 
				
			||||||
	for _, cmd := range ast.Commands {
 | 
						for _, cmd := range ast.Commands {
 | 
				
			||||||
		if cmd.Vars != nil {
 | 
							if cmd.Vars != nil {
 | 
				
			||||||
			for _, Var := range cmd.Vars {
 | 
								for _, Var := range cmd.Vars {
 | 
				
			||||||
				val := ParseValue(Var.Value)
 | 
									val, err := ParseValue(Var.Value)
 | 
				
			||||||
				if strings.Contains(reflect.TypeOf(val).String(), ".FuncCall") {
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return fmt.Errorf("%s: %s", Var.Value.Pos, err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if IsFuncCall(val) {
 | 
				
			||||||
					Call := val.(*FuncCall)
 | 
										Call := val.(*FuncCall)
 | 
				
			||||||
					Vars[Var.Key], _ = CallFunction(Call)
 | 
										Vars[Var.Key], err = CallFunction(Call)
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											return fmt.Errorf("%s: %s", Var.Value.Pos, err)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					Vars[Var.Key] = val
 | 
										Vars[Var.Key] = val
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else if cmd.Calls != nil {
 | 
							} else if cmd.Calls != nil {
 | 
				
			||||||
			for _, Call := range cmd.Calls {
 | 
								for _, Call := range cmd.Calls {
 | 
				
			||||||
				_, _ = CallFunction(Call)
 | 
									_, err := CallFunction(Call)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return fmt.Errorf("%s: %s", Call.Pos, err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Command stores any commands encountered while parsing a script
 | 
				
			||||||
type Command struct {
 | 
					type Command struct {
 | 
				
			||||||
 | 
						Pos   lexer.Position
 | 
				
			||||||
	Vars  []*Var      `( @@`
 | 
						Vars  []*Var      `( @@`
 | 
				
			||||||
	Calls []*FuncCall `| @@ )`
 | 
						Calls []*FuncCall `| @@ )`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FuncCall stores any function calls encountered while parsing a script
 | 
				
			||||||
type FuncCall struct {
 | 
					type FuncCall struct {
 | 
				
			||||||
	Name string `@Ident`
 | 
						Pos  lexer.Position
 | 
				
			||||||
	Args []*Arg  `@@*`
 | 
						Name string `@Ident @("-" Ident)*`
 | 
				
			||||||
 | 
						Args []*Arg `@@*`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Arg stores arguments for function calls
 | 
				
			||||||
type Arg struct {
 | 
					type Arg struct {
 | 
				
			||||||
	Key string `"with" @Ident`
 | 
						Pos   lexer.Position
 | 
				
			||||||
	Value *Value  `@@`
 | 
						Key   string `("with" @Ident)?`
 | 
				
			||||||
 | 
						Value *Value `@@`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Var stores any variables encountered while parsing a script
 | 
				
			||||||
type Var struct {
 | 
					type Var struct {
 | 
				
			||||||
	Key string `"set" @Ident "to"`
 | 
						Pos   lexer.Position
 | 
				
			||||||
	Value  *Value  `@@`
 | 
						Key   string `"set" @Ident "to"`
 | 
				
			||||||
 | 
						Value *Value `@@`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Value stores any literal values encountered while parsing a script
 | 
				
			||||||
type Value struct {
 | 
					type Value struct {
 | 
				
			||||||
	String  *string  `  @String`
 | 
						Pos     lexer.Position
 | 
				
			||||||
	Float   *float64 `| @Float`
 | 
						String  *string     `  @String`
 | 
				
			||||||
	Integer *int64   `| @Int`
 | 
						Float   *float64    `| @Float`
 | 
				
			||||||
	Bool    *Bool    `| @("true" | "false")`
 | 
						Integer *int64      `| @Int`
 | 
				
			||||||
	SubCmd *FuncCall `| "(" @@ ")"`
 | 
						Bool    *Bool       `| @("true" | "false")`
 | 
				
			||||||
	VarVal *string `| "$" @Ident`
 | 
						SubCmd  *FuncCall   `| "(" @@ ")"`
 | 
				
			||||||
 | 
						VarVal  *string     `| "$" @Ident`
 | 
				
			||||||
 | 
						Expr    *Expression `| "{" @@ "}"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Bool stores boolean values encountered while parsing a script.
 | 
				
			||||||
 | 
					// It is required for the Capture method
 | 
				
			||||||
type Bool bool
 | 
					type Bool bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Capture parses a boolean literal encountered in the script into
 | 
				
			||||||
 | 
					// a Go boolean value
 | 
				
			||||||
func (b *Bool) Capture(values []string) error {
 | 
					func (b *Bool) Capture(values []string) error {
 | 
				
			||||||
	*b = values[0] == "true"
 | 
						*b = values[0] == "true"
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Expression stores any expressions encountered while parsing a
 | 
				
			||||||
 | 
					// script for later evaluation
 | 
				
			||||||
 | 
					type Expression struct {
 | 
				
			||||||
 | 
						Pos   lexer.Position
 | 
				
			||||||
 | 
						Left  *Value `@@`
 | 
				
			||||||
 | 
						Op    string `@( ">" | ">" "=" | "<" | "<" "=" | "!" "=" | "=" "=" | "+" | "-" | "*" | "/" | "^" | "%")`
 | 
				
			||||||
 | 
						Right *Value `@@`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,5 +23,8 @@ func main() {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatalln("Error parsing file:", err)
 | 
							log.Fatalln("Error parsing file:", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ast.Execute()
 | 
						err = ast.Execute()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalln("Error executing script:", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										54
									
								
								defaults.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								defaults.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					package scpt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/gen2brain/dlgs"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func displayDialog(args map[string]interface{}) (interface{}, error) {
 | 
				
			||||||
 | 
						title, ok := args["title"]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return nil, errors.New("title not provided")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						text, ok := args[""]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return nil, errors.New("text not provided")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						switch args["type"] {
 | 
				
			||||||
 | 
						case "yesno":
 | 
				
			||||||
 | 
							return dlgs.Question(fmt.Sprint(title), fmt.Sprint(text), true)
 | 
				
			||||||
 | 
						case "info":
 | 
				
			||||||
 | 
							return dlgs.Info(fmt.Sprint(title), fmt.Sprint(text))
 | 
				
			||||||
 | 
						case "error":
 | 
				
			||||||
 | 
							return dlgs.Error(fmt.Sprint(title), fmt.Sprint(text))
 | 
				
			||||||
 | 
						case "entry":
 | 
				
			||||||
 | 
							defaultText, ok := args["default"]
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								defaultText = ""
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							input, _, err := dlgs.Entry(fmt.Sprint(title), fmt.Sprint(text), fmt.Sprint(defaultText))
 | 
				
			||||||
 | 
							return input, err
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("unknown dialog type: %v", args["type"])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func doShellScript(args map[string]interface{}) (interface{}, error) {
 | 
				
			||||||
 | 
						script, ok := args[""].(string)
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							cmd := exec.Command("sh", "-c", script)
 | 
				
			||||||
 | 
							cmd.Stdout = os.Stdout
 | 
				
			||||||
 | 
							_ = cmd.Run()
 | 
				
			||||||
 | 
							return "", nil
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return nil, errors.New("script not provided")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func print(args map[string]interface{}) (interface{}, error) {
 | 
				
			||||||
 | 
						fmt.Println(args)
 | 
				
			||||||
 | 
						return nil, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								go.mod
									
									
									
									
									
								
							@@ -4,5 +4,8 @@ go 1.16
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/alecthomas/participle v0.7.1
 | 
						github.com/alecthomas/participle v0.7.1
 | 
				
			||||||
 | 
						github.com/antonmedv/expr v1.8.9
 | 
				
			||||||
	github.com/gen2brain/dlgs v0.0.0-20210222160047-2f436553172f
 | 
						github.com/gen2brain/dlgs v0.0.0-20210222160047-2f436553172f
 | 
				
			||||||
 | 
						github.com/gopherjs/gopherjs v0.0.0-20210202160940-bed99a852dfe // indirect
 | 
				
			||||||
 | 
						github.com/rs/zerolog v1.20.0
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										54
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								go.sum
									
									
									
									
									
								
							@@ -1,18 +1,70 @@
 | 
				
			|||||||
 | 
					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 h1:2bN7reTw//5f0cugJcTOnY/NYZcWQOaajW+BwZB5xWs=
 | 
				
			||||||
github.com/alecthomas/participle v0.7.1/go.mod h1:HfdmEuwvr12HXQN44HPWXR0lHmVolVYe4dyL6lQ3duY=
 | 
					github.com/alecthomas/participle v0.7.1/go.mod h1:HfdmEuwvr12HXQN44HPWXR0lHmVolVYe4dyL6lQ3duY=
 | 
				
			||||||
 | 
					github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 h1:GDQdwm/gAcJcLAKQQZGOJ4knlw+7rfEQQcmwTbt4p5E=
 | 
				
			||||||
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
 | 
					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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					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 h1:HPKrg4xeWLWOGAJGVJIYXWhVSdy2kaihuoSy7kBP7S4=
 | 
				
			||||||
github.com/gen2brain/dlgs v0.0.0-20210222160047-2f436553172f/go.mod h1:/eFcjDXaU2THSOOqLxOPETIbHETnamk8FA/hMjhg/gU=
 | 
					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 h1:rcf1P0fm+1l0EjG16p06mYLj9gW9X36KgdHJ/88hS4g=
 | 
				
			||||||
github.com/gopherjs/gopherjs v0.0.0-20210202160940-bed99a852dfe/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 | 
					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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 | 
					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.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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 | 
				
			||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										161
									
								
								scpt.go
									
									
									
									
									
								
							
							
						
						
									
										161
									
								
								scpt.go
									
									
									
									
									
								
							@@ -4,34 +4,42 @@ import (
 | 
				
			|||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/alecthomas/participle"
 | 
						"github.com/alecthomas/participle"
 | 
				
			||||||
	"github.com/gen2brain/dlgs"
 | 
						"github.com/antonmedv/expr"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"os"
 | 
						"reflect"
 | 
				
			||||||
	"os/exec"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Vars stores any variables set during script runtime
 | 
				
			||||||
var Vars = map[string]interface{}{}
 | 
					var Vars = map[string]interface{}{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FuncMap is a map of strings mapped to suitable script functions
 | 
				
			||||||
type FuncMap map[string]func(map[string]interface{}) (interface{}, error)
 | 
					type FuncMap map[string]func(map[string]interface{}) (interface{}, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Funcs stores the functions allowed for use in a script
 | 
				
			||||||
var Funcs = FuncMap{
 | 
					var Funcs = FuncMap{
 | 
				
			||||||
	"displaydialog": displayDialog,
 | 
						"display-dialog":  displayDialog,
 | 
				
			||||||
	"doshellscript": doShellScript,
 | 
						"do-shell-script": doShellScript,
 | 
				
			||||||
 | 
						"print":           print,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddFuncs adds all functions from the provided FuncMap into
 | 
				
			||||||
 | 
					// the Funcs variable
 | 
				
			||||||
func AddFuncs(fnMap FuncMap) {
 | 
					func AddFuncs(fnMap FuncMap) {
 | 
				
			||||||
	for name, fn := range fnMap {
 | 
						for name, fn := range fnMap {
 | 
				
			||||||
		Funcs[name] = fn
 | 
							Funcs[name] = fn
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddVars adds all functions from the provided map into
 | 
				
			||||||
 | 
					// the Vars variable
 | 
				
			||||||
func AddVars(varMap map[string]interface{}) {
 | 
					func AddVars(varMap map[string]interface{}) {
 | 
				
			||||||
	for name, val := range varMap {
 | 
						for name, val := range varMap {
 | 
				
			||||||
		Vars[name] = val
 | 
							Vars[name] = val
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Parse uses participle to parse a script from r into a new AST
 | 
				
			||||||
func Parse(r io.Reader) (*AST, error) {
 | 
					func Parse(r io.Reader) (*AST, error) {
 | 
				
			||||||
	parser, err := participle.Build(&AST{})
 | 
						parser, err := participle.Build(&AST{})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -45,71 +53,102 @@ func Parse(r io.Reader) (*AST, error) {
 | 
				
			|||||||
	return ast, nil
 | 
						return ast, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ParseValue(val *Value) interface{} {
 | 
					// ParseValue parses a Value struct into a go value
 | 
				
			||||||
 | 
					func ParseValue(val *Value) (interface{}, error) {
 | 
				
			||||||
	if val.String != nil {
 | 
						if val.String != nil {
 | 
				
			||||||
		return strings.Trim(*val.String, `"`)
 | 
							return strings.Trim(*val.String, `"`), nil
 | 
				
			||||||
	} else if val.Bool != nil {
 | 
						} else if val.Bool != nil {
 | 
				
			||||||
		return *val.Bool
 | 
							return *val.Bool, nil
 | 
				
			||||||
	} else if val.Float != nil {
 | 
						} else if val.Float != nil {
 | 
				
			||||||
		return *val.Float
 | 
							return *val.Float, nil
 | 
				
			||||||
	} else if val.Integer != nil {
 | 
						} else if val.Integer != nil {
 | 
				
			||||||
		return *val.Integer
 | 
							return *val.Integer, nil
 | 
				
			||||||
	} else if val.SubCmd != nil {
 | 
						} else if val.SubCmd != nil {
 | 
				
			||||||
		return val.SubCmd
 | 
							return val.SubCmd, nil
 | 
				
			||||||
	} else if val.VarVal != nil {
 | 
						} else if val.VarVal != nil {
 | 
				
			||||||
		return Vars[*val.VarVal]
 | 
							return Vars[*val.VarVal], nil
 | 
				
			||||||
	}
 | 
						} else if val.Expr != nil {
 | 
				
			||||||
	return nil
 | 
							left, _ := ParseValue(val.Expr.Left)
 | 
				
			||||||
}
 | 
							if isStr(left) {
 | 
				
			||||||
 | 
								left = requoteStr(left.(string))
 | 
				
			||||||
func UnwrapArgs(args []*Arg) map[string]interface{} {
 | 
					 | 
				
			||||||
	argMap := map[string]interface{}{}
 | 
					 | 
				
			||||||
	for _, arg := range args {
 | 
					 | 
				
			||||||
		argMap[arg.Key] = ParseValue(arg.Value)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return argMap
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func CallFunction(call *FuncCall) (interface{}, error) {
 | 
					 | 
				
			||||||
	argMap := UnwrapArgs(call.Args)
 | 
					 | 
				
			||||||
	return Funcs[call.Name](argMap)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func displayDialog(args map[string]interface{}) (interface{}, error) {
 | 
					 | 
				
			||||||
	title, ok := args["title"]
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return nil, errors.New("title not provided")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	text, ok := args["text"]
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return nil, errors.New("text not provided")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	switch args["type"] {
 | 
					 | 
				
			||||||
	case "yesno":
 | 
					 | 
				
			||||||
		return dlgs.Question(fmt.Sprint(title), fmt.Sprint(text), true)
 | 
					 | 
				
			||||||
	case "info":
 | 
					 | 
				
			||||||
		return dlgs.Info(fmt.Sprint(title), fmt.Sprint(text))
 | 
					 | 
				
			||||||
	case "error":
 | 
					 | 
				
			||||||
		return dlgs.Error(fmt.Sprint(title), fmt.Sprint(text))
 | 
					 | 
				
			||||||
	case "entry":
 | 
					 | 
				
			||||||
		defaultText, ok := args["default"]
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			defaultText = ""
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		input, _, err := dlgs.Entry(fmt.Sprint(title), fmt.Sprint(text), fmt.Sprint(defaultText))
 | 
							right, _ := ParseValue(val.Expr.Right)
 | 
				
			||||||
		return input, err
 | 
							if isStr(right) {
 | 
				
			||||||
 | 
								right = requoteStr(right.(string))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							exp := fmt.Sprintf(
 | 
				
			||||||
 | 
								"%v %s %v",
 | 
				
			||||||
 | 
								left,
 | 
				
			||||||
 | 
								val.Expr.Op,
 | 
				
			||||||
 | 
								right,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							fmt.Println(exp)
 | 
				
			||||||
 | 
							program, err := expr.Compile(strings.ReplaceAll(exp, "^", "**"))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							out, err := expr.Run(program, Vars)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return out, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil, nil
 | 
						return nil, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func doShellScript(args map[string]interface{}) (interface{}, error) {
 | 
					// Add quotes to an unquoted string
 | 
				
			||||||
	script, ok := args["content"].(string)
 | 
					func requoteStr(s string) string {
 | 
				
			||||||
	if ok {
 | 
						return `"` + s + `"`
 | 
				
			||||||
		cmd := exec.Command("sh", "-c", script)
 | 
					}
 | 
				
			||||||
		cmd.Stdout = os.Stdout
 | 
					
 | 
				
			||||||
		_ = cmd.Run()
 | 
					// Check if i is a string
 | 
				
			||||||
		return "", nil
 | 
					func isStr(i interface{}) bool {
 | 
				
			||||||
	} else {
 | 
						if reflect.TypeOf(i).String() == "string" {
 | 
				
			||||||
		return nil, errors.New("script not provided")
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UnwrapArgs takes a slice of Arg structs and returns a map
 | 
				
			||||||
 | 
					// storing the argument name and its value. If the argument has
 | 
				
			||||||
 | 
					// no name, its key will be an empty string
 | 
				
			||||||
 | 
					func UnwrapArgs(args []*Arg) (map[string]interface{}, error) {
 | 
				
			||||||
 | 
						argMap := map[string]interface{}{}
 | 
				
			||||||
 | 
						for _, arg := range args {
 | 
				
			||||||
 | 
							val, err := ParseValue(arg.Value)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if IsFuncCall(val) {
 | 
				
			||||||
 | 
								argMap[arg.Key], err = CallFunction(val.(*FuncCall))
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							argMap[arg.Key] = val
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return argMap, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsFuncCall checks if val is a FuncCall struct
 | 
				
			||||||
 | 
					func IsFuncCall(val interface{}) bool {
 | 
				
			||||||
 | 
						if strings.Contains(reflect.TypeOf(val).String(), ".FuncCall") {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CallFunction executes a given function call in the form of
 | 
				
			||||||
 | 
					// a FuncCall struct
 | 
				
			||||||
 | 
					func CallFunction(call *FuncCall) (interface{}, error) {
 | 
				
			||||||
 | 
						argMap, err := UnwrapArgs(call.Args)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fn, ok := Funcs[call.Name]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return nil, errors.New("no such function: " + call.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return fn(argMap)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								test.scpt
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								test.scpt
									
									
									
									
									
								
							@@ -1,5 +1,6 @@
 | 
				
			|||||||
set y to (displaydialog with text "Hello" with title 12 with type "yesno")
 | 
					set y to (display-dialog "Hello" with title 12 with type "yesno")
 | 
				
			||||||
displaydialog with text "Goodbye" with title 21 with type "error"
 | 
					display-dialog "Goodbye" with title 21 with type "error"
 | 
				
			||||||
doshellscript with content "notify-send Test Notification"
 | 
					do-shell-script "notify-send Test Notification"
 | 
				
			||||||
set g to (displaydialog with text "Test 2" with title 30 with type "entry" with default "text")
 | 
					print (display-dialog {"Test " + "2"} with title 30 with type "entry" with default "text")
 | 
				
			||||||
set x to $g
 | 
					set x to $y
 | 
				
			||||||
 | 
					print { 3+4 }
 | 
				
			||||||
		Reference in New Issue
	
	Block a user