Clean up code and add support for multiple operations in expression

This commit is contained in:
Elara 2021-03-01 16:44:33 -08:00
parent a95d054189
commit 291b7a906c
5 changed files with 71 additions and 21 deletions

27
ast.go
View File

@ -1,6 +1,7 @@
package scpt package scpt
import ( import (
"errors"
"fmt" "fmt"
"github.com/alecthomas/participle/lexer" "github.com/alecthomas/participle/lexer"
) )
@ -50,6 +51,21 @@ func (ast *AST) Execute() error {
return fmt.Errorf("%s: %s", Call.Pos, err) 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 return nil
@ -59,9 +75,16 @@ func (ast *AST) Execute() error {
type Command struct { type Command struct {
Pos lexer.Position Pos lexer.Position
Vars []*Var `( @@` Vars []*Var `( @@`
Ifs []*If `| @@`
Calls []*FuncCall `| @@ )` Calls []*FuncCall `| @@ )`
} }
type If struct {
Pos lexer.Position
Condition *Value `"if" @@`
Action *FuncCall `"then" @@`
}
// FuncCall stores any function calls encountered while parsing a script // FuncCall stores any function calls encountered while parsing a script
type FuncCall struct { type FuncCall struct {
Pos lexer.Position Pos lexer.Position
@ -112,6 +135,10 @@ func (b *Bool) Capture(values []string) error {
type Expression struct { type Expression struct {
Pos lexer.Position Pos lexer.Position
Left *Value `@@` Left *Value `@@`
RightSegs []*ExprRightSeg `@@*`
}
type ExprRightSeg struct {
Op string `@( ">" | ">" "=" | "<" | "<" "=" | "!" "=" | "=" "=" | "+" | "-" | "*" | "/" | "^" | "%")` Op string `@( ">" | ">" "=" | "<" | "<" "=" | "!" "=" | "=" "=" | "+" | "-" | "*" | "/" | "^" | "%")`
Right *Value `@@` Right *Value `@@`
} }

2
go.mod
View File

@ -6,6 +6,4 @@ require (
github.com/alecthomas/participle v0.7.1 github.com/alecthomas/participle v0.7.1
github.com/antonmedv/expr v1.8.9 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
) )

48
scpt.go
View File

@ -67,8 +67,8 @@ func ParseValue(val *Value) (interface{}, error) {
// Return unquoted string // 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 dereferenced Bool converted to bool
return *val.Bool, nil return bool(*val.Bool), nil
} else if val.Float != nil { } else if val.Float != nil {
// Return dereferenced float // Return dereferenced float
return *val.Float, nil return *val.Float, nil
@ -83,22 +83,32 @@ func ParseValue(val *Value) (interface{}, error) {
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 // Parse value of left side of expression
left, _ := ParseValue(val.Expr.Left) left, _ := callIfFunc(ParseValue(val.Expr.Left))
// If value is string, requote // 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 // Create new nil string
right, _ := ParseValue(val.Expr.Right) 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 value is string, requote
if isStr(right) { if isStr(rVal) {
right = requoteStr(right.(string)) rVal = requoteStr(rVal.(string))
} }
// Create string expression from halves and operator // Append right segment to right string
right = right + fmt.Sprintf(
" %s %v",
segment.Op,
rVal,
)
}
// Create string expression from segments and operator
exp := fmt.Sprintf( exp := fmt.Sprintf(
"%v %s %v", "%v %s",
left, left,
val.Expr.Op,
right, right,
) )
// Compile string expression // Compile string expression
@ -132,6 +142,20 @@ func isStr(i interface{}) bool {
return false 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 // UnwrapArgs takes a slice of Arg structs and returns a map
// 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
@ -164,8 +188,8 @@ func UnwrapArgs(args []*Arg) (map[string]interface{}, error) {
// 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 type of val is a pointer to FuncCall, return true
if strings.Contains(reflect.TypeOf(val).String(), ".FuncCall") { if reflect.TypeOf(val) == reflect.TypeOf(&FuncCall{}) {
return true return true
} }
return false return false

View File

@ -1,6 +1,7 @@
set y to (display-dialog "Hello" with title 12 with type "yesno") set y to (display-dialog "Hello" with title 12 with type "yesno")
display-dialog "Goodbye" with title 21 with type "error" display-dialog "Goodbye" with title 21 with type "error"
do-shell-script "notify-send Test Notification" 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 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"