Skip to content

Instantly share code, notes, and snippets.

@cgsdev0
Created March 31, 2026 15:08
Show Gist options
  • Select an option

  • Save cgsdev0/80c2c8072c5fd267417936a299149c10 to your computer and use it in GitHub Desktop.

Select an option

Save cgsdev0/80c2c8072c5fd267417936a299149c10 to your computer and use it in GitHub Desktop.
lisp interpreter
#!/usr/bin/env bash
# terrible lisp interpreter
#
# example run:
#
# (first (list 1 (+ 2 3) 9)))
#
# evaluating + 2 3 = 5
# evaluating list 1 5 9 = 1,5,9
# evaluating first 1 5 9 = 1
DEBUG=true
output_path=/tmp/parsed
usage() {
cat << USAGE
usage: $0 [path_to_program]
example program:
(first (list 1 (+ 2 3) 9))
example output:
1
USAGE
exit 1
}
source="$1"
[[ -f "$source" ]] || usage
rm -rf "$output_path"
mkdir -p "$output_path"
tokenizer() {
local acc char
emit() {
[[ -n "$acc" ]] && echo "$acc"
acc=
}
# loop character by character
while IFS= read -rn 1 char; do
char=${char:- } # newlines become spaces
case $char in
'(' | ')')
emit
echo "$char"
;;
' ') emit ;;
# accumulate characters
*) acc="${acc}${char}" ;;
esac
done
}
lexer() {
local i=0
cd "$1"
while read -r token; do
case "$token" in
'(')
read -r token
mkdir "$i.$token"
pushd "$i.$token" &> /dev/null
;;
')') popd &> /dev/null ;;
*) touch "$i.$token" ;;
esac
((i++))
done
}
show_ast() {
cd "$1"
tree . -v --noreport \
| sed 's/[0-9]\+ //'
}
cat "$source" \
| tokenizer \
| lexer "$output_path"
# show_ast "$output_path"
evaluate() {
local fn=${1#*.}
pushd "$1" &> /dev/null
local file
local -a args
# DFS to resolve our args
while read -r file; do
if [[ -d "$file" ]]; then
local returned=${ evaluate "$file"; }
local arg
while read -r arg; do
args+=("${arg#*.}")
done <<< "$returned"
else
args+=("${file#*.}")
fi
done < <(ls | sort -n)
$DEBUG && echo -n "evaluating $fn ${args[@]} = " 1>&2
execute() {
case "$fn" in
'') # root function
printf '%s\n' "${args[@]}"
;;
'+') # sum all arguments
local acc=0
for arg in "${args[@]}"; do
((acc+=arg))
done
echo $acc
;;
list) # pass through?
printf '%s\n' "${args[@]}"
;;
first)
echo "${args[0]}"
;;
esac
}
local result=${ execute; }
echo "$result"
$DEBUG && echo "$result" | paste -sd, 1>&2
popd &> /dev/null
}
run() {
$DEBUG && {
echo "running '$source'"
cat "$source"
echo
} 1>&2
cd "$1"
evaluate *
}
run "$output_path"
@cgsdev0
Copy link
Copy Markdown
Author

cgsdev0 commented Mar 31, 2026

because of lines 96 and 129, this requries bash 5.3+

if you change the ${ } to $( ) it will work on older versions (not sure how old tho lol)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment