Commit | Line | Data |
---|---|---|
d7d197f9 JM |
1 | Import-Module $PSScriptRoot/types.psm1 |
2 | ||
3 | Class 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 | ||
22 | function 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 | ||
30 | function 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 |
56 | function 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 | ||
73 | function read_list([Reader] $rdr) { | |
74 | return new-list (read_seq $rdr "(" ")") | |
75 | } | |
76 | ||
77 | function read_vector([Reader] $rdr) { | |
78 | return new-vector (read_seq $rdr "[" "]") | |
79 | } | |
80 | ||
81 | function read_hash_map([Reader] $rdr) { | |
82 | return new-hashmap (read_seq $rdr "{" "}") | |
d7d197f9 JM |
83 | } |
84 | ||
85 | function 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 | ||
126 | function 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 | } |