From fc1dd7a2e3e6149764536cb9139bfd0e99c24dad Mon Sep 17 00:00:00 2001 From: Aidan Date: Wed, 15 Jul 2020 17:25:49 -0700 Subject: [PATCH 01/14] add util module --- util/scripts.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 util/scripts.go diff --git a/util/scripts.go b/util/scripts.go new file mode 100644 index 0000000..03847d7 --- /dev/null +++ b/util/scripts.go @@ -0,0 +1,48 @@ +/* SHS: Syntactically Homogeneous Shell + * Copyright (C) 2019 Aidan Hahn + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package util + +import ( + "os" + "ioutil" + "gitlab.com/whom/shs/log" + "gitlab.com/whom/shs/ast" +) + +func LoadScript(path string, vt ast.VarTable, ft ast.FuncTable) { + scriptFile, err := os.Open(path) + if err != nil { + log.Log(log.ERR, + "unable to open script: " + err.Error(), + "util") + return + } + + var body string + body, err := ioutil.ReadFile(path) + scriptFile.Close() + if err !- nil { + log.Log(log.ERR, + "unable to read script: " + err.Error(), + "util") + return + } + + set := ast.Lex(body) + set.Eval(ft, vt, false) +} From 654e8bd55beed6b5ee180acd03e85e46992ceb08 Mon Sep 17 00:00:00 2001 From: Aidan Date: Wed, 15 Jul 2020 17:26:44 -0700 Subject: [PATCH 02/14] fix typo --- util/scripts.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/scripts.go b/util/scripts.go index 03847d7..08cfd35 100644 --- a/util/scripts.go +++ b/util/scripts.go @@ -19,7 +19,7 @@ package util import ( "os" - "ioutil" + "os/ioutil" "gitlab.com/whom/shs/log" "gitlab.com/whom/shs/ast" ) From bd22b8469930f298a58f0584dd69f27ef486a803 Mon Sep 17 00:00:00 2001 From: Aidan Date: Wed, 15 Jul 2020 18:41:54 -0700 Subject: [PATCH 03/14] comments support, script loading support --- Readme.md | 15 +++++++++++-- ast/eval.go | 2 ++ ast/lex.go | 18 +++++++++++++++- cmd/shs_repl.go | 55 ++++++++++++++++++++++++++++++++++++------------ config/config.go | 28 ++---------------------- go.mod | 5 ++++- go.sum | 4 ++++ stdlib/call.go | 8 ++++++- stdlib/funcs.go | 40 +++++++++++++++++++---------------- stdlib/stdlib.go | 25 +++++++++++++++++++++- util/scripts.go | 14 ++++++------ 11 files changed, 145 insertions(+), 69 deletions(-) diff --git a/Readme.md b/Readme.md index e0d032e..f8abce7 100644 --- a/Readme.md +++ b/Readme.md @@ -49,6 +49,10 @@ See `stdlib/control_flow.go`. We have if and while forms: We also have functioning implementations of map and reduce in the stdlib (incomplete) +## Comments +The standard delimiter for comments is ; +any characters after a semicolon will be ignored until end of line + ## How to build ### Compiling/Installation - For now simply run `go install cmd/...` for each utility you wish to use. If you have GOPATH and GOBIN set it should be usable from PATH @@ -69,9 +73,14 @@ We also have functioning implementations of map and reduce in the stdlib (incomp * one can write arbitrary shs script into `.shsrc` including function and variable declarations * of note are the following variables - `SH_LOGGING` Sets the log level (from 0 to 3) - - `SHS_SH_PROMPT` Sets the prompt + - `SHS_STATIC_PROMPT` Sets the prompt - `SH_HIST_FILE` Sets the history file - - `SH_DEBUG_MODE` Adds additional debug output for the lexer + - `SH_DEBUG_MODE` Adds additional debug output for the lexer (high clutter) +* additionally, the repl will evaluate any function you define as `_SH_PROMPT` before the shell prompt + - if defined, the function will be evaluated before printing the prompt + - the function will be given 0 arguments + - if the function does not return a string, its output will be discarded + - afterwards, the repl will print the values in `SHS_STATIC_PROMPT` Here is an example of a shs configuration file: ```lisp (export GOPATH (concat HOME "/go")) @@ -80,6 +89,8 @@ Here is an example of a shs configuration file: (export GIT_TERMINAL_PROMPT 1) (export SH_HIST_FILE (concat HOME "/.shs_hist")) (export SH_LOGGING 0) +(export SHS_STATIC_PROMPT ">") +(func _SH_PROMPT () (concat (?) ($ basename ($ pwd)) "\n")) ``` ## Contributing diff --git a/ast/eval.go b/ast/eval.go index 0f6cc98..48c6ff4 100644 --- a/ast/eval.go +++ b/ast/eval.go @@ -61,6 +61,8 @@ func (in *Token) Eval(funcs FuncTable, vars VarTable, cnvtUndefVars bool) *Token "eval") return nil } + } else { + res.Next = in.Next } case LIST: diff --git a/ast/lex.go b/ast/lex.go index 532f32a..285fd94 100644 --- a/ast/lex.go +++ b/ast/lex.go @@ -121,6 +121,17 @@ func lex(input string) *Token { return -2 } + // returns the end of the string OR the end of the line + matchLineEnd := func(start int) int { + for i := start; i < len(input); i++ { + if input[i] == '\n' { + return i + } + } + + return len(input) + } + needs_alloc := false start_pos := 0 for i := 0; i < len(input); i++ { @@ -137,13 +148,18 @@ func lex(input string) *Token { is_str = true needs_alloc = true - case ' ': + case ' ', '\n', '\t', '\v', '\f', '\r': if i == start_pos { start_pos += 1 continue } needs_alloc = true + + // comment case + case ';': + start_pos = i + 1 + i = matchLineEnd(start_pos) } if needs_alloc { diff --git a/cmd/shs_repl.go b/cmd/shs_repl.go index ea6007c..1535264 100644 --- a/cmd/shs_repl.go +++ b/cmd/shs_repl.go @@ -18,9 +18,10 @@ package main import ( + "os" "fmt" "strconv" - "github.com/chzyer/readline" + "github.com/peterh/liner" "gitlab.com/whom/shs/ast" "gitlab.com/whom/shs/log" "gitlab.com/whom/shs/config" @@ -54,6 +55,7 @@ func main() { var prompt string var debug string var hist string + no_hist := false ast.SyncTablesWithOSEnviron = true ast.ExecWhenFuncUndef = true @@ -67,34 +69,61 @@ func main() { hist_t := ast.GetVar("SH_HIST_FILE", vars) if hist_t != nil { hist = hist_t.Value() + } else { + no_hist = true } - prompt_t := ast.GetVar("SHS_SH_PROMPT", vars) + dyn_prompt := ast.GetFunction("_SH_PROMPT", funcs) + if dyn_prompt == nil || dyn_prompt.Args != 0 { + dyn_prompt = nil + } + + prompt_t := ast.GetVar("SHS_STATIC_PROMPT", vars) if prompt_t != nil { prompt = prompt_t.Value() } else { prompt = def_prompt } - rl, err := readline.NewEx(&readline.Config{ - Prompt: prompt, - HistoryFile: hist, - InterruptPrompt: "^C", - }) + line := liner.NewLiner() + defer line.Close() - defer rl.Close() - if err != nil { - log.Log(log.ERR, "Couldnt initialize readline: " + err.Error(), "repl") - return + line.SetCtrlCAborts(true) + + var histFile *os.File + var err error + if !no_hist { + histFile, err = os.Open(hist) + if err == nil { + line.ReadHistory(histFile) + } else { + log.Log(log.ERR, + "couldnt read history: " + err.Error(), + "repl") + } + defer histFile.Close() } for { setLogLvl(vars) - text, err := rl.Readline() - if err != nil { + var prePrompt string + if dyn_prompt != nil { + p_tok := dyn_prompt.CallFunction(nil, vars, funcs) + if p_tok != nil && p_tok.Tag == ast.STRING { + prePrompt = p_tok.Value() + } + } + + fmt.Printf(prePrompt) + text, err := line.Prompt(prompt) + if err != nil && err != liner.ErrPromptAborted{ log.Log(log.ERR, "couldnt read user input: " + err.Error(), "repl") } + if !no_hist { + line.WriteHistory(histFile) + } + userInput := ast.Lex(text) if userInput == nil { // errors handled in Lex diff --git a/config/config.go b/config/config.go index 6ce7d51..bf4c303 100644 --- a/config/config.go +++ b/config/config.go @@ -18,11 +18,9 @@ package config import ( - "os" - "io" - "bufio" "gitlab.com/whom/shs/log" "gitlab.com/whom/shs/ast" + "gitlab.com/whom/shs/util" "gitlab.com/whom/shs/stdlib" ) @@ -35,32 +33,10 @@ func InitFromConfig(configFile string) (ast.VarTable, ast.FuncTable) { p := ast.GetVar("HOME", vars) configFile = p.Value() + "/" + configFile - cfile, err := os.Open(configFile) - if err != nil { - log.Log(log.DEBUG, - "unable to open config file: " + err.Error(), - "config") - return vars, funcs - } - - r := bufio.NewReader(cfile) - text, err := r.ReadString('\n') - for err != io.EOF { - if err != nil { - log.Log(log.ERR, - "unable to read from config file: " + err.Error(), - "config") - break - } - - // Eval lines in config - ast.Lex(text).Eval(funcs, vars, false) - text, err = r.ReadString('\n') - } + util.LoadScript(configFile, vars, funcs) log.Log(log.DEBUG, "config file fully evaluated", "config") - cfile.Close() return vars, funcs } diff --git a/go.mod b/go.mod index 125370b..2b85407 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module gitlab.com/whom/shs go 1.14 -require github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect +require ( + github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect + github.com/peterh/liner v1.2.0 // indirect +) diff --git a/go.sum b/go.sum index 454fe40..285b298 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/peterh/liner v1.2.0 h1:w/UPXyl5GfahFxcTOz2j9wCIHNI+pUPr2laqpojKNCg= +github.com/peterh/liner v1.2.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= diff --git a/stdlib/call.go b/stdlib/call.go index 4359503..4a1891e 100644 --- a/stdlib/call.go +++ b/stdlib/call.go @@ -254,8 +254,14 @@ func read_cmd(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { } } + output := out.String() + olen := len(output) + if olen > 0 && output[olen - 1] == '\n' { + output = output[:olen - 1] + } + ret := &ast.Token{Tag: ast.STRING} - ret.Set(out.String()) + ret.Set(output) return ret } diff --git a/stdlib/funcs.go b/stdlib/funcs.go index 93de50d..24805e0 100644 --- a/stdlib/funcs.go +++ b/stdlib/funcs.go @@ -60,30 +60,34 @@ func decl_func(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.To ASTSYNCSTATE := ast.SyncTablesWithOSEnviron inner := func(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { - temp := in.Eval(ft, vt, false) - if temp == nil { - log.Log(log.ERR, - "error parsing arguments", - name.Value()) - return nil - } - - ast.SyncTablesWithOSEnviron = false - key_iter := args.Expand() - val_iter := temp - - for key_iter != nil { - if val_iter == nil { + var temp *ast.Token + if numArgs != 0 || in != nil { + temp = in.Eval(ft, vt, false) + if temp == nil { log.Log(log.ERR, - "Not enough arguments supplied", + "error parsing arguments", name.Value()) + return nil } - ast.SetVar(key_iter.Value(), val_iter, vt) - key_iter = key_iter.Next - val_iter = val_iter.Next + ast.SyncTablesWithOSEnviron = false + key_iter := args.Expand() + val_iter := temp + + for key_iter != nil { + if val_iter == nil { + log.Log(log.ERR, + "Not enough arguments supplied", + name.Value()) + } + + ast.SetVar(key_iter.Value(), val_iter, vt) + key_iter = key_iter.Next + val_iter = val_iter.Next + } } + // maybe we actually should put the inner scope var into the env ast.SyncTablesWithOSEnviron = ASTSYNCSTATE ret := form.Eval(ft, vt, false) ast.SyncTablesWithOSEnviron = false diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index 5ad08a5..8b36763 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -22,6 +22,7 @@ import ( "fmt" "gitlab.com/whom/shs/log" "gitlab.com/whom/shs/ast" + "gitlab.com/whom/shs/util" ) func GenFuncTable() ast.FuncTable { @@ -62,13 +63,20 @@ func GenFuncTable() ast.FuncTable { Args: 2, }, - "input": &ast.Function{ + "input": &ast.Function{ Function: input, Name: "input", TimesCalled: 0, Args: 1, }, + "load": &ast.Function{ + Function: load, + Name: "load", + TimesCalled: 0, + Args: 1, + }, + "...": &ast.Function{ Function: expand, Name: "...", @@ -337,3 +345,18 @@ func input(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { ret.Set(output) return ret } + +func load(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + in = in.Eval(ft, vt, true) + if in.Tag != ast.STRING { + log.Log(log.ERR, + "argument to load must be a string", + "load") + return nil + } + + bp := in.Value() + bp = AbsPath(bp) + util.LoadScript(bp, vt, ft) + return nil +} diff --git a/util/scripts.go b/util/scripts.go index 08cfd35..310fd3e 100644 --- a/util/scripts.go +++ b/util/scripts.go @@ -19,7 +19,7 @@ package util import ( "os" - "os/ioutil" + "io/ioutil" "gitlab.com/whom/shs/log" "gitlab.com/whom/shs/ast" ) @@ -33,16 +33,18 @@ func LoadScript(path string, vt ast.VarTable, ft ast.FuncTable) { return } - var body string - body, err := ioutil.ReadFile(path) + var body []byte + body, err = ioutil.ReadFile(path) scriptFile.Close() - if err !- nil { + if err != nil { log.Log(log.ERR, "unable to read script: " + err.Error(), "util") return } - set := ast.Lex(body) - set.Eval(ft, vt, false) + set := ast.Lex(string(body)) + for iter := set; iter != nil; iter = iter.Next { + iter.Eval(ft, vt, false) + } } From d0e3946aff6848bc1e965b36b668ac611c761af0 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Jul 2020 11:36:13 -0700 Subject: [PATCH 04/14] rename repl --- cmd/{shs_repl.go => shs.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cmd/{shs_repl.go => shs.go} (100%) diff --git a/cmd/shs_repl.go b/cmd/shs.go similarity index 100% rename from cmd/shs_repl.go rename to cmd/shs.go From 77ce00970ff13322997758c7f248b482db2828ea Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Jul 2020 22:38:15 -0700 Subject: [PATCH 05/14] print statement should allow for escaping stuff --- ast/print.go | 3 ++- cmd/shs.go | 20 ++++++++++++++++++-- stdlib/string.go | 16 +++++++++++++++- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/ast/print.go b/ast/print.go index 944b698..ea5251c 100644 --- a/ast/print.go +++ b/ast/print.go @@ -18,6 +18,7 @@ package ast import ( + "fmt" "strings" ) @@ -49,7 +50,7 @@ loop: constructor.WriteString(iter.FmtToken()) } - println(constructor.String()) + fmt.Printf(constructor.String() + "\n") goto loop } diff --git a/cmd/shs.go b/cmd/shs.go index 1535264..4b43d3c 100644 --- a/cmd/shs.go +++ b/cmd/shs.go @@ -31,6 +31,22 @@ const ( def_prompt string = "λ " ) + +// useful for when input contains escape sequences +// not checking delims cause thats up to the user who defines their prompts +func parseString(in string) string { + in = "\"" + in + "\"" + out, err := strconv.Unquote(in) + if err != nil { + log.Log(log.ERR, + "Couldnt parse (pre?)prompt", + "init") + return "" + } + + return out +} + func setLogLvl(vars ast.VarTable) { var loglvl string @@ -80,7 +96,7 @@ func main() { prompt_t := ast.GetVar("SHS_STATIC_PROMPT", vars) if prompt_t != nil { - prompt = prompt_t.Value() + prompt = parseString(prompt_t.Value()) } else { prompt = def_prompt } @@ -110,7 +126,7 @@ func main() { if dyn_prompt != nil { p_tok := dyn_prompt.CallFunction(nil, vars, funcs) if p_tok != nil && p_tok.Tag == ast.STRING { - prePrompt = p_tok.Value() + prePrompt = parseString(p_tok.Value()) } } diff --git a/stdlib/string.go b/stdlib/string.go index b8321d7..ed1b861 100644 --- a/stdlib/string.go +++ b/stdlib/string.go @@ -19,6 +19,7 @@ package stdlib import ( "fmt" + "strconv" "gitlab.com/whom/shs/ast" "gitlab.com/whom/shs/log" ) @@ -43,6 +44,19 @@ func concat(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { } func print_str(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { - fmt.Println(in.Eval(ft, vt, false)) + body := in.Eval(ft, vt, false).String() + if body[0] != body[len(body)-1] && body[0] != '"' { + body = "`" + body + "`" + } + + text, err := strconv.Unquote(body) + if err != nil { + log.Log(log.ERR, + "error unquoting string", + "print") + return nil + } + + fmt.Printf(text + "\n") return nil } From 8278430882a299fc5e8e0656134994da63aef692 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 18 Jul 2020 00:22:43 -0700 Subject: [PATCH 06/14] added type casts --- stdlib/arith.go | 21 +++++++++++++++++++++ stdlib/bool.go | 22 ++++++++++++++++++++++ stdlib/stdlib.go | 23 ++++++++++++++++++++++- stdlib/string.go | 7 +++++++ 4 files changed, 72 insertions(+), 1 deletion(-) diff --git a/stdlib/arith.go b/stdlib/arith.go index 7a61754..a9051eb 100644 --- a/stdlib/arith.go +++ b/stdlib/arith.go @@ -29,6 +29,27 @@ import ( // perhaps we simply write out arithmetic routines that operate on the strings // then we need not worry about storage length. +func num_cast(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { + in = in.Eval(f, a, false) + if in.Tag != ast.STRING { + log.Log(log.ERR, + "only a string can successfully be cast to a number", + "number_cast") + return nil + } + + if !ast.StrIsNumber(in.Value()) { + log.Log(log.ERR, + "string failed number cast", + "number_cast") + return nil + } + + out := in.Copy() + out.Tag = ast.NUMBER + return out +} + func add(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { var res float64 diff --git a/stdlib/bool.go b/stdlib/bool.go index 328c4e7..035fa2f 100644 --- a/stdlib/bool.go +++ b/stdlib/bool.go @@ -23,6 +23,28 @@ import ( "gitlab.com/whom/shs/ast" ) +func bool_cast(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + in = in.Eval(ft, vt, false) + if in.Tag == ast.LIST || in.Tag == ast.NUMBER { + log.Log(log.ERR, + "only strings successfully cast to bool", + "bool cast") + return nil + } + + body := in.Value() + if body != ast.TRUE && body != ast.FALSE { + log.Log(log.ERR, + "cast to bool failed", + "bool cast") + return nil + } + + res := &ast.Token{ Tag: ast.BOOL } + res.Set(body) + return res +} + func not(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, false) diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index 8b36763..c50b42c 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -77,7 +77,28 @@ func GenFuncTable() ast.FuncTable { Args: 1, }, - "...": &ast.Function{ + "bool": &ast.Function{ + Function: bool_cast, + Name: "bool", + TimesCalled: 0, + Args: 1, + }, + + "string": &ast.Function{ + Function: str_cast, + Name: "string", + TimesCalled: 0, + Args: 1, + }, + + "number": &ast.Function{ + Function: num_cast, + Name: "number", + TimesCalled: 0, + Args: 1, + }, + + "...": &ast.Function{ Function: expand, Name: "...", TimesCalled: 0, diff --git a/stdlib/string.go b/stdlib/string.go index ed1b861..92f9a9b 100644 --- a/stdlib/string.go +++ b/stdlib/string.go @@ -43,6 +43,13 @@ func concat(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return t } +func str_cast(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + body := in.Eval(ft, vt, false).String() + res := &ast.Token{ Tag: ast.STRING } + res.Set(body) + return res +} + func print_str(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { body := in.Eval(ft, vt, false).String() if body[0] != body[len(body)-1] && body[0] != '"' { From 44beab651c5f1e028aed6e9c3a8462150d79ca38 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 18 Jul 2020 10:44:18 -0700 Subject: [PATCH 07/14] added progn form --- Readme.md | 5 +++-- stdlib/control_flow.go | 11 +++++++++++ stdlib/stdlib.go | 7 +++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index f8abce7..63d0b30 100644 --- a/Readme.md +++ b/Readme.md @@ -43,10 +43,11 @@ Use the `func` function from the stdlib: In this case, `(form_to_be_evaluated)` will not be evaluated until the function is called. ### Control flow -See `stdlib/control_flow.go`. We have if and while forms: +See `stdlib/control_flow.go`. We have if, while, and progn forms: `(if (cond) (then) (else))` `(when (cond) (form1)....... (formN))` - +`(progn (form1)..... (formN))` +If and While should be self explanatory. For those new to LISP, the rough idea of progn is to evaluate a sequence of N forms and return the result of the final one. We also have functioning implementations of map and reduce in the stdlib (incomplete) ## Comments diff --git a/stdlib/control_flow.go b/stdlib/control_flow.go index 5889e33..279f94f 100644 --- a/stdlib/control_flow.go +++ b/stdlib/control_flow.go @@ -22,6 +22,17 @@ import ( "gitlab.com/whom/shs/log" ) +/* eval N forms. return the last one + */ +func shs_progn(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + var res *ast.Token + for iter := in; iter != nil; iter = iter.Next { + res = iter.Eval(ft, vt, false) + } + + return res +} + /* return one evaluated form or another based on the boolean statement */ func shs_if(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index c50b42c..a243700 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -42,6 +42,13 @@ func GenFuncTable() ast.FuncTable { Args: -1, }, + "progn": &ast.Function{ + Function: shs_progn, + Name: "shs_progn", + TimesCalled: 0, + Args: -1, + }, + "eval": &ast.Function{ Function: eval, Name: "eval", From d5edb2bad2b5ea5c89fc8d35345231de53a372e6 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 18 Jul 2020 10:44:34 -0700 Subject: [PATCH 08/14] added shell tab complete --- ast/func_table.go | 15 +++++++ ast/var_table.go | 12 ++++++ cmd/shs.go | 12 ++++-- util/shell_complete.go | 90 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 util/shell_complete.go diff --git a/ast/func_table.go b/ast/func_table.go index b70f758..3a2db29 100644 --- a/ast/func_table.go +++ b/ast/func_table.go @@ -75,3 +75,18 @@ func GetFunction(arg string, table FuncTable) *Function { return target } + + +/* lists all functions in table + */ +func ListFuncs(ft FuncTable) []string { + keys := make([]string, len(*ft)) + i := 0 + + for k := range *ft { + keys[i] = k + i++ + } + + return keys +} diff --git a/ast/var_table.go b/ast/var_table.go index 7037e4a..072ec99 100644 --- a/ast/var_table.go +++ b/ast/var_table.go @@ -73,6 +73,18 @@ func SetVar(variable string, value *Token, vt VarTable) { } } +/* lists all vars in tables + */ +func ListVars(vt VarTable) []string { + keys := make([]string, len(*vt)) + i := 0 + for k := range *vt { + keys[i] = k + } + + return keys +} + // Library represents variables defined in inner scope // It is assumed library is ordered from innermost scope to outermost scope func GetVarFromTables(arg string, library []VarTable) *Token { diff --git a/cmd/shs.go b/cmd/shs.go index 4b43d3c..3413d21 100644 --- a/cmd/shs.go +++ b/cmd/shs.go @@ -20,10 +20,12 @@ package main import ( "os" "fmt" + "strings" "strconv" "github.com/peterh/liner" "gitlab.com/whom/shs/ast" "gitlab.com/whom/shs/log" + "gitlab.com/whom/shs/util" "gitlab.com/whom/shs/config" ) @@ -105,6 +107,10 @@ func main() { defer line.Close() line.SetCtrlCAborts(true) + line.SetCompleter(func(line string) (c []string) { + strtoks := strings.Split(line, " ") + return util.ShellCompleter(strtoks[len(strtoks) - 1], vars, funcs) + }) var histFile *os.File var err error @@ -134,12 +140,10 @@ func main() { text, err := line.Prompt(prompt) if err != nil && err != liner.ErrPromptAborted{ log.Log(log.ERR, "couldnt read user input: " + err.Error(), "repl") + continue } - if !no_hist { - line.WriteHistory(histFile) - } - + line.AppendHistory(text) userInput := ast.Lex(text) if userInput == nil { // errors handled in Lex diff --git a/util/shell_complete.go b/util/shell_complete.go new file mode 100644 index 0000000..8246ff5 --- /dev/null +++ b/util/shell_complete.go @@ -0,0 +1,90 @@ +/* SHS: Syntactically Homogeneous Shell + * Copyright (C) 2019 Aidan Hahn + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package util + +import ( + "fmt" + "io/ioutil" + "strings" + "gitlab.com/whom/shs/log" + "gitlab.com/whom/shs/ast" +) + +// wrap this in a lambda that passes in the vt and ft +// I suppose it could be more optimal. Fix if it bothers you +func ShellCompleter(line string, vt ast.VarTable, ft ast.FuncTable) []string { + dir, path, tok := getPathBase(line) + compSource := []string{} + + if !path { + dir = "." + } else { + line = tok + } + + fobjs, err := ioutil.ReadDir(dir) + if err != nil { + log.Log(log.DEBUG, + "couldnt read dir " + dir + ": " + err.Error(), + "complete") + if path { + return nil + } + } else { + for _, f := range fobjs { + compSource = append(compSource, f.Name()) + } + } + + if !path { + compSource = append(compSource, ast.ListVars(vt)...) + compSource = append(compSource, ast.ListFuncs(ft)...) + } + + completions := []string{} + for _, i := range compSource { + if strings.HasPrefix(i, line) { + completions = append(completions, i) + } + } + + return completions +} + +// returns everything up to the last '/' +// as well as whether or not a / was found +// and finally the token after the last / +func getPathBase(in string) (string, bool, string) { + if in == "" { + return "", false, "" + } + + isPath := false + + i := len(in) - 1 + for i > 0 { + if in[i] == '/' { + isPath = true + break + } + + i -= 1 + } + + return in[:i], isPath, in[i+1:] +} From 956044cfae84865ffbe82ed927a072321cabdee4 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 18 Jul 2020 14:19:58 -0700 Subject: [PATCH 09/14] improve autocomplete --- cmd/shs.go | 4 +--- stdlib/call.go | 3 ++- util/shell_complete.go | 21 ++++++++++++++++----- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/cmd/shs.go b/cmd/shs.go index 3413d21..3379c7c 100644 --- a/cmd/shs.go +++ b/cmd/shs.go @@ -20,7 +20,6 @@ package main import ( "os" "fmt" - "strings" "strconv" "github.com/peterh/liner" "gitlab.com/whom/shs/ast" @@ -108,8 +107,7 @@ func main() { line.SetCtrlCAborts(true) line.SetCompleter(func(line string) (c []string) { - strtoks := strings.Split(line, " ") - return util.ShellCompleter(strtoks[len(strtoks) - 1], vars, funcs) + return util.ShellCompleter(line, vars, funcs) }) var histFile *os.File diff --git a/stdlib/call.go b/stdlib/call.go index 4a1891e..27c2e12 100644 --- a/stdlib/call.go +++ b/stdlib/call.go @@ -74,9 +74,10 @@ func call(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { } else { cmd = exec.Command(path) } + cmd.Env = os.Environ() + cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - cmd.Stdin = os.Stdin signalChan := make(chan os.Signal, 2) signal.Notify(signalChan, sigs...) diff --git a/util/shell_complete.go b/util/shell_complete.go index 8246ff5..67e9b6c 100644 --- a/util/shell_complete.go +++ b/util/shell_complete.go @@ -19,8 +19,8 @@ package util import ( "fmt" - "io/ioutil" "strings" + "io/ioutil" "gitlab.com/whom/shs/log" "gitlab.com/whom/shs/ast" ) @@ -28,7 +28,18 @@ import ( // wrap this in a lambda that passes in the vt and ft // I suppose it could be more optimal. Fix if it bothers you func ShellCompleter(line string, vt ast.VarTable, ft ast.FuncTable) []string { - dir, path, tok := getPathBase(line) + var head, tail string + + idx := strings.LastIndex(line, " ") + if idx > 0 { + head = line[:idx+1] + tail = line[idx+1:] + } else { + head = "" + tail = line + } + + dir, path, tok := getPathBase(tail) compSource := []string{} if !path { @@ -47,7 +58,7 @@ func ShellCompleter(line string, vt ast.VarTable, ft ast.FuncTable) []string { } } else { for _, f := range fobjs { - compSource = append(compSource, f.Name()) + compSource = append(compSource, dir + "/" + f.Name()) } } @@ -58,8 +69,8 @@ func ShellCompleter(line string, vt ast.VarTable, ft ast.FuncTable) []string { completions := []string{} for _, i := range compSource { - if strings.HasPrefix(i, line) { - completions = append(completions, i) + if strings.HasPrefix(i, tail) { + completions = append(completions, head + i) } } From fec3550702288546052bd68c1819ddf49514c820 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 18 Jul 2020 14:40:35 -0700 Subject: [PATCH 10/14] ast package full godoc --- ast/eval.go | 6 ++++-- ast/func_table.go | 28 +++++++++++++++++++++++++--- ast/lex.go | 5 +++++ ast/token.go | 7 +++++++ ast/tokenstack.go | 7 +++++++ ast/var_table.go | 40 ++++++++++++++++++++-------------------- util/shell_complete.go | 1 - 7 files changed, 68 insertions(+), 26 deletions(-) diff --git a/ast/eval.go b/ast/eval.go index 48c6ff4..e82a89b 100644 --- a/ast/eval.go +++ b/ast/eval.go @@ -19,12 +19,14 @@ package ast import "gitlab.com/whom/shs/log" -/* determines whether or not to execute a system call +/* determines whether or not to execute a system binary * when a function cannot be found in the functable * (use case: shell) - * ExecFunc determines the name of the system call function to fetch */ var ExecWhenFuncUndef = false + +/* name of the command used to execute a system binary + */ var ExecFunc = "l" /* Runs through an AST of tokens diff --git a/ast/func_table.go b/ast/func_table.go index 3a2db29..b939e0c 100644 --- a/ast/func_table.go +++ b/ast/func_table.go @@ -19,18 +19,35 @@ package ast import "gitlab.com/whom/shs/log" +/* expected function header for any stdlib function + */ type Operation func(*Token, VarTable, FuncTable) *Token +/* holds a stdlib function along with relevant metadata + */ type Function struct { + // go function that list of args are passed to Function Operation + + // name of function Name string + + // number of times user has called this function TimesCalled int - Args int // TODO: Make this a list of expected types (TAGs) + + // number of args required + Args int } +/* holds a mapping of key to function + * passed to eval and into function calls + * initialized by repl at startup + */ type FuncTable *map[string]*Function -// TODO: Currently only checks arg list length +/* validates an individual call of a function + * makes sure correct arguments are passed in + */ func (f Function) ParseFunction(args *Token) bool { // handle infinite args if f.Args < 0 { @@ -52,6 +69,9 @@ func (f Function) ParseFunction(args *Token) bool { return true } +/* handles a call to a function + * calls ParseFunction and increments TimesCalled + */ func (f Function) CallFunction(args *Token, vt VarTable, ft FuncTable) *Token { if !f.ParseFunction(args) { log.Log(log.ERR, @@ -64,6 +84,8 @@ func (f Function) CallFunction(args *Token, vt VarTable, ft FuncTable) *Token { return f.Function(args, vt, ft) } +/* searches for function mapped to argument in FuncTable + */ func GetFunction(arg string, table FuncTable) *Function { target, ok := (*table)[arg] if !ok { @@ -77,7 +99,7 @@ func GetFunction(arg string, table FuncTable) *Function { } -/* lists all functions in table +/* returns list of all functions in table */ func ListFuncs(ft FuncTable) []string { keys := make([]string, len(*ft)) diff --git a/ast/lex.go b/ast/lex.go index 285fd94..3031cab 100644 --- a/ast/lex.go +++ b/ast/lex.go @@ -22,8 +22,12 @@ import ( "unicode" ) +// all delimiters that work on strings const string_delims string = "\"'`" +/* takes a line of user input + * returns an unsimplified tree of tokens + */ func Lex(input string) *Token { ret := lex(input) if ret == nil { @@ -201,6 +205,7 @@ error: return nil } +// returns true if a string could contain an int or float func StrIsNumber(arg string) bool { dotCount := 0 diff --git a/ast/token.go b/ast/token.go index 367cd42..b9ba0ca 100644 --- a/ast/token.go +++ b/ast/token.go @@ -19,7 +19,11 @@ package ast import "fmt" +/* token_t is a tag that declares the type of the + * datum contained in a token + */ type Token_t int + const ( LIST Token_t = iota STRING Token_t = iota @@ -31,6 +35,9 @@ const ( FALSE string = "F" ) +/* Contains a parsed lexeme + * and a pointer to the next parsed lexeme in the same scope + */ type Token struct { Next *Token Tag Token_t diff --git a/ast/tokenstack.go b/ast/tokenstack.go index 9f7360b..136ccae 100644 --- a/ast/tokenstack.go +++ b/ast/tokenstack.go @@ -17,16 +17,23 @@ package ast +/* primitive stack type for tokens + * useful for iterative algorithms on tokens + */ type TokenStack struct { buffer []*Token capacity int } +/* push token onto stack + */ func (s *TokenStack) Push(v *Token) { s.capacity++ s.buffer = append(s.buffer, v) } +/* pop token off stack + */ func (s *TokenStack) Pop() *Token { if s.capacity <= 0 { return nil diff --git a/ast/var_table.go b/ast/var_table.go index 072ec99..ab1eec2 100644 --- a/ast/var_table.go +++ b/ast/var_table.go @@ -26,11 +26,19 @@ import ( "gitlab.com/whom/shs/log" ) -// Trigger this if you are using this for a shell +/* defines whether or not to synchronize tokens wiht os environment vars + * will not sync non stringable tokens + */ var SyncTablesWithOSEnviron = false +/* mapping of key to token. + */ type VarTable *map[string]*Token +/* retrieve the token cooresponding to a given key + * if SyncTablesWithOSEnviron is true and no token exists for a key + * os Environment variables will be searched for the key + */ func GetVar(arg string, vt VarTable) *Token { val, ok := (*vt)[arg] if !ok { @@ -55,8 +63,10 @@ func GetVar(arg string, vt VarTable) *Token { return val } -// TODO: this could be much more optimal -// probably a stdlib thing +/* adds a key->token mapping to the table + * if SyncTablesWithOSEnviron is true, will also add value to os environment + * will not do so for non stringable tokens + */ func SetVar(variable string, value *Token, vt VarTable) { (*vt)[variable] = value if SyncTablesWithOSEnviron && @@ -85,22 +95,9 @@ func ListVars(vt VarTable) []string { return keys } -// Library represents variables defined in inner scope -// It is assumed library is ordered from innermost scope to outermost scope -func GetVarFromTables(arg string, library []VarTable) *Token { - var res *Token - res = nil - for i := 0; i < len(library); i += 1 { - res = GetVar(arg, library[i]) - if res != nil { - // TODO: Log scope res was found in? - break - } - } - - return res -} - +/* if SyncTablesWithOSEnviron is true + * function will put ever environment variable into VarTable + */ func InitVarTable(table VarTable) { if !SyncTablesWithOSEnviron { return @@ -133,7 +130,10 @@ func DeleteVarTable(table VarTable) { } } } - +a +/* removes var from vartable + * if SyncTablesWithOSENviron is true, also unsets environment variable + */ func RemoveVar(arg string, table VarTable) { if SyncTablesWithOSEnviron { err := os.Unsetenv(arg) diff --git a/util/shell_complete.go b/util/shell_complete.go index 67e9b6c..e2e4731 100644 --- a/util/shell_complete.go +++ b/util/shell_complete.go @@ -18,7 +18,6 @@ package util import ( - "fmt" "strings" "io/ioutil" "gitlab.com/whom/shs/log" From b941df68e17ec2291d8150da306d965dfb50ca98 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 18 Jul 2020 14:45:36 -0700 Subject: [PATCH 11/14] godoc for extra packages --- config/config.go | 4 ++++ log/logger.go | 7 +++++++ util/scripts.go | 2 ++ util/shell_complete.go | 6 ++++-- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index bf4c303..fc8eda3 100644 --- a/config/config.go +++ b/config/config.go @@ -24,6 +24,10 @@ import ( "gitlab.com/whom/shs/stdlib" ) +/* creates new VarTable and FuncTable + * reads a configuration file + * executes contents and returns tables + */ func InitFromConfig(configFile string) (ast.VarTable, ast.FuncTable) { funcs := stdlib.GenFuncTable() vars := &map[string]*ast.Token{} diff --git a/log/logger.go b/log/logger.go index 5f3d773..2d736e6 100644 --- a/log/logger.go +++ b/log/logger.go @@ -29,16 +29,23 @@ const ( var logLevel int64 +/* Set the log level from 0 to 4 + * currently only 0, 1, and 2 are used. + */ func SetLogLvl(lvl int64) { if lvl < 4 && lvl > 0 { logLevel = lvl } } +/* get current log level as int + */ func GetLogLvl() int64 { return logLevel } +/* writes a message to the log + */ func Log(lvl int, msg, context string) { if int64(lvl) > logLevel { return diff --git a/util/scripts.go b/util/scripts.go index 310fd3e..3d2115b 100644 --- a/util/scripts.go +++ b/util/scripts.go @@ -24,6 +24,8 @@ import ( "gitlab.com/whom/shs/ast" ) +/* Opens a file and lexes+evaluates the contents + */ func LoadScript(path string, vt ast.VarTable, ft ast.FuncTable) { scriptFile, err := os.Open(path) if err != nil { diff --git a/util/shell_complete.go b/util/shell_complete.go index e2e4731..a6e3289 100644 --- a/util/shell_complete.go +++ b/util/shell_complete.go @@ -24,8 +24,10 @@ import ( "gitlab.com/whom/shs/ast" ) -// wrap this in a lambda that passes in the vt and ft -// I suppose it could be more optimal. Fix if it bothers you +/* gathers completions for the last word in a given line + * shell wraps this in a lambda that passes in the vt and ft + * I suppose it could be more optimal. Fix if it bothers you + */ func ShellCompleter(line string, vt ast.VarTable, ft ast.FuncTable) []string { var head, tail string From ee39de01fd1224e1b95af54b4c4a3adf3235568e Mon Sep 17 00:00:00 2001 From: Aidan Date: Sun, 19 Jul 2020 14:37:20 -0700 Subject: [PATCH 12/14] much stdlib docs --- stdlib/arith.go | 10 +++- stdlib/call.go | 62 ++++++++++++++++++++--- stdlib/control_flow.go | 21 ++++++-- stdlib/filesys.go | 50 +++++++++++++++--- stdlib/funcs.go | 12 ++++- stdlib/list.go | 4 +- stdlib/stdlib.go | 112 ++++++++++++++++++++++++----------------- stdlib/string.go | 21 ++++++-- stdlib/vars.go | 10 +++- 9 files changed, 228 insertions(+), 74 deletions(-) diff --git a/stdlib/arith.go b/stdlib/arith.go index a9051eb..43e23fb 100644 --- a/stdlib/arith.go +++ b/stdlib/arith.go @@ -29,7 +29,13 @@ import ( // perhaps we simply write out arithmetic routines that operate on the strings // then we need not worry about storage length. -func num_cast(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { +/* Takes 1 argument (must be a string) + * will attempt to cast it to a number. + * will return nil if cast fails + * + * Example: (number "3.4") + */ +func NumCast(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { in = in.Eval(f, a, false) if in.Tag != ast.STRING { log.Log(log.ERR, @@ -50,7 +56,7 @@ func num_cast(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { return out } -func add(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { +func Add(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { var res float64 in = in.Eval(f, a, false) diff --git a/stdlib/call.go b/stdlib/call.go index 27c2e12..99e0647 100644 --- a/stdlib/call.go +++ b/stdlib/call.go @@ -30,7 +30,6 @@ import ( ) var bgProcs = make([]*exec.Cmd, 0) -var LastExitCode int var sigs = []os.Signal{ os.Interrupt, syscall.SIGTERM, @@ -40,8 +39,17 @@ var sigs = []os.Signal{ syscall.SIGCONT, } +/* Exit code of last run process + */ +var LastExitCode int -func call(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* Takes n arguments (list of tokens generated by lexing a shell command) + * Evaluates arguments, but does not err on undefined symbols (note the last arg to Eval(...)) + * Executes shell command and returns nil + * + * Example (l vim file.txt) + */ +func Call(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, true) if in == nil { return nil @@ -100,7 +108,12 @@ func call(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return nil } -func bgcall(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* Starts a call in the background + * Takes n args (a shell command not delimited by string delimiters) + * + * Example: (bg vim file.txt) + */ +func Bgcall(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { if in == nil { return nil } @@ -144,7 +157,14 @@ func bgcall(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return nil } -func fg(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* brings last BG'ed process into the foreground + * returns nil + * + * Example: + * (bg vim file.txt) + * (fg) + */ +func Fg(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { if len(bgProcs) < 1 { return nil } @@ -174,7 +194,16 @@ func fg(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return nil } -func jobs(in *ast.Token, vt ast.VarTable, fg ast.FuncTable) *ast.Token { +/* Takes 0 args + * returns a string containing info about current jobs + * returns total jobs as well as their PIDs and place in the bg queue + * + * Example: + * (bg ping google.com) + * (bg .........) + * (jobs) + */ +func Jobs(in *ast.Token, vt ast.VarTable, fg ast.FuncTable) *ast.Token { ret := &ast.Token{ Tag: ast.LIST, } @@ -198,7 +227,12 @@ func jobs(in *ast.Token, vt ast.VarTable, fg ast.FuncTable) *ast.Token { return ret } -func read_cmd(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* calls a command (blocks until completion) + * captures stdout and returns it as a string + * + * Example: ($ echo hello world) + */ +func ReadCmd(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, true) if in == nil { @@ -267,13 +301,25 @@ func read_cmd(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { } -func get_exit(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* Takes 0 arguments + * returns the exit code of the last executed program + * + * Example: + * (sudo apt update) + * (?) <- gets exit code + */ +func GetExit(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { ret := &ast.Token{Tag: ast.NUMBER} ret.Set(fmt.Sprintf("%d", LastExitCode)) return ret } -func kill(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* takes an argument (pid of process to be killed) + * calls Process.Kill() on it + * do not use this if you already have a native implementation + * (this function not added to functable in stdlib.go) + */ +func Kill(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, true) if in.Tag == ast.LIST { diff --git a/stdlib/control_flow.go b/stdlib/control_flow.go index 279f94f..0238ff5 100644 --- a/stdlib/control_flow.go +++ b/stdlib/control_flow.go @@ -23,8 +23,12 @@ import ( ) /* eval N forms. return the last one + * + * Example: + * (progn (print "hello") (print "world") (+ 1 2)) + * This example will print "hello world" to stdout and return 3 */ -func shs_progn(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +func ShsProgn(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { var res *ast.Token for iter := in; iter != nil; iter = iter.Next { res = iter.Eval(ft, vt, false) @@ -34,8 +38,13 @@ func shs_progn(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { } /* return one evaluated form or another based on the boolean statement + * arg 1 is a boolean cond, arg 2 is evaluated if the cond is true, arg 3 is evaluated if cond is not true + * in total it takes 3 arguments + * + * Example: + * (if (eq (number "3") 3) (print "test passed") (print "test failed")) */ -func shs_if(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +func ShsIf(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { cond := in t := cond.Next f := t.Next @@ -66,8 +75,14 @@ func shs_if(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { } /* continually eval n forms while element #1 evals to T + * has rather progn like behavior in that it returns the result of the last form to be evaluated + * + * Example: + * (export cond F) + * (while cond (export cond T) (print "will only be printed once") (+ 1 2)) + * loop will iter one time, print "will only be printed once" and return 3 */ -func shs_while(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +func ShsWhile(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { cond := in forms := in.Next in.Next = nil diff --git a/stdlib/filesys.go b/stdlib/filesys.go index 6504221..77030a7 100644 --- a/stdlib/filesys.go +++ b/stdlib/filesys.go @@ -46,7 +46,14 @@ func AbsPath(arg string) string { return arg } -func cd(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* Takes one arg, returns nil + * changes directory to the path in the arg + * fails if arg is not stringable + * + * Example: + * (cd (concat HOME "/go")) + */ +func Cd(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, true) if in == nil { @@ -68,11 +75,19 @@ func cd(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return nil } -func fexists(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* Takes one arg, returns a bool + * Returns true if arg is a filepath that exists + * returns nil if arg is not a string type + * + * Example: + * (touch test) + * (fexists test) + */ +func Fexists(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, false) - if in == nil || (in.Tag != ast.NUMBER && in.Tag != ast.STRING) { + if in == nil || in.Tag != ast.STRING { log.Log(log.ERR, - "argument to fexists must be a string or number", + "argument to fexists must be a string", "fexists") return nil } @@ -92,7 +107,14 @@ func fexists(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return ret } -func fread(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* Takes one arg, returns a string + * Returns contents of file in arg + * returns nil if file doesnt exist + * + * Example: + * (fread (concat HOME ".shsrc")) + */ +func Fread(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, false) exists := fexists(in, vt, ft) // some waste, extra use of Eval if exists == nil || exists.Tag != ast.BOOL || exists.Value() == ast.FALSE { @@ -116,7 +138,14 @@ func fread(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return ret } -func fwrite(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* Takes two arguments a filepath and a string + * CLOBBERS FILE CONTENTS + * Returns nil + * + * Example: + * (fwrite "test" "one two three") + */ +func Fwrite(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, false) if in == nil || in.Tag == ast.SYMBOL || in.Tag == ast.LIST { log.Log(log.ERR, @@ -146,7 +175,14 @@ func fwrite(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return nil } -func fappend(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* Takes two arguments a filepath and a string + * DOES NOT CLOBBER FILE CONTENTS + * Returns nil + * + * Example: + * (fwrite "test" "one two three") + */ +func Fappend(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, false) if in == nil || in.Tag == ast.SYMBOL || in.Tag == ast.LIST { log.Log(log.ERR, diff --git a/stdlib/funcs.go b/stdlib/funcs.go index 24805e0..63f3130 100644 --- a/stdlib/funcs.go +++ b/stdlib/funcs.go @@ -22,7 +22,17 @@ import ( "gitlab.com/whom/shs/log" ) -func decl_func(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { +/* Takes 3 arguments: name, list of arg names, and logic form + * DOES NOT EVALUATE THE LOGIC FORM + * adds an anonymous function to the FuncTable under the name specified + * anonymous function will expect the args declared in arg2 and expand them in arg3 + * Then evaluates and returns result of arg3. This constitutes a function call + * + * Example: + * (func foo (x) (print x)) + * (foo 4) -> prints 4 + */ +func DeclFunc(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { name := input if name.Tag != ast.SYMBOL { log.Log(log.ERR, diff --git a/stdlib/list.go b/stdlib/list.go index ac20d28..cec7d80 100644 --- a/stdlib/list.go +++ b/stdlib/list.go @@ -27,7 +27,7 @@ import ( * retuns a sequence of elements (list contents) * in event a not-list is passed in, returns the arg. */ -func expand(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { +func Expand(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { if input.Tag != ast.LIST { log.Log(log.DEBUG, "expand called on not a list", "expand") return input @@ -41,7 +41,7 @@ func expand(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token * Arg one is a list, next args are appended * if no args are a list, a list is made from all args */ -func l_append(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { +func L_append(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { src := input if input.Tag != ast.LIST { diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index a243700..abed934 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -29,231 +29,231 @@ func GenFuncTable() ast.FuncTable { var stdlib ast.FuncTable stdlib = &map[string]*ast.Function{ "if": &ast.Function{ - Function: shs_if, + Function: ShsIf, Name: "if", TimesCalled: 0, Args: 3, }, "while": &ast.Function{ - Function: shs_while, + Function: ShsWhile, Name: "while", TimesCalled: 0, Args: -1, }, "progn": &ast.Function{ - Function: shs_progn, + Function: ShsProgn, Name: "shs_progn", TimesCalled: 0, Args: -1, }, "eval": &ast.Function{ - Function: eval, + Function: Eval, Name: "eval", TimesCalled: 0, Args: -1, }, "func": &ast.Function{ - Function: decl_func, + Function: DeclFunc, Name: "decl_func", TimesCalled: 0, Args: 3, }, "export": &ast.Function{ - Function: export, + Function: Export, Name: "export", TimesCalled: 0, Args: 2, }, "input": &ast.Function{ - Function: input, + Function: Input, Name: "input", TimesCalled: 0, Args: 1, }, "load": &ast.Function{ - Function: load, + Function: Load, Name: "load", TimesCalled: 0, Args: 1, }, "bool": &ast.Function{ - Function: bool_cast, + Function: BoolCast, Name: "bool", TimesCalled: 0, Args: 1, }, "string": &ast.Function{ - Function: str_cast, + Function: StrCast, Name: "string", TimesCalled: 0, Args: 1, }, "number": &ast.Function{ - Function: num_cast, + Function: NumCast, Name: "number", TimesCalled: 0, Args: 1, }, "...": &ast.Function{ - Function: expand, + Function: Expand, Name: "...", TimesCalled: 0, Args: 1, }, "append": &ast.Function{ - Function: l_append, + Function: L_append, Name: "append", TimesCalled: 0, Args: -1, }, "exit": &ast.Function{ - Function: exit_shell, + Function: ExitShell, Name: "exit", TimesCalled: 0, Args: 0, }, "eq": &ast.Function{ - Function: eq, + Function: Eq, Name: "==", TimesCalled: 0, Args: 2, }, "ne": &ast.Function{ - Function: ne, + Function: Ne, Name: "!=", TimesCalled: 0, Args: 2, }, "<": &ast.Function{ - Function: lt, + Function: Lt, Name: "<", TimesCalled: 0, Args: 2, }, ">": &ast.Function{ - Function: gt, + Function: Gt, Name: ">", TimesCalled: 0, Args: 2, }, "<=": &ast.Function{ - Function: lte, + Function: Lte, Name: "<=", TimesCalled: 0, Args: 2, }, ">=": &ast.Function{ - Function: gte, + Function: Gte, Name: ">=", TimesCalled: 0, Args: 2, }, "!": &ast.Function{ - Function: not, + Function: Not, Name: "!", TimesCalled: 0, Args: 1, }, "+": &ast.Function{ - Function: add, + Function: Add, Name: "add", TimesCalled: 0, Args: -1, }, "-": &ast.Function{ - Function: sub, + Function: Sub, Name: "sub", TimesCalled: 0, Args: -1, }, "*": &ast.Function{ - Function: mult, + Function: Mult, Name: "mult", TimesCalled: 0, Args: -1, }, "/": &ast.Function{ - Function: div, + Function: Div, Name: "div", TimesCalled: 0, Args: -1, }, "cd": &ast.Function{ - Function: cd, + Function: Cd, Name: "changedir", TimesCalled: 0, Args: 1, }, "concat": &ast.Function{ - Function: concat, + Function: Concat, Name:"concatenate", TimesCalled: 0, Args: -1, }, "print": &ast.Function{ - Function:print_str, + Function: PrintStr, Name: "print", TimesCalled: 0, Args: 1, }, "l": &ast.Function{ - Function: call, + Function: Call, Name: "call", TimesCalled: 0, Args: -1, }, "bg": &ast.Function{ - Function: bgcall, + Function: Bgcall, Name: "background call", TimesCalled: 0, Args: -1, }, "fg": &ast.Function{ - Function: fg, + Function: Fg, Name: "foreground", TimesCalled: 0, Args: 0, }, "$": &ast.Function{ - Function: read_cmd, + Function: ReadCmd, Name: "read cmd", TimesCalled: 0, Args: -1, }, "?": &ast.Function{ - Function: get_exit, + Function: GetExit, Name:"get exit code", TimesCalled: 0, Args: 0, @@ -270,42 +270,42 @@ func GenFuncTable() ast.FuncTable { */ "jobs": &ast.Function{ - Function: jobs, + Function: Jobs, Name: "list jobs", TimesCalled: 0, Args: 0, }, "info": &ast.Function{ - Function: sh_info, + Function: ShInfo, Name: "Shell Info", TimesCalled: 0, Args: 1, }, "fexists": &ast.Function{ - Function: fexists, + Function: Fexists, Name: "file exists", TimesCalled: 0, Args: 1, }, "fread": &ast.Function{ - Function: fread, + Function: Fread, Name: "read file", TimesCalled: 0, Args: 1, }, "fwrite": &ast.Function{ - Function: fwrite, + Function: Fwrite, Name: "write file", TimesCalled: 0, Args: 2, }, "fappend": &ast.Function{ - Function: fappend, + Function: Fappend, Name:"append to file", TimesCalled: 0, Args: 2, @@ -315,12 +315,22 @@ func GenFuncTable() ast.FuncTable { return stdlib } -func exit_shell(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* takes no args + * exits shell when called + * + * Example: (exit) + */ +func ExitShell(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { os.Exit(0) return nil // I hope execution doesnt get here } -func sh_info(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* takes one arg, doesnt evaluate it + * returns type or metadata + * + * Example: (info append) + */ +func ShInfo(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { switch in.Tag { case ast.BOOL: fmt.Printf("BOOL LITERAL\nValue: %s\n", in.Value()) @@ -350,11 +360,14 @@ func sh_info(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return nil } -func eval(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { - return in.Eval(ft, vt, false) -} - -func input(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* Takes 1 arg, uses it as a prompt + * errs if prompt is not a string or number + * gets a line from stdin + * returns it as a string + * + * Example: (print (input)) + */ +func Input(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, false) if in.Tag != ast.STRING && in.Tag != ast.NUMBER { log.Log(log.ERR, @@ -374,7 +387,12 @@ func input(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return ret } -func load(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* Takes 1 arg, returns nil + * if arg is a valid existing file than load will execute it as a script + * + * Example: (load "myscript.shs") + */ +func Load(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, true) if in.Tag != ast.STRING { log.Log(log.ERR, diff --git a/stdlib/string.go b/stdlib/string.go index 92f9a9b..3cae616 100644 --- a/stdlib/string.go +++ b/stdlib/string.go @@ -24,7 +24,11 @@ import ( "gitlab.com/whom/shs/log" ) -func concat(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* Concatenates N stringables + * + * Example: (concat "hello" " " "world") + */ +func Concat(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, false) var res string @@ -43,14 +47,25 @@ func concat(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return t } -func str_cast(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* Takes 1 argument, returns its value as a string + * works on lists too. + * + * Example: (string 1) -> 1.0 + */ +func StrCast(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { body := in.Eval(ft, vt, false).String() res := &ast.Token{ Tag: ast.STRING } res.Set(body) return res } -func print_str(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* Takes one arg, returns nil + * Prints a string to stdout + * Unquotes string so user can add escaped chars like \n, \t, etc + * + * Example: (print "Line: \n, Tab: \t") + */ +func PrintStr(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { body := in.Eval(ft, vt, false).String() if body[0] != body[len(body)-1] && body[0] != '"' { body = "`" + body + "`" diff --git a/stdlib/vars.go b/stdlib/vars.go index 6aed9f9..52938f2 100644 --- a/stdlib/vars.go +++ b/stdlib/vars.go @@ -22,7 +22,15 @@ import ( "gitlab.com/whom/shs/log" ) -func export(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { +/* Takes 2 args, a name and a value + * Exports a varable + * both args are evaluated + * + * Example: + * (export hw (concat "hello" " " "world")) + * (print hw) + */ +func Export(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { name := input form := name.Next.Eval(funcs, vars, false) From e1913fea2cae85a3bcc286010b67e3cd359ced6c Mon Sep 17 00:00:00 2001 From: Aidan Date: Sun, 19 Jul 2020 14:53:27 -0700 Subject: [PATCH 13/14] incomplete docs for stdlib --- stdlib/arith.go | 18 ++++++++++++++++++ stdlib/stdlib.go | 3 +++ 2 files changed, 21 insertions(+) diff --git a/stdlib/arith.go b/stdlib/arith.go index a9051eb..5682177 100644 --- a/stdlib/arith.go +++ b/stdlib/arith.go @@ -29,6 +29,10 @@ import ( // perhaps we simply write out arithmetic routines that operate on the strings // then we need not worry about storage length. +/* Cast a string to a number + * Takes 1 argument + * returns nil if fails + */ func num_cast(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { in = in.Eval(f, a, false) if in.Tag != ast.STRING { @@ -50,6 +54,10 @@ func num_cast(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { return out } +/* adds N number arguments + * takes N arguments + * returns the sum, or nil if improper arguments were given + */ func add(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { var res float64 @@ -92,6 +100,9 @@ func add(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { return t } +/* subtract N args from the final arg + * takes N args, returns nil if improper args given + */ func sub(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { var res float64 var sub float64 @@ -142,6 +153,9 @@ func sub(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { return t } +/* multiplies N arguments + * returns nil if an improper argument is given + */ func mult(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { res := 1.0 @@ -184,6 +198,10 @@ func mult(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { return t } +/* divide N arguments + * the first argument is divided by each subsequent argument in order + * returns nil if an improper argument is given + */ func div(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { var res float64 diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index a243700..d92e754 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -25,6 +25,9 @@ import ( "gitlab.com/whom/shs/util" ) +/* Makes a FuncTable from all functions in the stdlib + * add your function here if you are extending the standard library + */ func GenFuncTable() ast.FuncTable { var stdlib ast.FuncTable stdlib = &map[string]*ast.Function{ From 26e331f331d23a88ddbf4b5102a6727c68818253 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sun, 19 Jul 2020 15:11:35 -0700 Subject: [PATCH 14/14] fix builds --- ast/var_table.go | 2 +- stdlib/bool.go | 28 +++++++++++++++++----------- stdlib/filesys.go | 4 ++-- stdlib/stdlib.go | 7 ------- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/ast/var_table.go b/ast/var_table.go index ab1eec2..f597eee 100644 --- a/ast/var_table.go +++ b/ast/var_table.go @@ -130,7 +130,7 @@ func DeleteVarTable(table VarTable) { } } } -a + /* removes var from vartable * if SyncTablesWithOSENviron is true, also unsets environment variable */ diff --git a/stdlib/bool.go b/stdlib/bool.go index 035fa2f..d6b07e6 100644 --- a/stdlib/bool.go +++ b/stdlib/bool.go @@ -23,7 +23,13 @@ import ( "gitlab.com/whom/shs/ast" ) -func bool_cast(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +/* Takes one argument, must be a string + * attempts to cast to bool (T or F are valid values) + * returns nil on failure + * + * Example: (bool "F") + */ +func BoolCast(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, false) if in.Tag == ast.LIST || in.Tag == ast.NUMBER { log.Log(log.ERR, @@ -45,7 +51,7 @@ func bool_cast(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return res } -func not(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +func Not(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, false) if in.Tag != ast.BOOL { @@ -63,7 +69,7 @@ func not(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return t } -func eq(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +func Eq(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { out := ast.TRUE in = in.Eval(ft, vt, false) @@ -145,7 +151,7 @@ func eq(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return t } -func lt(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +func Lt(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { out := ast.TRUE second := in.Next @@ -168,7 +174,7 @@ func lt(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return t } -func gt(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { +func Gt(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { out := ast.TRUE second := in.Next @@ -191,14 +197,14 @@ func gt(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return t } -func ne(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { - return not(eq(in, vt, ft), vt, ft) +func Ne(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + return Not(Eq(in, vt, ft), vt, ft) } -func gte(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { - return not(lt(in, vt, ft), vt, ft) +func Gte(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + return Not(Lt(in, vt, ft), vt, ft) } -func lte(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { - return not(gt(in, vt, ft), vt, ft) +func Lte(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + return Not(Gt(in, vt, ft), vt, ft) } diff --git a/stdlib/filesys.go b/stdlib/filesys.go index 77030a7..182cdca 100644 --- a/stdlib/filesys.go +++ b/stdlib/filesys.go @@ -116,7 +116,7 @@ func Fexists(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { */ func Fread(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { in = in.Eval(ft, vt, false) - exists := fexists(in, vt, ft) // some waste, extra use of Eval + exists := Fexists(in, vt, ft) // some waste, extra use of Eval if exists == nil || exists.Tag != ast.BOOL || exists.Value() == ast.FALSE { log.Log(log.ERR, "error calling fexists or file doesnt exist", @@ -199,7 +199,7 @@ func Fappend(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return nil } - exists := fexists(in, vt, ft) + exists := Fexists(in, vt, ft) if exists.Value() == ast.FALSE { log.Log(log.ERR, "file "+in.Value()+" does not exist", diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index 9e75e2a..83749c2 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -52,13 +52,6 @@ func GenFuncTable() ast.FuncTable { Args: -1, }, - "eval": &ast.Function{ - Function: Eval, - Name: "eval", - TimesCalled: 0, - Args: -1, - }, - "func": &ast.Function{ Function: DeclFunc, Name: "decl_func",