From 15e294085c1f6a5873cde6d0ec028a959f54c82a Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 24 Jul 2020 19:58:09 -0700 Subject: [PATCH 01/16] some rudimentary list operations --- stdlib/list.go | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ stdlib/stdlib.go | 22 ++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/stdlib/list.go b/stdlib/list.go index 748414b..863718e 100644 --- a/stdlib/list.go +++ b/stdlib/list.go @@ -18,6 +18,7 @@ package stdlib import ( + "fmt" "gitlab.com/whom/shs/ast" "gitlab.com/whom/shs/log" ) @@ -28,6 +29,7 @@ import ( * in event a not-list is passed in, returns the arg. */ func Expand(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { + input = input.Eval(funcs, vars, false) if input.Tag != ast.LIST { log.Log(log.INFO, "expand called on not a list", "expand") return input @@ -42,6 +44,7 @@ func Expand(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token * 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 { + input = input.Eval(funcs, vars, false) src := input if input.Tag != ast.LIST { @@ -68,3 +71,76 @@ func L_append(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Tok return src } + +/* Len + * Returns length of list as a number + * Returns nil if not a list + * + * Example: () -> 0 + * Example: (1 2 3) -> 3 + */ +func Len(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { + input = input.Eval(funcs, vars, false) + if input.Tag != ast.LIST { + log.Log(log.ERR, + "non-list as parameter to head", + "head") + return nil + } + + length := 0 + for iter := input.Expand(); iter != nil; iter = iter.Next { + length += 1 + } + + ret := &ast.Token{Tag: ast.NUMBER} + ret.Set(fmt.Sprintf("%d", length)) + return ret +} + +/* Head + * Returns first element in the list + * Returns nil if input is not a list or if list is empty + * + * Example: (head (2 3 4)) -> 2 + */ +func Head(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { + input = input.Eval(funcs, vars, false) + if input.Tag != ast.LIST { + log.Log(log.ERR, + "non-list as parameter to head", + "head") + return nil + } + + li := input.Expand().Copy() + if li != nil { + li.Next = nil + } + + return li +} + +/* Tail + * Returns last element in a list + * Returns nil if not a list + * + * Example: (tail (2 3 4)) -> 4 + */ +func Tail(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { + input = input.Eval(funcs, vars, false) + if input.Tag != ast.LIST { + log.Log(log.ERR, + "non-list as parameter to head", + "head") + return nil + } + + + iter := input.Expand() + for iter != nil && iter.Next != nil { + iter = iter.Next + } + + return iter.Copy() +} diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index 292b7da..2479249 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -59,6 +59,28 @@ func GenFuncTable() ast.FuncTable { Args: 3, }, + "len": &ast.Function{ + Function: Len, + Name: "len", + TimesCalled: 0, + Args: 1, + }, + + "head": &ast.Function{ + Function: Head, + Name: "head", + TimesCalled: 0, + Args: 1, + }, + + "tail": &ast.Function{ + Function: Tail, + Name: "tail", + TimesCalled: 0, + Args: 1, + }, + + "export": &ast.Function{ Function: Export, Name: "export", From adff10b56a77cd39463f24abd0d1bdd42a93fff4 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 24 Jul 2020 20:32:08 -0700 Subject: [PATCH 02/16] added slice function --- stdlib/list.go | 101 +++++++++++++++++++++++++++++++++++++++++++++++ stdlib/stdlib.go | 6 +++ 2 files changed, 107 insertions(+) diff --git a/stdlib/list.go b/stdlib/list.go index 863718e..8f5d35e 100644 --- a/stdlib/list.go +++ b/stdlib/list.go @@ -19,6 +19,7 @@ package stdlib import ( "fmt" + "strconv" "gitlab.com/whom/shs/ast" "gitlab.com/whom/shs/log" ) @@ -144,3 +145,103 @@ func Tail(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { return iter.Copy() } + +/* Slice + * Takes 3 args and returns a list + * Arg 1: starting index of sublist + * Arg 2: end index of sublist + * Arg 3: source list + * returns sublist, or nil if non list applied, or nil if start or end arent INTEGERS + * first index in a list is 0 + * + * Example: (slice 1 2 (1 2 3)) -> (2 3) + * Example: (slice 0 0 (1 2 3)) -> (1) + */ +func Slice(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { + input = input.Eval(funcs, vars, false) + start := input + end := input.Next + source := end.Next + + if start.Tag != ast.NUMBER || end.Tag != ast.NUMBER { + log.Log(log.ERR, + "start and end must both be integers", + "slice") + return nil + } + + if source.Tag != ast.LIST { + log.Log(log.ERR, + "non-list as parameter to head", + "head") + return nil + } + + st, err := strconv.ParseInt(start.Value(), 10, 64) + en, errr := strconv.ParseInt(end.Value(), 10, 64) + + if err != nil { + log.Log(log.ERR, + "couldnt parse integer from start value: " + err.Error(), + "slice") + return nil + } + + if errr != nil { + log.Log(log.ERR, + "couldnt parse integer from end value: " + errr.Error(), + "slice") + return nil + } + + if st < 0 || en < 0 { + log.Log(log.ERR, + "both indices must be positive", + "slice") + return nil + } + + if st > en { + log.Log(log.ERR, + "end index must be greater than start index", + "slice") + return nil + } + + en = en - st + var inner *ast.Token + buildIter := &inner + sourceIter := source.Expand() + + for st > 0 { + if sourceIter == nil { + log.Log(log.ERR, + "start index out of bounds", + "slice") + return nil + } + + sourceIter = sourceIter.Next + st -= 1 + } + + for en >= 0 { + if sourceIter == nil { + log.Log(log.ERR, + "end index out of bounds", + "slice") + return nil + } + + *buildIter = sourceIter.Copy() + (*buildIter).Next = nil + + buildIter = &((*buildIter).Next) + sourceIter = sourceIter.Next + en -= 1 + } + + ret := &ast.Token{Tag: ast.LIST} + ret.Direct(inner) + return ret +} diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index 2479249..9080b15 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -80,6 +80,12 @@ func GenFuncTable() ast.FuncTable { Args: 1, }, + "slice": &ast.Function{ + Function: Slice, + Name: "slice", + TimesCalled: 0, + Args: 3, + }, "export": &ast.Function{ Function: Export, From 530dbe7e21cb925449a203008ed5afb618046085 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 24 Jul 2020 20:37:46 -0700 Subject: [PATCH 03/16] EOF aborts shell --- cmd/shs.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/shs.go b/cmd/shs.go index 82fbf8d..cf58d44 100644 --- a/cmd/shs.go +++ b/cmd/shs.go @@ -149,6 +149,11 @@ func main() { fmt.Printf(prePrompt) text, err := line.Prompt(prompt) if err != nil && err != liner.ErrPromptAborted{ + // must be a better way to do this check + if err.Error() == "EOF" { + return + } + log.Log(log.ERR, "couldnt read user input: " + err.Error(), "repl") continue } From bc8ed0712566d918c0463d0466304f73da0b965b Mon Sep 17 00:00:00 2001 From: Aidan Date: Wed, 29 Jul 2020 06:46:54 -0700 Subject: [PATCH 04/16] clean up segfaults on bad variable decls --- stdlib/vars.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stdlib/vars.go b/stdlib/vars.go index 52938f2..3856378 100644 --- a/stdlib/vars.go +++ b/stdlib/vars.go @@ -34,6 +34,12 @@ func Export(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token name := input form := name.Next.Eval(funcs, vars, false) + + // error in eval process + if form == nil { + return nil + } + if name.Tag != ast.SYMBOL { log.Log(log.ERR, "first arg should be a symbol", From 61dd498d2767529ad2616598f1c40602784da76e Mon Sep 17 00:00:00 2001 From: Aidan Date: Wed, 29 Jul 2020 07:41:35 -0700 Subject: [PATCH 05/16] fix comment parsing --- ast/lex.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ast/lex.go b/ast/lex.go index 3031cab..6f672e5 100644 --- a/ast/lex.go +++ b/ast/lex.go @@ -162,14 +162,13 @@ func lex(input string) *Token { // comment case case ';': + i = matchLineEnd(i) start_pos = i + 1 - i = matchLineEnd(start_pos) } if needs_alloc { needs_alloc = false if (i < 0) { - // TODO: Maybe not overload this. start_pos = i goto error } @@ -186,8 +185,6 @@ func lex(input string) *Token { return ret error: - // TODO: Hook into error module - // TODO: Finalize and GC alloced tokens if start_pos == -1 { log.Log(log.ERR, "Unmatched string delimiter in input. discarding.", From 546e1711e549c03b56cab5e829dee3e56376c58d Mon Sep 17 00:00:00 2001 From: Aidan Date: Thu, 13 Aug 2020 12:14:10 -0700 Subject: [PATCH 06/16] handle escaped spaces in tokens + filepaths in completions --- ast/lex.go | 9 +++++++++ util/shell_complete.go | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ast/lex.go b/ast/lex.go index 6f672e5..d7db581 100644 --- a/ast/lex.go +++ b/ast/lex.go @@ -164,6 +164,15 @@ func lex(input string) *Token { case ';': i = matchLineEnd(i) start_pos = i + 1 + + // this isnt to handle string escaping + // its only to make sure that escaped spaces stay in + // the same token. + case '\\': + if i != len(input) - 1 && input[i+1] == ' '{ + // eat the backslash + input = input[:i] + input[i+1:] + } } if needs_alloc { diff --git a/util/shell_complete.go b/util/shell_complete.go index 86410d8..197b51b 100644 --- a/util/shell_complete.go +++ b/util/shell_complete.go @@ -80,7 +80,8 @@ func ShellCompleter(line string, vt ast.VarTable, ft ast.FuncTable) []string { completions := []string{} for _, i := range compSource { if strings.HasPrefix(i, tail) { - completions = append(completions, head + i) + str := strings.ReplaceAll(i, " ", "\\ ") + completions = append(completions, head + str) } } From 37e6e24447936225a459a2ebc0fee84ba09dd0d0 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 14 Aug 2020 15:59:38 -0700 Subject: [PATCH 07/16] add split function --- stdlib/stdlib.go | 7 +++++++ stdlib/string.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index 9080b15..83f40fd 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -143,6 +143,13 @@ func GenFuncTable() ast.FuncTable { Args: -1, }, + "split": &ast.Function{ + Function: Split, + Name: "split", + TimesCalled: 0, + Args: 2, + }, + "exit": &ast.Function{ Function: ExitShell, Name: "exit", diff --git a/stdlib/string.go b/stdlib/string.go index 3cae616..e9f00be 100644 --- a/stdlib/string.go +++ b/stdlib/string.go @@ -19,6 +19,7 @@ package stdlib import ( "fmt" + "strings" "strconv" "gitlab.com/whom/shs/ast" "gitlab.com/whom/shs/log" @@ -59,6 +60,47 @@ func StrCast(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return res } +/* Takes 2 arguments, a string and a delimiter + * returns a list of substrings found delimited by the delimiter from the parent string + * Filters out all empty segments between delimiters + * + * Example: (split "/path/to/file" "/") -> ("path" "to" "file") + */ +func Split(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + in = in.Eval(ft, vt, false) + if in == nil || in.Tag != ast.STRING || in.Next == nil || in.Next.Tag != ast.STRING { + log.Log(log.ERR, + "Args must be two strings", + "split") + return nil + } + + body := in.Value() + delim := in.Next.Value() + if len(body) < len(delim) { + log.Log(log.DEBUG, + "possibly mismatched args" + + "delimiter longer than body", + "split") + } + + var res *ast.Token + builder := &res + strtoks := strings.Split(body, delim) + for _, i := range strtoks { + if i == "" { + continue + } + + *builder = &ast.Token{Tag: ast.STRING} + (*builder).Set(i) + builder = &(*builder).Next + } + + return res +} + + /* Takes one arg, returns nil * Prints a string to stdout * Unquotes string so user can add escaped chars like \n, \t, etc From 69536783c71e0b018936e4381441e095a38cf3aa Mon Sep 17 00:00:00 2001 From: Aidan Date: Thu, 20 Aug 2020 23:39:57 -0700 Subject: [PATCH 08/16] added string join method --- stdlib/stdlib.go | 7 +++++++ stdlib/string.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index 83f40fd..fcd0b30 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -143,6 +143,13 @@ func GenFuncTable() ast.FuncTable { Args: -1, }, + "join": &ast.Function{ + Function: Join, + Name: "join", + TimesCalled: 0, + Args: 2, + }, + "split": &ast.Function{ Function: Split, Name: "split", diff --git a/stdlib/string.go b/stdlib/string.go index e9f00be..0cfe9f0 100644 --- a/stdlib/string.go +++ b/stdlib/string.go @@ -100,6 +100,50 @@ func Split(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return res } +/* Takes two args, a delimiter and a list of strings + * Returns the list of strings concatenated together with the delimiter in between each element + * On error returns nil + * + * Example: (join ", " ("apple" "ananas" "pear")) -> "apple, ananas, pear" + */ +func Join(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + in = in.Eval(ft, vt, false) + if in == nil || in.Next == nil { + log.Log(log.ERR, + "one or more arguments evaluated to nil", + "join") + return nil + } + + delim := in + strs := in.Next + if delim.Tag != ast.STRING || strs.Tag != ast.LIST { + log.Log(log.ERR, + "first argument must be a string (delimiter) and second argument must be a list (strings)", + "join") + return nil + } + + de := delim.Value() + res := "" + for i := strs.Expand(); i != nil; i = i.Next { + if i.Tag != ast.STRING { + log.Log(log.ERR, + "all items to be joined must be strings", + "join") + return nil + } + + res += i.Value() + if i.Next != nil { + res += de + } + } + + ret := &ast.Token{Tag: ast.STRING} + ret.Set(res) + return ret +} /* Takes one arg, returns nil * Prints a string to stdout From 90284f2d06da32be013fef5f427a4be194b48c76 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 21 Aug 2020 00:14:31 -0700 Subject: [PATCH 09/16] negative number parsing --- ast/lex.go | 5 ++++ stdlib/stdlib.go | 7 +++++ stdlib/string.go | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/ast/lex.go b/ast/lex.go index d7db581..aa677f5 100644 --- a/ast/lex.go +++ b/ast/lex.go @@ -215,6 +215,11 @@ error: func StrIsNumber(arg string) bool { dotCount := 0 + // negative nums + if len(arg) > 0 && arg[0] == '-' { + arg = arg[1:] + } + for _, char := range arg { if !unicode.IsDigit(char) { if char == '.' && dotCount == 0 { diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index fcd0b30..699bbfb 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -157,6 +157,13 @@ func GenFuncTable() ast.FuncTable { Args: 2, }, + "substr": &ast.Function{ + Function: Substr, + Name: "substr", + TimesCalled: 0, + Args: 3, + }, + "exit": &ast.Function{ Function: ExitShell, Name: "exit", diff --git a/stdlib/string.go b/stdlib/string.go index 0cfe9f0..140600a 100644 --- a/stdlib/string.go +++ b/stdlib/string.go @@ -145,6 +145,80 @@ func Join(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return ret } +/* takes three arguments: + * 1. start index + * 2. end index + * 3. source + * Returns a substring from source delimited by args 1 and 2. + * First two args must be integers (4 or 4.0 but not 4.3) + * + * Example: (substr 1 5 "Linus Torvalds") -> "inus " + */ +func Substr(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + start := in.Eval(ft, vt, false) + if start == nil || start.Next == nil || start.Next.Next == nil { + log.Log(log.ERR, + "an argument evaluated to nil", + "substr") + return nil + } + + end := start.Next + str := end.Next + + if start.Tag != ast.NUMBER || end.Tag != ast.NUMBER || str.Tag != ast.STRING { + log.Log(log.ERR, + "incorrect types of args", + "substr") + return nil + } + + ed_idx := 0 + st_idx, err := strconv.Atoi(start.Value()) + ed_idx, err = strconv.Atoi(end.Value()) + if err != nil { + log.Log(log.ERR, + "error parsing args: " + err.Error(), + "substr") + return nil + } + + strlen := len(str.Value()) + if st_idx < 0 { + st_idx += strlen + } + + if ed_idx < 0 { + ed_idx += strlen + } + + if st_idx < 0 || st_idx >= strlen { + log.Log(log.ERR, + "first index out of bounds", + "substr") + return nil + } + + if ed_idx < 0 || ed_idx >= strlen { + log.Log(log.ERR, + "last index out of bounds", + "substr") + return nil + } + + if st_idx > ed_idx { + log.Log(log.ERR, + "start must be less than end", + "substr") + return nil + } + + res := str.Value()[st_idx:ed_idx] + ret := &ast.Token{Tag: ast.STRING} + ret.Set(res) + return ret +} + /* Takes one arg, returns nil * Prints a string to stdout * Unquotes string so user can add escaped chars like \n, \t, etc From ab340ceb0ad25260af7d620dd6262ed3d4031394 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 21 Aug 2020 01:37:04 -0700 Subject: [PATCH 10/16] perform arg type checking and evaluation before function call --- ast/func_table.go | 77 ++++++++++++--- stdlib/arith.go | 20 ---- stdlib/bool.go | 32 +----- stdlib/filesys.go | 57 +---------- stdlib/funcs.go | 3 +- stdlib/list.go | 59 +++-------- stdlib/shell.go | 31 ------ stdlib/stdlib.go | 247 ++++++++++++++++++++++++++-------------------- stdlib/string.go | 51 +--------- 9 files changed, 223 insertions(+), 354 deletions(-) diff --git a/ast/func_table.go b/ast/func_table.go index 734f9ee..f6c9da3 100644 --- a/ast/func_table.go +++ b/ast/func_table.go @@ -38,8 +38,18 @@ type Function struct { // number of times user has called this function TimesCalled int - // number of args required - Args int + // list of types (LIST, NUMBER, STRING, etc) representing args + Args []Token_t + NumArgs bool // -1 means infinite + + // lazy arg checking (use NumArgs instead of args) + ArgLazy bool + + // dont fail on undefined symbol (passed to eval when parsing args) + SymLazy bool + + // dont eval args at all, leave that to the function + EvalLazy bool } /* holds a mapping of key to function @@ -52,24 +62,52 @@ type FuncTable *map[string]*Function * makes sure correct arguments are passed in */ func (f Function) ParseFunction(args *Token) bool { - // handle infinite args - if f.Args < 0 { + total = len(f.Args) + for iter := args; iter != nil; iter = iter.Next { + total -= 1 + if total <= 0 { + log.Log(log.ERR, + "too many arguments", + "ftable") + return false + } + + if iter.Tag != f.Args[len(f.Args) - total] { + log.Log(log.ERR, + "argument of type " + GetTagAsStr(iter.Tag) + + "passed in when " + GetTagAsStr(f.Args[len(f.Args) - total]) + + " was expected", + "ftable") + return false + } + } + + return true +} + +/* same as ParseFunction but only evaluates the number of args + */ +func (f Function) LazyParseFunction(args *Token) bool { + if (f.NumArgs < 0) { return true } - i := f.Args + total := 0 for iter := args; iter != nil; iter = iter.Next { - i -= 1 + total += 1 } - if i != 0 { + if total < f.NumArgs { log.Log(log.ERR, - "Incorrect number of arguments", - "eval") - log.Log(log.DEBUG, - fmt.Sprintf("Function %s expects %d arguments. You've provided %d arguments.", - f.Name, f.Args, f.Args - i), - "eval") + "expected more arguments, try calling info on function", + "ftable") + return false + } + + if total > f.NumArgs { + log.Log(log.ERR, + "too many args. try calling info on function", + "ftable") return false } @@ -80,7 +118,18 @@ func (f Function) ParseFunction(args *Token) bool { * calls ParseFunction and increments TimesCalled */ func (f Function) CallFunction(args *Token, vt VarTable, ft FuncTable) *Token { - if !f.ParseFunction(args) { + if not f.EvalLazy { + args = args.Eval(ft, vt, !f.SymLazy) + } + + passes := false + if f.ArgsLazy { + passes = LazyParseFunction(args) + } else { + passes = ParseFunction(args) + } + + if passes { log.Log(log.ERR, "Couldnt call " + f.Name, "eval") diff --git a/stdlib/arith.go b/stdlib/arith.go index d516551..fb1e6d6 100644 --- a/stdlib/arith.go +++ b/stdlib/arith.go @@ -36,14 +36,6 @@ import ( * 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, - "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", @@ -64,9 +56,6 @@ func NumCast(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) - for i := in; i != nil; i = i.Next { if i.Tag != ast.NUMBER { log.Log(log.ERR, "Non-number given to ADD", "add") @@ -112,9 +101,6 @@ func Add(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { func Sub(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { var res float64 var sub float64 - - in = in.Eval(f, a, false) - for i := in; i != nil; i = i.Next { if i.Tag != ast.NUMBER { log.Log(log.ERR, "Non-number given to SUB", "sub") @@ -166,9 +152,6 @@ func Sub(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { */ func Mult(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { res := 1.0 - - in = in.Eval(f, a, false) - for i := in; i != nil; i = i.Next { if i.Tag != ast.NUMBER { log.Log(log.ERR, "Non-number given to MULT", "mult") @@ -214,9 +197,6 @@ func Mult(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { */ func Div(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { var res float64 - - in = in.Eval(f, a, false) - for i := in; i != nil; i = i.Next { inner := 0.0 diff --git a/stdlib/bool.go b/stdlib/bool.go index d6b07e6..833de51 100644 --- a/stdlib/bool.go +++ b/stdlib/bool.go @@ -30,14 +30,6 @@ import ( * 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, - "only strings successfully cast to bool", - "bool cast") - return nil - } - body := in.Value() if body != ast.TRUE && body != ast.FALSE { log.Log(log.ERR, @@ -52,13 +44,6 @@ func BoolCast(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 { - log.Log(log.ERR, "non-bool argument to 'not'", "not") - return nil - } - out := ast.TRUE if in.Value() == ast.TRUE { out = ast.FALSE @@ -69,10 +54,9 @@ func Not(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return t } +// Lazy args this func Eq(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { out := ast.TRUE - - in = in.Eval(ft, vt, false) second := in.Next if in.Tag != second.Tag { @@ -155,13 +139,6 @@ func Lt(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { out := ast.TRUE second := in.Next - in = in.Eval(ft, vt, false) - - if in.Tag != ast.NUMBER || second.Tag != ast.NUMBER { - log.Log(log.ERR, "non-number argument to numeric boolean operator", ">/<=") - return nil - } - l, _ := strconv.ParseInt(in.Value(), 10, 64) r, _ := strconv.ParseInt(second.Value(), 10, 64) @@ -178,13 +155,6 @@ func Gt(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { out := ast.TRUE second := in.Next - in = in.Eval(ft, vt, false) - - if in.Tag != ast.NUMBER || second.Tag != ast.NUMBER { - log.Log(log.ERR, "non-number argument to numeric boolean operator", ">/<=") - return nil - } - l, _ := strconv.ParseInt(in.Value(), 10, 64) r, _ := strconv.ParseInt(second.Value(), 10, 64) diff --git a/stdlib/filesys.go b/stdlib/filesys.go index 182cdca..861a4a7 100644 --- a/stdlib/filesys.go +++ b/stdlib/filesys.go @@ -54,20 +54,6 @@ func AbsPath(arg string) string { * (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 { - log.Log(log.ERR, - "arguments to cd evaluated to nil!", - "cd") - return nil - } - - if in.Tag == ast.LIST { - log.Log(log.ERR, "Couldnt change dir to a list", "cd") - return nil - } - err := os.Chdir(in.Value()) if err != nil { log.Log(log.ERR, err.Error(), "cd") @@ -84,14 +70,6 @@ func Cd(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { * (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.STRING { - log.Log(log.ERR, - "argument to fexists must be a string", - "fexists") - return nil - } - filename := in.Value() out := ast.TRUE @@ -115,7 +93,6 @@ func Fexists(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { * (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 { log.Log(log.ERR, @@ -146,26 +123,12 @@ func Fread(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { * (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, - "first argument must be a filename", - "fwrite") - return nil - } - text := in.Next - if text == nil || text.Tag == ast.SYMBOL || text.Tag == ast.LIST { - log.Log(log.ERR, - "second argument must be stringable", - "fwrite") - return nil - } - err := ioutil.WriteFile( AbsPath(in.Value()), []byte(text.Value()), 0644) + if err != nil { log.Log(log.ERR, "error writing file: " + err.Error(), @@ -183,22 +146,7 @@ func Fwrite(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { * (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, - "first argument must be a filename", - "fappend") - return nil - } - text := in.Next - if text == nil || text.Tag == ast.SYMBOL || text.Tag == ast.LIST { - log.Log(log.ERR, - "second argument must be stringable", - "fappend") - return nil - } - exists := Fexists(in, vt, ft) if exists.Value() == ast.FALSE { log.Log(log.ERR, @@ -212,14 +160,15 @@ func Fappend(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { AbsPath(in.Value()), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { log.Log(log.ERR, "couldnt open file for append: " + err.Error(), "fappend") return nil } - defer f.Close() + defer f.Close() if _, err := f.WriteString(text.Value()); err != nil { log.Log(log.ERR, "error appending to file: " + err.Error(), diff --git a/stdlib/funcs.go b/stdlib/funcs.go index 63f3130..a0dd068 100644 --- a/stdlib/funcs.go +++ b/stdlib/funcs.go @@ -113,7 +113,8 @@ func DeclFunc(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Tok Function: inner, Name: name.Value(), TimesCalled: 0, - Args: numArgs, + NumArgs: numArgs, + LazyArgs: true } return nil } diff --git a/stdlib/list.go b/stdlib/list.go index 8f5d35e..e1ac6f6 100644 --- a/stdlib/list.go +++ b/stdlib/list.go @@ -30,13 +30,7 @@ import ( * in event a not-list is passed in, returns the arg. */ func Expand(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { - input = input.Eval(funcs, vars, false) - if input.Tag != ast.LIST { - log.Log(log.INFO, "expand called on not a list", "expand") - return input - } - - return input.Eval(funcs, vars, false).Expand() + return input.Expand() } /* L_APPEND (append from repl) @@ -45,7 +39,6 @@ func Expand(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token * 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 { - input = input.Eval(funcs, vars, false) src := input if input.Tag != ast.LIST { @@ -74,24 +67,28 @@ func L_append(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Tok } /* Len - * Returns length of list as a number - * Returns nil if not a list + * Returns length of list or string as a number + * Returns nil if not a list or string * * Example: () -> 0 * Example: (1 2 3) -> 3 */ func Len(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { - input = input.Eval(funcs, vars, false) - if input.Tag != ast.LIST { + if input.Tag != ast.LIST && input.Tag != ast.STRING { log.Log(log.ERR, - "non-list as parameter to head", + "must provide list or strinig", "head") return nil } length := 0 - for iter := input.Expand(); iter != nil; iter = iter.Next { - length += 1 + if input.Tag = ast.STRING { + length = len(input.Value()) + + } else { + for iter := input.Expand(); iter != nil; iter = iter.Next { + length += 1 + } } ret := &ast.Token{Tag: ast.NUMBER} @@ -106,14 +103,6 @@ func Len(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { * Example: (head (2 3 4)) -> 2 */ func Head(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { - input = input.Eval(funcs, vars, false) - if input.Tag != ast.LIST { - log.Log(log.ERR, - "non-list as parameter to head", - "head") - return nil - } - li := input.Expand().Copy() if li != nil { li.Next = nil @@ -129,15 +118,6 @@ func Head(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { * Example: (tail (2 3 4)) -> 4 */ func Tail(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { - input = input.Eval(funcs, vars, false) - if input.Tag != ast.LIST { - log.Log(log.ERR, - "non-list as parameter to head", - "head") - return nil - } - - iter := input.Expand() for iter != nil && iter.Next != nil { iter = iter.Next @@ -158,25 +138,10 @@ func Tail(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { * Example: (slice 0 0 (1 2 3)) -> (1) */ func Slice(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { - input = input.Eval(funcs, vars, false) start := input end := input.Next source := end.Next - if start.Tag != ast.NUMBER || end.Tag != ast.NUMBER { - log.Log(log.ERR, - "start and end must both be integers", - "slice") - return nil - } - - if source.Tag != ast.LIST { - log.Log(log.ERR, - "non-list as parameter to head", - "head") - return nil - } - st, err := strconv.ParseInt(start.Value(), 10, 64) en, errr := strconv.ParseInt(end.Value(), 10, 64) diff --git a/stdlib/shell.go b/stdlib/shell.go index e67a4bd..0ad770d 100644 --- a/stdlib/shell.go +++ b/stdlib/shell.go @@ -225,11 +225,6 @@ func LaunchProcess( * 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 - } - if in.Tag == ast.LIST { log.Log(log.ERR, "couldnt exec, target bin is a list", "call") return nil @@ -261,11 +256,6 @@ func Call(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { * Example: (bg vim file.txt) */ func Bgcall(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { - in = in.Eval(ft, vt, true) - if in == nil { - return nil - } - if in.Tag == ast.LIST { log.Log(log.ERR, "couldnt exec, target bin is a list", "call") return nil @@ -304,14 +294,6 @@ func Fg(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return nil } - in = in.Eval(ft, vt, false) - if in.Tag != ast.NUMBER && in.Tag != ast.STRING { - log.Log(log.ERR, - "must supply a number or string to fg", - "fg") - return nil - } - pid, err := strconv.ParseFloat(in.Value(), 64) if err != nil { log.Log(log.ERR, @@ -374,12 +356,6 @@ func Jobs(in *ast.Token, vt ast.VarTable, fg ast.FuncTable) *ast.Token { * 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 { - return nil - } - if in.Tag == ast.LIST { log.Log(log.ERR, "couldnt exec, target bin is a list", "call") return nil @@ -434,13 +410,6 @@ func GetExit(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { * (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 { - log.Log(log.ERR, "non-number argument to kill function", "kill") - return nil - } - pid, err := strconv.ParseInt(in.Value(), 10, 64) if err != nil { log.Log(log.ERR, "error parsing arg to kill: " + err.Error(), "kill") diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index 699bbfb..24d1de4 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -34,274 +34,314 @@ func GenFuncTable() ast.FuncTable { "if": &ast.Function{ Function: ShsIf, Name: "if", - TimesCalled: 0, - Args: 3, + NumArgs: 3, + EvalLazy true, + ArgLazy true }, "while": &ast.Function{ Function: ShsWhile, Name: "while", - TimesCalled: 0, - Args: -1, + NumArgs: -1, + EvalLazy true, + ArgLazy true, }, "progn": &ast.Function{ Function: ShsProgn, Name: "shs_progn", - TimesCalled: 0, - Args: -1, + NumArgs: -1, + EvalLazy: true, + ArgLazy: true, }, "func": &ast.Function{ Function: DeclFunc, Name: "decl_func", - TimesCalled: 0, - Args: 3, + Args: []ast.Token_t{ + ast.STRING, + ast.LIST, + ast.LIST + }, + EvalLazy: true, }, "len": &ast.Function{ Function: Len, Name: "len", - TimesCalled: 0, - Args: 1, + NumArgs: 1, + ArgLazy: true, }, "head": &ast.Function{ Function: Head, Name: "head", - TimesCalled: 0, - Args: 1, + Args: []ast.Token_t{ + ast.LIST + }, }, "tail": &ast.Function{ Function: Tail, Name: "tail", - TimesCalled: 0, - Args: 1, + Args: []ast.Token_t{ + ast.LIST + }, }, "slice": &ast.Function{ Function: Slice, Name: "slice", - TimesCalled: 0, - Args: 3, + Args: []ast.Token_t{ + ast.NUMBER, + ast.NUMBER, + ast.LIST + }, }, "export": &ast.Function{ Function: Export, Name: "export", - TimesCalled: 0, - Args: 2, + LazyEval: true, + Args: []ast.Token_t{ + ast.STRING, + ast.LIST + }, }, "input": &ast.Function{ Function: Input, Name: "input", - TimesCalled: 0, - Args: 1, + Args: []ast.Token_t{ + ast.STRING + }, }, "load": &ast.Function{ Function: Load, Name: "load", - TimesCalled: 0, - Args: 1, + Args: []ast.Token_t{ + ast.STRING + }, }, "bool": &ast.Function{ Function: BoolCast, Name: "bool", - TimesCalled: 0, - Args: 1, + Args: []ast.Token_t{ + ast.STRING + }, }, "string": &ast.Function{ Function: StrCast, Name: "string", - TimesCalled: 0, - Args: 1, + NumArgs: 1, + ArgLazy: true, }, "number": &ast.Function{ Function: NumCast, Name: "number", - TimesCalled: 0, - Args: 1, + Args: []ast.Token{ + ast.NUMBER + }, }, - "...": &ast.Function{ + "...": &ast.Function{ Function: Expand, Name: "...", - TimesCalled: 0, - Args: 1, + Args: []ast.Token_t{ + ast.LIST + }, }, "append": &ast.Function{ Function: L_append, Name: "append", - TimesCalled: 0, - Args: -1, - }, + NumArgs: -1, + ArgLazy: true, + }, "join": &ast.Function{ Function: Join, Name: "join", - TimesCalled: 0, - Args: 2, + Args: []ast.Token_t{ + ast.STRING, + ast.LIST + }, }, "split": &ast.Function{ Function: Split, Name: "split", - TimesCalled: 0, - Args: 2, + Args: ast.Token_t{ + ast.STRING, + ast.STRING + }, }, "substr": &ast.Function{ Function: Substr, Name: "substr", - TimesCalled: 0, - Args: 3, + Args: []ast.Token_t{ + ast.NUMBER, + ast.NUMBER, + ast.STRING + }, }, "exit": &ast.Function{ Function: ExitShell, Name: "exit", - TimesCalled: 0, - Args: 0, + Args: []ast.Token_t{}, }, "eq": &ast.Function{ Function: Eq, Name: "==", - TimesCalled: 0, Args: 2, + ArgLazy: true, }, "ne": &ast.Function{ Function: Ne, Name: "!=", - TimesCalled: 0, Args: 2, + ArgLazy: true, }, "<": &ast.Function{ Function: Lt, Name: "<", - TimesCalled: 0, - Args: 2, + Args: []ast.Token_t{ + ast.NUMBER, + ast.NUMBER + }, }, ">": &ast.Function{ Function: Gt, Name: ">", - TimesCalled: 0, - Args: 2, + Args: []ast.Token_t{ + ast.NUMBER, + ast.NUMBER + }, }, "<=": &ast.Function{ Function: Lte, Name: "<=", - TimesCalled: 0, - Args: 2, + Args: []ast.Token_t{ + ast.NUMBER, + ast.NUMBER + }, }, ">=": &ast.Function{ Function: Gte, Name: ">=", - TimesCalled: 0, - Args: 2, + Args: []ast.Token_t{ + ast.NUMBER, + ast.NUMBER + }, }, "!": &ast.Function{ Function: Not, Name: "!", - TimesCalled: 0, - Args: 1, + Args: []ast.Token_t{ + ast.Bool + }, }, "+": &ast.Function{ Function: Add, Name: "add", - TimesCalled: 0, - Args: -1, + NumArgs: -1, + ArgLazy: true, }, "-": &ast.Function{ Function: Sub, Name: "sub", - TimesCalled: 0, - Args: -1, + NumArgs: -1, + ArgLazy: true, }, "*": &ast.Function{ Function: Mult, Name: "mult", - TimesCalled: 0, - Args: -1, + NumArgs: -1, + ArgLazy: true, }, "/": &ast.Function{ Function: Div, Name: "div", - TimesCalled: 0, - Args: -1, + NumArgs: -1, + ArgLazy: true, }, "cd": &ast.Function{ Function: Cd, Name: "changedir", - TimesCalled: 0, - Args: 1, + Args: []ast.Token_t{ + ast.STRING + }, }, "concat": &ast.Function{ Function: Concat, Name:"concatenate", - TimesCalled: 0, - Args: -1, + NumArgs: -1, + ArgLazy: true, }, "print": &ast.Function{ Function: PrintStr, Name: "print", - TimesCalled: 0, - Args: 1, + Args: []ast.Token_t{ + ast.STRING + }, }, "l": &ast.Function{ Function: Call, Name: "call", - TimesCalled: 0, - Args: -1, + NumArgs: -1, + ArgLazy: true, + SymLazy: true, }, "bg": &ast.Function{ Function: Bgcall, Name: "background call", - TimesCalled: 0, - Args: -1, + NumArgs: -1, + ArgLazy: true, + SymLazy: true, }, "fg": &ast.Function{ Function: Fg, Name: "foreground", - TimesCalled: 0, - Args: 1, + NumArgs: []ast.Token_t{ + ast.NUMBER + }, }, "$": &ast.Function{ Function: ReadCmd, Name: "read cmd", - TimesCalled: 0, - Args: -1, + NumnArgs: -1, + ArgLazy: true, + SymLazy: true, }, "?": &ast.Function{ Function: GetExit, Name:"get exit code", - TimesCalled: 0, - Args: 0, + Args: ast.Token_t{}, }, /* @@ -309,7 +349,6 @@ func GenFuncTable() ast.FuncTable { "kill": &ast.Function{ Function: kill, Name: "kill job", - TimesCalled: 0, Args: 1, }, */ @@ -317,43 +356,49 @@ func GenFuncTable() ast.FuncTable { "jobs": &ast.Function{ Function: Jobs, Name: "list jobs", - TimesCalled: 0, - Args: 0, + Args: ast.Token_t{}, }, "info": &ast.Function{ Function: ShInfo, Name: "Shell Info", - TimesCalled: 0, - Args: 1, + Args: ast.Token_t{ + ast.SYMBOL + }, }, "fexists": &ast.Function{ Function: Fexists, Name: "file exists", - TimesCalled: 0, - Args: 1, + Args: []ast.Token_t{ + ast.STRING + }, }, "fread": &ast.Function{ Function: Fread, Name: "read file", - TimesCalled: 0, - Args: 1, + Args: []ast.Token_t{ + ast.STRING + }, }, "fwrite": &ast.Function{ Function: Fwrite, Name: "write file", - TimesCalled: 0, - Args: 2, + Args: []ast.Token_t{ + ast.STRING, + ast.STRING + }, }, "fappend": &ast.Function{ Function: Fappend, Name:"append to file", - TimesCalled: 0, - Args: 2, + Args: []ast.Token_t{ + ast.STRING, + ast.STRING + }, }, } @@ -395,10 +440,12 @@ func ShInfo(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { funct := ast.GetFunction(in.Value(), ft) if funct != nil { - fmt.Printf("FUNCTION\nNAME: %s\nTIMES CALLED: %s\nNUM ARGS: %d\n", funct.Name, funct.TimesCalled, funct.Args) + fmt.Printf("FUNCTION\nNAME: %s\nTIMES CALLED: %s\nNUM ARGS: %d\n", funct.Name, funct.TimesCalled, funct.NumArgs) break } + // TODO: print func args + fmt.Printf("UNKNOWN SYMBOL\n") } @@ -413,14 +460,6 @@ func ShInfo(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { * 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, - "argument to input must be a string or number", - "input") - return nil - } - prompt := in.Value() var output string @@ -438,14 +477,6 @@ func Input(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { * 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, - "argument to load must be a string", - "load") - return nil - } - bp := in.Value() bp = AbsPath(bp) util.LoadScript(bp, vt, ft) diff --git a/stdlib/string.go b/stdlib/string.go index 140600a..01d1f60 100644 --- a/stdlib/string.go +++ b/stdlib/string.go @@ -30,8 +30,6 @@ import ( * 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 for i := in; i != nil; i = i.Next { if i.Tag == ast.LIST { @@ -54,7 +52,7 @@ func Concat(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { * 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() + body := in.String() res := &ast.Token{ Tag: ast.STRING } res.Set(body) return res @@ -67,22 +65,8 @@ func StrCast(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { * Example: (split "/path/to/file" "/") -> ("path" "to" "file") */ func Split(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { - in = in.Eval(ft, vt, false) - if in == nil || in.Tag != ast.STRING || in.Next == nil || in.Next.Tag != ast.STRING { - log.Log(log.ERR, - "Args must be two strings", - "split") - return nil - } - body := in.Value() delim := in.Next.Value() - if len(body) < len(delim) { - log.Log(log.DEBUG, - "possibly mismatched args" + - "delimiter longer than body", - "split") - } var res *ast.Token builder := &res @@ -107,25 +91,11 @@ func Split(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { * Example: (join ", " ("apple" "ananas" "pear")) -> "apple, ananas, pear" */ func Join(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { - in = in.Eval(ft, vt, false) - if in == nil || in.Next == nil { - log.Log(log.ERR, - "one or more arguments evaluated to nil", - "join") - return nil - } - delim := in strs := in.Next - if delim.Tag != ast.STRING || strs.Tag != ast.LIST { - log.Log(log.ERR, - "first argument must be a string (delimiter) and second argument must be a list (strings)", - "join") - return nil - } - de := delim.Value() res := "" + for i := strs.Expand(); i != nil; i = i.Next { if i.Tag != ast.STRING { log.Log(log.ERR, @@ -155,24 +125,9 @@ func Join(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { * Example: (substr 1 5 "Linus Torvalds") -> "inus " */ func Substr(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { - start := in.Eval(ft, vt, false) - if start == nil || start.Next == nil || start.Next.Next == nil { - log.Log(log.ERR, - "an argument evaluated to nil", - "substr") - return nil - } - end := start.Next str := end.Next - if start.Tag != ast.NUMBER || end.Tag != ast.NUMBER || str.Tag != ast.STRING { - log.Log(log.ERR, - "incorrect types of args", - "substr") - return nil - } - ed_idx := 0 st_idx, err := strconv.Atoi(start.Value()) ed_idx, err = strconv.Atoi(end.Value()) @@ -226,7 +181,7 @@ func Substr(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { * 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() + body := in.String() if body[0] != body[len(body)-1] && body[0] != '"' { body = "`" + body + "`" } From 87004ff7fd7ca7c08d2c4e90fc74d22ea4b18cd1 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 21 Aug 2020 17:37:54 -0700 Subject: [PATCH 11/16] fix build from last night --- ast/func_table.go | 15 +- cmd/shs.go | 2 +- stdlib/funcs.go | 2 +- stdlib/list.go | 2 +- stdlib/stdlib.go | 401 +++++++++++++++++++++++----------------------- stdlib/string.go | 1 + 6 files changed, 210 insertions(+), 213 deletions(-) diff --git a/ast/func_table.go b/ast/func_table.go index f6c9da3..03e16bd 100644 --- a/ast/func_table.go +++ b/ast/func_table.go @@ -18,7 +18,6 @@ package ast import ( - "fmt" "gitlab.com/whom/shs/log" ) @@ -40,7 +39,7 @@ type Function struct { // list of types (LIST, NUMBER, STRING, etc) representing args Args []Token_t - NumArgs bool // -1 means infinite + NumArgs int // -1 means infinite // lazy arg checking (use NumArgs instead of args) ArgLazy bool @@ -62,7 +61,7 @@ type FuncTable *map[string]*Function * makes sure correct arguments are passed in */ func (f Function) ParseFunction(args *Token) bool { - total = len(f.Args) + total := len(f.Args) for iter := args; iter != nil; iter = iter.Next { total -= 1 if total <= 0 { @@ -88,7 +87,7 @@ func (f Function) ParseFunction(args *Token) bool { /* same as ParseFunction but only evaluates the number of args */ func (f Function) LazyParseFunction(args *Token) bool { - if (f.NumArgs < 0) { + if f.NumArgs < 0 { return true } @@ -118,15 +117,15 @@ func (f Function) LazyParseFunction(args *Token) bool { * calls ParseFunction and increments TimesCalled */ func (f Function) CallFunction(args *Token, vt VarTable, ft FuncTable) *Token { - if not f.EvalLazy { + if !f.EvalLazy { args = args.Eval(ft, vt, !f.SymLazy) } passes := false - if f.ArgsLazy { - passes = LazyParseFunction(args) + if f.ArgLazy { + passes = f.LazyParseFunction(args) } else { - passes = ParseFunction(args) + passes = f.ParseFunction(args) } if passes { diff --git a/cmd/shs.go b/cmd/shs.go index cf58d44..bad550c 100644 --- a/cmd/shs.go +++ b/cmd/shs.go @@ -95,7 +95,7 @@ func main() { } dyn_prompt := ast.GetFunction("_SH_PROMPT", funcs) - if dyn_prompt == nil || dyn_prompt.Args != 0 { + if dyn_prompt == nil || dyn_prompt.NumArgs != 0 { dyn_prompt = nil } diff --git a/stdlib/funcs.go b/stdlib/funcs.go index a0dd068..2e39599 100644 --- a/stdlib/funcs.go +++ b/stdlib/funcs.go @@ -114,7 +114,7 @@ func DeclFunc(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Tok Name: name.Value(), TimesCalled: 0, NumArgs: numArgs, - LazyArgs: true + ArgLazy: true, } return nil } diff --git a/stdlib/list.go b/stdlib/list.go index e1ac6f6..13c1cdc 100644 --- a/stdlib/list.go +++ b/stdlib/list.go @@ -82,7 +82,7 @@ func Len(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { } length := 0 - if input.Tag = ast.STRING { + if input.Tag == ast.STRING { length = len(input.Value()) } else { diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index 24d1de4..be21deb 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -20,7 +20,6 @@ package stdlib import ( "os" "fmt" - "gitlab.com/whom/shs/log" "gitlab.com/whom/shs/ast" "gitlab.com/whom/shs/util" ) @@ -32,316 +31,316 @@ func GenFuncTable() ast.FuncTable { var stdlib ast.FuncTable stdlib = &map[string]*ast.Function{ "if": &ast.Function{ - Function: ShsIf, + Function: ShsIf, Name: "if", - NumArgs: 3, - EvalLazy true, - ArgLazy true + NumArgs: 3, + EvalLazy: true, + ArgLazy: true, }, "while": &ast.Function{ - Function: ShsWhile, + Function: ShsWhile, Name: "while", - NumArgs: -1, - EvalLazy true, - ArgLazy true, + NumArgs: -1, + EvalLazy: true, + ArgLazy: true, }, "progn": &ast.Function{ Function: ShsProgn, - Name: "shs_progn", - NumArgs: -1, - EvalLazy: true, - ArgLazy: true, + Name: "shs_progn", + NumArgs: -1, + EvalLazy: true, + ArgLazy: true, }, "func": &ast.Function{ - Function: DeclFunc, - Name: "decl_func", - Args: []ast.Token_t{ + Function: DeclFunc, + Name: "decl_func", + Args: []ast.Token_t{ ast.STRING, ast.LIST, - ast.LIST + ast.LIST, }, - EvalLazy: true, + EvalLazy: true, }, "len": &ast.Function{ - Function: Len, - Name: "len", - NumArgs: 1, - ArgLazy: true, + Function: Len, + Name: "len", + NumArgs: 1, + ArgLazy: true, }, "head": &ast.Function{ - Function: Head, + Function: Head, Name: "head", - Args: []ast.Token_t{ - ast.LIST + Args: []ast.Token_t{ + ast.LIST, }, }, "tail": &ast.Function{ Function: Tail, - Name: "tail", - Args: []ast.Token_t{ - ast.LIST + Name: "tail", + Args: []ast.Token_t{ + ast.LIST, }, }, "slice": &ast.Function{ Function: Slice, - Name: "slice", - Args: []ast.Token_t{ + Name: "slice", + Args: []ast.Token_t{ ast.NUMBER, ast.NUMBER, - ast.LIST + ast.LIST, }, }, "export": &ast.Function{ - Function: Export, - Name: "export", - LazyEval: true, - Args: []ast.Token_t{ + Function: Export, + Name: "export", + EvalLazy: true, + Args: []ast.Token_t{ ast.STRING, - ast.LIST + ast.LIST, }, }, "input": &ast.Function{ - Function: Input, - Name: "input", - Args: []ast.Token_t{ - ast.STRING + Function: Input, + Name: "input", + Args: []ast.Token_t{ + ast.STRING, }, }, "load": &ast.Function{ - Function: Load, - Name: "load", - Args: []ast.Token_t{ - ast.STRING + Function: Load, + Name: "load", + Args: []ast.Token_t{ + ast.STRING, }, }, "bool": &ast.Function{ - Function: BoolCast, - Name: "bool", - Args: []ast.Token_t{ - ast.STRING + Function: BoolCast, + Name: "bool", + Args: []ast.Token_t{ + ast.STRING, }, }, "string": &ast.Function{ - Function: StrCast, - Name: "string", - NumArgs: 1, - ArgLazy: true, + Function: StrCast, + Name: "string", + NumArgs: 1, + ArgLazy: true, }, "number": &ast.Function{ - Function: NumCast, - Name: "number", - Args: []ast.Token{ - ast.NUMBER + Function: NumCast, + Name: "number", + Args: []ast.Token_t{ + ast.NUMBER, }, }, "...": &ast.Function{ - Function: Expand, - Name: "...", - Args: []ast.Token_t{ - ast.LIST + Function: Expand, + Name: "...", + Args: []ast.Token_t{ + ast.LIST, }, }, "append": &ast.Function{ - Function: L_append, - Name: "append", - NumArgs: -1, - ArgLazy: true, + Function: L_append, + Name: "append", + NumArgs: -1, + ArgLazy: true, }, "join": &ast.Function{ - Function: Join, - Name: "join", - Args: []ast.Token_t{ + Function: Join, + Name: "join", + Args: []ast.Token_t{ ast.STRING, - ast.LIST + ast.LIST, }, }, "split": &ast.Function{ Function: Split, - Name: "split", - Args: ast.Token_t{ + Name: "split", + Args: []ast.Token_t{ + ast.STRING, ast.STRING, - ast.STRING }, }, "substr": &ast.Function{ - Function: Substr, - Name: "substr", - Args: []ast.Token_t{ + Function: Substr, + Name: "substr", + Args: []ast.Token_t{ ast.NUMBER, ast.NUMBER, - ast.STRING + ast.STRING, }, }, "exit": &ast.Function{ - Function: ExitShell, - Name: "exit", - Args: []ast.Token_t{}, + Function: ExitShell, + Name: "exit", + Args: []ast.Token_t{}, }, "eq": &ast.Function{ - Function: Eq, - Name: "==", - Args: 2, - ArgLazy: true, + Function: Eq, + Name: "==", + NumArgs: 2, + ArgLazy: true, }, "ne": &ast.Function{ - Function: Ne, - Name: "!=", - Args: 2, - ArgLazy: true, + Function: Ne, + Name: "!=", + NumArgs: 2, + ArgLazy: true, }, "<": &ast.Function{ - Function: Lt, - Name: "<", - Args: []ast.Token_t{ + Function: Lt, + Name: "<", + Args: []ast.Token_t{ + ast.NUMBER, ast.NUMBER, - ast.NUMBER }, }, ">": &ast.Function{ - Function: Gt, - Name: ">", - Args: []ast.Token_t{ + Function: Gt, + Name: ">", + Args: []ast.Token_t{ + ast.NUMBER, ast.NUMBER, - ast.NUMBER }, }, "<=": &ast.Function{ - Function: Lte, - Name: "<=", - Args: []ast.Token_t{ + Function: Lte, + Name: "<=", + Args: []ast.Token_t{ + ast.NUMBER, ast.NUMBER, - ast.NUMBER }, }, ">=": &ast.Function{ - Function: Gte, - Name: ">=", - Args: []ast.Token_t{ + Function: Gte, + Name: ">=", + Args: []ast.Token_t{ + ast.NUMBER, ast.NUMBER, - ast.NUMBER }, }, "!": &ast.Function{ - Function: Not, - Name: "!", - Args: []ast.Token_t{ - ast.Bool + Function: Not, + Name: "!", + Args: []ast.Token_t{ + ast.BOOL, }, }, "+": &ast.Function{ - Function: Add, - Name: "add", - NumArgs: -1, - ArgLazy: true, + Function: Add, + Name: "add", + NumArgs: -1, + ArgLazy: true, }, "-": &ast.Function{ - Function: Sub, - Name: "sub", - NumArgs: -1, - ArgLazy: true, + Function: Sub, + Name: "sub", + NumArgs: -1, + ArgLazy: true, }, "*": &ast.Function{ - Function: Mult, + Function: Mult, Name: "mult", - NumArgs: -1, - ArgLazy: true, + NumArgs: -1, + ArgLazy: true, }, "/": &ast.Function{ - Function: Div, - Name: "div", - NumArgs: -1, - ArgLazy: true, + Function: Div, + Name: "div", + NumArgs: -1, + ArgLazy: true, }, "cd": &ast.Function{ - Function: Cd, - Name: "changedir", - Args: []ast.Token_t{ - ast.STRING + Function: Cd, + Name: "changedir", + Args: []ast.Token_t{ + ast.STRING, }, }, "concat": &ast.Function{ Function: Concat, - Name:"concatenate", - NumArgs: -1, - ArgLazy: true, + Name: "concatenate", + NumArgs: -1, + ArgLazy: true, }, "print": &ast.Function{ - Function: PrintStr, - Name: "print", - Args: []ast.Token_t{ - ast.STRING + Function: PrintStr, + Name: "print", + Args: []ast.Token_t{ + ast.STRING, }, }, "l": &ast.Function{ - Function: Call, + Function: Call, Name: "call", - NumArgs: -1, - ArgLazy: true, - SymLazy: true, + NumArgs: -1, + ArgLazy: true, + SymLazy: true, }, "bg": &ast.Function{ - Function: Bgcall, - Name: "background call", - NumArgs: -1, - ArgLazy: true, - SymLazy: true, + Function: Bgcall, + Name: "background call", + NumArgs: -1, + ArgLazy: true, + SymLazy: true, }, "fg": &ast.Function{ - Function: Fg, - Name: "foreground", - NumArgs: []ast.Token_t{ - ast.NUMBER + Function: Fg, + Name: "foreground", + Args: []ast.Token_t{ + ast.NUMBER, }, }, "$": &ast.Function{ - Function: ReadCmd, - Name: "read cmd", - NumnArgs: -1, - ArgLazy: true, - SymLazy: true, + Function: ReadCmd, + Name: "read cmd", + NumArgs: -1, + ArgLazy: true, + SymLazy: true, }, "?": &ast.Function{ - Function: GetExit, - Name:"get exit code", - Args: ast.Token_t{}, + Function: GetExit, + Name: "get exit code", + Args: []ast.Token_t{}, }, /* @@ -354,50 +353,50 @@ func GenFuncTable() ast.FuncTable { */ "jobs": &ast.Function{ - Function: Jobs, - Name: "list jobs", - Args: ast.Token_t{}, + Function: Jobs, + Name: "list jobs", + Args: []ast.Token_t{}, }, "info": &ast.Function{ - Function: ShInfo, - Name: "Shell Info", - Args: ast.Token_t{ - ast.SYMBOL + Function: ShInfo, + Name: "Shell Info", + Args: []ast.Token_t{ + ast.SYMBOL, }, }, "fexists": &ast.Function{ - Function: Fexists, - Name: "file exists", - Args: []ast.Token_t{ - ast.STRING + Function: Fexists, + Name: "file exists", + Args: []ast.Token_t{ + ast.STRING, }, }, "fread": &ast.Function{ - Function: Fread, - Name: "read file", - Args: []ast.Token_t{ - ast.STRING + Function: Fread, + Name: "read file", + Args: []ast.Token_t{ + ast.STRING, }, }, "fwrite": &ast.Function{ - Function: Fwrite, - Name: "write file", - Args: []ast.Token_t{ + Function: Fwrite, + Name: "write file", + Args: []ast.Token_t{ + ast.STRING, ast.STRING, - ast.STRING }, }, "fappend": &ast.Function{ - Function: Fappend, - Name:"append to file", - Args: []ast.Token_t{ + Function: Fappend, + Name: "append to file", + Args: []ast.Token_t{ + ast.STRING, ast.STRING, - ast.STRING }, }, } @@ -421,34 +420,32 @@ func ExitShell(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { * 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()) - case ast.STRING: - fmt.Printf("STRING LITERAL \nValue: %s\n", in.Value()) - case ast.NUMBER: - fmt.Printf("NUMBER LITERAL \nValue: %s\n", in.Value()) - case ast.LIST: - fmt.Printf("LIST \nString Value: %s, AST:\n", in.String()) - ast.PrintSExprsIndividually(in) - case ast.SYMBOL: - repr := ast.GetVar(in.Value(), vt) - if repr != nil { - fmt.Printf("VARIABLE\nTYPE: %s\nVALUE: %s\n", ast.GetTagAsStr(repr.Tag), repr.Value()) - break - } - - funct := ast.GetFunction(in.Value(), ft) - if funct != nil { - fmt.Printf("FUNCTION\nNAME: %s\nTIMES CALLED: %s\nNUM ARGS: %d\n", funct.Name, funct.TimesCalled, funct.NumArgs) - break - } - - // TODO: print func args - - fmt.Printf("UNKNOWN SYMBOL\n") + repr := ast.GetVar(in.Value(), vt) + if repr != nil { + fmt.Printf("VARIABLE\nTYPE: %s\nVALUE: %s\n", ast.GetTagAsStr(repr.Tag), repr.Value()) + return nil } + funct := ast.GetFunction(in.Value(), ft) + if funct != nil { + fmt.Printf("FUNCTION\nNAME: %s\nTIMES CALLED: %s\n\n", funct.Name, funct.TimesCalled) + if funct.ArgLazy { + fmt.Printf("function does not evaluate args by type\nARG COUNT: %s\n", funct.NumArgs) + } else { + fmt.Printf("function evaluates args by type\nARGS: ") + for _, i := range(funct.Args) { + fmt.Printf(ast.GetTagAsStr(i)) + } + } + + fmt.Printf("SYMLAZY: %t\n", funct.SymLazy) + fmt.Printf("(can undefined symbols be passed in)\n") + fmt.Printf("EVALLAZY: %t\n", funct.EvalLazy) + fmt.Printf("(are all forms in evaluated before function call)\n") + return nil + } + + fmt.Printf("UNKNOWN SYMBOL\n") return nil } diff --git a/stdlib/string.go b/stdlib/string.go index 01d1f60..090932e 100644 --- a/stdlib/string.go +++ b/stdlib/string.go @@ -125,6 +125,7 @@ func Join(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { * Example: (substr 1 5 "Linus Torvalds") -> "inus " */ func Substr(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + start := in end := start.Next str := end.Next From 4ce1f7137cf01353c71d6bbf173578e107472b8f Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 21 Aug 2020 18:02:49 -0700 Subject: [PATCH 12/16] better log line, fix issue with sym eval --- ast/func_table.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ast/func_table.go b/ast/func_table.go index 03e16bd..2121874 100644 --- a/ast/func_table.go +++ b/ast/func_table.go @@ -73,9 +73,8 @@ func (f Function) ParseFunction(args *Token) bool { if iter.Tag != f.Args[len(f.Args) - total] { log.Log(log.ERR, - "argument of type " + GetTagAsStr(iter.Tag) + - "passed in when " + GetTagAsStr(f.Args[len(f.Args) - total]) + - " was expected", + "argument is " + GetTagAsStr(iter.Tag) + + "should be " + GetTagAsStr(f.Args[len(f.Args) - total]), "ftable") return false } @@ -118,7 +117,7 @@ func (f Function) LazyParseFunction(args *Token) bool { */ func (f Function) CallFunction(args *Token, vt VarTable, ft FuncTable) *Token { if !f.EvalLazy { - args = args.Eval(ft, vt, !f.SymLazy) + args = args.Eval(ft, vt, f.SymLazy) } passes := false From 6afd01da2ac11a22a6382018abde0aacbdd21362 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 22 Aug 2020 12:37:06 -0700 Subject: [PATCH 13/16] syncing unfinished work with dev --- ast/func_table.go | 15 ++++++++++++--- stdlib/stdlib.go | 6 ++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/ast/func_table.go b/ast/func_table.go index 2121874..0d8fa12 100644 --- a/ast/func_table.go +++ b/ast/func_table.go @@ -18,6 +18,7 @@ package ast import ( + "fmt" "gitlab.com/whom/shs/log" ) @@ -64,7 +65,7 @@ func (f Function) ParseFunction(args *Token) bool { total := len(f.Args) for iter := args; iter != nil; iter = iter.Next { total -= 1 - if total <= 0 { + if total < 0 { log.Log(log.ERR, "too many arguments", "ftable") @@ -74,12 +75,19 @@ func (f Function) ParseFunction(args *Token) bool { if iter.Tag != f.Args[len(f.Args) - total] { log.Log(log.ERR, "argument is " + GetTagAsStr(iter.Tag) + - "should be " + GetTagAsStr(f.Args[len(f.Args) - total]), + " should be " + GetTagAsStr(f.Args[len(f.Args) - total]), "ftable") return false } } + if total > 0 { + log.Log(log.ERR, + "not enough args given", + "ftable") + return false + } + return true } @@ -127,13 +135,14 @@ func (f Function) CallFunction(args *Token, vt VarTable, ft FuncTable) *Token { passes = f.ParseFunction(args) } - if passes { + if !passes { log.Log(log.ERR, "Couldnt call " + f.Name, "eval") return nil } + fmt.Printf("ARGS: %+v", *args) f.TimesCalled += 1 return f.Function(args, vt, ft) } diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index be21deb..d76f7e6 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -102,10 +102,8 @@ func GenFuncTable() ast.FuncTable { Function: Export, Name: "export", EvalLazy: true, - Args: []ast.Token_t{ - ast.STRING, - ast.LIST, - }, + ArgLazy: true, + NumArgs: 2, }, "input": &ast.Function{ From 591402b428426e5ffe8143b0dfdfc20738657302 Mon Sep 17 00:00:00 2001 From: Aidan Date: Wed, 26 Aug 2020 21:36:06 -0700 Subject: [PATCH 14/16] finish fixing things --- ast/eval.go | 4 +++- ast/func_table.go | 25 +++++++++++++------------ ast/lex.go | 2 +- stdlib/stdlib.go | 4 ++-- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/ast/eval.go b/ast/eval.go index e82a89b..cbbae5d 100644 --- a/ast/eval.go +++ b/ast/eval.go @@ -17,7 +17,9 @@ package ast -import "gitlab.com/whom/shs/log" +import ( + "gitlab.com/whom/shs/log" +) /* determines whether or not to execute a system binary * when a function cannot be found in the functable diff --git a/ast/func_table.go b/ast/func_table.go index 0d8fa12..001f149 100644 --- a/ast/func_table.go +++ b/ast/func_table.go @@ -18,7 +18,6 @@ package ast import ( - "fmt" "gitlab.com/whom/shs/log" ) @@ -62,26 +61,26 @@ type FuncTable *map[string]*Function * makes sure correct arguments are passed in */ func (f Function) ParseFunction(args *Token) bool { - total := len(f.Args) + inc := 0 for iter := args; iter != nil; iter = iter.Next { - total -= 1 - if total < 0 { + inc += 1 + if inc > len(f.Args) { log.Log(log.ERR, "too many arguments", "ftable") return false } - if iter.Tag != f.Args[len(f.Args) - total] { + if iter.Tag != f.Args[inc - 1] { log.Log(log.ERR, "argument is " + GetTagAsStr(iter.Tag) + - " should be " + GetTagAsStr(f.Args[len(f.Args) - total]), + " should be " + GetTagAsStr(f.Args[inc - 1]), "ftable") return false } } - if total > 0 { + if inc < len(f.Args) { log.Log(log.ERR, "not enough args given", "ftable") @@ -124,15 +123,18 @@ func (f Function) LazyParseFunction(args *Token) bool { * calls ParseFunction and increments TimesCalled */ func (f Function) CallFunction(args *Token, vt VarTable, ft FuncTable) *Token { + var n_args *Token if !f.EvalLazy { - args = args.Eval(ft, vt, f.SymLazy) + n_args = args.Eval(ft, vt, f.SymLazy) + } else { + n_args = args } passes := false if f.ArgLazy { - passes = f.LazyParseFunction(args) + passes = f.LazyParseFunction(n_args) } else { - passes = f.ParseFunction(args) + passes = f.ParseFunction(n_args) } if !passes { @@ -142,9 +144,8 @@ func (f Function) CallFunction(args *Token, vt VarTable, ft FuncTable) *Token { return nil } - fmt.Printf("ARGS: %+v", *args) f.TimesCalled += 1 - return f.Function(args, vt, ft) + return f.Function(n_args, vt, ft) } /* searches for function mapped to argument in FuncTable diff --git a/ast/lex.go b/ast/lex.go index aa677f5..7c33bac 100644 --- a/ast/lex.go +++ b/ast/lex.go @@ -216,7 +216,7 @@ func StrIsNumber(arg string) bool { dotCount := 0 // negative nums - if len(arg) > 0 && arg[0] == '-' { + if len(arg) > 1 && arg[0] == '-' { arg = arg[1:] } diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index d76f7e6..dd70b5e 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -58,7 +58,7 @@ func GenFuncTable() ast.FuncTable { Function: DeclFunc, Name: "decl_func", Args: []ast.Token_t{ - ast.STRING, + ast.SYMBOL, ast.LIST, ast.LIST, }, @@ -141,7 +141,7 @@ func GenFuncTable() ast.FuncTable { Function: NumCast, Name: "number", Args: []ast.Token_t{ - ast.NUMBER, + ast.STRING, }, }, From 12caeedf682532d890f07e683b454a769393c6b2 Mon Sep 17 00:00:00 2001 From: Aidan Date: Wed, 26 Aug 2020 22:32:01 -0700 Subject: [PATCH 15/16] add a string replace method --- ast/func_table.go | 4 +--- stdlib/shell.go | 5 +++++ stdlib/stdlib.go | 11 +++++++++++ stdlib/string.go | 19 +++++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/ast/func_table.go b/ast/func_table.go index 001f149..81e006b 100644 --- a/ast/func_table.go +++ b/ast/func_table.go @@ -123,11 +123,9 @@ func (f Function) LazyParseFunction(args *Token) bool { * calls ParseFunction and increments TimesCalled */ func (f Function) CallFunction(args *Token, vt VarTable, ft FuncTable) *Token { - var n_args *Token + n_args := args if !f.EvalLazy { n_args = args.Eval(ft, vt, f.SymLazy) - } else { - n_args = args } passes := false diff --git a/stdlib/shell.go b/stdlib/shell.go index 0ad770d..823bff3 100644 --- a/stdlib/shell.go +++ b/stdlib/shell.go @@ -225,6 +225,11 @@ func LaunchProcess( * Example (l vim file.txt) */ func Call(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + if in == nil { + log.Log(log.ERR, "no arguments given", "call") + return nil + } + if in.Tag == ast.LIST { log.Log(log.ERR, "couldnt exec, target bin is a list", "call") return nil diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index dd70b5e..7f85b05 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -178,6 +178,16 @@ func GenFuncTable() ast.FuncTable { }, }, + "replace": &ast.Function{ + Function: Replace, + Name: "replace", + Args: []ast.Token_t{ + ast.STRING, + ast.STRING, + ast.STRING, + }, + }, + "substr": &ast.Function{ Function: Substr, Name: "substr", @@ -283,6 +293,7 @@ func GenFuncTable() ast.FuncTable { "cd": &ast.Function{ Function: Cd, Name: "changedir", + SymLazy: true, Args: []ast.Token_t{ ast.STRING, }, diff --git a/stdlib/string.go b/stdlib/string.go index 090932e..b3d7a72 100644 --- a/stdlib/string.go +++ b/stdlib/string.go @@ -84,6 +84,25 @@ func Split(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { return res } +/* Takes three args + * 1. source string + * 2. token to be replaced + * 3. token to swap in place + * All three args are strings + * Returns final string + */ +func Replace(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { + body := in.Value() + sub := in.Next.Value() + tok := in.Next.Next.Value() + + body = strings.ReplaceAll(body, sub, tok) + + res := &ast.Token{Tag: ast.STRING} + res.Set(body) + return res +} + /* Takes two args, a delimiter and a list of strings * Returns the list of strings concatenated together with the delimiter in between each element * On error returns nil From fcdde50faaaac5e79a2de08b70de0ade9b190cd9 Mon Sep 17 00:00:00 2001 From: Aidan Date: Wed, 26 Aug 2020 23:45:02 -0700 Subject: [PATCH 16/16] added list index function --- stdlib/list.go | 39 +++++++++++++++++++++++++++++++++++++++ stdlib/stdlib.go | 9 +++++++++ 2 files changed, 48 insertions(+) diff --git a/stdlib/list.go b/stdlib/list.go index 13c1cdc..5e34364 100644 --- a/stdlib/list.go +++ b/stdlib/list.go @@ -96,6 +96,45 @@ func Len(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { return ret } +/* Takes two arguments + * An index and a list + * Returns element indexed by index arg + * can return nil if index is out of bounds + * + * Example (index 2 (1 2 3)) -> 2 + */ +func Index(in *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { + idx, err := strconv.ParseInt(in.Value(), 10, 64) + if err != nil || idx < 0 { + log.Log(log.ERR, + "index must be a positive integer: " + err.Error(), + "index") + return nil + } + + series := in.Next.Expand() + iter := &series + i := int64(0) + for i <= idx { + if *iter == nil { + log.Log(log.ERR, + "index out of bounds", + "index") + return nil + } + + if i < idx { + iter = &((*iter).Next) + } + + i += 1 + } + + ret := (*iter).Copy() + ret.Next = nil + return ret +} + /* Head * Returns first element in the list * Returns nil if input is not a list or if list is empty diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go index 7f85b05..7781a96 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -72,6 +72,15 @@ func GenFuncTable() ast.FuncTable { ArgLazy: true, }, + "index": &ast.Function{ + Function: Index, + Name: "index", + Args: []ast.Token_t{ + ast.NUMBER, + ast.LIST, + }, + }, + "head": &ast.Function{ Function: Head, Name: "head",