From ee39de01fd1224e1b95af54b4c4a3adf3235568e Mon Sep 17 00:00:00 2001 From: Aidan Date: Sun, 19 Jul 2020 14:37:20 -0700 Subject: [PATCH] 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)