134 lines
4.4 KiB
Text
134 lines
4.4 KiB
Text
#!/bin/relish
|
|
|
|
;; relish: versatile lisp shell
|
|
;; Copyright (C) 2021 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 <http://www.gnu.org/licenses/>.
|
|
|
|
;; USERLIB
|
|
;; This file contains useful features that are not shipped in the STL
|
|
|
|
;; this would be way faster as code in stl
|
|
;; but stl already suffers scope creep
|
|
(def prepend
|
|
'takes a list and appends an element to the back of it.
|
|
returns prepended list'
|
|
(elem list)
|
|
(reverse (cons (reverse list) elem)))
|
|
|
|
(def set
|
|
'sets an existing variable without touching its docstring.
|
|
|
|
WARNING:
|
|
If you find yourself struggling to debug a complex error in state access,
|
|
or you are having issues re-running commands in the shell consider the
|
|
following advice:
|
|
|
|
It is very much an anti pattern to mutate global variable that contain state.
|
|
refactor your program: put iterators, counters, procedurally generated code,
|
|
and all other mutable state into a let loop.
|
|
|
|
A cozy script in relish is one where each root level form (or eval at repl)
|
|
is self contained, and does not permanently modify any other one.
|
|
|
|
See the userlib tests for an easy to follow example of this.'
|
|
|
|
(var val)
|
|
(let ((doc (get-doc var)))
|
|
(def (eval var) doc val)))
|
|
|
|
(def map
|
|
'Takes two arguments: a function and a list.
|
|
for each element in the list, the function is applied and the
|
|
result is added to a new list. Returns the new list.'
|
|
(func list)
|
|
(let ((list-iter (pop list))
|
|
(result ()))
|
|
(while (gt? (len list-iter) 1)
|
|
(let ((current (car list-iter))
|
|
(remaining (cdr list-iter))
|
|
(current-res (func current)))
|
|
(set (q result) (cons result current-res))
|
|
(set (q list-iter) (pop remaining))))
|
|
result))
|
|
|
|
(def reduce
|
|
'Takes two arguments: a function and a list.
|
|
The function is expected to take two arguments:
|
|
* the current list item
|
|
* the previous result
|
|
Initially the function will take element1 and element2, outputting result1.
|
|
Then the function will take result1 and element3, outputting result2.
|
|
this will continue iuntil the list is exhausted.'
|
|
(func list)
|
|
(let ((list-iter (pop list))
|
|
(result (car list-iter)))
|
|
(set (q list-iter) (pop (cdr list-iter)))
|
|
(if (lt? (len list) 2)
|
|
(echo "list too short!")
|
|
(while (gt? (len list-iter) 1)
|
|
(let ((current (car list-iter))
|
|
(remaining (cdr list-iter)))
|
|
(set (q result) (func result current))
|
|
(set (q list-iter) (pop remaining)))))
|
|
result))
|
|
|
|
(def cond
|
|
'Takes one argument: a list of pairs consisting of a form returning a bool and a form to execute
|
|
The function iterates over the list checking if the first form of a pair evaluates to true, in which
|
|
case it will execute the second form of the pair, returning its result, and stop the loop.'
|
|
(list)
|
|
(let ((iter list))
|
|
(while (gt? (len iter) 0)
|
|
(let ((current (car iter))
|
|
(remaining (pop iter)))
|
|
(if (eval (car current))
|
|
(car (pop ((set (q iter) ())
|
|
(eval (cdr current)))))
|
|
(set (q iter) (cdr remaining)))))))
|
|
|
|
(def contains?
|
|
'Takes two arguments: a list and an element.
|
|
Returns true if the list contains the element.'
|
|
(list elem)
|
|
(let ((found false)
|
|
(list-iter (pop list)))
|
|
(while (and (gt? (len list-iter) 1)
|
|
(not found))
|
|
(let ((current (car list-iter))
|
|
(remaining (cdr list-iter)))
|
|
(if (eq? current elem)
|
|
(set (q found) true)
|
|
(set (q list-iter) (pop remaining)))))
|
|
found))
|
|
|
|
(def get-paths
|
|
'returns each individual directory in PATH'
|
|
() (split PATH ':'))
|
|
|
|
(def add-path
|
|
'Takes one argument.
|
|
adds a directory to PATH'
|
|
(path) (set (q PATH)
|
|
(concat PATH ':' path)))
|
|
|
|
(def display-paths
|
|
'prints out each element of $PATH one by one'
|
|
()
|
|
(let ((path-iter (pop (get-paths))))
|
|
(while (gt? (len path-iter) 1)
|
|
(let ((pat (car path-iter))
|
|
(rem (cdr path-iter)))
|
|
(echo pat)
|
|
(set (q path-iter) (pop rem))))))
|