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..f597eee 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 @@ -134,6 +131,9 @@ func DeleteVarTable(table VarTable) { } } +/* 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/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/stdlib/arith.go b/stdlib/arith.go index a9051eb..d516551 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,13 @@ 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 { +/* adds N number arguments + * takes N arguments + * returns the sum, or nil if improper arguments were given + * + * Example: (+ 1 2) + */ +func Add(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { var res float64 in = in.Eval(f, a, false) @@ -92,7 +104,12 @@ func add(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { return t } -func sub(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { +/* subtract N args from the final arg + * takes N args, returns nil if improper args given + * + * Example: (- 2 1) + */ +func Sub(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { var res float64 var sub float64 @@ -142,7 +159,12 @@ func sub(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { return t } -func mult(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { +/* multiplies N arguments + * returns nil if an improper argument is given + * + * Example: (* 1 2) + */ +func Mult(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { res := 1.0 in = in.Eval(f, a, false) @@ -184,7 +206,13 @@ func mult(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { return t } -func div(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { +/* divide N arguments + * the first argument is divided by each subsequent argument in order + * returns nil if an improper argument is given + * + * Example (/ 25 5) + */ +func Div(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token { var res float64 in = in.Eval(f, a, false) 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/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..182cdca 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,9 +107,16 @@ 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 + 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", @@ -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, @@ -163,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/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..83749c2 100644 --- a/stdlib/stdlib.go +++ b/stdlib/stdlib.go @@ -25,235 +25,231 @@ 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{ "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, - 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 +266,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 +311,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 +356,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 +383,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) 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 67e9b6c..a6e3289 100644 --- a/util/shell_complete.go +++ b/util/shell_complete.go @@ -18,15 +18,16 @@ package util import ( - "fmt" "strings" "io/ioutil" "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 +/* 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