aboutsummaryrefslogtreecommitdiff
path: root/forth/reader.fs
blob: 7ff46fd83488d13e473bdf6d86cf0a91c427ff3e (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
require types.fs
require printer.fs

-2 constant skip-elem

\ Drop a char off the front of string by advancing the addr and
\ decrementing the length, and fetch next char
: adv-str ( str-addr str-len -- str-addr str-len char )
    swap 1+ swap 1-
    dup 0= if 0 ( eof )
    else over c@ endif ;

: skip-spaces ( str-addr str-len char -- str-addr str-len non-space-char )
    begin
        dup bl = if
            -1
        else
            dup [char] , =
        endif
    while ( str-addr str-len space-char )
        drop adv-str
    repeat ;

: mal-digit? ( char -- flag )
    dup [char] 9 <= if
        [char] 0 >=
    else
        drop 0
    endif ;

: char-in-str? ( char str-addr str-len )
    rot { needle }
    begin ( str-addr str-len )
        adv-str needle = if
            2drop -1 -1 \ success! drop and exit
        else
            dup 0= if
                2drop 0 -1 \ str consumed, char not found.
            else
                0 \ continue
            endif
        endif
    until ;

s\" []{}()'\"`,; " constant non-sym-chars-len constant non-sym-chars
: sym-char? ( char -- flag )
    non-sym-chars non-sym-chars-len char-in-str? 0= ;

defer read-form ( str-addr str-len -- str-addr str-len mal-obj )

: read-int ( str-addr str-len digit-char -- str-addr str-len non-digit-char mal-int )
    0 { int }
    begin ( str-addr str-len digit-char )
        [char] 0 - int 10 * + to int ( str-addr str-len )
        adv-str dup mal-digit? 0= ( str-addr str-len digit-char )
    until
    int MalInt. ;

: read-comment ( str-addr str-len sym-char -- str-addr str-len char skim-elem )
    drop
    begin
        adv-str = 10
    until
    adv-str skip-elem ;

: read-symbol-str ( str-addr str-len sym-char -- str-addr str-len char sym-addr sym-len )
    new-str { sym-addr sym-len }
    begin ( str-addr str-len sym-char )
        sym-addr sym-len rot str-append-char to sym-len to sym-addr
        adv-str dup sym-char? 0=
    until
    sym-addr sym-len ;

: read-string-literal ( in-addr in-len quote-char -- in-addr in-len mal-string )
    new-str { out-addr out-len }
    drop \ drop leading quote
    begin ( in-addr in-len )
        adv-str over 0= if
            2drop s\" expected '\"', got EOF\n" safe-type 1 throw
        endif
        dup [char] " <>
    while
        dup [char] \ = if
            drop adv-str
            dup [char] n = if drop 10 endif
            dup [char] r = if drop 13 endif
        endif
        out-addr out-len rot str-append-char to out-len to out-addr
    repeat
    drop adv-str \ skip trailing quote
    out-addr out-len MalString. ;

: read-list ( str-addr str-len open-paren-char -- str-addr str-len non-paren-char mal-list )
    \ push objects onto "dictionary" -- maybe not the best stack for this?
    0 { close-char len }
    drop adv-str
    begin ( str-addr str-len char )
        skip-spaces ( str-addr str-len non-space-char )
        over 0= if
            drop 2drop
            s\" expected '" close-char str-append-char
            s\" ', got EOF" str-append safe-type 1 throw
        endif
        dup close-char <>
    while ( str-addr str-len non-space-non-paren-char )
        read-form , len 1+ to len
    repeat
    drop adv-str

    \ pop objects out of "dictionary" into MalList
    mal-nil
    len 0 ?do
        0 cell - allot
        here @ swap conj
    loop
    ;

: read-wrapped ( buf-addr buf-len quote-char sym-addr sym-len -- buf-addr buf-len char mal-list )
    MalSymbol. { sym } ( buf-addr buf-len char )
    read-form mal-nil conj ( buf-addr buf-len char mal-list )
    sym swap conj ;

: read-form2 ( str-addr str-len char -- str-addr str-len char mal-obj )
    begin
        skip-spaces
        dup mal-digit? if read-int else
        dup [char] ( = if [char] ) read-list else
        dup [char] [ = if [char] ] read-list MalVector new tuck MalVector/list ! else
        dup [char] { = if [char] } read-list MalMap new tuck MalMap/list ! else
        dup [char] " = if read-string-literal else
        dup [char] ; = if read-comment else
        dup [char] @ = if drop adv-str s" deref" read-wrapped else
        dup [char] ' = if drop adv-str s" quote" read-wrapped else
        dup [char] ` = if drop adv-str s" quasiquote" read-wrapped else
        dup [char] ~ = if
            drop adv-str
            dup [char] @ = if drop adv-str s" splice-unquote" read-wrapped
            else s" unquote" read-wrapped
            endif
        else
        dup [char] ^ = if
            drop adv-str
            read-form { meta } read-form { obj }
            meta mal-nil conj
            obj swap conj
            s" with-meta" MalSymbol. swap conj
        else
            read-symbol-str MalSymbol.
        endif endif endif endif endif endif endif endif endif endif endif
        dup skip-elem =
    while drop repeat ;
' read-form2 is read-form

: read-str ( str-addr str-len - mal-obj )
    over c@ read-form { obj } drop 2drop obj ;