1 $ErrorActionPreference = "Stop"
3 Import-Module
$PSScriptRoot/types
.psm1
4 Import-Module
$PSScriptRoot/reader
.psm1
5 Import-Module
$PSScriptRoot/printer
.psm1
6 Import-Module
$PSScriptRoot/env
.psm1
7 Import-Module
$PSScriptRoot/core
.psm1
10 function READ
([String
] $str) {
15 function pair
?($ast) {
16 (sequential
? $ast) -and
$ast.values
.Count
-gt
0
19 function quasiquote
($ast) {
20 if (-not
(pair
? $ast)) {
21 return (new-list @
((new-symbol
"quote"), $ast))
24 if ((symbol
? $a0) -and
$a0.value
-ceq
"unquote") {
26 } elseif
(pair
? $a0) {
28 if ((symbol
? $a00) -and
$a00.value
-ceq
"splice-unquote") {
29 return (new-list @
((new-symbol
"concat"),
31 (quasiquote
$ast.rest
())))
34 return (new-list @
((new-symbol
"cons"),
36 (quasiquote
$ast.rest
())))
40 function macro
?($ast, $env) {
41 return (list
? $ast) -and
42 (symbol
? $ast.nth
(0)) -and
43 $env.find
($ast.nth
(0)) -and
44 $env.get
($ast.nth
(0)).macro
47 function macroexpand
($ast, $env) {
48 while (macro
? $ast $env) {
49 $mac = $env.get
($ast.nth
(0)).fn
50 $margs = $ast.rest
().values
56 function eval_ast
($ast, $env) {
57 if ($ast -eq
$null) { return $ast }
58 switch ($ast.GetType
().Name
) {
59 "Symbol" { return $env.get
($ast) }
60 "List" { return new-list
($ast.values
| ForEach
{ EVAL
$_ $env }) }
61 "Vector" { return new-vector
($ast.values
| ForEach
{ EVAL
$_ $env }) }
64 foreach ($k in $ast.values
.Keys
) {
65 $hm.values
[$k] = EVAL
$ast.values
[$k] $env
69 default
{ return $ast }
73 function EVAL
($ast, $env) {
75 #Write-Host "EVAL $(pr_str $ast)"
76 if (-not
(list
? $ast)) {
77 return (eval_ast
$ast $env)
80 $ast = (macroexpand
$ast $env)
81 if (-not
(list
? $ast)) {
82 return (eval_ast
$ast $env)
84 if (empty
? $ast) { return $ast }
86 $a0, $a1, $a2 = $ast.nth
(0), $ast.nth
(1), $ast.nth
(2)
87 switch -casesensitive
($a0.value
) {
89 return $env.set($a1, (EVAL
$a2 $env))
92 $let_env = new-env
$env
93 for
($i=0; $i -lt
$a1.values
.Count
; $i+=2) {
94 $_ = $let_env.set($a1.nth
($i), (EVAL
$a1.nth
(($i+1)) $let_env))
103 $ast = quasiquote
$a1
108 return $env.set($a1, $m)
111 return (macroexpand
$a1 $env)
114 if ($ast.values
.Count
-gt
2) {
115 $middle = new-list
$ast.values
[1..($ast.values
.Count-2
)]
116 $_ = eval_ast
$middle $env
118 $ast = $ast.last
() # TCO
121 $cond = (EVAL
$a1 $env)
122 if ($cond -eq
$null -or
123 ($cond -is
[Boolean
] -and
$cond -eq
$false)) {
124 $ast = $ast.nth
(3) # TCO
130 # Save EVAL into a variable that will get closed over
131 $feval = Get-Command EVAL
133 return (&$feval $a2 (new-env
$env $a1.values
$args))
135 return new-malfunc
$a2 $a1.values
$env $fn
138 $el = (eval_ast
$ast $env)
139 $f, $fargs = $el.first
(), $el.rest
().values
141 $env = (new-env
$f.env
$f.params
$fargs)
152 function PRINT
($exp) {
153 return pr_str
$exp $true
159 function REP
([String
] $str) {
160 return PRINT
(EVAL
(READ
$str) $repl_env)
163 # core.EXT: defined using PowerShell
164 foreach ($kv in $core_ns.GetEnumerator
()) {
165 $_ = $repl_env.set((new-symbol
$kv.Key
), $kv.Value
)
167 $_ = $repl_env.set((new-symbol
"eval"), { param
($a); (EVAL
$a $repl_env) })
168 $_ = $repl_env.set((new-symbol
"*ARGV*"), (new-list
$args[1..$args.Count
]))
170 # core.mal: defined using the language itself
171 $_ = REP
('(def! not (fn* (a) (if a false true)))')
172 $_ = REP
('(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) "\nnil)")))))')
173 $_ = REP
("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw `"odd number of forms to cond`
")) (cons 'cond (rest (rest xs)))))))")
176 if ($args.Count
-gt
0) {
177 $_ = REP
('(load-file "' + $args[0] + '")')
182 Write-Host "user> " -NoNewline
183 $line = [Console
]::ReadLine
()
184 if ($line -eq
$null) {
188 Write-Host (REP
($line))
190 Write-Host "Exception: $($_.Exception.Message)"