Add comments and remove debug code
This commit is contained in:
parent
5fed3e3ce4
commit
a95d054189
12
ast.go
12
ast.go
@ -15,25 +15,36 @@ type AST struct {
|
|||||||
// containing the position at which the error was encountered and the error
|
// containing the position at which the error was encountered and the error
|
||||||
// itself
|
// itself
|
||||||
func (ast *AST) Execute() error {
|
func (ast *AST) Execute() error {
|
||||||
|
// For each command in AST
|
||||||
for _, cmd := range ast.Commands {
|
for _, cmd := range ast.Commands {
|
||||||
|
// If parsing variables
|
||||||
if cmd.Vars != nil {
|
if cmd.Vars != nil {
|
||||||
|
// For each variable
|
||||||
for _, Var := range cmd.Vars {
|
for _, Var := range cmd.Vars {
|
||||||
|
// Parse value of variable
|
||||||
val, err := ParseValue(Var.Value)
|
val, err := ParseValue(Var.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %s", Var.Value.Pos, err)
|
return fmt.Errorf("%s: %s", Var.Value.Pos, err)
|
||||||
}
|
}
|
||||||
|
// If value of variable is a function call
|
||||||
if IsFuncCall(val) {
|
if IsFuncCall(val) {
|
||||||
|
// Assert type of val as *FuncCall
|
||||||
Call := val.(*FuncCall)
|
Call := val.(*FuncCall)
|
||||||
|
// Set variable value to function return value
|
||||||
Vars[Var.Key], err = CallFunction(Call)
|
Vars[Var.Key], err = CallFunction(Call)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %s", Var.Value.Pos, err)
|
return fmt.Errorf("%s: %s", Var.Value.Pos, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// If value is not a function call, set variable value to parsed value
|
||||||
Vars[Var.Key] = val
|
Vars[Var.Key] = val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// If parsing function calls
|
||||||
} else if cmd.Calls != nil {
|
} else if cmd.Calls != nil {
|
||||||
|
// For each function call
|
||||||
for _, Call := range cmd.Calls {
|
for _, Call := range cmd.Calls {
|
||||||
|
// Attempt to call function
|
||||||
_, err := CallFunction(Call)
|
_, err := CallFunction(Call)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %s", Call.Pos, err)
|
return fmt.Errorf("%s: %s", Call.Pos, err)
|
||||||
@ -91,6 +102,7 @@ type Bool bool
|
|||||||
// Capture parses a boolean literal encountered in the script into
|
// Capture parses a boolean literal encountered in the script into
|
||||||
// a Go boolean value
|
// a Go boolean value
|
||||||
func (b *Bool) Capture(values []string) error {
|
func (b *Bool) Capture(values []string) error {
|
||||||
|
// Convert string to boolean
|
||||||
*b = values[0] == "true"
|
*b = values[0] == "true"
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
25
defaults.go
25
defaults.go
@ -8,47 +8,62 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Default function to display a dialog
|
||||||
func displayDialog(args map[string]interface{}) (interface{}, error) {
|
func displayDialog(args map[string]interface{}) (interface{}, error) {
|
||||||
|
// Get title
|
||||||
title, ok := args["title"]
|
title, ok := args["title"]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("title not provided")
|
return nil, errors.New("title not provided")
|
||||||
}
|
}
|
||||||
|
// Get unnamed argument as text
|
||||||
text, ok := args[""]
|
text, ok := args[""]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("text not provided")
|
return nil, errors.New("text not provided")
|
||||||
}
|
}
|
||||||
|
// Display correct dialog based on given type
|
||||||
switch args["type"] {
|
switch args["type"] {
|
||||||
case "yesno":
|
case "yesno":
|
||||||
|
// Display yes or no dialog, returning bool based on user input
|
||||||
return dlgs.Question(fmt.Sprint(title), fmt.Sprint(text), true)
|
return dlgs.Question(fmt.Sprint(title), fmt.Sprint(text), true)
|
||||||
case "info":
|
case "info":
|
||||||
|
// Display info dialog, returning bool based on success
|
||||||
return dlgs.Info(fmt.Sprint(title), fmt.Sprint(text))
|
return dlgs.Info(fmt.Sprint(title), fmt.Sprint(text))
|
||||||
case "error":
|
case "error":
|
||||||
|
// Display error dialog, returning bool based on success
|
||||||
return dlgs.Error(fmt.Sprint(title), fmt.Sprint(text))
|
return dlgs.Error(fmt.Sprint(title), fmt.Sprint(text))
|
||||||
case "entry":
|
case "entry":
|
||||||
|
// Check if default text given
|
||||||
defaultText, ok := args["default"]
|
defaultText, ok := args["default"]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
// Set to empty if not given
|
||||||
defaultText = ""
|
defaultText = ""
|
||||||
}
|
}
|
||||||
|
// Display entry dialog
|
||||||
input, _, err := dlgs.Entry(fmt.Sprint(title), fmt.Sprint(text), fmt.Sprint(defaultText))
|
input, _, err := dlgs.Entry(fmt.Sprint(title), fmt.Sprint(text), fmt.Sprint(defaultText))
|
||||||
|
// Return user input
|
||||||
return input, err
|
return input, err
|
||||||
default:
|
default:
|
||||||
|
// If type unknown, return error
|
||||||
return nil, fmt.Errorf("unknown dialog type: %v", args["type"])
|
return nil, fmt.Errorf("unknown dialog type: %v", args["type"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default function to run a shell script using `sh -c`
|
||||||
func doShellScript(args map[string]interface{}) (interface{}, error) {
|
func doShellScript(args map[string]interface{}) (interface{}, error) {
|
||||||
|
// Get unnamed argument and assert its type as string
|
||||||
script, ok := args[""].(string)
|
script, ok := args[""].(string)
|
||||||
|
// If assertion successful
|
||||||
if ok {
|
if ok {
|
||||||
|
// Create new exec.Cmd containing `sh -c <script>`
|
||||||
cmd := exec.Command("sh", "-c", script)
|
cmd := exec.Command("sh", "-c", script)
|
||||||
|
// Set command I/O
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
// Run command
|
||||||
_ = cmd.Run()
|
_ = cmd.Run()
|
||||||
return "", nil
|
return "", nil
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("script not provided")
|
return nil, errors.New("script not provided")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func print(args map[string]interface{}) (interface{}, error) {
|
|
||||||
fmt.Println(args)
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
39
scpt.go
39
scpt.go
@ -1,3 +1,5 @@
|
|||||||
|
// Package scpt provides an interpreter for my simple applescript-like
|
||||||
|
// scripting language
|
||||||
package scpt
|
package scpt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -20,12 +22,12 @@ type FuncMap map[string]func(map[string]interface{}) (interface{}, error)
|
|||||||
var Funcs = FuncMap{
|
var Funcs = FuncMap{
|
||||||
"display-dialog": displayDialog,
|
"display-dialog": displayDialog,
|
||||||
"do-shell-script": doShellScript,
|
"do-shell-script": doShellScript,
|
||||||
"print": print,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddFuncs adds all functions from the provided FuncMap into
|
// AddFuncs adds all functions from the provided FuncMap into
|
||||||
// the Funcs variable
|
// the Funcs variable
|
||||||
func AddFuncs(fnMap FuncMap) {
|
func AddFuncs(fnMap FuncMap) {
|
||||||
|
// Add each function to Funcs
|
||||||
for name, fn := range fnMap {
|
for name, fn := range fnMap {
|
||||||
Funcs[name] = fn
|
Funcs[name] = fn
|
||||||
}
|
}
|
||||||
@ -35,62 +37,81 @@ func AddFuncs(fnMap FuncMap) {
|
|||||||
// the Vars variable
|
// 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 {
|
||||||
|
// Add each variable to Vars
|
||||||
Vars[name] = val
|
Vars[name] = val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse uses participle to parse a script from r into a new AST
|
// 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) {
|
||||||
|
// Build parser from empty AST struct
|
||||||
parser, err := participle.Build(&AST{})
|
parser, err := participle.Build(&AST{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Create new empty AST struct
|
||||||
ast := &AST{}
|
ast := &AST{}
|
||||||
|
// Parse script from provided reader into ast
|
||||||
err = parser.Parse(r, ast)
|
err = parser.Parse(r, ast)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Return filled AST struct
|
||||||
return ast, nil
|
return ast, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseValue parses a Value struct into a go value
|
// ParseValue parses a Value struct into a go value
|
||||||
func ParseValue(val *Value) (interface{}, error) {
|
func ParseValue(val *Value) (interface{}, error) {
|
||||||
|
// Determine which value was provided and return it
|
||||||
if val.String != nil {
|
if val.String != nil {
|
||||||
|
// Return unquoted string
|
||||||
return strings.Trim(*val.String, `"`), nil
|
return strings.Trim(*val.String, `"`), nil
|
||||||
} else if val.Bool != nil {
|
} else if val.Bool != nil {
|
||||||
|
// Return dereferenced boolean
|
||||||
return *val.Bool, nil
|
return *val.Bool, nil
|
||||||
} else if val.Float != nil {
|
} else if val.Float != nil {
|
||||||
|
// Return dereferenced float
|
||||||
return *val.Float, nil
|
return *val.Float, nil
|
||||||
} else if val.Integer != nil {
|
} else if val.Integer != nil {
|
||||||
|
// Return dereferenced integer
|
||||||
return *val.Integer, nil
|
return *val.Integer, nil
|
||||||
} else if val.SubCmd != nil {
|
} else if val.SubCmd != nil {
|
||||||
|
// Return reference to subcommand
|
||||||
return val.SubCmd, nil
|
return val.SubCmd, nil
|
||||||
} else if val.VarVal != nil {
|
} else if val.VarVal != nil {
|
||||||
|
// Return value of provided key
|
||||||
return Vars[*val.VarVal], nil
|
return Vars[*val.VarVal], nil
|
||||||
} else if val.Expr != nil {
|
} else if val.Expr != nil {
|
||||||
|
// Parse value of left side of expression
|
||||||
left, _ := ParseValue(val.Expr.Left)
|
left, _ := ParseValue(val.Expr.Left)
|
||||||
|
// If value is string, requote
|
||||||
if isStr(left) {
|
if isStr(left) {
|
||||||
left = requoteStr(left.(string))
|
left = requoteStr(left.(string))
|
||||||
}
|
}
|
||||||
|
// Parse value of right side of expression
|
||||||
right, _ := ParseValue(val.Expr.Right)
|
right, _ := ParseValue(val.Expr.Right)
|
||||||
|
// If value is string, requote
|
||||||
if isStr(right) {
|
if isStr(right) {
|
||||||
right = requoteStr(right.(string))
|
right = requoteStr(right.(string))
|
||||||
}
|
}
|
||||||
|
// Create string expression from halves and operator
|
||||||
exp := fmt.Sprintf(
|
exp := fmt.Sprintf(
|
||||||
"%v %s %v",
|
"%v %s %v",
|
||||||
left,
|
left,
|
||||||
val.Expr.Op,
|
val.Expr.Op,
|
||||||
right,
|
right,
|
||||||
)
|
)
|
||||||
fmt.Println(exp)
|
// Compile string expression
|
||||||
program, err := expr.Compile(strings.ReplaceAll(exp, "^", "**"))
|
program, err := expr.Compile(strings.ReplaceAll(exp, "^", "**"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Run expression
|
||||||
out, err := expr.Run(program, Vars)
|
out, err := expr.Run(program, Vars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Return expression output value
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -98,11 +119,13 @@ func ParseValue(val *Value) (interface{}, error) {
|
|||||||
|
|
||||||
// Add quotes to an unquoted string
|
// Add quotes to an unquoted string
|
||||||
func requoteStr(s string) string {
|
func requoteStr(s string) string {
|
||||||
|
// Return quoted string
|
||||||
return `"` + s + `"`
|
return `"` + s + `"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if i is a string
|
// Check if i is a string
|
||||||
func isStr(i interface{}) bool {
|
func isStr(i interface{}) bool {
|
||||||
|
// if type of input is string, return true
|
||||||
if reflect.TypeOf(i).String() == "string" {
|
if reflect.TypeOf(i).String() == "string" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -113,26 +136,35 @@ func isStr(i interface{}) bool {
|
|||||||
// storing the argument name and its value. If the argument has
|
// storing the argument name and its value. If the argument has
|
||||||
// no name, its key will be an empty string
|
// no name, its key will be an empty string
|
||||||
func UnwrapArgs(args []*Arg) (map[string]interface{}, error) {
|
func UnwrapArgs(args []*Arg) (map[string]interface{}, error) {
|
||||||
|
// Create new empty map of strings to any type
|
||||||
argMap := map[string]interface{}{}
|
argMap := map[string]interface{}{}
|
||||||
|
// For each argument
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
|
// Parse value into interface{}
|
||||||
val, err := ParseValue(arg.Value)
|
val, err := ParseValue(arg.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// If value is function call
|
||||||
if IsFuncCall(val) {
|
if IsFuncCall(val) {
|
||||||
|
// Call function, setting its value as the argument's value
|
||||||
argMap[arg.Key], err = CallFunction(val.(*FuncCall))
|
argMap[arg.Key], err = CallFunction(val.(*FuncCall))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Skip further code and start next loop
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// Set argument value to parsed value
|
||||||
argMap[arg.Key] = val
|
argMap[arg.Key] = val
|
||||||
}
|
}
|
||||||
|
// Return map of arguments
|
||||||
return argMap, nil
|
return argMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsFuncCall checks if val is a FuncCall struct
|
// IsFuncCall checks if val is a FuncCall struct
|
||||||
func IsFuncCall(val interface{}) bool {
|
func IsFuncCall(val interface{}) bool {
|
||||||
|
// If type of val contains .FuncCall, return true
|
||||||
if strings.Contains(reflect.TypeOf(val).String(), ".FuncCall") {
|
if strings.Contains(reflect.TypeOf(val).String(), ".FuncCall") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -142,13 +174,16 @@ func IsFuncCall(val interface{}) bool {
|
|||||||
// CallFunction executes a given function call in the form of
|
// CallFunction executes a given function call in the form of
|
||||||
// a FuncCall struct
|
// a FuncCall struct
|
||||||
func CallFunction(call *FuncCall) (interface{}, error) {
|
func CallFunction(call *FuncCall) (interface{}, error) {
|
||||||
|
// Unwrap provided arguments
|
||||||
argMap, err := UnwrapArgs(call.Args)
|
argMap, err := UnwrapArgs(call.Args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Attempt to get function from Funcs map
|
||||||
fn, ok := Funcs[call.Name]
|
fn, ok := Funcs[call.Name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("no such function: " + call.Name)
|
return nil, errors.New("no such function: " + call.Name)
|
||||||
}
|
}
|
||||||
|
// Return value received from function
|
||||||
return fn(argMap)
|
return fn(argMap)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user