Merge pull request #387 from asarhaddon/test-macroexpand-no-quasiquote
[jackhill/mal.git] / bash / reader.sh
CommitLineData
31690700
JM
1#
2# mal (Make Lisp) Parser/Reader
3#
4
ea81a808
JM
5if [ -z "${__mal_readerr_included__}" ]; then
6__mal_readerr_included=true
7
31690700
JM
8source $(dirname $0)/types.sh
9
10READ_ATOM () {
11 local token=${__reader_tokens[${__reader_idx}]}
12 __reader_idx=$(( __reader_idx + 1 ))
13 case "${token}" in
a61be4d3
JM
14 [0-9]*) _number "${token}" ;;
15 -[0-9]*) _number "${token}" ;;
4ad84d24 16 \"*) if [[ ! "${token}" =~ ^\"(\\.|[^\\\"])*\"$ ]]; then
970935da
JM
17 _error "expected '\"', got EOF"
18 return
19 fi
20 token="${token:1:-1}"
ea02f464 21 token="${token//\\\\/${__keyw}}"
31690700 22 token="${token//\\\"/\"}"
8d78bc26 23 token="${token//\\n/$'\n'}"
ea02f464 24 token="${token//${__keyw}/\\}"
ea81a808 25 _string "${token}" ;;
b8ee29b2 26 :*) _keyword "${token:1}" ;;
31690700
JM
27 nil) r="${__nil}" ;;
28 true) r="${__true}" ;;
29 false) r="${__false}" ;;
ea81a808 30 *) _symbol "${token}" ;;
31690700
JM
31 esac
32}
33
34# Return seqence of tokens into r.
35# ${1}: Type of r (vector, list)
36# ${2}: starting symbol
37# ${3}: ending symbol
38READ_SEQ () {
39 local start="${1}"
40 local end="${2}"
41 local items=""
42 local token=${__reader_tokens[${__reader_idx}]}
43 __reader_idx=$(( __reader_idx + 1 ))
44 if [[ "${token}" != "${start}" ]]; then
45 r=
46 _error "expected '${start}'"
47 return
48 fi
49 token=${__reader_tokens[${__reader_idx}]}
50 while [[ "${token}" != "${end}" ]]; do
51 if [[ ! "${token}" ]]; then
52 r=
970935da 53 _error "expected '${end}', got EOF"
31690700
JM
54 return
55 fi
56 READ_FORM
57 items="${items} ${r}"
58 token=${__reader_tokens[${__reader_idx}]}
59 done
60 __reader_idx=$(( __reader_idx + 1 ))
61 r="${items:1}"
62}
63
64# Return form in r
65READ_FORM () {
66 local token=${__reader_tokens[${__reader_idx}]}
67 case "${token}" in
68 \') __reader_idx=$(( __reader_idx + 1 ))
ea81a808 69 _symbol quote; local q="${r}"
31690700 70 READ_FORM; local f="${r}"
ea81a808 71 _list "${q}" "${f}" ;;
31690700 72 \`) __reader_idx=$(( __reader_idx + 1 ))
ea81a808 73 _symbol quasiquote; local q="${r}"
31690700 74 READ_FORM; local f="${r}"
ea81a808 75 _list "${q}" "${f}" ;;
31690700 76 \~) __reader_idx=$(( __reader_idx + 1 ))
ea81a808 77 _symbol unquote; local q="${r}"
31690700 78 READ_FORM; local f="${r}"
ea81a808 79 _list "${q}" "${f}" ;;
31690700 80 \~\@) __reader_idx=$(( __reader_idx + 1 ))
ea81a808 81 _symbol splice-unquote; local q="${r}"
31690700 82 READ_FORM; local f="${r}"
ea81a808 83 _list "${q}" "${f}" ;;
31690700 84 ^) __reader_idx=$(( __reader_idx + 1 ))
ea81a808 85 _symbol with-meta; local wm="${r}"
31690700
JM
86 READ_FORM; local meta="${r}"
87 READ_FORM; local obj="${r}"
ea81a808 88 _list "${wm}" "${obj}" "${meta}" ;;
31690700 89 @) __reader_idx=$(( __reader_idx + 1 ))
ea81a808 90 _symbol deref; local d="${r}"
31690700 91 READ_FORM; local f="${r}"
ea81a808 92 _list "${d}" "${f}" ;;
31690700
JM
93 \)) _error "unexpected ')'" ;;
94 \() READ_SEQ "(" ")"
ea81a808 95 _list ${r} ;;
31690700
JM
96 \]) _error "unexpected ']'" ;;
97 \[) READ_SEQ "[" "]"
ea81a808 98 _vector ${r} ;;
31690700
JM
99 \}) _error "unexpected '}'" ;;
100 \{) READ_SEQ "{" "}"
ea81a808 101 _hash_map ${r} ;;
31690700
JM
102 *) READ_ATOM
103 esac
104}
105
106# Returns __reader_tokens as an indexed array of tokens
107TOKENIZE () {
108 local data="${*}"
109 local datalen=${#data}
110 local idx=0
111 local chunk=0
112 local chunksz=500
113 local match=
114 local token=
115 local str=
116
117 __reader_idx=0
118 __reader_tokens=
119 while true; do
120 if (( ${#str} < ( chunksz / 2) )) && (( chunk < datalen )); then
121 str="${str}${data:${chunk}:${chunksz}}"
122 chunk=$(( chunk + ${chunksz} ))
123 fi
124 (( ${#str} == 0 )) && break
970935da 125 [[ "${str}" =~ ^^([][{}\(\)^@])|^(~@)|(\"(\\.|[^\\\"])*\"?)|^(;[^$'\n']*)|^([~\'\`])|^([^][ ~\`\'\";{}\(\)^@\,]+)|^[,]|^[[:space:]]+ ]]
31690700
JM
126 match=${BASH_REMATCH[0]}
127 str="${str:${#match}}"
128 token="${match//$'\n'/}"
129 #echo "MATCH: '${token}' / [${str}]"
130 if ! [[ "${token}" =~ (^[,]$|^[[:space:]]*;.*$|^[[:space:]]*$) ]]; then
131 __reader_tokens[${idx}]="${token}"
132 idx=$(( idx + 1 ))
133 fi
134 if [ -z "${match}" ]; then
31690700 135 _error "Tokenizing error at: ${str:0:50}"
70aff0c1 136 return 1
31690700
JM
137 fi
138 done
139}
140
141# read-str from a raw "string" or from a string object. Retruns object
142# read in r.
143READ_STR () {
144 declare -a __reader_tokens
70aff0c1 145 TOKENIZE "${*}" || return 1 # sets __reader_tokens
31690700 146 #set | grep ^__reader_tokens
b8ee29b2 147 if [ -z "${__reader_tokens[0]}" ]; then
31690700
JM
148 r=
149 return 1 # No tokens
150 fi
151 READ_FORM
152 #echo "Token: ${r}: <${ANON["${r}"]}>"
153 return
154}
155
156# Call readline and save the history. Returns the string read in r.
157READLINE_EOF=
158READLINE_HISTORY_FILE=${HOME}/.mal-history
159READLINE () {
c9de2e82 160 history -r "${READLINE_HISTORY_FILE}" 2>/dev/null || true
31690700
JM
161 read -r -e -p "${1}" r || return "$?"
162 history -s -- "${r}"
c9de2e82 163 history -a "${READLINE_HISTORY_FILE}" 2>/dev/null || true
31690700 164}
ea81a808
JM
165
166fi