1
1
mirror of https://github.com/kanaka/mal.git synced 2024-08-18 02:00:40 +03:00
mal/impls/gnu-smalltalk/reader.st
Joel Martin 8a19f60386 Move implementations into impls/ dir
- Reorder README to have implementation list after "learning tool"
  bullet.

- This also moves tests/ and libs/ into impls. It would be preferrable
  to have these directories at the top level.  However, this causes
  difficulties with the wasm implementations which need pre-open
  directories and have trouble with paths starting with "../../". So
  in lieu of that, symlink those directories to the top-level.

- Move the run_argv_test.sh script into the tests directory for
  general hygiene.
2020-02-10 23:50:16 -06:00

171 lines
4.6 KiB
Smalltalk

Object subclass: Reader [
| storage index |
TokenRegex := '[\s,]*(~@|[\[\]{}()''`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}(''"`,;)]*)'.
CommentRegex := ';.*'.
NumberRegex := '-?[0-9]+(?:\.[0-9]+)?'.
StringRegex := '"(?:\\.|[^\\"])*"'.
Reader class >> tokenizer: input [
| tokens token hit pos done |
tokens := OrderedCollection new.
pos := 1.
done := false.
[done] whileFalse: [
hit := input searchRegex: TokenRegex startingAt: pos.
token := hit at: 1.
token size = 0 ifTrue: [
tokens add: (input copyFrom: pos to: input size) trimSeparators.
done := true.
].
(token size = 0 or: [token matchRegex: CommentRegex]) ifFalse: [
tokens add: token
].
pos := pos + (hit match size).
pos > input size ifTrue: [
done := true.
].
].
^tokens
]
Reader class >> readStr: input [
| tokens reader form |
tokens := self tokenizer: input.
reader := self new: tokens.
tokens isEmpty ifTrue: [
^MALEmptyInput new signal
].
^self readForm: reader.
]
Reader class >> readForm: reader [
| token |
token := reader peek.
token = '(' ifTrue: [
^self readList: reader class: MALList ender: ')'
].
token = '[' ifTrue: [
^self readList: reader class: MALVector ender: ']'
].
token = '{' ifTrue: [
^self readList: reader class: MALMap ender: '}'
].
(token matchRegex: '[])}]') ifTrue: [
^MALUnexpectedToken new signal: token
].
token = '''' ifTrue: [
^self readSimpleMacro: reader name: #quote
].
token = '`' ifTrue: [
^self readSimpleMacro: reader name: #quasiquote
].
token = '~' ifTrue: [
^self readSimpleMacro: reader name: #unquote
].
token = '~@' ifTrue: [
^self readSimpleMacro: reader name: #'splice-unquote'
].
token = '@' ifTrue: [
^self readSimpleMacro: reader name: #deref
].
token = '^' ifTrue: [
^self readWithMetaMacro: reader
].
^self readAtom: reader
]
Reader class >> readList: reader class: aClass ender: ender [
| storage token |
storage := OrderedCollection new.
"pop opening token"
reader next.
[ token := reader peek. token isNil ] whileFalse: [
token = ender ifTrue: [
ender = '}' ifTrue: [
storage := storage asDictionary.
].
"pop closing token"
reader next.
^aClass new: storage
].
storage add: (self readForm: reader).
].
^MALUnterminatedSequence new signal: ender
]
Reader class >> readAtom: reader [
| token |
token := reader next.
token = 'true' ifTrue: [ ^MALObject True ].
token = 'false' ifTrue: [ ^MALObject False ].
token = 'nil' ifTrue: [ ^MALObject Nil ].
(token matchRegex: StringRegex) ifTrue: [
^MALString new: token parse
].
(token first = $") ifTrue: [
^MALUnterminatedSequence new signal: '"'
].
(token matchRegex: NumberRegex) ifTrue: [
^MALNumber new: token asNumber
].
(token first = $:) ifTrue: [
^MALKeyword new: token allButFirst asSymbol
].
^MALSymbol new: token asSymbol
]
Reader class >> readSimpleMacro: reader name: name [
| form list |
"pop reader macro token"
reader next.
form := self readForm: reader.
list := OrderedCollection from: { MALSymbol new: name. form }.
^MALList new: list
]
Reader class >> readWithMetaMacro: reader [
| form meta list |
"pop reader macro token"
reader next.
meta := self readForm: reader.
form := self readForm: reader.
list := OrderedCollection from:
{ MALSymbol new: #'with-meta'. form. meta }.
^MALList new: list
]
Reader class >> new: tokens [
| reader |
reader := super new.
reader init: tokens.
^reader
]
init: tokens [
storage := tokens.
index := 1.
]
peek [
^storage at: index ifAbsent: [ nil ]
]
next [
| token |
token := self peek.
index := index + 1.
^token
]
]