mirror of
https://github.com/peklaiho/madlisp.git
synced 2024-11-22 21:35:03 +00:00
added support for try-catch exception handlers
This commit is contained in:
parent
6077dc135c
commit
167711c4f0
23
README.md
23
README.md
@ -223,6 +223,26 @@ For another example, lets combine `if` and `not` into a macro named `unless`, th
|
||||
|
||||
The `quasiquote` form described above is essential for declaring macros. Internally macros are just functions with a special flag.
|
||||
|
||||
## Exceptions
|
||||
|
||||
The language has support for `try-catch` style exception handlers. The syntax is `(try A (catch B C))` where A is evaluated first and if exception is thrown, then C is evaluated with the symbol B bound to the value of the exception. Exceptions are thrown using the `throw` core function. You can give any data structure as argument to `throw` and it is passed along to `catch`. This way exceptions can contain more data than just a string that represents an error message.
|
||||
|
||||
Simple example of throwing and catching an exception:
|
||||
|
||||
```
|
||||
> (try (throw {"msg":"message"}) (catch ex (str "error: " (get ex "msg"))))
|
||||
"error: message"
|
||||
```
|
||||
|
||||
Exceptions generated by PHP are catched as well. Their value will be a hash-map with keys `type`, `file`, `line` and `message`:
|
||||
|
||||
```
|
||||
> (try (get "wrong" "type") (catch ex (get ex "type")))
|
||||
"TypeError"
|
||||
```
|
||||
|
||||
The Repl contains its own exception handler defined in PHP that will catch any exceptions thrown outside of `try-catch` form.
|
||||
|
||||
## Reflection
|
||||
|
||||
You can use the `meta` special form to retrieve the arguments, body, code or full definition of user-defined functions:
|
||||
@ -276,6 +296,7 @@ or | yes | `(or false 0 1)` | `1` | Return the first value that evaluates to
|
||||
quote | yes | | | See the section Quoting.
|
||||
quasiquote | yes | | | See the section Quoting.
|
||||
quasiquote-expand | yes | | | See the section Quoting.
|
||||
try | yes | | | See the section Exceptions.
|
||||
undef | yes | `(undef myFn)` | `<function>` | Remove a definition from the current environment. Return the removed value.
|
||||
|
||||
## Functions
|
||||
@ -293,7 +314,7 @@ print | no | `(print "hello world")` | `hello world` | Print expression on the
|
||||
pstr | yes | `(pstr {"a":"b"})` | `"{\"a\":\"b\"}"` | Print expression to string.
|
||||
read | yes | `(read "(+ 1 2 3)")` | `(+ 1 2 3)` | Read a string as code and return the expression.
|
||||
sleep | no | `(sleep 2000)` | `null` | Sleep for the given period given in milliseconds using [usleep](https://www.php.net/manual/en/function.usleep).
|
||||
throw | yes | `(throw "invalid value")` | `error: "invalid value"` | Throw an exception. The given value is passed to catch.
|
||||
throw | yes | `(throw "invalid value")` | `error: "invalid value"` | Throw an exception. The given value is passed to catch. See the section Exceptions.
|
||||
timer | no | `(timer (fn (d) (sleep d)) 200)` | `0.20010209` | Measure the execution time of a function and return it in seconds.
|
||||
|
||||
### Collection functions
|
||||
|
@ -116,9 +116,7 @@ class Evaller
|
||||
} elseif ($symbolName == 'def') {
|
||||
if ($astLength != 3) {
|
||||
throw new MadLispException("def requires exactly 2 arguments");
|
||||
}
|
||||
|
||||
if (!($astData[1] instanceof Symbol)) {
|
||||
} elseif (!($astData[1] instanceof Symbol)) {
|
||||
throw new MadLispException("first argument to def is not symbol");
|
||||
}
|
||||
|
||||
@ -170,9 +168,7 @@ class Evaller
|
||||
} elseif ($symbolName == 'fn' || $symbolName == 'macro') {
|
||||
if ($astLength != 3) {
|
||||
throw new MadLispException("$symbolName requires exactly 2 arguments");
|
||||
}
|
||||
|
||||
if (!($astData[1] instanceof Seq)) {
|
||||
} elseif (!($astData[1] instanceof Seq)) {
|
||||
throw new MadLispException("first argument to $symbolName is not seq");
|
||||
}
|
||||
|
||||
@ -213,9 +209,7 @@ class Evaller
|
||||
} elseif ($symbolName == 'let') {
|
||||
if ($astLength != 3) {
|
||||
throw new MadLispException("let requires exactly 2 arguments");
|
||||
}
|
||||
|
||||
if (!($astData[1] instanceof MList)) {
|
||||
} elseif (!($astData[1] instanceof MList)) {
|
||||
throw new MadLispException("first argument to let is not list");
|
||||
}
|
||||
|
||||
@ -367,12 +361,47 @@ class Evaller
|
||||
}
|
||||
|
||||
return $this->quasiquote($astData[1]);
|
||||
} elseif ($symbolName == 'try') {
|
||||
if ($astLength != 3) {
|
||||
throw new MadLispException("try requires exactly 2 arguments");
|
||||
} elseif (!($astData[2] instanceof MList)) {
|
||||
throw new MadLispException("second argument to try is not list");
|
||||
}
|
||||
|
||||
$catch = $astData[2]->getData();
|
||||
|
||||
if (count($catch) == 3 && $catch[0] instanceof Symbol &&
|
||||
$catch[0]->getName() == 'catch' && $catch[1] instanceof Symbol) {
|
||||
|
||||
try {
|
||||
return $this->eval($astData[1], $env, $depth + 1);
|
||||
} catch (\Throwable $ex) {
|
||||
if ($ex instanceof MadLispUserException) {
|
||||
$exVal = $ex->getValue();
|
||||
} else {
|
||||
// Return a hash-map for PHP exceptions
|
||||
$exVal = new Hash([
|
||||
'type' => get_class($ex),
|
||||
'file' => $ex->getFile(),
|
||||
'line' => $ex->getLine(),
|
||||
'message' => $ex->getMessage()
|
||||
]);
|
||||
}
|
||||
|
||||
$newEnv = new Env('catch', $env);
|
||||
$newEnv->set($catch[1]->getName(), $exVal);
|
||||
|
||||
$env = $newEnv;
|
||||
$ast = $catch[2];
|
||||
continue; // tco
|
||||
}
|
||||
} else {
|
||||
throw new MadLispException("invalid form for catch");
|
||||
}
|
||||
} elseif ($symbolName == 'undef') {
|
||||
if ($astLength != 2) {
|
||||
throw new MadLispException("undef requires exactly 1 argument");
|
||||
}
|
||||
|
||||
if (!($astData[1] instanceof Symbol)) {
|
||||
} elseif (!($astData[1] instanceof Symbol)) {
|
||||
throw new MadLispException("first argument to undef is not symbol");
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user