#!/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 . ;; USERLIB ;; This file contains useful features that are not shipped in the STL ;; TODO ;; dir-path ;; abs-path ;; rel-path ;; unique (make set) ;; set add ;; set del ;; superset ;; common-set ;; make-map ;; ins-mapping ;; del-mapping ;; get-mapping ;; mapped? ;; 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))))))