1
1
mirror of https://github.com/kanaka/mal.git synced 2024-11-08 23:27:30 +03:00
mal/logo/reader.lg
Joel Martin 4aa0ebdf47 Error on unterminated strings.
Add a step1 test to make sure that implementations are properly
throwing an error on unclosed strings.

Fix 47 implementations and update the guide to note the correct
behavior.
2019-01-25 16:16:06 -06:00

222 lines
5.1 KiB
Plaintext

load "../logo/types.lg
make "open_paren_char char 40
make "close_paren_char char 41
make "open_bracket_char char 91
make "close_bracket_char char 93
make "open_brace_char char 123
make "close_brace_char char 125
to newlinep :char
output case ascii :char [
[[10 13] "true]
[else "false]
]
end
to whitespacep :char
output case ascii :char [
[[9 10 13 32] "true]
[else "false]
]
end
to singlechartokenp :char
output case :char [
[[ ( ) \[ \] \{ \} ' ` \^ @ ] "true]
[else "false]
]
end
to separatorp :char
output ifelse whitespacep :char [
"true
] [
case :char [
[[ ( ) \[ \] \{ \} ' \" ` , \; ] "true]
[else "false]
]
]
end
to read_comment_token :s
localmake "rest :s
while [not emptyp :rest] [
localmake "c first :rest
ifelse newlinep :c [
output list " butfirst :rest
] [
make "rest butfirst :rest
]
]
output list " :rest
end
to read_word_token :s
localmake "w "
localmake "rest :s
while [not emptyp :rest] [
localmake "c first :rest
ifelse separatorp :c [
output list :w :rest
] [
make "w word :w :c
make "rest butfirst :rest
]
]
output list :w :rest
end
to read_string_token :s
localmake "w first :s
localmake "rest butfirst :s
while [not emptyp :rest] [
localmake "c first :rest
if :c = "" [
make "w word :w :c
output list :w butfirst :rest
]
if :c = "\\ [
make "w word :w :c
make "rest butfirst :rest
make "c first :rest
]
make "w word :w :c
make "rest butfirst :rest
]
(throw "error [Expected closing quotes, not EOF])
end
to read_next_token :s
localmake "c first :s
localmake "rest butfirst :s
output cond [
[[whitespacep :c] list " :rest]
[[:c = ",] list " :rest]
[[:c = "~] ifelse ((first :rest) = "@) [list "~@ butfirst :rest] [list "~ :rest] ]
[[singlechartokenp :c] list :c :rest]
[[:c = "\;] read_comment_token :s]
[[:c = ""] read_string_token :s]
[else read_word_token :s]
]
output list first :s butfirst :s
end
to tokenize :str
localmake "tokens []
localmake "s :str
while [not emptyp :s] [
localmake "res read_next_token :s
localmake "token first :res
make "s last :res
if not emptyp :token [
make "tokens lput :token :tokens
]
]
output :tokens
end
to reader_new :tokens
output listtoarray list :tokens 1
end
to reader_peek :reader
localmake "tokens item 1 :reader
localmake "pos item 2 :reader
if :pos > count :tokens [output []]
output item :pos :tokens
end
to reader_next :reader
make "token reader_peek :reader
localmake "pos item 2 :reader
setitem 2 :reader (1 + :pos)
output :token
end
to unescape_string :token
localmake "s butfirst butlast :token ; remove surrounding double-quotes
localmake "i 1
localmake "res "
while [:i <= count :s] [
localmake "c item :i :s
ifelse :c = "\\ [
make "i (:i + 1)
make "c item :i :s
make "res word :res case :c [
[[ n ] char 10]
[[ " ] "\" ]
[[ \\ ] "\\ ]
[else :c]
]
] [
make "res word :res :c
]
make "i (:i + 1)
]
output :res
end
to read_atom :reader
localmake "token reader_next :reader
output cond [
[[:token = "nil] nil_new]
[[:token = "true] true_new]
[[:token = "false] false_new]
[[numberp :token] obj_new "number :token]
[[(first :token) = ": ] obj_new "keyword butfirst :token]
[[(first :token) = "\" ] obj_new "string unescape_string :token]
[else symbol_new :token]
]
end
to read_seq :reader :value_type :start_char :end_char
localmake "token reader_next :reader
if :token <> :start_char [(throw "error sentence "expected (word "' :start_char "'))]
localmake "seq []
make "token reader_peek :reader
while [:token <> :end_char] [
if emptyp :token [(throw "error (sentence [expected] (word "' :end_char "',) [got EOF]))]
make "seq lput read_form :reader :seq
make "token reader_peek :reader
]
ignore reader_next :reader
output obj_new :value_type :seq
end
to reader_macro :reader :symbol_name
ignore reader_next :reader
output obj_new "list list symbol_new :symbol_name read_form :reader
end
to with_meta_reader_macro :reader
ignore reader_next :reader
localmake "meta read_form :reader
output obj_new "list (list symbol_new "with-meta read_form :reader :meta)
end
to read_form :reader
output case reader_peek :reader [
[[ ' ] reader_macro :reader "quote ]
[[ ` ] reader_macro :reader "quasiquote ]
[[ ~ ] reader_macro :reader "unquote ]
[[ ~@ ] reader_macro :reader "splice-unquote ]
[[ \^ ] with_meta_reader_macro :reader ]
[[ @ ] reader_macro :reader "deref ]
[[ ( ] read_seq :reader "list :open_paren_char :close_paren_char ]
[[ ) ] (throw "error sentence [unexpected] (word "' :close_paren_char "')) ]
[[ \[ ] read_seq :reader "vector :open_bracket_char :close_bracket_char ]
[[ \] ] (throw "error sentence [unexpected] (word "' :close_bracket_char "')) ]
[[ \{ ] read_seq :reader "hashmap :open_brace_char :close_brace_char ]
[[ \} ] (throw "error sentence [unexpected] (word "' :close_brace_char "')) ]
[else read_atom :reader]
]
end
to read_str :str
localmake "tokens tokenize :str
if emptyp :tokens [output []]
localmake "reader reader_new :tokens
output read_form :reader
end