Initial Commit

This commit is contained in:
Arsen Musayelyan 2021-03-01 09:11:22 -08:00
commit e8df2f4afc
6 changed files with 212 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
scpt
cmd/scpt/scpt
.idea/

41
ast.go Normal file
View File

@ -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
}

8
go.mod Normal file
View File

@ -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
)

19
go.sum Normal file
View File

@ -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=

136
scpt.go Normal file
View File

@ -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")
}
}

5
test.scpt Normal file
View File

@ -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