/* 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 ( "fmt" "strconv" "gitlab.com/whom/shs/ast" "gitlab.com/whom/shs/log" ) /* EXPAND * Takes 1 element: a list * 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 { return input.Expand() } /* L_APPEND (append from repl) * Appends N elements to the end of a list * 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 { src := input if input.Tag != ast.LIST { r := &ast.Token{Tag: ast.LIST} r.Direct(input) return r } // deref inner first i := src.Expand() iter := &i if *iter == nil { src.Direct(input.Next) src.Next = nil } else { for (*iter).Next != nil { iter = &((*iter).Next) } (*iter).Next = input.Next input.Next = nil } 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 } /* 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 }