diff --git a/README.md b/README.md new file mode 100644 index 0000000..5ce2c39 --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +# scpt + +scpt is an applescript-inspired scripting language written for fun and to see if I could. + +[![Go Reference](https://pkg.go.dev/badge/gitea.arsenm.dev/Arsen6331/scpt.svg)](https://pkg.go.dev/gitea.arsenm.dev/Arsen6331/scpt) + +--- + +### Usage + +scpt is to be used as a library imported into Go. A basic interpreter with no extra functionality would look like this: + +```go +package main + +import ( + "gitea.arsenm.dev/Arsen6331/scpt" + "log" + "os" + "path/filepath" +) + +func main() { + filename := os.Args[1] + file, err := os.Open(filepath.Clean(filename)) + if err != nil { + log.Fatalln(err) + } + ast, err := scpt.Parse(file) + if err != nil { + log.Fatalln(err) + } + err = ast.Execute() + if err != nil { + log.Fatalln(err) + } +} +``` + +--- + +### Basic Syntax + +The basic syntax of scpt can be learned from the test.scpt file. + +--- + +### Default Functions + +scpt comes with the following default functions: + +- `str`: Convert value to string +- `num`: Parse string to number (`float64`) +- `bool`: Parse string to boolean +- `break`: Break out of loop (Errors if not in loop) +- `append`: Return an array with given items appended +- `exit`: Exit with given exit code +- `return`: Return value in function (Errors if not within function) +- `print`: Print using `fmt.Println()` + +--- + +### Adding functionality: + +Adding functionality is simple and requires a call to `scpt.AddFuncs()` or `scpt.AddVars()`. Here are some examples: + +```go +scpt.AddFuncs(scpt.FuncMap{ + "my-function": myFunction +}) +``` +Where `myFunction` is: +```go +func myFunction(args map[string]interface{}) (interface{}, error) { + fmt.Println(args) + return nil, nil +} +``` + +After the call to `scpt.AddFuncs()`, `my-function` can be used to run the function from within an scpt script. Variables work similarly. \ No newline at end of file diff --git a/ast.go b/ast.go index 19cda46..a5ba4ed 100644 --- a/ast.go +++ b/ast.go @@ -344,6 +344,7 @@ type Value struct { Expr *Expression `| "{" @@ "}"` Map []*MapKVPair `| "[" (@@ ("," @@)* )? "]"` Array []*Value `| "[" (@@ ("," @@)* )? "]"` + Opposite *Value `| "!" @@` } // Bool stores boolean values encountered while parsing a script. diff --git a/lexer.go b/lexer.go index 426d1f2..4507912 100644 --- a/lexer.go +++ b/lexer.go @@ -24,8 +24,8 @@ var scptLexer = lexer.Must(stateful.NewSimple([]stateful.Rule{ {"Ident", `[a-zA-Z_]\w*`, nil}, {"String", `"[^"]*"`, nil}, {"Number", `(?:\d*\.)?\d+`, nil}, - {"Punct", `[-[!@$&(){}\|:;"',.?/]|]`, nil}, + {"Punct", `[![@$&(){}\|:;"',.?]|]`, nil}, {"Whitespace", `[ \t\r\n]+`, nil}, {"Comment", `(###(.|\n)+###|#[^\n]+)`, nil}, - {"Operator", `(>=|<=|>|<|==|!=)|[-+*/^%]`, nil}, + {"Operator", `(>=|<=|>|<|==|!=)|[-+*%/^]`, nil}, })) diff --git a/scpt.go b/scpt.go index 7e23e54..43caaae 100644 --- a/scpt.go +++ b/scpt.go @@ -165,6 +165,16 @@ func ParseValue(val *Value) (interface{}, error) { } // Return map[interface{}]interface{} return iMap, nil + } else if val.Opposite != nil { + value, err := callIfFunc(ParseValue(val.Opposite)) + if err != nil { + return nil, err + } + boolean, ok := value.(bool) + if !ok { + return nil, errors.New("cannot take opposite of a non-boolean value") + } + return !boolean, nil } return nil, nil }