commit
e8df2f4afc
6 changed files with 212 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||
scpt |
|||
cmd/scpt/scpt |
|||
.idea/ |
@ -0,0 +1,41 @@ |
|||
package scpt |
|||
|
|||
type AST struct { |
|||
Commands []*Command `@@*` |
|||
} |
|||
|
|||
type Command struct { |
|||
Vars []*Var `( @@` |
|||
Calls []*FuncCall `| @@ )` |
|||
} |
|||
|
|||
type FuncCall struct { |
|||
Name string `@Ident` |
|||
Args []*Arg `@@*` |
|||
} |
|||
|
|||
type Arg struct { |
|||
Key string `"with" @Ident` |
|||
Value *Value `@@` |
|||
} |
|||
|
|||
type Var struct { |
|||
Key string `"set" @Ident "to"` |
|||
Value *Value `@@` |
|||
} |
|||
|
|||
type Value struct { |
|||
String *string ` @String` |
|||
Float *float64 `| @Float` |
|||
Integer *int64 `| @Int` |
|||
Bool *Bool `| @("true" | "false")` |
|||
SubCmd *FuncCall `| "(" @@ ")"` |
|||
VarVal *string `| "$" @Ident` |
|||
} |
|||
|
|||
type Bool bool |
|||
|
|||
func (b *Bool) Capture(values []string) error { |
|||
*b = values[0] == "true" |
|||
return nil |
|||
} |
@ -0,0 +1,8 @@ |
|||
module scpt |
|||
|
|||
go 1.16 |
|||
|
|||
require ( |
|||
github.com/alecthomas/participle v0.7.1 |
|||
github.com/gen2brain/dlgs v0.0.0-20210222160047-2f436553172f |
|||
) |
@ -0,0 +1,19 @@ |
|||
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/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= |
|||
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/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= |
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
|||
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 v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= |
|||
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= |
@ -0,0 +1,136 @@ |
|||
package scpt |
|||
|
|||
import ( |
|||
"errors" |
|||
"fmt" |
|||
"github.com/alecthomas/participle" |
|||
"github.com/gen2brain/dlgs" |
|||
"io" |
|||
"os" |
|||
"os/exec" |
|||
"reflect" |
|||
"strings" |
|||
) |
|||
|
|||
var Vars = map[string]interface{}{} |
|||
|
|||
type FuncMap map[string]func(map[string]interface{}) (interface{}, error) |
|||
|
|||
var Funcs = FuncMap{ |
|||
"displaydialog": displayDialog, |
|||
"doshellscript": doShellScript, |
|||
} |
|||
|
|||
func AddFuncs(fnMap FuncMap) { |
|||
for name, fn := range fnMap { |
|||
Funcs[name] = fn |
|||
} |
|||
} |
|||
|
|||
func AddVars(varMap map[string]interface{}) { |
|||
for name, val := range varMap { |
|||
Vars[name] = val |
|||
} |
|||
} |
|||
|
|||
func Parse(r io.Reader) (*AST, error) { |
|||
parser, err := participle.Build(&AST{}) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
ast := &AST{} |
|||
err = parser.Parse("", r, ast) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return ast, nil |
|||
} |
|||
|
|||
func Execute(ast AST) { |
|||
for _, cmd := range ast.Commands { |
|||
if cmd.Vars != nil { |
|||
for _, Var := range cmd.Vars { |
|||
val := ParseValue(Var.Value) |
|||
if strings.Contains(reflect.TypeOf(val).String(), ".FuncCall") { |
|||
Call := val.(*FuncCall) |
|||
Vars[Var.Key], _ = CallFunction(Call) |
|||
} else { |
|||
Vars[Var.Key] = val |
|||
} |
|||
} |
|||
} else if cmd.Calls != nil { |
|||
for _, Call := range cmd.Calls { |
|||
_, _ = CallFunction(Call) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
func ParseValue(val *Value) interface{} { |
|||
if val.String != nil { |
|||
return strings.Trim(*val.String, `"`) |
|||
} else if val.Bool != nil { |
|||
return *val.Bool |
|||
} else if val.Float != nil { |
|||
return *val.Float |
|||
} else if val.Integer != nil { |
|||
return *val.Integer |
|||
} else if val.SubCmd != nil { |
|||
return val.SubCmd |
|||
} else if val.VarVal != nil { |
|||
return Vars[*val.VarVal] |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
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)) |
|||
return input, err |
|||
} |
|||
return nil, nil |
|||
} |
|||
|
|||
func doShellScript(args map[string]interface{}) (interface{}, error) { |
|||
script, ok := args["content"].(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") |
|||
} |
|||
} |
@ -0,0 +1,5 @@ |
|||
set y to (displaydialog with text "Hello" with title 12 with type "yesno") |
|||
displaydialog with text "Goodbye" with title 21 with type "error" |
|||
doshellscript with content "notify-send Test Notification" |
|||
set g to (displaydialog with text "Test 2" with title 30 with type "entry" with default "text") |
|||
set x to $g |
Loading…
Reference in new issue