Merge branch 'dev' into 'master'
Merge new features to master See merge request whom/shs!10
This commit is contained in:
commit
c2b86b7f4d
14 changed files with 720 additions and 355 deletions
|
|
@ -17,7 +17,9 @@
|
||||||
|
|
||||||
package ast
|
package ast
|
||||||
|
|
||||||
import "gitlab.com/whom/shs/log"
|
import (
|
||||||
|
"gitlab.com/whom/shs/log"
|
||||||
|
)
|
||||||
|
|
||||||
/* determines whether or not to execute a system binary
|
/* determines whether or not to execute a system binary
|
||||||
* when a function cannot be found in the functable
|
* when a function cannot be found in the functable
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"gitlab.com/whom/shs/log"
|
"gitlab.com/whom/shs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -38,8 +37,18 @@ type Function struct {
|
||||||
// number of times user has called this function
|
// number of times user has called this function
|
||||||
TimesCalled int
|
TimesCalled int
|
||||||
|
|
||||||
// number of args required
|
// list of types (LIST, NUMBER, STRING, etc) representing args
|
||||||
Args int
|
Args []Token_t
|
||||||
|
NumArgs int // -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
|
/* holds a mapping of key to function
|
||||||
|
|
@ -52,24 +61,58 @@ type FuncTable *map[string]*Function
|
||||||
* makes sure correct arguments are passed in
|
* makes sure correct arguments are passed in
|
||||||
*/
|
*/
|
||||||
func (f Function) ParseFunction(args *Token) bool {
|
func (f Function) ParseFunction(args *Token) bool {
|
||||||
// handle infinite args
|
inc := 0
|
||||||
if f.Args < 0 {
|
for iter := args; iter != nil; iter = iter.Next {
|
||||||
|
inc += 1
|
||||||
|
if inc > len(f.Args) {
|
||||||
|
log.Log(log.ERR,
|
||||||
|
"too many arguments",
|
||||||
|
"ftable")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if iter.Tag != f.Args[inc - 1] {
|
||||||
|
log.Log(log.ERR,
|
||||||
|
"argument is " + GetTagAsStr(iter.Tag) +
|
||||||
|
" should be " + GetTagAsStr(f.Args[inc - 1]),
|
||||||
|
"ftable")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if inc < len(f.Args) {
|
||||||
|
log.Log(log.ERR,
|
||||||
|
"not enough args given",
|
||||||
|
"ftable")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
i := f.Args
|
/* same as ParseFunction but only evaluates the number of args
|
||||||
for iter := args; iter != nil; iter = iter.Next {
|
*/
|
||||||
i -= 1
|
func (f Function) LazyParseFunction(args *Token) bool {
|
||||||
|
if f.NumArgs < 0 {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if i != 0 {
|
total := 0
|
||||||
|
for iter := args; iter != nil; iter = iter.Next {
|
||||||
|
total += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if total < f.NumArgs {
|
||||||
log.Log(log.ERR,
|
log.Log(log.ERR,
|
||||||
"Incorrect number of arguments",
|
"expected more arguments, try calling info on function",
|
||||||
"eval")
|
"ftable")
|
||||||
log.Log(log.DEBUG,
|
return false
|
||||||
fmt.Sprintf("Function %s expects %d arguments. You've provided %d arguments.",
|
}
|
||||||
f.Name, f.Args, f.Args - i),
|
|
||||||
"eval")
|
if total > f.NumArgs {
|
||||||
|
log.Log(log.ERR,
|
||||||
|
"too many args. try calling info on function",
|
||||||
|
"ftable")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,7 +123,19 @@ func (f Function) ParseFunction(args *Token) bool {
|
||||||
* calls ParseFunction and increments TimesCalled
|
* calls ParseFunction and increments TimesCalled
|
||||||
*/
|
*/
|
||||||
func (f Function) CallFunction(args *Token, vt VarTable, ft FuncTable) *Token {
|
func (f Function) CallFunction(args *Token, vt VarTable, ft FuncTable) *Token {
|
||||||
if !f.ParseFunction(args) {
|
n_args := args
|
||||||
|
if !f.EvalLazy {
|
||||||
|
n_args = args.Eval(ft, vt, f.SymLazy)
|
||||||
|
}
|
||||||
|
|
||||||
|
passes := false
|
||||||
|
if f.ArgLazy {
|
||||||
|
passes = f.LazyParseFunction(n_args)
|
||||||
|
} else {
|
||||||
|
passes = f.ParseFunction(n_args)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !passes {
|
||||||
log.Log(log.ERR,
|
log.Log(log.ERR,
|
||||||
"Couldnt call " + f.Name,
|
"Couldnt call " + f.Name,
|
||||||
"eval")
|
"eval")
|
||||||
|
|
@ -88,7 +143,7 @@ func (f Function) CallFunction(args *Token, vt VarTable, ft FuncTable) *Token {
|
||||||
}
|
}
|
||||||
|
|
||||||
f.TimesCalled += 1
|
f.TimesCalled += 1
|
||||||
return f.Function(args, vt, ft)
|
return f.Function(n_args, vt, ft)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* searches for function mapped to argument in FuncTable
|
/* searches for function mapped to argument in FuncTable
|
||||||
|
|
|
||||||
19
ast/lex.go
19
ast/lex.go
|
|
@ -162,14 +162,22 @@ func lex(input string) *Token {
|
||||||
|
|
||||||
// comment case
|
// comment case
|
||||||
case ';':
|
case ';':
|
||||||
|
i = matchLineEnd(i)
|
||||||
start_pos = i + 1
|
start_pos = i + 1
|
||||||
i = matchLineEnd(start_pos)
|
|
||||||
|
// 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 {
|
if needs_alloc {
|
||||||
needs_alloc = false
|
needs_alloc = false
|
||||||
if (i < 0) {
|
if (i < 0) {
|
||||||
// TODO: Maybe not overload this.
|
|
||||||
start_pos = i
|
start_pos = i
|
||||||
goto error
|
goto error
|
||||||
}
|
}
|
||||||
|
|
@ -186,8 +194,6 @@ func lex(input string) *Token {
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
error:
|
error:
|
||||||
// TODO: Hook into error module
|
|
||||||
// TODO: Finalize and GC alloced tokens
|
|
||||||
if start_pos == -1 {
|
if start_pos == -1 {
|
||||||
log.Log(log.ERR,
|
log.Log(log.ERR,
|
||||||
"Unmatched string delimiter in input. discarding.",
|
"Unmatched string delimiter in input. discarding.",
|
||||||
|
|
@ -209,6 +215,11 @@ error:
|
||||||
func StrIsNumber(arg string) bool {
|
func StrIsNumber(arg string) bool {
|
||||||
dotCount := 0
|
dotCount := 0
|
||||||
|
|
||||||
|
// negative nums
|
||||||
|
if len(arg) > 1 && arg[0] == '-' {
|
||||||
|
arg = arg[1:]
|
||||||
|
}
|
||||||
|
|
||||||
for _, char := range arg {
|
for _, char := range arg {
|
||||||
if !unicode.IsDigit(char) {
|
if !unicode.IsDigit(char) {
|
||||||
if char == '.' && dotCount == 0 {
|
if char == '.' && dotCount == 0 {
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
dyn_prompt := ast.GetFunction("_SH_PROMPT", funcs)
|
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
|
dyn_prompt = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,6 +149,11 @@ func main() {
|
||||||
fmt.Printf(prePrompt)
|
fmt.Printf(prePrompt)
|
||||||
text, err := line.Prompt(prompt)
|
text, err := line.Prompt(prompt)
|
||||||
if err != nil && err != liner.ErrPromptAborted{
|
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")
|
log.Log(log.ERR, "couldnt read user input: " + err.Error(), "repl")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,14 +36,6 @@ import (
|
||||||
* Example: (number "3.4")
|
* Example: (number "3.4")
|
||||||
*/
|
*/
|
||||||
func NumCast(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token {
|
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()) {
|
if !ast.StrIsNumber(in.Value()) {
|
||||||
log.Log(log.ERR,
|
log.Log(log.ERR,
|
||||||
"string failed number cast",
|
"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 {
|
func Add(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token {
|
||||||
var res float64
|
var res float64
|
||||||
|
|
||||||
in = in.Eval(f, a, false)
|
|
||||||
|
|
||||||
for i := in; i != nil; i = i.Next {
|
for i := in; i != nil; i = i.Next {
|
||||||
if i.Tag != ast.NUMBER {
|
if i.Tag != ast.NUMBER {
|
||||||
log.Log(log.ERR, "Non-number given to ADD", "add")
|
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 {
|
func Sub(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token {
|
||||||
var res float64
|
var res float64
|
||||||
var sub float64
|
var sub float64
|
||||||
|
|
||||||
in = in.Eval(f, a, false)
|
|
||||||
|
|
||||||
for i := in; i != nil; i = i.Next {
|
for i := in; i != nil; i = i.Next {
|
||||||
if i.Tag != ast.NUMBER {
|
if i.Tag != ast.NUMBER {
|
||||||
log.Log(log.ERR, "Non-number given to SUB", "sub")
|
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 {
|
func Mult(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token {
|
||||||
res := 1.0
|
res := 1.0
|
||||||
|
|
||||||
in = in.Eval(f, a, false)
|
|
||||||
|
|
||||||
for i := in; i != nil; i = i.Next {
|
for i := in; i != nil; i = i.Next {
|
||||||
if i.Tag != ast.NUMBER {
|
if i.Tag != ast.NUMBER {
|
||||||
log.Log(log.ERR, "Non-number given to MULT", "mult")
|
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 {
|
func Div(in *ast.Token, a ast.VarTable, f ast.FuncTable) *ast.Token {
|
||||||
var res float64
|
var res float64
|
||||||
|
|
||||||
in = in.Eval(f, a, false)
|
|
||||||
|
|
||||||
for i := in; i != nil; i = i.Next {
|
for i := in; i != nil; i = i.Next {
|
||||||
inner := 0.0
|
inner := 0.0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,6 @@ import (
|
||||||
* Example: (bool "F")
|
* Example: (bool "F")
|
||||||
*/
|
*/
|
||||||
func BoolCast(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
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()
|
body := in.Value()
|
||||||
if body != ast.TRUE && body != ast.FALSE {
|
if body != ast.TRUE && body != ast.FALSE {
|
||||||
log.Log(log.ERR,
|
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 {
|
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
|
out := ast.TRUE
|
||||||
if in.Value() == ast.TRUE {
|
if in.Value() == ast.TRUE {
|
||||||
out = ast.FALSE
|
out = ast.FALSE
|
||||||
|
|
@ -69,10 +54,9 @@ func Not(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lazy args this
|
||||||
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
|
out := ast.TRUE
|
||||||
|
|
||||||
in = in.Eval(ft, vt, false)
|
|
||||||
second := in.Next
|
second := in.Next
|
||||||
|
|
||||||
if in.Tag != second.Tag {
|
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
|
out := ast.TRUE
|
||||||
second := in.Next
|
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)
|
l, _ := strconv.ParseInt(in.Value(), 10, 64)
|
||||||
r, _ := strconv.ParseInt(second.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
|
out := ast.TRUE
|
||||||
second := in.Next
|
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)
|
l, _ := strconv.ParseInt(in.Value(), 10, 64)
|
||||||
r, _ := strconv.ParseInt(second.Value(), 10, 64)
|
r, _ := strconv.ParseInt(second.Value(), 10, 64)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,20 +54,6 @@ func AbsPath(arg string) string {
|
||||||
* (cd (concat HOME "/go"))
|
* (cd (concat HOME "/go"))
|
||||||
*/
|
*/
|
||||||
func Cd(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
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())
|
err := os.Chdir(in.Value())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log(log.ERR, err.Error(), "cd")
|
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)
|
* (fexists test)
|
||||||
*/
|
*/
|
||||||
func Fexists(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
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()
|
filename := in.Value()
|
||||||
out := ast.TRUE
|
out := ast.TRUE
|
||||||
|
|
||||||
|
|
@ -115,7 +93,6 @@ func Fexists(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
||||||
* (fread (concat HOME ".shsrc"))
|
* (fread (concat HOME ".shsrc"))
|
||||||
*/
|
*/
|
||||||
func Fread(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
func Fread(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
||||||
in = in.Eval(ft, vt, false)
|
|
||||||
exists := Fexists(in, vt, ft) // some waste, extra use of Eval
|
exists := Fexists(in, vt, ft) // some waste, extra use of Eval
|
||||||
if exists == nil || exists.Tag != ast.BOOL || exists.Value() == ast.FALSE {
|
if exists == nil || exists.Tag != ast.BOOL || exists.Value() == ast.FALSE {
|
||||||
log.Log(log.ERR,
|
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")
|
* (fwrite "test" "one two three")
|
||||||
*/
|
*/
|
||||||
func Fwrite(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
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
|
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(
|
err := ioutil.WriteFile(
|
||||||
AbsPath(in.Value()),
|
AbsPath(in.Value()),
|
||||||
[]byte(text.Value()),
|
[]byte(text.Value()),
|
||||||
0644)
|
0644)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log(log.ERR,
|
log.Log(log.ERR,
|
||||||
"error writing file: " + err.Error(),
|
"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")
|
* (fwrite "test" "one two three")
|
||||||
*/
|
*/
|
||||||
func Fappend(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
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
|
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)
|
exists := Fexists(in, vt, ft)
|
||||||
if exists.Value() == ast.FALSE {
|
if exists.Value() == ast.FALSE {
|
||||||
log.Log(log.ERR,
|
log.Log(log.ERR,
|
||||||
|
|
@ -212,14 +160,15 @@ func Fappend(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
||||||
AbsPath(in.Value()),
|
AbsPath(in.Value()),
|
||||||
os.O_APPEND|os.O_CREATE|os.O_WRONLY,
|
os.O_APPEND|os.O_CREATE|os.O_WRONLY,
|
||||||
0644)
|
0644)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log(log.ERR,
|
log.Log(log.ERR,
|
||||||
"couldnt open file for append: " + err.Error(),
|
"couldnt open file for append: " + err.Error(),
|
||||||
"fappend")
|
"fappend")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
|
defer f.Close()
|
||||||
if _, err := f.WriteString(text.Value()); err != nil {
|
if _, err := f.WriteString(text.Value()); err != nil {
|
||||||
log.Log(log.ERR,
|
log.Log(log.ERR,
|
||||||
"error appending to file: " + err.Error(),
|
"error appending to file: " + err.Error(),
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,8 @@ func DeclFunc(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Tok
|
||||||
Function: inner,
|
Function: inner,
|
||||||
Name: name.Value(),
|
Name: name.Value(),
|
||||||
TimesCalled: 0,
|
TimesCalled: 0,
|
||||||
Args: numArgs,
|
NumArgs: numArgs,
|
||||||
|
ArgLazy: true,
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
193
stdlib/list.go
193
stdlib/list.go
|
|
@ -18,6 +18,8 @@
|
||||||
package stdlib
|
package stdlib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"gitlab.com/whom/shs/ast"
|
"gitlab.com/whom/shs/ast"
|
||||||
"gitlab.com/whom/shs/log"
|
"gitlab.com/whom/shs/log"
|
||||||
)
|
)
|
||||||
|
|
@ -28,12 +30,7 @@ import (
|
||||||
* in event a not-list is passed in, returns the arg.
|
* 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 {
|
return input.Expand()
|
||||||
log.Log(log.INFO, "expand called on not a list", "expand")
|
|
||||||
return input
|
|
||||||
}
|
|
||||||
|
|
||||||
return input.Eval(funcs, vars, false).Expand()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* L_APPEND (append from repl)
|
/* L_APPEND (append from repl)
|
||||||
|
|
@ -68,3 +65,187 @@ func L_append(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Tok
|
||||||
|
|
||||||
return src
|
return src
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Len
|
||||||
|
* 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 {
|
||||||
|
if input.Tag != ast.LIST && input.Tag != ast.STRING {
|
||||||
|
log.Log(log.ERR,
|
||||||
|
"must provide list or strinig",
|
||||||
|
"head")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
length := 0
|
||||||
|
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}
|
||||||
|
ret.Set(fmt.Sprintf("%d", length))
|
||||||
|
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
|
||||||
|
*
|
||||||
|
* Example: (head (2 3 4)) -> 2
|
||||||
|
*/
|
||||||
|
func Head(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token {
|
||||||
|
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 {
|
||||||
|
iter := input.Expand()
|
||||||
|
for iter != nil && iter.Next != nil {
|
||||||
|
iter = iter.Next
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
start := input
|
||||||
|
end := input.Next
|
||||||
|
source := end.Next
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -225,8 +225,8 @@ func LaunchProcess(
|
||||||
* Example (l vim file.txt)
|
* Example (l vim file.txt)
|
||||||
*/
|
*/
|
||||||
func Call(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
func Call(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
||||||
in = in.Eval(ft, vt, true)
|
|
||||||
if in == nil {
|
if in == nil {
|
||||||
|
log.Log(log.ERR, "no arguments given", "call")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,11 +261,6 @@ func Call(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
||||||
* Example: (bg vim file.txt)
|
* Example: (bg vim file.txt)
|
||||||
*/
|
*/
|
||||||
func Bgcall(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
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 {
|
if in.Tag == ast.LIST {
|
||||||
log.Log(log.ERR, "couldnt exec, target bin is a list", "call")
|
log.Log(log.ERR, "couldnt exec, target bin is a list", "call")
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -304,14 +299,6 @@ func Fg(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
||||||
return nil
|
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)
|
pid, err := strconv.ParseFloat(in.Value(), 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log(log.ERR,
|
log.Log(log.ERR,
|
||||||
|
|
@ -374,12 +361,6 @@ func Jobs(in *ast.Token, vt ast.VarTable, fg ast.FuncTable) *ast.Token {
|
||||||
* Example: ($ echo hello world)
|
* Example: ($ echo hello world)
|
||||||
*/
|
*/
|
||||||
func ReadCmd(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
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 {
|
if in.Tag == ast.LIST {
|
||||||
log.Log(log.ERR, "couldnt exec, target bin is a list", "call")
|
log.Log(log.ERR, "couldnt exec, target bin is a list", "call")
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -434,13 +415,6 @@ func GetExit(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
||||||
* (this function not added to functable in stdlib.go)
|
* (this function not added to functable in stdlib.go)
|
||||||
*/
|
*/
|
||||||
func Kill(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
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)
|
pid, err := strconv.ParseInt(in.Value(), 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log(log.ERR, "error parsing arg to kill: " + err.Error(), "kill")
|
log.Log(log.ERR, "error parsing arg to kill: " + err.Error(), "kill")
|
||||||
|
|
|
||||||
315
stdlib/stdlib.go
315
stdlib/stdlib.go
|
|
@ -20,7 +20,6 @@ package stdlib
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"fmt"
|
"fmt"
|
||||||
"gitlab.com/whom/shs/log"
|
|
||||||
"gitlab.com/whom/shs/ast"
|
"gitlab.com/whom/shs/ast"
|
||||||
"gitlab.com/whom/shs/util"
|
"gitlab.com/whom/shs/util"
|
||||||
)
|
)
|
||||||
|
|
@ -34,225 +33,332 @@ func GenFuncTable() ast.FuncTable {
|
||||||
"if": &ast.Function{
|
"if": &ast.Function{
|
||||||
Function: ShsIf,
|
Function: ShsIf,
|
||||||
Name: "if",
|
Name: "if",
|
||||||
TimesCalled: 0,
|
NumArgs: 3,
|
||||||
Args: 3,
|
EvalLazy: true,
|
||||||
|
ArgLazy: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"while": &ast.Function{
|
"while": &ast.Function{
|
||||||
Function: ShsWhile,
|
Function: ShsWhile,
|
||||||
Name: "while",
|
Name: "while",
|
||||||
TimesCalled: 0,
|
NumArgs: -1,
|
||||||
Args: -1,
|
EvalLazy: true,
|
||||||
|
ArgLazy: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"progn": &ast.Function{
|
"progn": &ast.Function{
|
||||||
Function: ShsProgn,
|
Function: ShsProgn,
|
||||||
Name: "shs_progn",
|
Name: "shs_progn",
|
||||||
TimesCalled: 0,
|
NumArgs: -1,
|
||||||
Args: -1,
|
EvalLazy: true,
|
||||||
|
ArgLazy: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"func": &ast.Function{
|
"func": &ast.Function{
|
||||||
Function: DeclFunc,
|
Function: DeclFunc,
|
||||||
Name: "decl_func",
|
Name: "decl_func",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 3,
|
ast.SYMBOL,
|
||||||
|
ast.LIST,
|
||||||
|
ast.LIST,
|
||||||
|
},
|
||||||
|
EvalLazy: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"len": &ast.Function{
|
||||||
|
Function: Len,
|
||||||
|
Name: "len",
|
||||||
|
NumArgs: 1,
|
||||||
|
ArgLazy: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"index": &ast.Function{
|
||||||
|
Function: Index,
|
||||||
|
Name: "index",
|
||||||
|
Args: []ast.Token_t{
|
||||||
|
ast.NUMBER,
|
||||||
|
ast.LIST,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"head": &ast.Function{
|
||||||
|
Function: Head,
|
||||||
|
Name: "head",
|
||||||
|
Args: []ast.Token_t{
|
||||||
|
ast.LIST,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"tail": &ast.Function{
|
||||||
|
Function: Tail,
|
||||||
|
Name: "tail",
|
||||||
|
Args: []ast.Token_t{
|
||||||
|
ast.LIST,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"slice": &ast.Function{
|
||||||
|
Function: Slice,
|
||||||
|
Name: "slice",
|
||||||
|
Args: []ast.Token_t{
|
||||||
|
ast.NUMBER,
|
||||||
|
ast.NUMBER,
|
||||||
|
ast.LIST,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"export": &ast.Function{
|
"export": &ast.Function{
|
||||||
Function: Export,
|
Function: Export,
|
||||||
Name: "export",
|
Name: "export",
|
||||||
TimesCalled: 0,
|
EvalLazy: true,
|
||||||
Args: 2,
|
ArgLazy: true,
|
||||||
|
NumArgs: 2,
|
||||||
},
|
},
|
||||||
|
|
||||||
"input": &ast.Function{
|
"input": &ast.Function{
|
||||||
Function: Input,
|
Function: Input,
|
||||||
Name: "input",
|
Name: "input",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 1,
|
ast.STRING,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"load": &ast.Function{
|
"load": &ast.Function{
|
||||||
Function: Load,
|
Function: Load,
|
||||||
Name: "load",
|
Name: "load",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 1,
|
ast.STRING,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"bool": &ast.Function{
|
"bool": &ast.Function{
|
||||||
Function: BoolCast,
|
Function: BoolCast,
|
||||||
Name: "bool",
|
Name: "bool",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 1,
|
ast.STRING,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"string": &ast.Function{
|
"string": &ast.Function{
|
||||||
Function: StrCast,
|
Function: StrCast,
|
||||||
Name: "string",
|
Name: "string",
|
||||||
TimesCalled: 0,
|
NumArgs: 1,
|
||||||
Args: 1,
|
ArgLazy: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"number": &ast.Function{
|
"number": &ast.Function{
|
||||||
Function: NumCast,
|
Function: NumCast,
|
||||||
Name: "number",
|
Name: "number",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 1,
|
ast.STRING,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"...": &ast.Function{
|
"...": &ast.Function{
|
||||||
Function: Expand,
|
Function: Expand,
|
||||||
Name: "...",
|
Name: "...",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 1,
|
ast.LIST,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"append": &ast.Function{
|
"append": &ast.Function{
|
||||||
Function: L_append,
|
Function: L_append,
|
||||||
Name: "append",
|
Name: "append",
|
||||||
TimesCalled: 0,
|
NumArgs: -1,
|
||||||
Args: -1,
|
ArgLazy: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"join": &ast.Function{
|
||||||
|
Function: Join,
|
||||||
|
Name: "join",
|
||||||
|
Args: []ast.Token_t{
|
||||||
|
ast.STRING,
|
||||||
|
ast.LIST,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"split": &ast.Function{
|
||||||
|
Function: Split,
|
||||||
|
Name: "split",
|
||||||
|
Args: []ast.Token_t{
|
||||||
|
ast.STRING,
|
||||||
|
ast.STRING,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"replace": &ast.Function{
|
||||||
|
Function: Replace,
|
||||||
|
Name: "replace",
|
||||||
|
Args: []ast.Token_t{
|
||||||
|
ast.STRING,
|
||||||
|
ast.STRING,
|
||||||
|
ast.STRING,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"substr": &ast.Function{
|
||||||
|
Function: Substr,
|
||||||
|
Name: "substr",
|
||||||
|
Args: []ast.Token_t{
|
||||||
|
ast.NUMBER,
|
||||||
|
ast.NUMBER,
|
||||||
|
ast.STRING,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"exit": &ast.Function{
|
"exit": &ast.Function{
|
||||||
Function: ExitShell,
|
Function: ExitShell,
|
||||||
Name: "exit",
|
Name: "exit",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{},
|
||||||
Args: 0,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"eq": &ast.Function{
|
"eq": &ast.Function{
|
||||||
Function: Eq,
|
Function: Eq,
|
||||||
Name: "==",
|
Name: "==",
|
||||||
TimesCalled: 0,
|
NumArgs: 2,
|
||||||
Args: 2,
|
ArgLazy: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"ne": &ast.Function{
|
"ne": &ast.Function{
|
||||||
Function: Ne,
|
Function: Ne,
|
||||||
Name: "!=",
|
Name: "!=",
|
||||||
TimesCalled: 0,
|
NumArgs: 2,
|
||||||
Args: 2,
|
ArgLazy: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"<": &ast.Function{
|
"<": &ast.Function{
|
||||||
Function: Lt,
|
Function: Lt,
|
||||||
Name: "<",
|
Name: "<",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 2,
|
ast.NUMBER,
|
||||||
|
ast.NUMBER,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
">": &ast.Function{
|
">": &ast.Function{
|
||||||
Function: Gt,
|
Function: Gt,
|
||||||
Name: ">",
|
Name: ">",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 2,
|
ast.NUMBER,
|
||||||
|
ast.NUMBER,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"<=": &ast.Function{
|
"<=": &ast.Function{
|
||||||
Function: Lte,
|
Function: Lte,
|
||||||
Name: "<=",
|
Name: "<=",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 2,
|
ast.NUMBER,
|
||||||
|
ast.NUMBER,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
">=": &ast.Function{
|
">=": &ast.Function{
|
||||||
Function: Gte,
|
Function: Gte,
|
||||||
Name: ">=",
|
Name: ">=",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 2,
|
ast.NUMBER,
|
||||||
|
ast.NUMBER,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"!": &ast.Function{
|
"!": &ast.Function{
|
||||||
Function: Not,
|
Function: Not,
|
||||||
Name: "!",
|
Name: "!",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 1,
|
ast.BOOL,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"+": &ast.Function{
|
"+": &ast.Function{
|
||||||
Function: Add,
|
Function: Add,
|
||||||
Name: "add",
|
Name: "add",
|
||||||
TimesCalled: 0,
|
NumArgs: -1,
|
||||||
Args: -1,
|
ArgLazy: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"-": &ast.Function{
|
"-": &ast.Function{
|
||||||
Function: Sub,
|
Function: Sub,
|
||||||
Name: "sub",
|
Name: "sub",
|
||||||
TimesCalled: 0,
|
NumArgs: -1,
|
||||||
Args: -1,
|
ArgLazy: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"*": &ast.Function{
|
"*": &ast.Function{
|
||||||
Function: Mult,
|
Function: Mult,
|
||||||
Name: "mult",
|
Name: "mult",
|
||||||
TimesCalled: 0,
|
NumArgs: -1,
|
||||||
Args: -1,
|
ArgLazy: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"/": &ast.Function{
|
"/": &ast.Function{
|
||||||
Function: Div,
|
Function: Div,
|
||||||
Name: "div",
|
Name: "div",
|
||||||
TimesCalled: 0,
|
NumArgs: -1,
|
||||||
Args: -1,
|
ArgLazy: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"cd": &ast.Function{
|
"cd": &ast.Function{
|
||||||
Function: Cd,
|
Function: Cd,
|
||||||
Name: "changedir",
|
Name: "changedir",
|
||||||
TimesCalled: 0,
|
SymLazy: true,
|
||||||
Args: 1,
|
Args: []ast.Token_t{
|
||||||
|
ast.STRING,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"concat": &ast.Function{
|
"concat": &ast.Function{
|
||||||
Function: Concat,
|
Function: Concat,
|
||||||
Name: "concatenate",
|
Name: "concatenate",
|
||||||
TimesCalled: 0,
|
NumArgs: -1,
|
||||||
Args: -1,
|
ArgLazy: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"print": &ast.Function{
|
"print": &ast.Function{
|
||||||
Function: PrintStr,
|
Function: PrintStr,
|
||||||
Name: "print",
|
Name: "print",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 1,
|
ast.STRING,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"l": &ast.Function{
|
"l": &ast.Function{
|
||||||
Function: Call,
|
Function: Call,
|
||||||
Name: "call",
|
Name: "call",
|
||||||
TimesCalled: 0,
|
NumArgs: -1,
|
||||||
Args: -1,
|
ArgLazy: true,
|
||||||
|
SymLazy: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"bg": &ast.Function{
|
"bg": &ast.Function{
|
||||||
Function: Bgcall,
|
Function: Bgcall,
|
||||||
Name: "background call",
|
Name: "background call",
|
||||||
TimesCalled: 0,
|
NumArgs: -1,
|
||||||
Args: -1,
|
ArgLazy: true,
|
||||||
|
SymLazy: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"fg": &ast.Function{
|
"fg": &ast.Function{
|
||||||
Function: Fg,
|
Function: Fg,
|
||||||
Name: "foreground",
|
Name: "foreground",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 1,
|
ast.NUMBER,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"$": &ast.Function{
|
"$": &ast.Function{
|
||||||
Function: ReadCmd,
|
Function: ReadCmd,
|
||||||
Name: "read cmd",
|
Name: "read cmd",
|
||||||
TimesCalled: 0,
|
NumArgs: -1,
|
||||||
Args: -1,
|
ArgLazy: true,
|
||||||
|
SymLazy: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"?": &ast.Function{
|
"?": &ast.Function{
|
||||||
Function: GetExit,
|
Function: GetExit,
|
||||||
Name: "get exit code",
|
Name: "get exit code",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{},
|
||||||
Args: 0,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -260,7 +366,6 @@ func GenFuncTable() ast.FuncTable {
|
||||||
"kill": &ast.Function{
|
"kill": &ast.Function{
|
||||||
Function: kill,
|
Function: kill,
|
||||||
Name: "kill job",
|
Name: "kill job",
|
||||||
TimesCalled: 0,
|
|
||||||
Args: 1,
|
Args: 1,
|
||||||
},
|
},
|
||||||
*/
|
*/
|
||||||
|
|
@ -268,43 +373,49 @@ func GenFuncTable() ast.FuncTable {
|
||||||
"jobs": &ast.Function{
|
"jobs": &ast.Function{
|
||||||
Function: Jobs,
|
Function: Jobs,
|
||||||
Name: "list jobs",
|
Name: "list jobs",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{},
|
||||||
Args: 0,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"info": &ast.Function{
|
"info": &ast.Function{
|
||||||
Function: ShInfo,
|
Function: ShInfo,
|
||||||
Name: "Shell Info",
|
Name: "Shell Info",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 1,
|
ast.SYMBOL,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"fexists": &ast.Function{
|
"fexists": &ast.Function{
|
||||||
Function: Fexists,
|
Function: Fexists,
|
||||||
Name: "file exists",
|
Name: "file exists",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 1,
|
ast.STRING,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"fread": &ast.Function{
|
"fread": &ast.Function{
|
||||||
Function: Fread,
|
Function: Fread,
|
||||||
Name: "read file",
|
Name: "read file",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 1,
|
ast.STRING,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"fwrite": &ast.Function{
|
"fwrite": &ast.Function{
|
||||||
Function: Fwrite,
|
Function: Fwrite,
|
||||||
Name: "write file",
|
Name: "write file",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 2,
|
ast.STRING,
|
||||||
|
ast.STRING,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"fappend": &ast.Function{
|
"fappend": &ast.Function{
|
||||||
Function: Fappend,
|
Function: Fappend,
|
||||||
Name: "append to file",
|
Name: "append to file",
|
||||||
TimesCalled: 0,
|
Args: []ast.Token_t{
|
||||||
Args: 2,
|
ast.STRING,
|
||||||
|
ast.STRING,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -327,32 +438,32 @@ func ExitShell(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
||||||
* Example: (info append)
|
* Example: (info append)
|
||||||
*/
|
*/
|
||||||
func ShInfo(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
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)
|
repr := ast.GetVar(in.Value(), vt)
|
||||||
if repr != nil {
|
if repr != nil {
|
||||||
fmt.Printf("VARIABLE\nTYPE: %s\nVALUE: %s\n", ast.GetTagAsStr(repr.Tag), repr.Value())
|
fmt.Printf("VARIABLE\nTYPE: %s\nVALUE: %s\n", ast.GetTagAsStr(repr.Tag), repr.Value())
|
||||||
break
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
funct := ast.GetFunction(in.Value(), ft)
|
funct := ast.GetFunction(in.Value(), ft)
|
||||||
if funct != nil {
|
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\n\n", funct.Name, funct.TimesCalled)
|
||||||
break
|
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")
|
fmt.Printf("UNKNOWN SYMBOL\n")
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -364,14 +475,6 @@ func ShInfo(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
||||||
* Example: (print (input))
|
* Example: (print (input))
|
||||||
*/
|
*/
|
||||||
func Input(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
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()
|
prompt := in.Value()
|
||||||
var output string
|
var output string
|
||||||
|
|
||||||
|
|
@ -389,14 +492,6 @@ func Input(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
||||||
* Example: (load "myscript.shs")
|
* Example: (load "myscript.shs")
|
||||||
*/
|
*/
|
||||||
func Load(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
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 := in.Value()
|
||||||
bp = AbsPath(bp)
|
bp = AbsPath(bp)
|
||||||
util.LoadScript(bp, vt, ft)
|
util.LoadScript(bp, vt, ft)
|
||||||
|
|
|
||||||
143
stdlib/string.go
143
stdlib/string.go
|
|
@ -19,6 +19,7 @@ package stdlib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"strconv"
|
"strconv"
|
||||||
"gitlab.com/whom/shs/ast"
|
"gitlab.com/whom/shs/ast"
|
||||||
"gitlab.com/whom/shs/log"
|
"gitlab.com/whom/shs/log"
|
||||||
|
|
@ -29,8 +30,6 @@ import (
|
||||||
* Example: (concat "hello" " " "world")
|
* Example: (concat "hello" " " "world")
|
||||||
*/
|
*/
|
||||||
func Concat(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
func Concat(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
||||||
in = in.Eval(ft, vt, false)
|
|
||||||
|
|
||||||
var res string
|
var res string
|
||||||
for i := in; i != nil; i = i.Next {
|
for i := in; i != nil; i = i.Next {
|
||||||
if i.Tag == ast.LIST {
|
if i.Tag == ast.LIST {
|
||||||
|
|
@ -53,12 +52,148 @@ func Concat(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
||||||
* Example: (string 1) -> 1.0
|
* Example: (string 1) -> 1.0
|
||||||
*/
|
*/
|
||||||
func StrCast(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
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 := &ast.Token{ Tag: ast.STRING }
|
||||||
res.Set(body)
|
res.Set(body)
|
||||||
return res
|
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 {
|
||||||
|
body := in.Value()
|
||||||
|
delim := in.Next.Value()
|
||||||
|
|
||||||
|
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 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
|
||||||
|
*
|
||||||
|
* Example: (join ", " ("apple" "ananas" "pear")) -> "apple, ananas, pear"
|
||||||
|
*/
|
||||||
|
func Join(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
||||||
|
delim := in
|
||||||
|
strs := in.Next
|
||||||
|
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 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
|
||||||
|
end := start.Next
|
||||||
|
str := end.Next
|
||||||
|
|
||||||
|
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
|
/* Takes one arg, returns nil
|
||||||
* Prints a string to stdout
|
* Prints a string to stdout
|
||||||
* Unquotes string so user can add escaped chars like \n, \t, etc
|
* Unquotes string so user can add escaped chars like \n, \t, etc
|
||||||
|
|
@ -66,7 +201,7 @@ func StrCast(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
||||||
* Example: (print "Line: \n, Tab: \t")
|
* Example: (print "Line: \n, Tab: \t")
|
||||||
*/
|
*/
|
||||||
func PrintStr(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token {
|
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] != '"' {
|
if body[0] != body[len(body)-1] && body[0] != '"' {
|
||||||
body = "`" + body + "`"
|
body = "`" + body + "`"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,12 @@ func Export(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token
|
||||||
name := input
|
name := input
|
||||||
|
|
||||||
form := name.Next.Eval(funcs, vars, false)
|
form := name.Next.Eval(funcs, vars, false)
|
||||||
|
|
||||||
|
// error in eval process
|
||||||
|
if form == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if name.Tag != ast.SYMBOL {
|
if name.Tag != ast.SYMBOL {
|
||||||
log.Log(log.ERR,
|
log.Log(log.ERR,
|
||||||
"first arg should be a symbol",
|
"first arg should be a symbol",
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,8 @@ func ShellCompleter(line string, vt ast.VarTable, ft ast.FuncTable) []string {
|
||||||
completions := []string{}
|
completions := []string{}
|
||||||
for _, i := range compSource {
|
for _, i := range compSource {
|
||||||
if strings.HasPrefix(i, tail) {
|
if strings.HasPrefix(i, tail) {
|
||||||
completions = append(completions, head + i)
|
str := strings.ReplaceAll(i, " ", "\\ ")
|
||||||
|
completions = append(completions, head + str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue