mirror of
https://github.com/peklaiho/madlisp.git
synced 2024-11-22 21:35:03 +00:00
add built-in macros, add macros to readme
This commit is contained in:
parent
496e1c2c2e
commit
69b3778975
44
README.md
44
README.md
@ -115,6 +115,24 @@ Internally hash maps are just regular associative PHP arrays wrapped in a class.
|
||||
|
||||
Symbols are words which do not match any other type and are separated by whitespace. They can contain special characters. Examples of symbols are `a`, `name` or `+`.
|
||||
|
||||
## Environments
|
||||
|
||||
Environments are hash-maps which store key-value pairs and use symbols as keys. Symbols are evaluated by looking up the corresponding value from the current environment. If the key is not defined in current environment the lookup proceeds to the parent environment and so forth. The initial environment is called `root` and contains all the built-in functions listed here. Then another environment called `user` is created for anything the user wants to define. The `let` and `fn` special forms create new local environments. Note that `def` always uses the current environment, so anything defined with `def` is not visible in the parent environment.
|
||||
|
||||
You can get the name of an environment using the `meta` function:
|
||||
|
||||
```
|
||||
> (meta (env) "name")
|
||||
"root/user"
|
||||
```
|
||||
|
||||
You can also retrieve the parent environment:
|
||||
|
||||
```
|
||||
> (meta (env) "parent")
|
||||
{}
|
||||
```
|
||||
|
||||
## Quoting
|
||||
|
||||
Use the `quote` special form to skip evaluation:
|
||||
@ -152,24 +170,34 @@ You can use the single-quote (`'`), backtick and tilde (`~`) characters as short
|
||||
|
||||
All special forms related to quoting require exactly one argument.
|
||||
|
||||
## Environments
|
||||
## Macros
|
||||
|
||||
Environments are hash-maps which store key-value pairs and use symbols as keys. Symbols are evaluated by looking up the corresponding value from the current environment. If the key is not defined in current environment the lookup proceeds to the parent environment and so forth. The initial environment is called `root` and contains all the built-in functions listed here. Then another environment called `user` is created for anything the user wants to define. The `let` and `fn` special forms create new local environments. Note that `def` always uses the current environment, so anything defined with `def` is not visible in the parent environment.
|
||||
The language has support for Lisp-style macros. Macros are like preprocessor directives and allow the manipulation of the language syntax before evaluation.
|
||||
|
||||
You can get the name of an environment using the `meta` function:
|
||||
There are two built-in macros: `defn` which is a shortcut for the form `(def ... (fn ...))` and `defmacro` which is a shortcut for the form `(def ... (macro ...))`.
|
||||
|
||||
We can use the special form `macroexpand` to test macro expansion without actually evaluating the resulting code. To illustrate how macros work, lets use `defn` as an example, and then view the expanded form using `macroexpand`:
|
||||
|
||||
```
|
||||
> (meta (env) "name")
|
||||
"root/user"
|
||||
> (def defn (macro (name args body) (quasiquote (def (unquote name) (fn (unquote args) (unquote body))))))
|
||||
<macro>
|
||||
> (macroexpand (defn add (a b) (+ a b)))
|
||||
(def add (fn (a b) (+ a b)))
|
||||
```
|
||||
|
||||
You can also retrieve the parent environment:
|
||||
For another example, lets combine `if` and `not` into a macro named `unless`, this time using a shorter syntax:
|
||||
|
||||
```
|
||||
> (meta (env) "parent")
|
||||
{}
|
||||
> (defmacro unless (pred a b) `(if (not ~pred) ~a ~b))
|
||||
<macro>
|
||||
> (macroexpand (unless false "is false" "not false"))
|
||||
(if (not false) "is false" "not false")
|
||||
> (unless false "is false" "not false")
|
||||
"is false"
|
||||
```
|
||||
|
||||
The `quasiquote` form described above is essential for declaring macros.
|
||||
|
||||
## Special forms
|
||||
|
||||
Name | Safe-mode | Example | Example result | Description
|
||||
|
@ -34,4 +34,9 @@ class Lisp
|
||||
|
||||
$this->printer->print($result, $printReadable);
|
||||
}
|
||||
|
||||
public function setEnv(Env $env): void
|
||||
{
|
||||
$this->env = $env;
|
||||
}
|
||||
}
|
||||
|
@ -33,9 +33,15 @@ class LispFactory
|
||||
(new Lib\IO())->register($env);
|
||||
}
|
||||
|
||||
// User environment
|
||||
$env = new Env('user', $env);
|
||||
$lisp = new Lisp($tokenizer, $reader, $eval, $printer, $env);
|
||||
|
||||
return new Lisp($tokenizer, $reader, $eval, $printer, $env);
|
||||
// Add some built-in macros
|
||||
$lisp->readEval('(def defn (macro (name args body) (quasiquote (def (unquote name) (fn (unquote args) (unquote body))))))');
|
||||
$lisp->readEval('(def defmacro (macro (name args body) (quasiquote (def (unquote name) (macro (unquote args) (unquote body))))))');
|
||||
|
||||
// Separate environment for user-defined stuff
|
||||
$lisp->setEnv(new Env('user', $env));
|
||||
|
||||
return $lisp;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user