/* SHS: Syntactically Homogeneous Shell * Copyright (C) 2019 Aidan Hahn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package stdlib import ( "os" "fmt" "gitlab.com/whom/shs/log" "gitlab.com/whom/shs/ast" "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: ShsIf, Name: "if", TimesCalled: 0, Args: 3, }, "while": &ast.Function{ Function: ShsWhile, Name: "while", TimesCalled: 0, Args: -1, }, "progn": &ast.Function{ Function: ShsProgn, Name: "shs_progn", TimesCalled: 0, Args: -1, }, "func": &ast.Function{ Function: DeclFunc, Name: "decl_func", TimesCalled: 0, 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, }, "slice": &ast.Function{ Function: Slice, Name: "slice", TimesCalled: 0, Args: 3, }, "export": &ast.Function{ Function: Export, Name: "export", TimesCalled: 0, Args: 2, }, "input": &ast.Function{ Function: Input, Name: "input", TimesCalled: 0, Args: 1, }, "load": &ast.Function{ Function: Load, Name: "load", TimesCalled: 0, Args: 1, }, "bool": &ast.Function{ Function: BoolCast, Name: "bool", TimesCalled: 0, Args: 1, }, "string": &ast.Function{ Function: StrCast, Name: "string", TimesCalled: 0, Args: 1, }, "number": &ast.Function{ Function: NumCast, Name: "number", TimesCalled: 0, Args: 1, }, "...": &ast.Function{ Function: Expand, Name: "...", TimesCalled: 0, Args: 1, }, "append": &ast.Function{ Function: L_append, Name: "append", TimesCalled: 0, Args: -1, }, "join": &ast.Function{ Function: Join, Name: "join", TimesCalled: 0, Args: 2, }, "split": &ast.Function{ Function: Split, Name: "split", TimesCalled: 0, Args: 2, }, "exit": &ast.Function{ Function: ExitShell, Name: "exit", TimesCalled: 0, Args: 0, }, "eq": &ast.Function{ Function: Eq, Name: "==", TimesCalled: 0, Args: 2, }, "ne": &ast.Function{ Function: Ne, Name: "!=", TimesCalled: 0, Args: 2, }, "<": &ast.Function{ Function: Lt, Name: "<", TimesCalled: 0, Args: 2, }, ">": &ast.Function{ Function: Gt, Name: ">", TimesCalled: 0, Args: 2, }, "<=": &ast.Function{ Function: Lte, Name: "<=", TimesCalled: 0, Args: 2, }, ">=": &ast.Function{ Function: Gte, Name: ">=", TimesCalled: 0, Args: 2, }, "!": &ast.Function{ Function: Not, Name: "!", TimesCalled: 0, Args: 1, }, "+": &ast.Function{ Function: Add, Name: "add", TimesCalled: 0, Args: -1, }, "-": &ast.Function{ Function: Sub, Name: "sub", TimesCalled: 0, Args: -1, }, "*": &ast.Function{ Function: Mult, Name: "mult", TimesCalled: 0, Args: -1, }, "/": &ast.Function{ Function: Div, Name: "div", TimesCalled: 0, Args: -1, }, "cd": &ast.Function{ Function: Cd, Name: "changedir", TimesCalled: 0, Args: 1, }, "concat": &ast.Function{ Function: Concat, Name:"concatenate", TimesCalled: 0, Args: -1, }, "print": &ast.Function{ Function: PrintStr, Name: "print", TimesCalled: 0, Args: 1, }, "l": &ast.Function{ Function: Call, Name: "call", TimesCalled: 0, Args: -1, }, "bg": &ast.Function{ Function: Bgcall, Name: "background call", TimesCalled: 0, Args: -1, }, "fg": &ast.Function{ Function: Fg, Name: "foreground", TimesCalled: 0, Args: 1, }, "$": &ast.Function{ Function: ReadCmd, Name: "read cmd", TimesCalled: 0, Args: -1, }, "?": &ast.Function{ Function: GetExit, Name:"get exit code", TimesCalled: 0, Args: 0, }, /* USE NATIVE KILL COMMAND. "kill": &ast.Function{ Function: kill, Name: "kill job", TimesCalled: 0, Args: 1, }, */ "jobs": &ast.Function{ Function: Jobs, Name: "list jobs", TimesCalled: 0, Args: 0, }, "info": &ast.Function{ Function: ShInfo, Name: "Shell Info", TimesCalled: 0, Args: 1, }, "fexists": &ast.Function{ Function: Fexists, Name: "file exists", TimesCalled: 0, Args: 1, }, "fread": &ast.Function{ Function: Fread, Name: "read file", TimesCalled: 0, Args: 1, }, "fwrite": &ast.Function{ Function: Fwrite, Name: "write file", TimesCalled: 0, Args: 2, }, "fappend": &ast.Function{ Function: Fappend, Name:"append to file", TimesCalled: 0, Args: 2, }, } return stdlib } /* 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 } /* 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()) 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.Args) break } fmt.Printf("UNKNOWN SYMBOL\n") } return nil } /* 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, "argument to input must be a string or number", "input") return nil } prompt := in.Value() var output string fmt.Printf(prompt) fmt.Scanln(&output) ret := &ast.Token{Tag: ast.STRING} ret.Set(output) return ret } /* 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, "argument to load must be a string", "load") return nil } bp := in.Value() bp = AbsPath(bp) util.LoadScript(bp, vt, ft) return nil }