aboutsummaryrefslogtreecommitdiff
path: root/bash/reader.sh
blob: a00e7a1bff7437c032f2dd02fd0812f77da35020 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#
# mal (Make Lisp) Parser/Reader
#

if [ -z "${__mal_readerr_included__}" ]; then
__mal_readerr_included=true

source $(dirname $0)/types.sh

READ_ATOM () {
    local token=${__reader_tokens[${__reader_idx}]}
    __reader_idx=$(( __reader_idx + 1 ))
    case "${token}" in
        [0-9]*) _number "${token}" ;;
        \"*)    token="${token:1:-1}"
                token="${token//\\\"/\"}"
                _string "${token}" ;;
        :*)     _keyword "${token:1}" ;;
        nil)    r="${__nil}" ;;
        true)   r="${__true}" ;;
        false)  r="${__false}" ;;
        *)      _symbol "${token}" ;;
    esac
}

# Return seqence of tokens into r.
#   ${1}: Type of r (vector, list)
#   ${2}: starting symbol
#   ${3}: ending symbol
READ_SEQ () {
    local start="${1}"
    local end="${2}"
    local items=""
    local token=${__reader_tokens[${__reader_idx}]}
    __reader_idx=$(( __reader_idx + 1 ))
    if [[ "${token}" != "${start}" ]]; then
        r=
        _error "expected '${start}'"
        return
    fi
    token=${__reader_tokens[${__reader_idx}]}
    while [[ "${token}" != "${end}" ]]; do
        if [[ ! "${token}" ]]; then
            r=
            _error "exepected '${end}', got EOF"
            return
        fi
        READ_FORM
        items="${items} ${r}"
        token=${__reader_tokens[${__reader_idx}]}
    done
    __reader_idx=$(( __reader_idx + 1 ))
    r="${items:1}"
}

# Return form in r
READ_FORM () {
    local token=${__reader_tokens[${__reader_idx}]}
    case "${token}" in
        \')     __reader_idx=$(( __reader_idx + 1 ))
                _symbol quote; local q="${r}"
                READ_FORM; local f="${r}"
                _list "${q}" "${f}" ;;
        \`)     __reader_idx=$(( __reader_idx + 1 ))
                _symbol quasiquote; local q="${r}"
                READ_FORM; local f="${r}"
                _list "${q}" "${f}" ;;
        \~)     __reader_idx=$(( __reader_idx + 1 ))
                _symbol unquote; local q="${r}"
                READ_FORM; local f="${r}"
                _list "${q}" "${f}" ;;
        \~\@)   __reader_idx=$(( __reader_idx + 1 ))
                _symbol splice-unquote; local q="${r}"
                READ_FORM; local f="${r}"
                _list "${q}" "${f}" ;;
        ^)      __reader_idx=$(( __reader_idx + 1 ))
                _symbol with-meta; local wm="${r}"
                READ_FORM; local meta="${r}"
                READ_FORM; local obj="${r}"
                _list "${wm}" "${obj}" "${meta}" ;;
        @)     __reader_idx=$(( __reader_idx + 1 ))
                _symbol deref; local d="${r}"
                READ_FORM; local f="${r}"
                _list "${d}" "${f}" ;;
        \))     _error "unexpected ')'" ;; 
        \()     READ_SEQ "(" ")"
                _list ${r} ;;
        \])     _error "unexpected ']'" ;; 
        \[)     READ_SEQ "[" "]"
                _vector ${r} ;;
        \})     _error "unexpected '}'" ;; 
        \{)     READ_SEQ "{" "}"
                _hash_map ${r} ;;
        *)      READ_ATOM
    esac
}

# Returns __reader_tokens as an indexed array of tokens
TOKENIZE () {
    local data="${*}"
    local datalen=${#data}
    local idx=0
    local chunk=0
    local chunksz=500
    local match=
    local token=
    local str=

    __reader_idx=0
    __reader_tokens=
    while true; do
        if (( ${#str} < ( chunksz / 2) )) && (( chunk < datalen )); then
            str="${str}${data:${chunk}:${chunksz}}"
            chunk=$(( chunk + ${chunksz} ))
        fi
        (( ${#str} == 0 )) && break
        [[ "${str}" =~ ^^([][{}\(\)^@])|^(~@)|(\"(\\.|[^\\\"])*\")|^(;[^$'\n']*)|^([~\'\`])|^([^][ ~\`\'\";{}\(\)^@\,]+)|^[,]|^[[:space:]]+ ]]
        match=${BASH_REMATCH[0]}
        str="${str:${#match}}"
        token="${match//$'\n'/}"
        #echo "MATCH: '${token}' / [${str}]"
        if ! [[ "${token}" =~ (^[,]$|^[[:space:]]*;.*$|^[[:space:]]*$) ]]; then 
            __reader_tokens[${idx}]="${token}"
            idx=$(( idx + 1 ))
        fi
        if [ -z "${match}" ]; then
            _error "Tokenizing error at: ${str:0:50}"
            return 1
        fi
    done
}

# read-str from a raw "string" or from a string object. Retruns object
# read in r.
READ_STR () {
    declare -a __reader_tokens
    TOKENIZE "${*}" || return 1  # sets __reader_tokens
    #set | grep ^__reader_tokens
    if [ -z "${__reader_tokens[0]}" ]; then
        r=
        return 1  # No tokens
    fi
    READ_FORM
    #echo "Token: ${r}: <${ANON["${r}"]}>"
    return
}

# Call readline and save the history. Returns the string read in r.
READLINE_EOF=
READLINE_HISTORY_FILE=${HOME}/.mal-history
READLINE () {
    history -r "${READLINE_HISTORY_FILE}"
    read -r -e -p "${1}" r || return "$?"
    history -s -- "${r}"
    history -a "${READLINE_HISTORY_FILE}"
}

fi