1 readline
= require
'readline'
2 {id
, map
, pairs
-to
-obj
} = require
'prelude-ls'
3 {list
-to
-pairs
} = require
'./utils'
5 export class OnlyComment
7 parse
-error
= (msg
) -> throw new
Error msg
14 # returns the token at the current position
15 # and increments position.
18 if result? then @pos +
= 1
21 # just returns the token at the current position.
23 if @pos
< @tokens.length
27 eof
-or
-comment
= (reader
) ->
29 if token? and not token.startsWith
';'
30 then parse
-error "expected
EOF, got
'#{token}'"
33 export read_str
= (str
) ->
36 |
> (tokens
) -> new Reader tokens
38 result
= read_form reader
39 if token? then parse
-error "expected
EOF, got
'#{token}'"
43 # This function will take a single string and return an array
/list
44 # of all the tokens
(strings
) in it.
47 [\s
,]* # whitespace or commas
48 ( ~@ # special two
-char ~@
49 |
[\
[\
]{}()'`~^@] # special single char one of []{}'`~^@
50 | "
(?
:\\.|
[^\\"
])*"? # double
-quoted string
51 |
;.
* # any seq of chars starting
;
52 |
[^\s\
[\
]{}('"`,;)]+ # seq of non-special chars: symbols, numbers,
53 ) # "true"
, "false" and "nil".
57 while re.lastIndex
< str.length
61 # Allow whitespace or commas at the end of the input.
62 break if
/[\s
,]+
/.exec str.substring idx
63 parse
-error "parse error at character #
{idx
}"
67 if tok
[0] != ';' then tokens.push m[1]
71 read_form
= (reader
) ->
73 |
'(' => read_list reader, ')'
74 |
'[' => read_list reader, ']'
75 |
'{' => read_list reader, '}'
76 |
'\'' => read-macro 'quote', reader
77 |
'\`' => read-macro 'quasiquote', reader
78 |
'~' => read-macro 'unquote', reader
79 |
'~@' => read-macro 'splice-unquote', reader
80 |
'@' => read-macro 'deref', reader # todo only symbol?
81 |
'^' => read-with-meta reader
83 if that? then read_atom reader
84 else parse
-error
'expected a form, got EOF'
87 read_list
= (reader
, end
) ->
89 reader.next
! # accept
'(', '[' or '{'
93 parse
-error "expected
'#{end}', got EOF"
98 list.push read_form reader
101 |
')' => {type: \list, value: list}
102 |
']' => {type: \vector, value: list}
103 |
'}' => list-to-map list
106 special_chars
= '[]{}\'`~^@'
107 constants
= [\true \false \nil
]
110 read_atom
= (reader
) ->
112 if token in constants
113 {type
: \const
, value
: reader.next
!}
114 else if token.match
/^"
(?
:\\.|
[^\\"
])*"$
/
115 {type
: \string
, value
: decode
-string reader.next
!}
116 else if token
[0] == '"'
117 parse
-error "expected
'\"', got EOF"
118 else if token.match
/^
-?\d+$
/
119 {type
: \int
, value
: parseInt reader.next
!}
120 else if token
!= '~@' and token not in special_chars
121 if token.startsWith
':'
122 {type
: \keyword
, value
: reader.next
!}
124 {type
: \symbol
, value
: reader.next
!}
126 parse
-error "expected an atom
, got #
{token
}"
129 decode
-string
= (str
) ->
130 str |
> (.slice
1, -1)
131 |
> (.replace
/\\
[\"\\n
]/g
,
138 export keyword
-prefix
= '\u029e'
140 export map
-keyword
= (key
) ->
142 | \string
=> key.value
143 | \keyword
=> keyword
-prefix + key.value
145 parse
-error "#
{key.type
} can
't be a map key"
147 export list
-to
-map
= (list
) ->
148 if list.length %
2 != 0
149 parse
-error "map should have an even number
150 of elements
, got #
{list.length
}"
153 |
> map
([key
, value
]) -> [(map
-keyword key
), value
]
155 |
> (obj
) -> {type
: \map
, value
: obj
}
158 read
-macro
= (symbol
, reader
) ->
159 reader.next
! # accept macro start token
164 * {type
: \symbol
, value
: symbol
}
168 read
-with
-meta
= (reader
) ->
169 reader.next
! # accept ^
170 if reader.peek
! != '{'
171 parse
-error "expected a map after with
-meta reader macro
'^'"
173 meta
= read_list reader
, '}'
174 form
= read_form reader
179 * {type
: \symbol
, value
: 'with-meta'}