From cf75c6eefd9f067c4942f86ee401abd98c43808a Mon Sep 17 00:00:00 2001 From: Pekka Laiho Date: Sat, 12 Dec 2020 14:53:48 +0700 Subject: [PATCH] add while, remove loop --- README.md | 19 +++++++++++++++---- mad/calc.mad | 12 ++++-------- src/Evaller.php | 16 ++++++++++++++++ src/Lib/Core.php | 9 --------- 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 8f64281..e9b2aee 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ The core project does not have any dependencies to external [Composer](https://g ## Usage -Use the **run.php** file to invoke the interpreter from the command line. You can start the Repl with the -r switch: +Use the **run.php** file to invoke the interpreter from the command line. You can start the REPL with the -r switch: ``` $ php run.php -r @@ -260,7 +260,7 @@ The value of the whole expression is the value of the last expression. ### If -Conditional evaluation is accomplished with the `if` expression that is of the form `(if test consequent alternate)`. If *test* evaluates to truthy value, *consequent* is evaluated and returned. If *test* evaluates to falsy value, *alternate* is evaluated and returned: +Conditional evaluation is accomplished with the `if` expression which is of the form `(if test consequent alternate)`. If *test* evaluates to truthy value, *consequent* is evaluated and returned. If *test* evaluates to falsy value, *alternate* is evaluated and returned: ```text > (if (< 1 2) "yes" "no") @@ -360,6 +360,17 @@ The arguments to `cond` and `case` can be also be given as vectors: If no match is found, and `else` is not defined, `cond` and `case` return null. +### While + +Looping is accomplished with the `while` expression which is of the form `(while test expr1 expr2 ...)`. The *test* is evaluated at the beginning of each iteration and if it returns truthy value, the remaining expressions are evaluated. The value of the whole expression is the value of the last evaluated sub-expression. + +```text +> (let (i 5) (while (> i 0) (print i) (def i (dec i)))) +543210 +``` + +Although the above example illustrates how to use `while`, this type of code is discouraged. Generally it is recommended to use recursion instead of iteration in these type of scenarios. Usually it results in cleaner code as well. The `while` expression is better suited for something like the main loop of a program. + ## Quoting Use the `quote` special form to skip evaluation: @@ -459,7 +470,7 @@ Exceptions generated by PHP are catched as well. Their value will be a hash-map "TypeError" ``` -The Repl contains its own exception handler defined in PHP that will catch any exceptions thrown outside of `try-catch` form. +The REPL contains its own exception handler defined in PHP that will catch any exceptions thrown outside of `try-catch` form. ## Reflection @@ -517,6 +528,7 @@ quasiquote | yes | Quoting quasiquote-expand | yes | Quoting try | yes | Exceptions undef | yes | Environments +while | yes | Control flow ## Built-in functions @@ -528,7 +540,6 @@ 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. 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. diff --git a/mad/calc.mad b/mad/calc.mad index c84c6cb..b160704 100644 --- a/mad/calc.mad +++ b/mad/calc.mad @@ -47,12 +47,8 @@ (readline-load readlineFile) ;; Main loop: Read input from user, add it to readline, and parse it -(defn mainLoop () +(while true (let (inp (readline "[calc] ")) - (do (readline-add inp) - (readline-save readlineFile) - (print (str (parseInput inp) EOL)) - true))) - -;; Run it -(loop mainLoop) + (readline-add inp) + (readline-save readlineFile) + (print (str "⇒ " (parseInput inp) EOL)))) diff --git a/src/Evaller.php b/src/Evaller.php index 745df73..ac5f845 100644 --- a/src/Evaller.php +++ b/src/Evaller.php @@ -465,6 +465,22 @@ class Evaller } return $env->unset($astData[1]->getName()); + } elseif ($symbolName == 'while') { + if ($astLength < 3) { + throw new MadLispException("while at least 2 arguments"); + } + + $result = null; + + $test = $this->eval($astData[1], $env, $depth + 1); + while ($test) { + for ($i = 2; $i < $astLength; $i++) { + $result = $this->eval($astData[$i], $env, $depth + 1); + } + $test = $this->eval($astData[1], $env, $depth + 1); + } + + return $result; } } diff --git a/src/Lib/Core.php b/src/Lib/Core.php index c574214..c96964e 100644 --- a/src/Lib/Core.php +++ b/src/Lib/Core.php @@ -68,15 +68,6 @@ class Core implements ILib )); } - $env->set('loop', new CoreFunc('loop', 'Call the given function repeatedly in a loop until it returns false.', 1, -1, - function (Func $f, ...$args) { - do { - $result = $f->call($args); - } while ($result); - return $result; - } - )); - if (!$this->safemode) { $env->set('print', new CoreFunc('print', 'Print argument. Give second argument as true to show strings in readable format.', 1, 2, function ($a, bool $readable = false) {