Merge pull request #386 from asarhaddon/test-let-recursive-def
[jackhill/mal.git] / powershell / reader.psm1
CommitLineData
d7d197f9
JM
1Import-Module $PSScriptRoot/types.psm1
2
3Class Reader {
4 [String[]] $tokens
5 [int] $pos
6
7 Reader([String[]] $toks) {
8 $this.tokens = $toks
9 $this.pos = 0
10 }
11
12 [String] peek() {
13 return $this.tokens[$this.pos]
14 }
15
16 [String] next() {
17 return $this.tokens[$this.pos++]
18 }
19}
20
21
22function tokenize {
4aa0ebdf 23 $r = [regex]"[\s,]*(~@|[\[\]{}()'``~^@]|`"(?:\\.|[^\\`"])*`"?|;.*|[^\s\[\]{}('`"``,;)]*)"
d7d197f9 24 $r.Matches($args) |
f6146aef
JM
25 Where-Object { $_.Groups.Item(1).Value.Length -gt 0 -and
26 $_.Groups.Item(1).Value[0] -ne ";" } |
d7d197f9
JM
27 Foreach-Object { $_.Groups.Item(1).Value }
28}
29
30function read_atom([Reader] $rdr) {
31 $token = $rdr.next()
32 if ($token -match "^-?[0-9]+$") {
33 return [convert]::ToInt32($token, 10)
9a726b56 34 } elseif ($token -match "^`"(?:\\.|[^\\`"])*`"$") {
f6146aef 35 $s = $token.Substring(1,$token.Length-2)
da9aef12 36 $s = $s -replace "\\\\", "$([char]0x29e)"
f6146aef
JM
37 $s = $s -replace "\\`"", "`""
38 $s = $s -replace "\\n", "`n"
da9aef12 39 $s = $s -replace "$([char]0x29e)", "\"
f6146aef 40 return $s
4aa0ebdf
JM
41 } elseif ($token -match "^`".*") {
42 throw "expected '`"', got EOF"
f6146aef
JM
43 } elseif ($token -match ":.*") {
44 return "$([char]0x29e)$($token.substring(1))"
45 } elseif ($token -eq "true") {
46 return $true
47 } elseif ($token -eq "false") {
48 return $false
49 } elseif ($token -eq "nil") {
50 return $null
d7d197f9
JM
51 } else {
52 return new-symbol($token)
53 }
54}
55
f6146aef
JM
56function read_seq([Reader] $rdr, $start, $end) {
57 $seq = @()
d7d197f9 58 $token = $rdr.next()
f6146aef
JM
59 if ($token -ne $start) {
60 throw "expected '$start'"
d7d197f9 61 }
f6146aef 62 while (($token = $rdr.peek()) -ne $end) {
d7d197f9 63 if ($token -eq "") {
f6146aef 64 throw "expected '$end', got EOF"
d7d197f9
JM
65 }
66 $form = read_form $rdr
f6146aef 67 $seq += $form
d7d197f9
JM
68 }
69 $token = $rdr.next()
f6146aef
JM
70 return ,$seq
71}
72
73function read_list([Reader] $rdr) {
74 return new-list (read_seq $rdr "(" ")")
75}
76
77function read_vector([Reader] $rdr) {
78 return new-vector (read_seq $rdr "[" "]")
79}
80
81function read_hash_map([Reader] $rdr) {
82 return new-hashmap (read_seq $rdr "{" "}")
d7d197f9
JM
83}
84
85function read_form([Reader] $rdr) {
86 $token = $rdr.peek()
87 switch ($token) {
f6146aef
JM
88 # reader macros/transforms
89 "'" { $_ = $rdr.next();
90 return new-list @((new-symbol "quote"),
91 (read_form $rdr)) }
92 "``" { $_ = $rdr.next();
93 return new-list @((new-symbol "quasiquote"),
94 (read_form $rdr)) }
95 "~" { $_ = $rdr.next();
96 return (new-list @((new-symbol "unquote"),
97 (read_form $rdr))) }
98 "~@" { $_ = $rdr.next();
99 return (new-list @((new-symbol "splice-unquote"),
100 (read_form $rdr))) }
101 "^" { $_ = $rdr.next();
102 $meta = read_form $rdr
103 return (new-list @((new-symbol "with-meta"),
104 (read_form $rdr),
105 $meta)) }
106 "@" { $_ = $rdr.next();
107 return (new-list @((new-symbol "deref"),
108 (read_form $rdr))) }
109
110 # list
d7d197f9 111 ")" { throw "unexpected ')'" }
f6146aef
JM
112 "(" { return read_list $rdr }
113
114 # vector
115 "]" { throw "unexpected ']'" }
116 "[" { return read_vector $rdr }
117
118 # hashmap
119 "}" { throw "unexpected '}'" }
120 "{" { return read_hash_map $rdr }
121
122 default { return read_atom $rdr }
d7d197f9
JM
123 }
124}
125
126function read_str {
127 $toks = tokenize($args[0])
f6146aef 128 if ($toks.Length -eq 0) { return $null }
d7d197f9
JM
129 read_form([Reader]::new($toks))
130}