diff --git a/README.md b/README.md index b5e0934..c45278f 100644 --- a/README.md +++ b/README.md @@ -279,8 +279,8 @@ This allows for some fun tricks. For example, we can retrieve the body of a func Name | Safe-mode | Example | Example result | Description ----- | --------- | ------- | -------------- | ----------- and | yes | `(and 1 0 2)` | `0` | Return the first value that evaluates to false, or the last value. -case | yes | `(case (= 1 0) 0 (= 1 1) 1)` | `1` | Treat odd arguments as tests and even arguments as values. Evaluate and return the value after the first test that evaluates to true. - | yes | `(case (= 1 0) 0 "no match")` | `"no match"` | You can also give optional last argument to case which is returned if none of the tests evaluated to true. +cond | yes | `(cond [(= 0 1) 0] [(= 1 1) (print "1") 1])` | `11` | Treat first item of each argument as test. If test evaluates to true, evaluate the following expressions and return the value of the last. + | yes | `(cond [(= 0 1) 0] [(= 1 2) 1] [else 3])` | `3` | The symbol `else` evaluates to true. It can be used as the last condition in case no previous test evaluates to true. def | yes | `(def addOne (fn (a) (+ a 1)))` | `` | Define a value in the current environment. do | yes | `(do (print 1) 2)` | `12` | Evaluate multiple expressions and return the value of the last. env | yes | `(env +)` | `` | Return a definition from the current environment represented by argument. Without arguments return the current environment as a hash-map. diff --git a/src/Evaller.php b/src/Evaller.php index 8e57a7c..2c3eab5 100644 --- a/src/Evaller.php +++ b/src/Evaller.php @@ -93,26 +93,42 @@ class Evaller $ast = $astData[$astLength - 1]; continue; // tco - } elseif ($symbolName == 'case') { + } elseif ($symbolName == 'cond') { if ($astLength < 2) { - throw new MadLispException("case requires at least 1 argument"); + throw new MadLispException("cond requires at least 1 argument"); } - for ($i = 1; $i < $astLength - 1; $i += 2) { - $test = $this->eval($astData[$i], $env, $depth + 1); + for ($i = 1; $i < $astLength; $i++) { + if (!($astData[$i] instanceof Seq)) { + throw new MadLispException("argument to cond is not seq"); + } + + $condData = $astData[$i]->getData(); + + if (count($condData) < 2) { + throw new MadLispException("clause for cond requires at least 2 arguments"); + } + + if ($condData[0] instanceof Symbol && $condData[0]->getName() == 'else') { + $test = true; + } else { + $test = $this->eval($condData[0], $env, $depth + 1); + } + if ($test == true) { - $ast = $astData[$i + 1]; + // Evaluate interval expressions + for ($a = 1; $a < count($condData) - 1; $a++) { + $this->eval($condData[$a], $env, $depth + 1); + } + + // Evaluate last expression + $ast = $condData[count($condData) - 1]; continue 2; // tco } } - // Last value, no test - if ($astLength % 2 == 0) { - $ast = $astData[$astLength - 1]; - continue; // tco - } else { - return null; - } + // No matches + return null; } elseif ($symbolName == 'def') { if ($astLength != 3) { throw new MadLispException("def requires exactly 2 arguments");