Browse Source

Clean up code and add support for multiple operations in expression

master
Arsen Musayelyan 8 months ago
parent
commit
291b7a906c
  1. 33
      ast.go
  2. 2
      defaults.go
  3. 2
      go.mod
  4. 50
      scpt.go
  5. 5
      test.scpt

33
ast.go

@ -1,6 +1,7 @@
package scpt
import (
"errors"
"fmt"
"github.com/alecthomas/participle/lexer"
)
@ -40,7 +41,7 @@ func (ast *AST) Execute() error {
Vars[Var.Key] = val
}
}
// If parsing function calls
// If parsing function calls
} else if cmd.Calls != nil {
// For each function call
for _, Call := range cmd.Calls {
@ -50,6 +51,21 @@ func (ast *AST) Execute() error {
return fmt.Errorf("%s: %s", Call.Pos, err)
}
}
} else if cmd.Ifs != nil {
for _, If := range cmd.Ifs {
condVal, err := callIfFunc(ParseValue(If.Condition))
if err != nil {
return err
}
condBool, ok := condVal.(bool)
if !ok {
return errors.New("condition must be a boolean")
}
if condBool {
_, err := CallFunction(If.Action)
return err
}
}
}
}
return nil
@ -59,9 +75,16 @@ func (ast *AST) Execute() error {
type Command struct {
Pos lexer.Position
Vars []*Var `( @@`
Ifs []*If `| @@`
Calls []*FuncCall `| @@ )`
}
type If struct {
Pos lexer.Position
Condition *Value `"if" @@`
Action *FuncCall `"then" @@`
}
// FuncCall stores any function calls encountered while parsing a script
type FuncCall struct {
Pos lexer.Position
@ -110,8 +133,12 @@ func (b *Bool) Capture(values []string) error {
// Expression stores any expressions encountered while parsing a
// script for later evaluation
type Expression struct {
Pos lexer.Position
Left *Value `@@`
Pos lexer.Position
Left *Value `@@`
RightSegs []*ExprRightSeg `@@*`
}
type ExprRightSeg struct {
Op string `@( ">" | ">" "=" | "<" | "<" "=" | "!" "=" | "=" "=" | "+" | "-" | "*" | "/" | "^" | "%")`
Right *Value `@@`
}

2
defaults.go

@ -66,4 +66,4 @@ func doShellScript(args map[string]interface{}) (interface{}, error) {
} else {
return nil, errors.New("script not provided")
}
}
}

2
go.mod

@ -6,6 +6,4 @@ require (
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/gopherjs/gopherjs v0.0.0-20210202160940-bed99a852dfe // indirect
github.com/rs/zerolog v1.20.0
)

50
scpt.go

@ -67,8 +67,8 @@ func ParseValue(val *Value) (interface{}, error) {
// Return unquoted string
return strings.Trim(*val.String, `"`), nil
} else if val.Bool != nil {
// Return dereferenced boolean
return *val.Bool, nil
// Return dereferenced Bool converted to bool
return bool(*val.Bool), nil
} else if val.Float != nil {
// Return dereferenced float
return *val.Float, nil
@ -83,22 +83,32 @@ func ParseValue(val *Value) (interface{}, error) {
return Vars[*val.VarVal], nil
} else if val.Expr != nil {
// Parse value of left side of expression
left, _ := ParseValue(val.Expr.Left)
left, _ := callIfFunc(ParseValue(val.Expr.Left))
// If value is string, requote
if isStr(left) {
left = requoteStr(left.(string))
}
// Parse value of right side of expression
right, _ := ParseValue(val.Expr.Right)
// If value is string, requote
if isStr(right) {
right = requoteStr(right.(string))
// Create new nil string
var right string
// For every right segment
for _, segment := range val.Expr.RightSegs {
// Parse value of right segment, calling it if it is a function
rVal, _ := callIfFunc(ParseValue(segment.Right))
// If value is string, requote
if isStr(rVal) {
rVal = requoteStr(rVal.(string))
}
// Append right segment to right string
right = right + fmt.Sprintf(
" %s %v",
segment.Op,
rVal,
)
}
// Create string expression from halves and operator
// Create string expression from segments and operator
exp := fmt.Sprintf(
"%v %s %v",
"%v %s",
left,
val.Expr.Op,
right,
)
// Compile string expression
@ -132,6 +142,20 @@ func isStr(i interface{}) bool {
return false
}
// Call val if it is a function, otherwise pass through return values
func callIfFunc(val interface{}, err error) (interface{}, error) {
if err != nil {
return nil, err
}
// If val is a pointer to a FuncCall
if IsFuncCall(val) {
// Pass through return values of function call
return CallFunction(val.(*FuncCall))
}
// Return given value
return val, nil
}
// 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
@ -164,8 +188,8 @@ 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 contains .FuncCall, return true
if strings.Contains(reflect.TypeOf(val).String(), ".FuncCall") {
// If type of val is a pointer to FuncCall, return true
if reflect.TypeOf(val) == reflect.TypeOf(&FuncCall{}) {
return true
}
return false

5
test.scpt

@ -1,6 +1,7 @@
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"
print (display-dialog {"Test " + "2"} with title 30 with type "entry" with default "text")
do-shell-script {"echo " + (display-dialog {"Test " + "2"} with title 30 with type "entry" with default "text")}
set x to $y
print { 3+4 }
if {3 == 4} then display-dialog "What? Why is 3 equal to 4?" with title "What?!" with type "info"
if {3 == 3} then display-dialog "3 is equal to 3!" with title "New Discovery" with type "info"
Loading…
Cancel
Save