From 6077dc135c054075909795bfa316879caa493fc0 Mon Sep 17 00:00:00 2001 From: Pekka Laiho Date: Wed, 9 Dec 2020 08:09:12 +0700 Subject: [PATCH] improve exceptions, add type for user exceptions --- README.md | 4 ++-- run.php | 3 +++ src/Lib/Core.php | 16 +++++++--------- src/Lisp.php | 9 ++++++--- src/MadLispUserException.php | 21 +++++++++++++++++++++ 5 files changed, 39 insertions(+), 14 deletions(-) create mode 100644 src/MadLispUserException.php diff --git a/README.md b/README.md index 0483517..a1f6855 100644 --- a/README.md +++ b/README.md @@ -287,13 +287,13 @@ Name | Safe-mode | Example | Example result | Description debug | no | `(debug)` | `true` | Toggle debug output. doc | yes | `(doc +)` | `"Return the sum of all arguments."` | Show the documentation string for a function. | yes | `(doc myfn "Documentation string.")` | `"Documentation string."` | Set the documentation string for a function. -error | yes | `(error "invalid value")` | `error: invalid value` | Throw an exception with message as argument. exit | no | `(exit 1)` | | Terminate the script with given exit code using [exit](https://www.php.net/manual/en/function.exit.php). loop | yes | `(loop (fn (a) (do (print a) (coinflip))) "hello ")` | `hello hello hello false` | Call the given function repeatedly in a loop until it returns false. print | no | `(print "hello world")` | `hello world` | Print expression on the screen. Give optional second argument as `true` to show strings in readable format. Print returns null (shown in REPL). 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. timer | no | `(timer (fn (d) (sleep d)) 200)` | `0.20010209` | Measure the execution time of a function and return it in seconds. ### Collection functions @@ -431,7 +431,7 @@ rand | `(rand 5 10)` | `8` | Return a random integer between given min and max Name | Example | Example result | Description ------------- | ------- | -------------- | ----------- re-match | `(re-match "/^[a-z]{4}[0-9]{4}$/" "test1234")` | `true` | Match subject to regular expression using [preg_match](https://www.php.net/manual/en/function.preg-match.php). -re-match | `(re-match "/[a-z]{5}/" "one three five" true)` | `"three"` | Give true as third argument to return the matched text. + | `(re-match "/[a-z]{5}/" "one three five" true)` | `"three"` | Give true as third argument to return the matched text. re-match-all | `(re-match-all "/[A-Z][a-z]{2}[0-9]/" "One1 Two2 Three3")` | `["One1" "Two2"]` | Find multiple matches to regular expression using [preg_match_all](https://www.php.net/manual/en/function.preg-match-all.php). re-replace | `(re-replace "/year ([0-9]{4}) month ([0-9]{2})/" "$1-$2-01" "year 2020 month 10")` | `"2020-10-01"` | Perform search and replace with regular expression using [preg_replace](https://www.php.net/manual/en/function.preg-replace.php). re-split | `(re-split "/\\s+/" "aa bb cc ")` | `["aa" "bb" "cc"]` | Split the subject by regular expression using [preg_split](https://www.php.net/manual/en/function.preg-split.php). The flag `PREG_SPLIT_NO_EMPTY` is enabled. diff --git a/run.php b/run.php index 4c3370f..1d9846b 100644 --- a/run.php +++ b/run.php @@ -18,6 +18,9 @@ function ml_repl($lisp) try { $lisp->rep($input, true); + } catch (MadLisp\MadLispUserException $ex) { + print('error: '); + $lisp->print($ex->getValue(), true); } catch (MadLisp\MadLispException $ex) { print('error: ' . $ex->getMessage()); } catch (TypeError $ex) { diff --git a/src/Lib/Core.php b/src/Lib/Core.php index 71372e2..88490f1 100644 --- a/src/Lib/Core.php +++ b/src/Lib/Core.php @@ -6,7 +6,7 @@ use MadLisp\CoreFunc; use MadLisp\Env; use MadLisp\Evaller; use MadLisp\Func; -use MadLisp\MadLispException; +use MadLisp\MadLispUserException; use MadLisp\MList; use MadLisp\Printer; use MadLisp\Reader; @@ -60,14 +60,6 @@ class Core implements ILib } )); - // This is allowed in safe-mode, because the evaluation should be wrapped in a try-catch in embedded use. - $env->set('error', new CoreFunc('error', 'Throw an exception using argument (string) as message.', 1, 1, - function (string $error) { - // We should probably use another exception type to distinguish user-thrown errors from built-in errors. - throw new MadLispException($error); - } - )); - if (!$this->safemode) { $env->set('exit', new CoreFunc('exit', 'Terminate the script with given exit code.', 0, 1, function (int $status = 0) { @@ -113,6 +105,12 @@ class Core implements ILib )); } + $env->set('throw', new CoreFunc('throw', 'Throw an exception. Takes one argument which is passed to catch.', 1, 1, + function ($error) { + throw new MadLispUserException($error); + } + )); + if (!$this->safemode) { $env->set('timer', new CoreFunc('timer', 'Measure the execution time of a function and return it in seconds.', 1, -1, function (Func $f, ...$args) { diff --git a/src/Lisp.php b/src/Lisp.php index 7b70cbd..48f77e5 100644 --- a/src/Lisp.php +++ b/src/Lisp.php @@ -18,6 +18,11 @@ class Lisp $this->env = $env; } + public function print($value, bool $printReadable): void + { + $this->printer->print($value, $printReadable); + } + public function readEval(string $input) { $tokens = $this->tokenizer->tokenize($input); @@ -30,9 +35,7 @@ class Lisp // read, eval, print public function rep(string $input, bool $printReadable): void { - $result = $this->readEval($input); - - $this->printer->print($result, $printReadable); + $this->print($this->readEval($input), $printReadable); } public function setEnv(Env $env): void diff --git a/src/MadLispUserException.php b/src/MadLispUserException.php new file mode 100644 index 0000000..9c9971e --- /dev/null +++ b/src/MadLispUserException.php @@ -0,0 +1,21 @@ +value = $value; + } + + public function getValue() + { + return $this->value; + } +}