mirror of
https://github.com/kanaka/mal.git
synced 2024-10-04 01:17:33 +03:00
Compare commits
36 Commits
0cc629684b
...
4cf41f2911
Author | SHA1 | Date | |
---|---|---|---|
|
4cf41f2911 | ||
|
a0fe8e4355 | ||
|
a60581e0a2 | ||
|
033892777a | ||
|
cb333f1387 | ||
|
6a6bc8cb73 | ||
|
10e8854c04 | ||
|
d0375ec9f8 | ||
|
3233e7821a | ||
|
3bd752c979 | ||
|
b4843b602c | ||
|
ee6c11da3b | ||
|
d4f6b1e6dd | ||
|
dd16a7d023 | ||
|
f3903a7141 | ||
|
6bf89ea118 | ||
|
e6ec37a468 | ||
|
f63d2e4672 | ||
|
b3759ab63b | ||
|
a1a784c572 | ||
|
714b718bd0 | ||
|
fe218df3d2 | ||
|
3f7b28652b | ||
|
69e570aeca | ||
|
5c979b5a19 | ||
|
7d49797697 | ||
|
3e90e42e94 | ||
|
7a2bc6f066 | ||
|
0a34968695 | ||
|
eef959fb82 | ||
|
8f8608a989 | ||
|
78d6dabcaf | ||
|
96e6d9c81a | ||
|
dcf8f4d7b9 | ||
|
c7d437c6d2 | ||
|
f20d62fb35 |
46
.github/workflows/main.yml
vendored
46
.github/workflows/main.yml
vendored
@ -1,5 +1,9 @@
|
||||
name: Build and Test
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
on:
|
||||
push: {}
|
||||
pull_request: {}
|
||||
@ -12,32 +16,46 @@ on:
|
||||
|
||||
jobs:
|
||||
get-matrix:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
do-linux: ${{ steps.get-matrix-step.outputs.do-linux }}
|
||||
do-linux: ${{ steps.get-matrix-step.outputs.do_linux }}
|
||||
matrix-linux: ${{ steps.get-matrix-step.outputs.linux }}
|
||||
do-macos: ${{ steps.get-matrix-step.outputs.do-macos }}
|
||||
do-macos: ${{ steps.get-matrix-step.outputs.do_macos }}
|
||||
matrix-macos: ${{ steps.get-matrix-step.outputs.macos }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- id: files
|
||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
uses: kanaka/get-changed-files@v1
|
||||
uses: kanaka/get-changed-files@v2
|
||||
with:
|
||||
default_base: master
|
||||
- id: get-matrix-step
|
||||
run: |
|
||||
export OVERRIDE_IMPLS="${{ github.event.inputs.impls }}" # "
|
||||
echo "OVERRIDE_IMPLS: ${OVERRIDE_IMPLS}"
|
||||
./get-ci-matrix.py ${{ steps.files.outputs.all }}
|
||||
./get-ci-matrix.py ${{ steps.files.outputs.all }} > "${GITHUB_OUTPUT}"
|
||||
|
||||
linux:
|
||||
needs: get-matrix
|
||||
if: ${{ needs.get-matrix.outputs.do-linux == 'true' }}
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix-linux) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Need full history for voom like versions
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Docker Build/Push
|
||||
run: |
|
||||
export ${{ matrix.IMPL }}
|
||||
./ci.sh docker-build-push ${IMPL}
|
||||
- name: Build
|
||||
run: |
|
||||
export ${{ matrix.IMPL }}
|
||||
@ -55,9 +73,9 @@ jobs:
|
||||
export ${{ matrix.IMPL }}
|
||||
./ci.sh perf ${IMPL}
|
||||
- name: Archive logs and debug output
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: logs
|
||||
name: logs.${{ matrix.IMPL }}
|
||||
path: |
|
||||
*.log
|
||||
*.debug
|
||||
@ -65,12 +83,12 @@ jobs:
|
||||
macos:
|
||||
needs: get-matrix
|
||||
if: ${{ needs.get-matrix.outputs.do-macos == 'true' }}
|
||||
runs-on: macos-10.15
|
||||
runs-on: macos-12
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix-macos) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: |
|
||||
export ${{ matrix.IMPL }}
|
||||
@ -88,9 +106,9 @@ jobs:
|
||||
export ${{ matrix.IMPL }}
|
||||
./ci.sh perf ${IMPL}
|
||||
- name: Archive logs and debug output
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: logs
|
||||
name: logs.${{ matrix.IMPL }}
|
||||
path: |
|
||||
*.log
|
||||
*.debug
|
||||
|
@ -49,7 +49,7 @@ IMPL:
|
||||
- {IMPL: julia}
|
||||
- {IMPL: kotlin}
|
||||
- {IMPL: livescript}
|
||||
- {IMPL: logo, NO_SELF_HOST: 1} # step4 timeout
|
||||
- {IMPL: logo}
|
||||
- {IMPL: lua}
|
||||
- {IMPL: make, NO_SELF_HOST: 1} # step4 timeout
|
||||
- {IMPL: mal, MAL_IMPL: js, BUILD_IMPL: js, NO_SELF_HOST: 1}
|
||||
|
4
Makefile
4
Makefile
@ -126,7 +126,7 @@ STEP_TEST_FILES = $(strip $(wildcard \
|
||||
|
||||
# DOCKERIZE utility functions
|
||||
lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1))))))))))))))))))))))))))
|
||||
impl_to_image = kanaka/mal-test-$(call lc,$(1))
|
||||
impl_to_image = ghcr.io/kanaka/mal-test-$(call lc,$(1)):$(shell ./voom-like-version.sh impls/$(1)/Dockerfile)
|
||||
|
||||
actual_impl = $(if $(filter mal,$(1)),$(patsubst %-mal,%,$(MAL_IMPL)),$(1))
|
||||
|
||||
@ -143,7 +143,7 @@ get_build_command = $(strip $(foreach mode,$(1)_MODE, \
|
||||
$(if $(strip $($(mode))),-e $(mode)=$($(mode)),) \
|
||||
$(if $(filter factor,$(1)),-e FACTOR_ROOTS=$(FACTOR_ROOTS),) \
|
||||
$(call impl_to_image,$(1)) \
|
||||
$(MAKE) $(if $(strip $($(mode))),$(mode)=$($(mode)),) \
|
||||
make $(if $(strip $($(mode))),$(mode)=$($(mode)),) \
|
||||
,\
|
||||
$(MAKE) $(if $(strip $($(mode))),$(mode)=$($(mode)),) -C impls/$(impl))))
|
||||
|
||||
|
@ -43,7 +43,6 @@ IMPLS = ada ada.2 awk bash basic bbc-basic c c.2 chuck clojure coffee common-lis
|
||||
|
||||
step5_EXCLUDES += bash # never completes at 10,000
|
||||
step5_EXCLUDES += basic # too slow, and limited to ints of 2^16
|
||||
step5_EXCLUDES += logo # too slow for 10,000
|
||||
step5_EXCLUDES += make # no TCO capability (iteration or recursion)
|
||||
step5_EXCLUDES += mal # host impl dependent
|
||||
step5_EXCLUDES += matlab # never completes at 10,000
|
||||
|
@ -35,9 +35,8 @@ Here is the final diagram for [step A](process/guide.md#stepA):
|
||||
![stepA_mal architecture](process/stepA_mal.png)
|
||||
|
||||
If you are interested in creating a mal implementation (or just
|
||||
interested in using mal for something), you are welcome to to join our
|
||||
[Discord](https://discord.gg/CKgnNbJBpF) or join #mal on
|
||||
[libera.chat](https://libera.chat/). In addition to the [make-a-lisp
|
||||
interested in using mal for something) you are welcome to to join our
|
||||
[Discord](https://discord.gg/CKgnNbJBpF). In addition to the [make-a-lisp
|
||||
process guide](process/guide.md) there is also a [mal/make-a-lisp
|
||||
FAQ](docs/FAQ.md) where I attempt to answer some common questions.
|
||||
|
||||
@ -1452,7 +1451,7 @@ make "docker-build^IMPL"
|
||||
|
||||
|
||||
**Notes**:
|
||||
* Docker images are named *"kanaka/mal-test-IMPL"*
|
||||
* Docker images are named *"ghcr.io/kanaka/mal-test-IMPL"*
|
||||
* JVM-based language implementations (Groovy, Java, Clojure, Scala):
|
||||
you will probably need to run this command once manually
|
||||
first `make DOCKERIZE=1 "repl^IMPL"` before you can run tests because
|
||||
|
47
ci.sh
47
ci.sh
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
@ -24,11 +24,30 @@ mode_var=${raw_mode_var/-/__}
|
||||
mode_var=${mode_var/./__}
|
||||
mode_val=${!mode_var}
|
||||
|
||||
MAKE="make ${mode_val:+${mode_var}=${mode_val}}"
|
||||
|
||||
log_prefix="${ACTION}${REGRESS:+-regress}-${IMPL}${mode_val:+-${mode_val}}${MAL_IMPL:+-${MAL_IMPL}}"
|
||||
TEST_OPTS="${TEST_OPTS} --debug-file ../../${log_prefix}.debug"
|
||||
|
||||
step_summary() {
|
||||
echo "${*}"
|
||||
if [ "${GITHUB_STEP_SUMMARY}" ]; then
|
||||
echo "${*}" >> "${GITHUB_STEP_SUMMARY}"
|
||||
fi
|
||||
}
|
||||
|
||||
img_base="${MAL_IMPL:-${IMPL}}"
|
||||
img_impl="${img_base%%-mal}"
|
||||
img_name="mal-test-$(echo "${img_impl}" | tr '[:upper:]' '[:lower:]')"
|
||||
img_ver=$(./voom-like-version.sh impls/${img_impl}/Dockerfile)
|
||||
IMAGE="ghcr.io/kanaka/${img_name}:${img_ver}"
|
||||
|
||||
# If NO_DOCKER is blank then run make in a docker image
|
||||
MAKE="make ${mode_val:+${mode_var}=${mode_val}}"
|
||||
if [ -z "${NO_DOCKER}" ]; then
|
||||
# We could just use make DOCKERIZE=1 instead but that does add
|
||||
# non-trivial startup overhead for each step.
|
||||
MAKE="docker run -i -u $(id -u) -v `pwd`:/mal ${IMAGE} ${MAKE}"
|
||||
fi
|
||||
|
||||
# Log everything below this point:
|
||||
exec &> >(tee ./${log_prefix}.log)
|
||||
|
||||
@ -47,17 +66,21 @@ echo "IMPL: ${IMPL}"
|
||||
echo "BUILD_IMPL: ${BUILD_IMPL}"
|
||||
echo "MAL_IMPL: ${MAL_IMPL}"
|
||||
echo "TEST_OPTS: ${TEST_OPTS}"
|
||||
|
||||
# If NO_DOCKER is blank then launch use a docker image, otherwise use
|
||||
# the Travis/Github Actions image/tools directly.
|
||||
if [ -z "${NO_DOCKER}" ]; then
|
||||
img_impl=$(echo "${MAL_IMPL:-${IMPL}}" | tr '[:upper:]' '[:lower:]')
|
||||
# We could just use make DOCKERIZE=1 instead but that does add
|
||||
# non-trivial startup overhead for each step.
|
||||
MAKE="docker run -i -u $(id -u) -v `pwd`:/mal kanaka/mal-test-${img_impl%%-mal} ${MAKE}"
|
||||
fi
|
||||
echo "IMAGE: ${IMAGE}"
|
||||
echo "MAKE: ${MAKE}"
|
||||
|
||||
case "${ACTION}" in
|
||||
docker-build-push)
|
||||
if ! docker pull ${IMAGE}; then
|
||||
step_summary "${MAL_IMPL:-${IMPL}} - building ${IMAGE}"
|
||||
make "docker-build^${MAL_IMPL:-${IMPL}}"
|
||||
step_summary "${MAL_IMPL:-${IMPL}} - built ${IMAGE}"
|
||||
if [ "${GITHUB_REF}" = "refs/heads/main" ]; then
|
||||
docker push ${IMAGE}
|
||||
step_summary "${MAL_IMPL:-${IMPL}} - pushed ${IMAGE}"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
build)
|
||||
# rpython often fails on step9 in compute_vars_longevity
|
||||
# so build step9, then continue with the full build
|
||||
|
59
docs/FAQ.md
59
docs/FAQ.md
@ -154,11 +154,57 @@ into the main repository:
|
||||
`time-ms` function which is needed to run the micro-benchmark tests).
|
||||
|
||||
* Create a `Dockerfile` in your directory that installs all the
|
||||
packages necessary to build and run your implementation. Refer to other
|
||||
implementations for examples of what the Dockerfile should contain.
|
||||
Build your docker image and tag it `kanaka/mal-test-[IMPL_NAME]`.
|
||||
The top-level Makefile has support for building/testing within
|
||||
docker with the `DOCKERIZE` flag:
|
||||
packages necessary to build and run your implementation. In order to
|
||||
integrate fully with the Github Actions CI workflow, the
|
||||
`Dockerfile` needs to include the following boilerplate (with your
|
||||
name, email, and implementation filled in):
|
||||
```
|
||||
MAINTAINER Your Name <your@email.com>
|
||||
LABEL org.opencontainers.image.source=https://github.com/kanaka/mal
|
||||
LABEL org.opencontainers.image.description="mal test container: Your_Implementation"
|
||||
```
|
||||
|
||||
In addition, the docker image should provide python3 (with a python
|
||||
symlink to it) to enable running tests using the image. Here is the
|
||||
typical `Dockerfile` template you should use if your
|
||||
implementation does not require a special base distro:
|
||||
|
||||
```
|
||||
FROM ubuntu:24.04
|
||||
MAINTAINER Your Name <your@email.com>
|
||||
LABEL org.opencontainers.image.source=https://github.com/kanaka/mal
|
||||
LABEL org.opencontainers.image.description="mal test container: Your_Implementation"
|
||||
##########################################################
|
||||
# General requirements for testing or common across many
|
||||
# implementations
|
||||
##########################################################
|
||||
|
||||
RUN apt-get -y update
|
||||
|
||||
# Required for running tests
|
||||
RUN apt-get -y install make python3
|
||||
RUN ln -sf /usr/bin/python3 /usr/bin/python
|
||||
|
||||
# Some typical implementation and test requirements
|
||||
RUN apt-get -y install curl libreadline-dev libedit-dev
|
||||
|
||||
RUN mkdir -p /mal
|
||||
WORKDIR /mal
|
||||
|
||||
##########################################################
|
||||
# Specific implementation requirements
|
||||
##########################################################
|
||||
|
||||
... Your packages ...
|
||||
```
|
||||
|
||||
* Build and tag your docker image. The image tag will have the
|
||||
form `ghcr.io/kanaka/mal-test-[IMPL_NAME]:[VOOM_VERSION]`.
|
||||
```
|
||||
make "docker-build^[IMPL_NAME]"
|
||||
|
||||
* The top-level Makefile has support for building/testing using
|
||||
the docker image with the `DOCKERIZE` flag:
|
||||
```bash
|
||||
make DOCKERIZE=1 "test^[IMPL_NAME]"
|
||||
make DOCKERIZE=1 MAL_IMPL=[IMPL_NAME] "test^mal"
|
||||
@ -170,6 +216,9 @@ into the main repository:
|
||||
./ci.sh test [IMPL_NAME]
|
||||
```
|
||||
|
||||
* Push your code to a branch and make sure that the automated Github
|
||||
Actions CI passes for your implementation.
|
||||
|
||||
* If you are creating a new implementation for an existing
|
||||
implementation (or somebody beats you to the punch while you are
|
||||
working on it), there is still a chance I will merge your
|
||||
|
@ -12,6 +12,9 @@ RE_IMPL = re.compile(r'^impls/(?!lib|tests)([^/]*)/')
|
||||
|
||||
OVERRIDE_IMPLS = os.environ.get('OVERRIDE_IMPLS', '').split()
|
||||
|
||||
def eprint(*args, **kwargs):
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
|
||||
def impl_text(impl):
|
||||
s = "IMPL=%s" % impl['IMPL']
|
||||
for k, v in impl.items():
|
||||
@ -37,11 +40,11 @@ if OVERRIDE_IMPLS:
|
||||
do_full = True
|
||||
|
||||
|
||||
print("OVERRIDE_IMPLS: %s" % OVERRIDE_IMPLS)
|
||||
print("code_changes: %s (%d)" % (code_changes, len(code_changes)))
|
||||
print("impl_changes: %s (%d)" % (impl_changes, len(impl_changes)))
|
||||
print("run_impls: %s (%d)" % (run_impls, len(run_impls)))
|
||||
print("do_full: %s" % do_full)
|
||||
eprint("OVERRIDE_IMPLS: %s" % OVERRIDE_IMPLS)
|
||||
eprint("code_changes: %s (%d)" % (code_changes, len(code_changes)))
|
||||
eprint("impl_changes: %s (%d)" % (impl_changes, len(impl_changes)))
|
||||
eprint("run_impls: %s (%d)" % (run_impls, len(run_impls)))
|
||||
eprint("do_full: %s" % do_full)
|
||||
|
||||
# Load the full implementation description file
|
||||
all_impls = yaml.safe_load(open(IMPLS_FILE))
|
||||
@ -60,7 +63,7 @@ for impl in all_impls['IMPL']:
|
||||
elif do_full:
|
||||
targ.append(impl_text(impl))
|
||||
|
||||
print("::set-output name=do-linux::%s" % json.dumps(len(linux_impls)>0))
|
||||
print("::set-output name=do-macos::%s" % json.dumps(len(macos_impls)>0))
|
||||
print("::set-output name=linux::{\"IMPL\":%s}" % json.dumps(linux_impls))
|
||||
print("::set-output name=macos::{\"IMPL\":%s}" % json.dumps(macos_impls))
|
||||
print("do_linux=%s" % json.dumps(len(linux_impls)>0))
|
||||
print("do_macos=%s" % json.dumps(len(macos_impls)>0))
|
||||
print("linux={\"IMPL\":%s}" % json.dumps(linux_impls))
|
||||
print("macos={\"IMPL\":%s}" % json.dumps(macos_impls))
|
||||
|
@ -53,6 +53,25 @@ package body Envs is
|
||||
return HM.Element (Position);
|
||||
end Get;
|
||||
|
||||
function Get_Or_Nil (Env : Instance;
|
||||
Key : Types.String_Ptr) return Types.T is
|
||||
Position : HM.Cursor := Env.Data.Find (Key);
|
||||
Ref : Link;
|
||||
begin
|
||||
if not HM.Has_Element (Position) then
|
||||
Ref := Env.Outer;
|
||||
loop
|
||||
if Ref = null then
|
||||
return Types.Nil;
|
||||
end if;
|
||||
Position := Ref.all.Data.Find (Key);
|
||||
exit when HM.Has_Element (Position);
|
||||
Ref := Ref.all.Outer;
|
||||
end loop;
|
||||
end if;
|
||||
return HM.Element (Position);
|
||||
end Get_Or_Nil;
|
||||
|
||||
procedure Keep_References (Object : in out Instance) is
|
||||
begin
|
||||
for Position in Object.Data.Iterate loop
|
||||
|
@ -27,6 +27,9 @@ package Envs is
|
||||
function Get (Env : in Instance;
|
||||
Key : in Types.String_Ptr) return Types.T;
|
||||
|
||||
function Get_Or_Nil (Env : Instance;
|
||||
Key : Types.String_Ptr) return Types.T;
|
||||
|
||||
procedure Set (Env : in out Instance;
|
||||
Key : in Types.T;
|
||||
New_Item : in Types.T) with Inline;
|
||||
|
@ -1,4 +1,3 @@
|
||||
with Ada.Environment_Variables;
|
||||
with Ada.Containers.Indefinite_Hashed_Maps;
|
||||
with Ada.Strings.Hash;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
@ -14,8 +13,6 @@ with Types.Strings;
|
||||
|
||||
procedure Step2_Eval is
|
||||
|
||||
Dbgeval : constant Boolean := Ada.Environment_Variables.Exists ("dbgeval");
|
||||
|
||||
use type Types.T;
|
||||
use all type Types.Kind_Type;
|
||||
|
||||
@ -52,12 +49,8 @@ procedure Step2_Eval is
|
||||
is
|
||||
First : Types.T;
|
||||
begin
|
||||
if Dbgeval then
|
||||
Ada.Text_IO.New_Line;
|
||||
Ada.Text_IO.Put ("EVAL: ");
|
||||
Print (Ast);
|
||||
end if;
|
||||
|
||||
-- Ada.Text_IO.Put ("EVAL: ");
|
||||
-- Print (Ast);
|
||||
case Ast.Kind is
|
||||
when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types.Kind_Key
|
||||
| Kind_Macro | Types.Kind_Function =>
|
||||
|
@ -1,4 +1,3 @@
|
||||
with Ada.Environment_Variables;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
|
||||
with Envs;
|
||||
@ -13,7 +12,7 @@ with Types.Strings;
|
||||
|
||||
procedure Step3_Env is
|
||||
|
||||
Dbgeval : constant Boolean := Ada.Environment_Variables.Exists ("dbgeval");
|
||||
Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc ("DEBUG-EVAL");
|
||||
|
||||
use type Types.T;
|
||||
use all type Types.Kind_Type;
|
||||
@ -45,8 +44,7 @@ procedure Step3_Env is
|
||||
is
|
||||
First : Types.T;
|
||||
begin
|
||||
if Dbgeval then
|
||||
Ada.Text_IO.New_Line;
|
||||
if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then
|
||||
Ada.Text_IO.Put ("EVAL: ");
|
||||
Print (Ast);
|
||||
Envs.Dump_Stack (Env.all);
|
||||
@ -209,6 +207,7 @@ begin
|
||||
-- Collect garbage.
|
||||
Err.Data := Types.Nil;
|
||||
Repl.all.Keep;
|
||||
Dbgeval.Keep;
|
||||
Garbage_Collected.Clean;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
|
@ -1,4 +1,3 @@
|
||||
with Ada.Environment_Variables;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
|
||||
with Core;
|
||||
@ -15,7 +14,7 @@ with Types.Strings;
|
||||
|
||||
procedure Step4_If_Fn_Do is
|
||||
|
||||
Dbgeval : constant Boolean := Ada.Environment_Variables.Exists ("dbgeval");
|
||||
Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc ("DEBUG-EVAL");
|
||||
|
||||
use type Types.T;
|
||||
use all type Types.Kind_Type;
|
||||
@ -47,8 +46,7 @@ procedure Step4_If_Fn_Do is
|
||||
is
|
||||
First : Types.T;
|
||||
begin
|
||||
if Dbgeval then
|
||||
Ada.Text_IO.New_Line;
|
||||
if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then
|
||||
Ada.Text_IO.Put ("EVAL: ");
|
||||
Print (Ast);
|
||||
Envs.Dump_Stack (Env.all);
|
||||
@ -81,17 +79,13 @@ procedure Step4_If_Fn_Do is
|
||||
if First.Str.all = "if" then
|
||||
Err.Check (Ast.Sequence.all.Length in 3 .. 4,
|
||||
"expected 2 or 3 parameters");
|
||||
declare
|
||||
Tst : constant Types.T := Eval (Ast.Sequence.all.Data (2), Env);
|
||||
begin
|
||||
if Tst /= Types.Nil and Tst /= (Kind_Boolean, False) then
|
||||
if Types.To_Boolean (Eval (Ast.Sequence.all.Data (2), Env)) then
|
||||
return Eval (Ast.Sequence.all.Data (3), Env);
|
||||
elsif Ast.Sequence.all.Length = 3 then
|
||||
return Types.Nil;
|
||||
else
|
||||
return Eval (Ast.Sequence.all.Data (4), Env);
|
||||
end if;
|
||||
end;
|
||||
elsif First.Str.all = "let*" then
|
||||
Err.Check (Ast.Sequence.all.Length = 3
|
||||
and then Ast.Sequence.all.Data (2).Kind in Types.Kind_Sequence,
|
||||
@ -251,6 +245,7 @@ begin
|
||||
-- Collect garbage.
|
||||
Err.Data := Types.Nil;
|
||||
Repl.all.Keep;
|
||||
Dbgeval.Keep;
|
||||
Garbage_Collected.Clean;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
|
@ -1,4 +1,3 @@
|
||||
with Ada.Environment_Variables;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
|
||||
with Core;
|
||||
@ -15,7 +14,7 @@ with Types.Strings;
|
||||
|
||||
procedure Step5_Tco is
|
||||
|
||||
Dbgeval : constant Boolean := Ada.Environment_Variables.Exists ("dbgeval");
|
||||
Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc ("DEBUG-EVAL");
|
||||
|
||||
use type Types.T;
|
||||
use all type Types.Kind_Type;
|
||||
@ -56,8 +55,7 @@ procedure Step5_Tco is
|
||||
First : Types.T;
|
||||
begin
|
||||
<<Restart>>
|
||||
if Dbgeval then
|
||||
Ada.Text_IO.New_Line;
|
||||
if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then
|
||||
Ada.Text_IO.Put ("EVAL: ");
|
||||
Print (Ast);
|
||||
Envs.Dump_Stack (Env.all);
|
||||
@ -90,10 +88,7 @@ procedure Step5_Tco is
|
||||
if First.Str.all = "if" then
|
||||
Err.Check (Ast.Sequence.all.Length in 3 .. 4,
|
||||
"expected 2 or 3 parameters");
|
||||
declare
|
||||
Tst : constant Types.T := Eval (Ast.Sequence.all.Data (2), Env);
|
||||
begin
|
||||
if Tst /= Types.Nil and Tst /= (Kind_Boolean, False) then
|
||||
if Types.To_Boolean (Eval (Ast.Sequence.all.Data (2), Env)) then
|
||||
Ast := Ast.Sequence.all.Data (3);
|
||||
goto Restart;
|
||||
elsif Ast.Sequence.all.Length = 3 then
|
||||
@ -102,7 +97,6 @@ procedure Step5_Tco is
|
||||
Ast := Ast.Sequence.all.Data (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
elsif First.Str.all = "let*" then
|
||||
Err.Check (Ast.Sequence.all.Length = 3
|
||||
and then Ast.Sequence.all.Data (2).Kind in Types.Kind_Sequence,
|
||||
@ -284,6 +278,7 @@ begin
|
||||
-- Collect garbage.
|
||||
Err.Data := Types.Nil;
|
||||
Repl.all.Keep;
|
||||
Dbgeval.Keep;
|
||||
Garbage_Collected.Clean;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
|
@ -1,5 +1,4 @@
|
||||
with Ada.Command_Line;
|
||||
with Ada.Environment_Variables;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
|
||||
with Core;
|
||||
@ -16,7 +15,7 @@ with Types.Strings;
|
||||
|
||||
procedure Step6_File is
|
||||
|
||||
Dbgeval : constant Boolean := Ada.Environment_Variables.Exists ("dbgeval");
|
||||
Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc ("DEBUG-EVAL");
|
||||
|
||||
use type Types.T;
|
||||
use all type Types.Kind_Type;
|
||||
@ -60,8 +59,7 @@ procedure Step6_File is
|
||||
First : Types.T;
|
||||
begin
|
||||
<<Restart>>
|
||||
if Dbgeval then
|
||||
Ada.Text_IO.New_Line;
|
||||
if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then
|
||||
Ada.Text_IO.Put ("EVAL: ");
|
||||
Print (Ast);
|
||||
Envs.Dump_Stack (Env.all);
|
||||
@ -94,10 +92,7 @@ procedure Step6_File is
|
||||
if First.Str.all = "if" then
|
||||
Err.Check (Ast.Sequence.all.Length in 3 .. 4,
|
||||
"expected 2 or 3 parameters");
|
||||
declare
|
||||
Tst : constant Types.T := Eval (Ast.Sequence.all.Data (2), Env);
|
||||
begin
|
||||
if Tst /= Types.Nil and Tst /= (Kind_Boolean, False) then
|
||||
if Types.To_Boolean (Eval (Ast.Sequence.all.Data (2), Env)) then
|
||||
Ast := Ast.Sequence.all.Data (3);
|
||||
goto Restart;
|
||||
elsif Ast.Sequence.all.Length = 3 then
|
||||
@ -106,7 +101,6 @@ procedure Step6_File is
|
||||
Ast := Ast.Sequence.all.Data (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
elsif First.Str.all = "let*" then
|
||||
Err.Check (Ast.Sequence.all.Length = 3
|
||||
and then Ast.Sequence.all.Data (2).Kind in Types.Kind_Sequence,
|
||||
@ -310,6 +304,7 @@ begin
|
||||
-- Collect garbage.
|
||||
Err.Data := Types.Nil;
|
||||
Repl.all.Keep;
|
||||
Dbgeval.Keep;
|
||||
Garbage_Collected.Clean;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
|
@ -1,5 +1,4 @@
|
||||
with Ada.Command_Line;
|
||||
with Ada.Environment_Variables;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
|
||||
with Core;
|
||||
@ -16,7 +15,7 @@ with Types.Strings;
|
||||
|
||||
procedure Step7_Quote is
|
||||
|
||||
Dbgeval : constant Boolean := Ada.Environment_Variables.Exists ("dbgeval");
|
||||
Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc ("DEBUG-EVAL");
|
||||
|
||||
use type Types.T;
|
||||
use all type Types.Kind_Type;
|
||||
@ -62,8 +61,7 @@ procedure Step7_Quote is
|
||||
First : Types.T;
|
||||
begin
|
||||
<<Restart>>
|
||||
if Dbgeval then
|
||||
Ada.Text_IO.New_Line;
|
||||
if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then
|
||||
Ada.Text_IO.Put ("EVAL: ");
|
||||
Print (Ast);
|
||||
Envs.Dump_Stack (Env.all);
|
||||
@ -96,10 +94,7 @@ procedure Step7_Quote is
|
||||
if First.Str.all = "if" then
|
||||
Err.Check (Ast.Sequence.all.Length in 3 .. 4,
|
||||
"expected 2 or 3 parameters");
|
||||
declare
|
||||
Tst : constant Types.T := Eval (Ast.Sequence.all.Data (2), Env);
|
||||
begin
|
||||
if Tst /= Types.Nil and Tst /= (Kind_Boolean, False) then
|
||||
if Types.To_Boolean (Eval (Ast.Sequence.all.Data (2), Env)) then
|
||||
Ast := Ast.Sequence.all.Data (3);
|
||||
goto Restart;
|
||||
elsif Ast.Sequence.all.Length = 3 then
|
||||
@ -108,7 +103,6 @@ procedure Step7_Quote is
|
||||
Ast := Ast.Sequence.all.Data (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
elsif First.Str.all = "let*" then
|
||||
Err.Check (Ast.Sequence.all.Length = 3
|
||||
and then Ast.Sequence.all.Data (2).Kind in Types.Kind_Sequence,
|
||||
@ -167,9 +161,6 @@ procedure Step7_Quote is
|
||||
Ast => Ast.Sequence.all.Data (3),
|
||||
Env => Env));
|
||||
end;
|
||||
elsif First.Str.all = "quasiquoteexpand" then
|
||||
Err.Check (Ast.Sequence.all.Length = 2, "expected 1 parameter");
|
||||
return Quasiquote (Ast.Sequence.all.Data (2));
|
||||
elsif First.Str.all = "quasiquote" then
|
||||
Err.Check (Ast.Sequence.all.Length = 2, "expected 1 parameter");
|
||||
Ast := Quasiquote (Ast.Sequence.all.Data (2));
|
||||
@ -379,6 +370,7 @@ begin
|
||||
-- Collect garbage.
|
||||
Err.Data := Types.Nil;
|
||||
Repl.all.Keep;
|
||||
Dbgeval.Keep;
|
||||
Garbage_Collected.Clean;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
|
@ -1,5 +1,4 @@
|
||||
with Ada.Command_Line;
|
||||
with Ada.Environment_Variables;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
|
||||
with Core;
|
||||
@ -16,7 +15,7 @@ with Types.Strings;
|
||||
|
||||
procedure Step8_Macros is
|
||||
|
||||
Dbgeval : constant Boolean := Ada.Environment_Variables.Exists ("dbgeval");
|
||||
Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc ("DEBUG-EVAL");
|
||||
|
||||
use type Types.T;
|
||||
use all type Types.Kind_Type;
|
||||
@ -59,12 +58,10 @@ procedure Step8_Macros is
|
||||
-- True when the environment has been created in this recursion
|
||||
-- level, and has not yet been referenced by a closure. If so,
|
||||
-- we can reuse it instead of creating a subenvironment.
|
||||
Macroexpanding : Boolean := False;
|
||||
First : Types.T;
|
||||
begin
|
||||
<<Restart>>
|
||||
if Dbgeval then
|
||||
Ada.Text_IO.New_Line;
|
||||
if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then
|
||||
Ada.Text_IO.Put ("EVAL: ");
|
||||
Print (Ast);
|
||||
Envs.Dump_Stack (Env.all);
|
||||
@ -97,10 +94,7 @@ procedure Step8_Macros is
|
||||
if First.Str.all = "if" then
|
||||
Err.Check (Ast.Sequence.all.Length in 3 .. 4,
|
||||
"expected 2 or 3 parameters");
|
||||
declare
|
||||
Tst : constant Types.T := Eval (Ast.Sequence.all.Data (2), Env);
|
||||
begin
|
||||
if Tst /= Types.Nil and Tst /= (Kind_Boolean, False) then
|
||||
if Types.To_Boolean (Eval (Ast.Sequence.all.Data (2), Env)) then
|
||||
Ast := Ast.Sequence.all.Data (3);
|
||||
goto Restart;
|
||||
elsif Ast.Sequence.all.Length = 3 then
|
||||
@ -109,7 +103,6 @@ procedure Step8_Macros is
|
||||
Ast := Ast.Sequence.all.Data (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
elsif First.Str.all = "let*" then
|
||||
Err.Check (Ast.Sequence.all.Length = 3
|
||||
and then Ast.Sequence.all.Data (2).Kind in Types.Kind_Sequence,
|
||||
@ -183,14 +176,6 @@ procedure Step8_Macros is
|
||||
Ast => Ast.Sequence.all.Data (3),
|
||||
Env => Env));
|
||||
end;
|
||||
elsif First.Str.all = "macroexpand" then
|
||||
Err.Check (Ast.Sequence.all.Length = 2, "expected 1 parameter");
|
||||
Macroexpanding := True;
|
||||
Ast := Ast.Sequence.all.Data (2);
|
||||
goto Restart;
|
||||
elsif First.Str.all = "quasiquoteexpand" then
|
||||
Err.Check (Ast.Sequence.all.Length = 2, "expected 1 parameter");
|
||||
return Quasiquote (Ast.Sequence.all.Data (2));
|
||||
elsif First.Str.all = "quasiquote" then
|
||||
Err.Check (Ast.Sequence.all.Length = 2, "expected 1 parameter");
|
||||
Ast := Quasiquote (Ast.Sequence.all.Data (2));
|
||||
@ -217,24 +202,10 @@ procedure Step8_Macros is
|
||||
case First.Kind is
|
||||
when Kind_Macro =>
|
||||
-- Use the unevaluated arguments.
|
||||
if Macroexpanding then
|
||||
-- Evaluate the macro with tail call optimization.
|
||||
if not Env_Reusable then
|
||||
Env := Envs.New_Env (Outer => First.Fn.all.Env);
|
||||
Env_Reusable := True;
|
||||
end if;
|
||||
Env.all.Set_Binds
|
||||
(Binds => First.Fn.all.Params.all.Data,
|
||||
Exprs => Ast.Sequence.all.Data (2 .. Ast.Sequence.all.Length));
|
||||
Ast := First.Fn.all.Ast;
|
||||
goto Restart;
|
||||
else
|
||||
-- Evaluate the macro normally.
|
||||
Ast := First.Fn.all.Apply
|
||||
(Ast.Sequence.all.Data (2 .. Ast.Sequence.all.Length));
|
||||
-- Then evaluate the result with TCO.
|
||||
goto Restart;
|
||||
end if;
|
||||
when Types.Kind_Function =>
|
||||
null;
|
||||
when others =>
|
||||
@ -260,11 +231,7 @@ procedure Step8_Macros is
|
||||
end;
|
||||
exception
|
||||
when Err.Error =>
|
||||
if Macroexpanding then
|
||||
Err.Add_Trace_Line ("macroexpand", Ast);
|
||||
else
|
||||
Err.Add_Trace_Line ("eval", Ast);
|
||||
end if;
|
||||
raise;
|
||||
end Eval;
|
||||
|
||||
@ -434,6 +401,7 @@ begin
|
||||
-- Collect garbage.
|
||||
Err.Data := Types.Nil;
|
||||
Repl.all.Keep;
|
||||
Dbgeval.Keep;
|
||||
Garbage_Collected.Clean;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
|
@ -1,5 +1,4 @@
|
||||
with Ada.Command_Line;
|
||||
with Ada.Environment_Variables;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
|
||||
with Core;
|
||||
@ -16,7 +15,7 @@ with Types.Strings;
|
||||
|
||||
procedure Step9_Try is
|
||||
|
||||
Dbgeval : constant Boolean := Ada.Environment_Variables.Exists ("dbgeval");
|
||||
Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc ("DEBUG-EVAL");
|
||||
|
||||
use type Types.T;
|
||||
use all type Types.Kind_Type;
|
||||
@ -59,12 +58,10 @@ procedure Step9_Try is
|
||||
-- True when the environment has been created in this recursion
|
||||
-- level, and has not yet been referenced by a closure. If so,
|
||||
-- we can reuse it instead of creating a subenvironment.
|
||||
Macroexpanding : Boolean := False;
|
||||
First : Types.T;
|
||||
begin
|
||||
<<Restart>>
|
||||
if Dbgeval then
|
||||
Ada.Text_IO.New_Line;
|
||||
if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then
|
||||
Ada.Text_IO.Put ("EVAL: ");
|
||||
Print (Ast);
|
||||
Envs.Dump_Stack (Env.all);
|
||||
@ -97,10 +94,7 @@ procedure Step9_Try is
|
||||
if First.Str.all = "if" then
|
||||
Err.Check (Ast.Sequence.all.Length in 3 .. 4,
|
||||
"expected 2 or 3 parameters");
|
||||
declare
|
||||
Tst : constant Types.T := Eval (Ast.Sequence.all.Data (2), Env);
|
||||
begin
|
||||
if Tst /= Types.Nil and Tst /= (Kind_Boolean, False) then
|
||||
if Types.To_Boolean (Eval (Ast.Sequence.all.Data (2), Env)) then
|
||||
Ast := Ast.Sequence.all.Data (3);
|
||||
goto Restart;
|
||||
elsif Ast.Sequence.all.Length = 3 then
|
||||
@ -109,7 +103,6 @@ procedure Step9_Try is
|
||||
Ast := Ast.Sequence.all.Data (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
elsif First.Str.all = "let*" then
|
||||
Err.Check (Ast.Sequence.all.Length = 3
|
||||
and then Ast.Sequence.all.Data (2).Kind in Types.Kind_Sequence,
|
||||
@ -183,14 +176,6 @@ procedure Step9_Try is
|
||||
Ast => Ast.Sequence.all.Data (3),
|
||||
Env => Env));
|
||||
end;
|
||||
elsif First.Str.all = "macroexpand" then
|
||||
Err.Check (Ast.Sequence.all.Length = 2, "expected 1 parameter");
|
||||
Macroexpanding := True;
|
||||
Ast := Ast.Sequence.all.Data (2);
|
||||
goto Restart;
|
||||
elsif First.Str.all = "quasiquoteexpand" then
|
||||
Err.Check (Ast.Sequence.all.Length = 2, "expected 1 parameter");
|
||||
return Quasiquote (Ast.Sequence.all.Data (2));
|
||||
elsif First.Str.all = "quasiquote" then
|
||||
Err.Check (Ast.Sequence.all.Length = 2, "expected 1 parameter");
|
||||
Ast := Quasiquote (Ast.Sequence.all.Data (2));
|
||||
@ -247,24 +232,10 @@ procedure Step9_Try is
|
||||
case First.Kind is
|
||||
when Kind_Macro =>
|
||||
-- Use the unevaluated arguments.
|
||||
if Macroexpanding then
|
||||
-- Evaluate the macro with tail call optimization.
|
||||
if not Env_Reusable then
|
||||
Env := Envs.New_Env (Outer => First.Fn.all.Env);
|
||||
Env_Reusable := True;
|
||||
end if;
|
||||
Env.all.Set_Binds
|
||||
(Binds => First.Fn.all.Params.all.Data,
|
||||
Exprs => Ast.Sequence.all.Data (2 .. Ast.Sequence.all.Length));
|
||||
Ast := First.Fn.all.Ast;
|
||||
goto Restart;
|
||||
else
|
||||
-- Evaluate the macro normally.
|
||||
Ast := First.Fn.all.Apply
|
||||
(Ast.Sequence.all.Data (2 .. Ast.Sequence.all.Length));
|
||||
-- Then evaluate the result with TCO.
|
||||
goto Restart;
|
||||
end if;
|
||||
when Types.Kind_Function =>
|
||||
null;
|
||||
when others =>
|
||||
@ -290,11 +261,7 @@ procedure Step9_Try is
|
||||
end;
|
||||
exception
|
||||
when Err.Error =>
|
||||
if Macroexpanding then
|
||||
Err.Add_Trace_Line ("macroexpand", Ast);
|
||||
else
|
||||
Err.Add_Trace_Line ("eval", Ast);
|
||||
end if;
|
||||
raise;
|
||||
end Eval;
|
||||
|
||||
@ -464,6 +431,7 @@ begin
|
||||
-- Collect garbage.
|
||||
Err.Data := Types.Nil;
|
||||
Repl.all.Keep;
|
||||
Dbgeval.Keep;
|
||||
Garbage_Collected.Clean;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
|
@ -1,5 +1,4 @@
|
||||
with Ada.Command_Line;
|
||||
with Ada.Environment_Variables;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
|
||||
with Core;
|
||||
@ -17,7 +16,7 @@ with Types.Strings;
|
||||
|
||||
procedure StepA_Mal is
|
||||
|
||||
Dbgeval : constant Boolean := Ada.Environment_Variables.Exists ("dbgeval");
|
||||
Dbgeval : constant Types.String_Ptr := Types.Strings.Alloc ("DEBUG-EVAL");
|
||||
|
||||
use type Types.T;
|
||||
use all type Types.Kind_Type;
|
||||
@ -60,12 +59,10 @@ procedure StepA_Mal is
|
||||
-- True when the environment has been created in this recursion
|
||||
-- level, and has not yet been referenced by a closure. If so,
|
||||
-- we can reuse it instead of creating a subenvironment.
|
||||
Macroexpanding : Boolean := False;
|
||||
First : Types.T;
|
||||
begin
|
||||
<<Restart>>
|
||||
if Dbgeval then
|
||||
Ada.Text_IO.New_Line;
|
||||
if Types.To_Boolean (Env.all.Get_Or_Nil (Dbgeval)) then
|
||||
Ada.Text_IO.Put ("EVAL: ");
|
||||
Print (Ast);
|
||||
Envs.Dump_Stack (Env.all);
|
||||
@ -98,10 +95,7 @@ procedure StepA_Mal is
|
||||
if First.Str.all = "if" then
|
||||
Err.Check (Ast.Sequence.all.Length in 3 .. 4,
|
||||
"expected 2 or 3 parameters");
|
||||
declare
|
||||
Tst : constant Types.T := Eval (Ast.Sequence.all.Data (2), Env);
|
||||
begin
|
||||
if Tst /= Types.Nil and Tst /= (Kind_Boolean, False) then
|
||||
if Types.To_Boolean (Eval (Ast.Sequence.all.Data (2), Env)) then
|
||||
Ast := Ast.Sequence.all.Data (3);
|
||||
goto Restart;
|
||||
elsif Ast.Sequence.all.Length = 3 then
|
||||
@ -110,7 +104,6 @@ procedure StepA_Mal is
|
||||
Ast := Ast.Sequence.all.Data (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
elsif First.Str.all = "let*" then
|
||||
Err.Check (Ast.Sequence.all.Length = 3
|
||||
and then Ast.Sequence.all.Data (2).Kind in Types.Kind_Sequence,
|
||||
@ -184,14 +177,6 @@ procedure StepA_Mal is
|
||||
Ast => Ast.Sequence.all.Data (3),
|
||||
Env => Env));
|
||||
end;
|
||||
elsif First.Str.all = "macroexpand" then
|
||||
Err.Check (Ast.Sequence.all.Length = 2, "expected 1 parameter");
|
||||
Macroexpanding := True;
|
||||
Ast := Ast.Sequence.all.Data (2);
|
||||
goto Restart;
|
||||
elsif First.Str.all = "quasiquoteexpand" then
|
||||
Err.Check (Ast.Sequence.all.Length = 2, "expected 1 parameter");
|
||||
return Quasiquote (Ast.Sequence.all.Data (2));
|
||||
elsif First.Str.all = "quasiquote" then
|
||||
Err.Check (Ast.Sequence.all.Length = 2, "expected 1 parameter");
|
||||
Ast := Quasiquote (Ast.Sequence.all.Data (2));
|
||||
@ -248,24 +233,10 @@ procedure StepA_Mal is
|
||||
case First.Kind is
|
||||
when Kind_Macro =>
|
||||
-- Use the unevaluated arguments.
|
||||
if Macroexpanding then
|
||||
-- Evaluate the macro with tail call optimization.
|
||||
if not Env_Reusable then
|
||||
Env := Envs.New_Env (Outer => First.Fn.all.Env);
|
||||
Env_Reusable := True;
|
||||
end if;
|
||||
Env.all.Set_Binds
|
||||
(Binds => First.Fn.all.Params.all.Data,
|
||||
Exprs => Ast.Sequence.all.Data (2 .. Ast.Sequence.all.Length));
|
||||
Ast := First.Fn.all.Ast;
|
||||
goto Restart;
|
||||
else
|
||||
-- Evaluate the macro normally.
|
||||
Ast := First.Fn.all.Apply
|
||||
(Ast.Sequence.all.Data (2 .. Ast.Sequence.all.Length));
|
||||
-- Then evaluate the result with TCO.
|
||||
goto Restart;
|
||||
end if;
|
||||
when Types.Kind_Function =>
|
||||
null;
|
||||
when others =>
|
||||
@ -296,11 +267,7 @@ procedure StepA_Mal is
|
||||
end;
|
||||
exception
|
||||
when Err.Error =>
|
||||
if Macroexpanding then
|
||||
Err.Add_Trace_Line ("macroexpand", Ast);
|
||||
else
|
||||
Err.Add_Trace_Line ("eval", Ast);
|
||||
end if;
|
||||
raise;
|
||||
end Eval;
|
||||
|
||||
@ -472,6 +439,7 @@ begin
|
||||
-- Collect garbage.
|
||||
Err.Data := Types.Nil;
|
||||
Repl.all.Keep;
|
||||
Dbgeval.Keep;
|
||||
Garbage_Collected.Clean;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
|
@ -55,4 +55,10 @@ package body Types is
|
||||
end case;
|
||||
end Keep;
|
||||
|
||||
function To_Boolean (Form : T) return Boolean is
|
||||
(case Form.Kind is
|
||||
when Kind_Nil => False,
|
||||
when Kind_Boolean => Form.Ada_Boolean,
|
||||
when others => True);
|
||||
|
||||
end Types;
|
||||
|
@ -83,6 +83,8 @@ package Types is
|
||||
|
||||
Nil : constant T := (Kind => Kind_Nil);
|
||||
|
||||
function To_Boolean (Form : T) return Boolean with Inline;
|
||||
|
||||
procedure Keep (Object : in T) with Inline;
|
||||
|
||||
type T_Array is array (Positive range <>) of T;
|
||||
|
@ -1,5 +1,7 @@
|
||||
FROM ubuntu:vivid
|
||||
FROM ubuntu:xenial
|
||||
MAINTAINER Joel Martin <github@martintribe.org>
|
||||
LABEL org.opencontainers.image.source=https://github.com/kanaka/mal
|
||||
LABEL org.opencontainers.image.description="mal test container: ada"
|
||||
|
||||
##########################################################
|
||||
# General requirements for testing or common across many
|
||||
|
@ -109,6 +109,19 @@ procedure Step2_Eval is
|
||||
end Call_Eval;
|
||||
|
||||
begin
|
||||
pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector
|
||||
return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);
|
||||
end Eval_Ast;
|
||||
|
||||
|
||||
function Eval (Param : Mal_Handle; Env : String_Mal_Hash.Map)
|
||||
return Mal_Handle is
|
||||
First_Elem : Mal_Handle;
|
||||
Ast : Mal_Handle renames Param; -- Historic
|
||||
begin
|
||||
if Debug then
|
||||
Ada.Text_IO.Put_Line ("EVAL: " & Deref (Param).To_String);
|
||||
end if;
|
||||
|
||||
case Deref (Ast).Sym_Type is
|
||||
|
||||
@ -129,27 +142,10 @@ procedure Step2_Eval is
|
||||
end;
|
||||
|
||||
when List =>
|
||||
|
||||
return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);
|
||||
|
||||
when others => return Ast;
|
||||
|
||||
end case;
|
||||
|
||||
end Eval_Ast;
|
||||
|
||||
|
||||
function Eval (Param : Mal_Handle; Env : String_Mal_Hash.Map)
|
||||
return Mal_Handle is
|
||||
First_Elem : Mal_Handle;
|
||||
begin
|
||||
|
||||
if Debug then
|
||||
Ada.Text_IO.Put_Line ("Evaling " & Deref (Param).To_String);
|
||||
end if;
|
||||
|
||||
if Deref (Param).Sym_Type = List and then
|
||||
Deref_List (Param).Get_List_Type = List_List then
|
||||
case Deref_List (Param).Get_List_Type is
|
||||
when Hashed_List | Vector_List =>
|
||||
return Eval_Ast (Param, Env);
|
||||
when List_List =>
|
||||
|
||||
declare
|
||||
Evaled_H, First_Param : Mal_Handle;
|
||||
@ -169,12 +165,10 @@ procedure Step2_Eval is
|
||||
return Call_Func (Deref_Func (First_Param).all, Cdr (Evaled_List));
|
||||
end;
|
||||
|
||||
else -- Not a List_List
|
||||
|
||||
return Eval_Ast (Param, Env);
|
||||
|
||||
end if;
|
||||
|
||||
end case;
|
||||
when others => -- not a list, map, symbol or vector
|
||||
return Param;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
|
||||
|
@ -65,8 +65,6 @@ procedure Step3_Env is
|
||||
function Eval (Param : Types.Mal_Handle; Env : Envs.Env_Handle)
|
||||
return Types.Mal_Handle;
|
||||
|
||||
Debug : Boolean := False;
|
||||
|
||||
|
||||
function Read (Param : String) return Types.Mal_Handle is
|
||||
begin
|
||||
@ -112,6 +110,31 @@ procedure Step3_Env is
|
||||
end Call_Eval;
|
||||
|
||||
begin
|
||||
pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector
|
||||
return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);
|
||||
end Eval_Ast;
|
||||
|
||||
|
||||
function Eval (Param : Mal_Handle; Env : Envs.Env_Handle)
|
||||
return Mal_Handle is
|
||||
Ast : Mal_Handle renames Param; -- Historic
|
||||
begin
|
||||
declare
|
||||
M : Mal_Handle;
|
||||
B : Boolean;
|
||||
begin
|
||||
M := Envs.Get (Env, "DEBUG-EVAL");
|
||||
case Deref (M).Sym_Type is
|
||||
when Bool => B := Deref_Bool (M).Get_Bool;
|
||||
when Nil => B := False;
|
||||
when others => B := True;
|
||||
end case;
|
||||
if B then
|
||||
Ada.Text_IO.Put_Line ("EVAL: " & Deref (Param).To_String);
|
||||
end if;
|
||||
exception
|
||||
when Envs.Not_Found => null;
|
||||
end;
|
||||
|
||||
case Deref (Ast).Sym_Type is
|
||||
|
||||
@ -132,27 +155,10 @@ procedure Step3_Env is
|
||||
end;
|
||||
|
||||
when List =>
|
||||
|
||||
return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);
|
||||
|
||||
when others => return Ast;
|
||||
|
||||
end case;
|
||||
|
||||
end Eval_Ast;
|
||||
|
||||
|
||||
function Eval (Param : Mal_Handle; Env : Envs.Env_Handle)
|
||||
return Mal_Handle is
|
||||
First_Elem : Mal_Handle;
|
||||
begin
|
||||
|
||||
if Debug then
|
||||
Ada.Text_IO.Put_Line ("Evaling " & Deref (Param).To_String);
|
||||
end if;
|
||||
|
||||
if Deref (Param).Sym_Type = List and then
|
||||
Deref_List (Param).Get_List_Type = List_List then
|
||||
case Deref_List (Param).Get_List_Type is
|
||||
when Hashed_List | Vector_List =>
|
||||
return Eval_Ast (Param, Env);
|
||||
when List_List =>
|
||||
|
||||
declare
|
||||
Evaled_H, First_Param, Rest_List : Mal_Handle;
|
||||
@ -184,12 +190,10 @@ procedure Step3_Env is
|
||||
|
||||
end;
|
||||
|
||||
else -- Not a List_List
|
||||
|
||||
return Eval_Ast (Param, Env);
|
||||
|
||||
end if;
|
||||
|
||||
end case;
|
||||
when others => -- not a list, map, symbol or vector
|
||||
return Param;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
|
||||
@ -244,12 +248,6 @@ begin
|
||||
-- as we know Eval will be in scope for the lifetime of the program.
|
||||
Eval_Callback.Eval := Eval'Unrestricted_Access;
|
||||
|
||||
if Ada.Command_Line.Argument_Count > 0 then
|
||||
if Ada.Command_Line.Argument (1) = "-d" then
|
||||
Debug := True;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
Repl_Env := Envs.New_Env;
|
||||
|
||||
Init (Repl_Env);
|
||||
|
@ -102,6 +102,23 @@ procedure Step4_If_Fn_Do is
|
||||
end Call_Eval;
|
||||
|
||||
begin
|
||||
pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector
|
||||
return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);
|
||||
end Eval_Ast;
|
||||
|
||||
|
||||
function Eval (Param : Mal_Handle; Env : Envs.Env_Handle) return Mal_Handle is
|
||||
First_Param, Rest_Params : Mal_Handle;
|
||||
Rest_List, Param_List : List_Mal_Type;
|
||||
Ast : Mal_Handle renames Param; -- Historic
|
||||
begin
|
||||
begin
|
||||
if Eval_As_Boolean (Envs.Get (Env, "DEBUG-EVAL")) then
|
||||
Ada.Text_IO.Put_Line ("EVAL: " & Deref (Param).To_String);
|
||||
end if;
|
||||
exception
|
||||
when Envs.Not_Found => null;
|
||||
end;
|
||||
|
||||
case Deref (Ast).Sym_Type is
|
||||
|
||||
@ -122,27 +139,10 @@ procedure Step4_If_Fn_Do is
|
||||
end;
|
||||
|
||||
when List =>
|
||||
|
||||
return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);
|
||||
|
||||
when others => return Ast;
|
||||
|
||||
end case;
|
||||
|
||||
end Eval_Ast;
|
||||
|
||||
|
||||
function Eval (Param : Mal_Handle; Env : Envs.Env_Handle) return Mal_Handle is
|
||||
First_Param, Rest_Params : Mal_Handle;
|
||||
Rest_List, Param_List : List_Mal_Type;
|
||||
begin
|
||||
|
||||
if Debug then
|
||||
Ada.Text_IO.Put_Line ("Evaling " & Deref (Param).To_String);
|
||||
end if;
|
||||
|
||||
if Deref (Param).Sym_Type = List and then
|
||||
Deref_List (Param).Get_List_Type = List_List then
|
||||
case Deref_List (Param).Get_List_Type is
|
||||
when Hashed_List | Vector_List =>
|
||||
return Eval_Ast (Param, Env);
|
||||
when List_List =>
|
||||
|
||||
Param_List := Deref_List (Param).all;
|
||||
|
||||
@ -234,12 +234,10 @@ procedure Step4_If_Fn_Do is
|
||||
|
||||
end if;
|
||||
|
||||
else -- Not a List_List
|
||||
|
||||
return Eval_Ast (Param, Env);
|
||||
|
||||
end if;
|
||||
|
||||
end case;
|
||||
when others => -- not a list, map, symbol or vector
|
||||
return Param;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
|
||||
|
@ -71,6 +71,31 @@ procedure Step5_TCO is
|
||||
end Call_Eval;
|
||||
|
||||
begin
|
||||
pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector
|
||||
return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);
|
||||
end Eval_Ast;
|
||||
|
||||
function Eval (AParam : Mal_Handle; AnEnv : Envs.Env_Handle)
|
||||
return Mal_Handle is
|
||||
Param : Mal_Handle;
|
||||
Env : Envs.Env_Handle;
|
||||
First_Param, Rest_Params : Mal_Handle;
|
||||
Rest_List, Param_List : List_Mal_Type;
|
||||
Ast : Mal_Handle renames Param; -- Historic
|
||||
begin
|
||||
|
||||
Param := AParam;
|
||||
Env := AnEnv;
|
||||
|
||||
<<Tail_Call_Opt>>
|
||||
|
||||
begin
|
||||
if Eval_As_Boolean (Envs.Get (Env, "DEBUG-EVAL")) then
|
||||
Ada.Text_IO.Put_Line ("EVAL: " & Deref (Param).To_String);
|
||||
end if;
|
||||
exception
|
||||
when Envs.Not_Found => null;
|
||||
end;
|
||||
|
||||
case Deref (Ast).Sym_Type is
|
||||
|
||||
@ -91,35 +116,10 @@ procedure Step5_TCO is
|
||||
end;
|
||||
|
||||
when List =>
|
||||
|
||||
return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);
|
||||
|
||||
when others => return Ast;
|
||||
|
||||
end case;
|
||||
|
||||
end Eval_Ast;
|
||||
|
||||
|
||||
function Eval (AParam : Mal_Handle; AnEnv : Envs.Env_Handle)
|
||||
return Mal_Handle is
|
||||
Param : Mal_Handle;
|
||||
Env : Envs.Env_Handle;
|
||||
First_Param, Rest_Params : Mal_Handle;
|
||||
Rest_List, Param_List : List_Mal_Type;
|
||||
begin
|
||||
|
||||
Param := AParam;
|
||||
Env := AnEnv;
|
||||
|
||||
<<Tail_Call_Opt>>
|
||||
|
||||
if Debug then
|
||||
Ada.Text_IO.Put_Line ("Evaling " & Deref (Param).To_String);
|
||||
end if;
|
||||
|
||||
if Deref (Param).Sym_Type = List and then
|
||||
Deref_List (Param).Get_List_Type = List_List then
|
||||
case Deref_List (Param).Get_List_Type is
|
||||
when Hashed_List | Vector_List =>
|
||||
return Eval_Ast (Param, Env);
|
||||
when List_List =>
|
||||
|
||||
Param_List := Deref_List (Param).all;
|
||||
|
||||
@ -278,12 +278,10 @@ procedure Step5_TCO is
|
||||
|
||||
end if;
|
||||
|
||||
else -- Not a List_List
|
||||
|
||||
return Eval_Ast (Param, Env);
|
||||
|
||||
end if;
|
||||
|
||||
end case;
|
||||
when others => -- not a list, map, symbol or vector
|
||||
return Param;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
|
||||
|
@ -74,6 +74,33 @@ procedure Step6_File is
|
||||
end Call_Eval;
|
||||
|
||||
begin
|
||||
pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector
|
||||
return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);
|
||||
end Eval_Ast;
|
||||
|
||||
|
||||
function Eval (AParam : Mal_Handle; AnEnv : Envs.Env_Handle)
|
||||
return Mal_Handle is
|
||||
Param : Mal_Handle;
|
||||
Env : Envs.Env_Handle;
|
||||
First_Param, Rest_Params : Mal_Handle;
|
||||
Rest_List, Param_List : List_Mal_Type;
|
||||
Ast : Mal_Handle renames Param; -- Historic
|
||||
begin
|
||||
|
||||
Param := AParam;
|
||||
Env := AnEnv;
|
||||
|
||||
<<Tail_Call_Opt>>
|
||||
|
||||
begin
|
||||
if Eval_As_Boolean (Envs.Get (Env, "DEBUG-EVAL")) then
|
||||
Ada.Text_IO.Put_Line ("EVAL: " & Deref (Param).To_String);
|
||||
end if;
|
||||
exception
|
||||
when Envs.Not_Found => null;
|
||||
end;
|
||||
|
||||
|
||||
case Deref (Ast).Sym_Type is
|
||||
|
||||
@ -94,35 +121,10 @@ procedure Step6_File is
|
||||
end;
|
||||
|
||||
when List =>
|
||||
|
||||
return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);
|
||||
|
||||
when others => return Ast;
|
||||
|
||||
end case;
|
||||
|
||||
end Eval_Ast;
|
||||
|
||||
|
||||
function Eval (AParam : Mal_Handle; AnEnv : Envs.Env_Handle)
|
||||
return Mal_Handle is
|
||||
Param : Mal_Handle;
|
||||
Env : Envs.Env_Handle;
|
||||
First_Param, Rest_Params : Mal_Handle;
|
||||
Rest_List, Param_List : List_Mal_Type;
|
||||
begin
|
||||
|
||||
Param := AParam;
|
||||
Env := AnEnv;
|
||||
|
||||
<<Tail_Call_Opt>>
|
||||
|
||||
if Debug then
|
||||
Ada.Text_IO.Put_Line ("Evaling " & Deref (Param).To_String);
|
||||
end if;
|
||||
|
||||
if Deref (Param).Sym_Type = List and then
|
||||
Deref_List (Param).Get_List_Type = List_List then
|
||||
case Deref_List (Param).Get_List_Type is
|
||||
when Hashed_List | Vector_List =>
|
||||
return Eval_Ast (Param, Env);
|
||||
when List_List =>
|
||||
|
||||
Param_List := Deref_List (Param).all;
|
||||
|
||||
@ -281,12 +283,10 @@ procedure Step6_File is
|
||||
|
||||
end if;
|
||||
|
||||
else -- not a List_List
|
||||
|
||||
return Eval_Ast (Param, Env);
|
||||
|
||||
end if;
|
||||
|
||||
end case;
|
||||
when others => -- not a list, map, symbol or vector
|
||||
return Param;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
|
||||
|
@ -71,33 +71,8 @@ procedure Step7_Quote is
|
||||
end Call_Eval;
|
||||
|
||||
begin
|
||||
|
||||
case Deref (Ast).Sym_Type is
|
||||
|
||||
when Sym =>
|
||||
|
||||
declare
|
||||
Sym : Mal_String := Deref_Sym (Ast).Get_Sym;
|
||||
begin
|
||||
-- if keyword, return it. Otherwise look it up in the environment.
|
||||
if Sym(1) = ':' then
|
||||
return Ast;
|
||||
else
|
||||
return Envs.Get (Env, Sym);
|
||||
end if;
|
||||
exception
|
||||
when Envs.Not_Found =>
|
||||
raise Envs.Not_Found with ("'" & Sym & "' not found");
|
||||
end;
|
||||
|
||||
when List =>
|
||||
|
||||
pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector
|
||||
return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);
|
||||
|
||||
when others => return Ast;
|
||||
|
||||
end case;
|
||||
|
||||
end Eval_Ast;
|
||||
|
||||
function Starts_With (Ast : Mal_Handle; Symbol : String) return Boolean is
|
||||
@ -190,12 +165,34 @@ procedure Step7_Quote is
|
||||
|
||||
<<Tail_Call_Opt>>
|
||||
|
||||
if Debug then
|
||||
Ada.Text_IO.Put_Line ("Evaling " & Deref (Param).To_String);
|
||||
begin
|
||||
if Eval_As_Boolean (Envs.Get (Env, "DEBUG-EVAL")) then
|
||||
Ada.Text_IO.Put_Line ("EVAL: " & Deref (Param).To_String);
|
||||
end if;
|
||||
exception
|
||||
when Envs.Not_Found => null;
|
||||
end;
|
||||
|
||||
if Deref (Param).Sym_Type = List and then
|
||||
Deref_List (Param).Get_List_Type = List_List then
|
||||
case Deref (Param).Sym_Type is
|
||||
when Sym =>
|
||||
declare
|
||||
Sym : Mal_String := Deref_Sym (Param).Get_Sym;
|
||||
begin
|
||||
-- if keyword, return it. Otherwise look it up in the environment.
|
||||
if Sym(1) = ':' then
|
||||
return Param;
|
||||
else
|
||||
return Envs.Get (Env, Sym);
|
||||
end if;
|
||||
exception
|
||||
when Envs.Not_Found =>
|
||||
raise Envs.Not_Found with ("'" & Sym & "' not found");
|
||||
end;
|
||||
when List =>
|
||||
case Deref_List (Param).Get_List_Type is
|
||||
when Hashed_List | Vector_List =>
|
||||
return Eval_Ast (Param, Env);
|
||||
when List_List =>
|
||||
|
||||
Param_List := Deref_List (Param).all;
|
||||
|
||||
@ -305,11 +302,6 @@ procedure Step7_Quote is
|
||||
|
||||
return Car (Rest_List);
|
||||
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "quasiquoteexpand" then
|
||||
|
||||
return Quasi_Quote_Processing (Car (Rest_List));
|
||||
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "quasiquote" then
|
||||
|
||||
@ -370,12 +362,10 @@ procedure Step7_Quote is
|
||||
|
||||
end if;
|
||||
|
||||
else -- not a List_List
|
||||
|
||||
return Eval_Ast (Param, Env);
|
||||
|
||||
end if;
|
||||
|
||||
end case;
|
||||
when others => -- not a list, map, symbol or vector
|
||||
return Param;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
|
||||
|
@ -59,52 +59,6 @@ procedure Step8_Macros is
|
||||
end Def_Macro;
|
||||
|
||||
|
||||
function Macro_Expand (Ast : Mal_Handle; Env : Envs.Env_Handle)
|
||||
return Mal_Handle is
|
||||
Res : Mal_Handle;
|
||||
E : Envs.Env_Handle;
|
||||
LMT : List_Mal_Type;
|
||||
LP : Lambda_Ptr;
|
||||
begin
|
||||
|
||||
Res := Ast;
|
||||
|
||||
loop
|
||||
|
||||
if Deref (Res).Sym_Type /= List then
|
||||
exit;
|
||||
end if;
|
||||
|
||||
LMT := Deref_List (Res).all;
|
||||
|
||||
-- Get the macro in the list from the env
|
||||
-- or return null if not applicable.
|
||||
LP := Get_Macro (Res, Env);
|
||||
|
||||
exit when LP = null or else not LP.Get_Is_Macro;
|
||||
|
||||
declare
|
||||
Fn_List : Mal_Handle := Cdr (LMT);
|
||||
Params : List_Mal_Type;
|
||||
begin
|
||||
E := Envs.New_Env (LP.Get_Env);
|
||||
|
||||
Params := Deref_List (LP.Get_Params).all;
|
||||
if Envs.Bind (E, Params, Deref_List (Fn_List).all) then
|
||||
|
||||
Res := Eval (LP.Get_Expr, E);
|
||||
|
||||
end if;
|
||||
|
||||
end;
|
||||
|
||||
end loop;
|
||||
|
||||
return Res;
|
||||
|
||||
end Macro_Expand;
|
||||
|
||||
|
||||
function Eval_As_Boolean (MH : Mal_Handle) return Boolean is
|
||||
Res : Boolean;
|
||||
begin
|
||||
@ -137,33 +91,8 @@ procedure Step8_Macros is
|
||||
end Call_Eval;
|
||||
|
||||
begin
|
||||
|
||||
case Deref (Ast).Sym_Type is
|
||||
|
||||
when Sym =>
|
||||
|
||||
declare
|
||||
Sym : Mal_String := Deref_Sym (Ast).Get_Sym;
|
||||
begin
|
||||
-- if keyword, return it. Otherwise look it up in the environment.
|
||||
if Sym(1) = ':' then
|
||||
return Ast;
|
||||
else
|
||||
return Envs.Get (Env, Sym);
|
||||
end if;
|
||||
exception
|
||||
when Envs.Not_Found =>
|
||||
raise Envs.Not_Found with ("'" & Sym & "' not found");
|
||||
end;
|
||||
|
||||
when List =>
|
||||
|
||||
pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector
|
||||
return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);
|
||||
|
||||
when others => return Ast;
|
||||
|
||||
end case;
|
||||
|
||||
end Eval_Ast;
|
||||
|
||||
function Starts_With (Ast : Mal_Handle; Symbol : String) return Boolean is
|
||||
@ -256,18 +185,34 @@ procedure Step8_Macros is
|
||||
|
||||
<<Tail_Call_Opt>>
|
||||
|
||||
if Debug then
|
||||
Ada.Text_IO.Put_Line ("Evaling " & Deref (Param).To_String);
|
||||
begin
|
||||
if Eval_As_Boolean (Envs.Get (Env, "DEBUG-EVAL")) then
|
||||
Ada.Text_IO.Put_Line ("EVAL: " & Deref (Param).To_String);
|
||||
end if;
|
||||
exception
|
||||
when Envs.Not_Found => null;
|
||||
end;
|
||||
|
||||
Param := Macro_Expand (Param, Env);
|
||||
|
||||
if Debug then
|
||||
Ada.Text_IO.Put_Line ("After expansion " & Deref (Param).To_String);
|
||||
case Deref (Param).Sym_Type is
|
||||
when Sym =>
|
||||
declare
|
||||
Sym : Mal_String := Deref_Sym (Param).Get_Sym;
|
||||
begin
|
||||
-- if keyword, return it. Otherwise look it up in the environment.
|
||||
if Sym(1) = ':' then
|
||||
return Param;
|
||||
else
|
||||
return Envs.Get (Env, Sym);
|
||||
end if;
|
||||
|
||||
if Deref (Param).Sym_Type = List and then
|
||||
Deref_List (Param).Get_List_Type = List_List then
|
||||
exception
|
||||
when Envs.Not_Found =>
|
||||
raise Envs.Not_Found with ("'" & Sym & "' not found");
|
||||
end;
|
||||
when List =>
|
||||
case Deref_List (Param).Get_List_Type is
|
||||
when Hashed_List | Vector_List =>
|
||||
return Eval_Ast (Param, Env);
|
||||
when List_List =>
|
||||
|
||||
Param_List := Deref_List (Param).all;
|
||||
|
||||
@ -286,9 +231,6 @@ procedure Step8_Macros is
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "defmacro!" then
|
||||
return Def_Macro (Rest_List, Env);
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "macroexpand" then
|
||||
return Macro_Expand (Car (Rest_List), Env);
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "let*" then
|
||||
declare
|
||||
@ -383,11 +325,6 @@ procedure Step8_Macros is
|
||||
|
||||
return Car (Rest_List);
|
||||
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "quasiquoteexpand" then
|
||||
|
||||
return Quasi_Quote_Processing (Car (Rest_List));
|
||||
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "quasiquote" then
|
||||
|
||||
@ -397,18 +334,10 @@ procedure Step8_Macros is
|
||||
else
|
||||
|
||||
-- The APPLY section.
|
||||
declare
|
||||
Evaled_H : Mal_Handle;
|
||||
begin
|
||||
Evaled_H := Eval_Ast (Param, Env);
|
||||
|
||||
Param_List := Deref_List (Evaled_H).all;
|
||||
|
||||
First_Param := Car (Param_List);
|
||||
Rest_Params := Cdr (Param_List);
|
||||
Rest_List := Deref_List (Rest_Params).all;
|
||||
First_Param := Eval (First_Param, Env);
|
||||
|
||||
if Deref (First_Param).Sym_Type = Func then
|
||||
Rest_Params := Eval_Ast (Rest_Params, Env);
|
||||
return Call_Func (Deref_Func (First_Param).all, Rest_Params);
|
||||
elsif Deref (First_Param).Sym_Type = Lambda then
|
||||
declare
|
||||
@ -421,6 +350,16 @@ procedure Step8_Macros is
|
||||
begin
|
||||
|
||||
L := Deref_Lambda (First_Param).all;
|
||||
|
||||
if L.Get_Is_Macro then
|
||||
-- Apply to *unevaluated* arguments
|
||||
Param := L.Apply (Rest_Params);
|
||||
-- then EVAL the result.
|
||||
goto Tail_Call_Opt;
|
||||
end if;
|
||||
|
||||
Rest_Params := Eval_Ast (Rest_Params, Env);
|
||||
|
||||
E := Envs.New_Env (L.Get_Env);
|
||||
|
||||
Param_Names := Deref_List (L.Get_Params).all;
|
||||
@ -444,16 +383,12 @@ procedure Step8_Macros is
|
||||
raise Runtime_Exception with "Deref called on non-Func/Lambda";
|
||||
end if;
|
||||
|
||||
end;
|
||||
|
||||
end if;
|
||||
|
||||
else -- not a List_List
|
||||
|
||||
return Eval_Ast (Param, Env);
|
||||
|
||||
end if;
|
||||
|
||||
end case;
|
||||
when others => -- not a list, map, symbol or vector
|
||||
return Param;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
|
||||
|
@ -59,52 +59,6 @@ procedure Step9_Try is
|
||||
end Def_Macro;
|
||||
|
||||
|
||||
function Macro_Expand (Ast : Mal_Handle; Env : Envs.Env_Handle)
|
||||
return Mal_Handle is
|
||||
Res : Mal_Handle;
|
||||
E : Envs.Env_Handle;
|
||||
LMT : List_Mal_Type;
|
||||
LP : Lambda_Ptr;
|
||||
begin
|
||||
|
||||
Res := Ast;
|
||||
|
||||
loop
|
||||
|
||||
if Deref (Res).Sym_Type /= List then
|
||||
exit;
|
||||
end if;
|
||||
|
||||
LMT := Deref_List (Res).all;
|
||||
|
||||
-- Get the macro in the list from the env
|
||||
-- or return null if not applicable.
|
||||
LP := Get_Macro (Res, Env);
|
||||
|
||||
exit when LP = null or else not LP.Get_Is_Macro;
|
||||
|
||||
declare
|
||||
Fn_List : Mal_Handle := Cdr (LMT);
|
||||
Params : List_Mal_Type;
|
||||
begin
|
||||
E := Envs.New_Env (LP.Get_Env);
|
||||
|
||||
Params := Deref_List (LP.Get_Params).all;
|
||||
if Envs.Bind (E, Params, Deref_List (Fn_List).all) then
|
||||
|
||||
Res := Eval (LP.Get_Expr, E);
|
||||
|
||||
end if;
|
||||
|
||||
end;
|
||||
|
||||
end loop;
|
||||
|
||||
return Res;
|
||||
|
||||
end Macro_Expand;
|
||||
|
||||
|
||||
function Eval_As_Boolean (MH : Mal_Handle) return Boolean is
|
||||
Res : Boolean;
|
||||
begin
|
||||
@ -137,33 +91,8 @@ procedure Step9_Try is
|
||||
end Call_Eval;
|
||||
|
||||
begin
|
||||
|
||||
case Deref (Ast).Sym_Type is
|
||||
|
||||
when Sym =>
|
||||
|
||||
declare
|
||||
Sym : Mal_String := Deref_Sym (Ast).Get_Sym;
|
||||
begin
|
||||
-- if keyword, return it. Otherwise look it up in the environment.
|
||||
if Sym(1) = ':' then
|
||||
return Ast;
|
||||
else
|
||||
return Envs.Get (Env, Sym);
|
||||
end if;
|
||||
exception
|
||||
when Envs.Not_Found =>
|
||||
raise Envs.Not_Found with ("'" & Sym & "' not found");
|
||||
end;
|
||||
|
||||
when List =>
|
||||
|
||||
pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector
|
||||
return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);
|
||||
|
||||
when others => return Ast;
|
||||
|
||||
end case;
|
||||
|
||||
end Eval_Ast;
|
||||
|
||||
function Starts_With (Ast : Mal_Handle; Symbol : String) return Boolean is
|
||||
@ -282,18 +211,34 @@ procedure Step9_Try is
|
||||
|
||||
<<Tail_Call_Opt>>
|
||||
|
||||
if Debug then
|
||||
Ada.Text_IO.Put_Line ("Evaling " & Deref (Param).To_String);
|
||||
begin
|
||||
if Eval_As_Boolean (Envs.Get (Env, "DEBUG-EVAL")) then
|
||||
Ada.Text_IO.Put_Line ("EVAL: " & Deref (Param).To_String);
|
||||
end if;
|
||||
exception
|
||||
when Envs.Not_Found => null;
|
||||
end;
|
||||
|
||||
Param := Macro_Expand (Param, Env);
|
||||
|
||||
if Debug then
|
||||
Ada.Text_IO.Put_Line ("After expansion " & Deref (Param).To_String);
|
||||
case Deref (Param).Sym_Type is
|
||||
when Sym =>
|
||||
declare
|
||||
Sym : Mal_String := Deref_Sym (Param).Get_Sym;
|
||||
begin
|
||||
-- if keyword, return it. Otherwise look it up in the environment.
|
||||
if Sym(1) = ':' then
|
||||
return Param;
|
||||
else
|
||||
return Envs.Get (Env, Sym);
|
||||
end if;
|
||||
|
||||
if Deref (Param).Sym_Type = List and then
|
||||
Deref_List (Param).Get_List_Type = List_List then
|
||||
exception
|
||||
when Envs.Not_Found =>
|
||||
raise Envs.Not_Found with ("'" & Sym & "' not found");
|
||||
end;
|
||||
when List =>
|
||||
case Deref_List (Param).Get_List_Type is
|
||||
when Hashed_List | Vector_List =>
|
||||
return Eval_Ast (Param, Env);
|
||||
when List_List =>
|
||||
|
||||
Param_List := Deref_List (Param).all;
|
||||
|
||||
@ -312,9 +257,6 @@ procedure Step9_Try is
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "defmacro!" then
|
||||
return Def_Macro (Rest_List, Env);
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "macroexpand" then
|
||||
return Macro_Expand (Car (Rest_List), Env);
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "let*" then
|
||||
declare
|
||||
@ -409,11 +351,6 @@ procedure Step9_Try is
|
||||
|
||||
return Car (Rest_List);
|
||||
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "quasiquoteexpand" then
|
||||
|
||||
return Quasi_Quote_Processing (Car (Rest_List));
|
||||
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "quasiquote" then
|
||||
|
||||
@ -450,18 +387,10 @@ procedure Step9_Try is
|
||||
else
|
||||
|
||||
-- The APPLY section.
|
||||
declare
|
||||
Evaled_H : Mal_Handle;
|
||||
begin
|
||||
Evaled_H := Eval_Ast (Param, Env);
|
||||
|
||||
Param_List := Deref_List (Evaled_H).all;
|
||||
|
||||
First_Param := Car (Param_List);
|
||||
Rest_Params := Cdr (Param_List);
|
||||
Rest_List := Deref_List (Rest_Params).all;
|
||||
First_Param := Eval (First_Param, Env);
|
||||
|
||||
if Deref (First_Param).Sym_Type = Func then
|
||||
Rest_Params := Eval_Ast (Rest_Params, Env);
|
||||
return Call_Func (Deref_Func (First_Param).all, Rest_Params);
|
||||
elsif Deref (First_Param).Sym_Type = Lambda then
|
||||
declare
|
||||
@ -474,6 +403,16 @@ procedure Step9_Try is
|
||||
begin
|
||||
|
||||
L := Deref_Lambda (First_Param).all;
|
||||
|
||||
if L.Get_Is_Macro then
|
||||
-- Apply to *unevaluated* arguments
|
||||
Param := L.Apply (Rest_Params);
|
||||
-- then EVAL the result.
|
||||
goto Tail_Call_Opt;
|
||||
end if;
|
||||
|
||||
Rest_Params := Eval_Ast (Rest_Params, Env);
|
||||
|
||||
E := Envs.New_Env (L.Get_Env);
|
||||
|
||||
Param_Names := Deref_List (L.Get_Params).all;
|
||||
@ -497,16 +436,12 @@ procedure Step9_Try is
|
||||
raise Runtime_Exception with "Deref called on non-Func/Lambda";
|
||||
end if;
|
||||
|
||||
end;
|
||||
|
||||
end if;
|
||||
|
||||
else -- not a List_List
|
||||
|
||||
return Eval_Ast (Param, Env);
|
||||
|
||||
end if;
|
||||
|
||||
end case;
|
||||
when others => -- not a list, map, symbol or vector
|
||||
return Param;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
|
||||
|
@ -59,53 +59,6 @@ procedure StepA_Mal is
|
||||
end Def_Macro;
|
||||
|
||||
|
||||
function Macro_Expand (Ast : Mal_Handle; Env : Envs.Env_Handle)
|
||||
return Mal_Handle is
|
||||
Res : Mal_Handle;
|
||||
E : Envs.Env_Handle;
|
||||
LMT : List_Mal_Type;
|
||||
LP : Lambda_Ptr;
|
||||
begin
|
||||
|
||||
Res := Ast;
|
||||
|
||||
loop
|
||||
|
||||
if Deref (Res).Sym_Type /= List then
|
||||
exit;
|
||||
end if;
|
||||
|
||||
LMT := Deref_List (Res).all;
|
||||
|
||||
-- Get the macro in the list from the env
|
||||
-- or return null if not applicable.
|
||||
LP := Get_Macro (Res, Env);
|
||||
|
||||
exit when LP = null or else not LP.Get_Is_Macro;
|
||||
|
||||
declare
|
||||
Fn_List : Mal_Handle := Cdr (LMT);
|
||||
Params : List_Mal_Type;
|
||||
begin
|
||||
E := Envs.New_Env (LP.Get_Env);
|
||||
|
||||
Params := Deref_List (LP.Get_Params).all;
|
||||
|
||||
if Envs.Bind (E, Params, Deref_List (Fn_List).all) then
|
||||
|
||||
Res := Eval (LP.Get_Expr, E);
|
||||
|
||||
end if;
|
||||
|
||||
end;
|
||||
|
||||
end loop;
|
||||
|
||||
return Res;
|
||||
|
||||
end Macro_Expand;
|
||||
|
||||
|
||||
function Eval_As_Boolean (MH : Mal_Handle) return Boolean is
|
||||
Res : Boolean;
|
||||
begin
|
||||
@ -138,33 +91,8 @@ procedure StepA_Mal is
|
||||
end Call_Eval;
|
||||
|
||||
begin
|
||||
|
||||
case Deref (Ast).Sym_Type is
|
||||
|
||||
when Sym =>
|
||||
|
||||
declare
|
||||
Sym : Mal_String := Deref_Sym (Ast).Get_Sym;
|
||||
begin
|
||||
-- if keyword, return it. Otherwise look it up in the environment.
|
||||
if Sym(1) = ':' then
|
||||
return Ast;
|
||||
else
|
||||
return Envs.Get (Env, Sym);
|
||||
end if;
|
||||
exception
|
||||
when Envs.Not_Found =>
|
||||
raise Envs.Not_Found with ("'" & Sym & "' not found");
|
||||
end;
|
||||
|
||||
when List =>
|
||||
|
||||
pragma Assert (Deref (Ast).Sym_Type = List); -- list, map or vector
|
||||
return Map (Call_Eval'Unrestricted_Access, Deref_List_Class (Ast).all);
|
||||
|
||||
when others => return Ast;
|
||||
|
||||
end case;
|
||||
|
||||
end Eval_Ast;
|
||||
|
||||
function Starts_With (Ast : Mal_Handle; Symbol : String) return Boolean is
|
||||
@ -283,18 +211,34 @@ procedure StepA_Mal is
|
||||
|
||||
<<Tail_Call_Opt>>
|
||||
|
||||
if Debug then
|
||||
Ada.Text_IO.Put_Line ("Evaling " & Deref (Param).To_String);
|
||||
begin
|
||||
if Eval_As_Boolean (Envs.Get (Env, "DEBUG-EVAL")) then
|
||||
Ada.Text_IO.Put_Line ("EVAL: " & Deref (Param).To_String);
|
||||
end if;
|
||||
exception
|
||||
when Envs.Not_Found => null;
|
||||
end;
|
||||
|
||||
Param := Macro_Expand (Param, Env);
|
||||
|
||||
if Debug then
|
||||
Ada.Text_IO.Put_Line ("After expansion " & Deref (Param).To_String);
|
||||
case Deref (Param).Sym_Type is
|
||||
when Sym =>
|
||||
declare
|
||||
Sym : Mal_String := Deref_Sym (Param).Get_Sym;
|
||||
begin
|
||||
-- if keyword, return it. Otherwise look it up in the environment.
|
||||
if Sym(1) = ':' then
|
||||
return Param;
|
||||
else
|
||||
return Envs.Get (Env, Sym);
|
||||
end if;
|
||||
|
||||
if Deref (Param).Sym_Type = List and then
|
||||
Deref_List (Param).Get_List_Type = List_List then
|
||||
exception
|
||||
when Envs.Not_Found =>
|
||||
raise Envs.Not_Found with ("'" & Sym & "' not found");
|
||||
end;
|
||||
when List =>
|
||||
case Deref_List (Param).Get_List_Type is
|
||||
when Hashed_List | Vector_List =>
|
||||
return Eval_Ast (Param, Env);
|
||||
when List_List =>
|
||||
|
||||
Param_List := Deref_List (Param).all;
|
||||
|
||||
@ -313,9 +257,6 @@ procedure StepA_Mal is
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "defmacro!" then
|
||||
return Def_Macro (Rest_List, Env);
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "macroexpand" then
|
||||
return Macro_Expand (Car (Rest_List), Env);
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "let*" then
|
||||
declare
|
||||
@ -410,11 +351,6 @@ procedure StepA_Mal is
|
||||
|
||||
return Car (Rest_List);
|
||||
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "quasiquoteexpand" then
|
||||
|
||||
return Quasi_Quote_Processing (Car (Rest_List));
|
||||
|
||||
elsif Deref (First_Param).Sym_Type = Sym and then
|
||||
Deref_Sym (First_Param).Get_Sym = "quasiquote" then
|
||||
|
||||
@ -451,18 +387,10 @@ procedure StepA_Mal is
|
||||
else
|
||||
|
||||
-- The APPLY section.
|
||||
declare
|
||||
Evaled_H : Mal_Handle;
|
||||
begin
|
||||
Evaled_H := Eval_Ast (Param, Env);
|
||||
|
||||
Param_List := Deref_List (Evaled_H).all;
|
||||
|
||||
First_Param := Car (Param_List);
|
||||
Rest_Params := Cdr (Param_List);
|
||||
Rest_List := Deref_List (Rest_Params).all;
|
||||
First_Param := Eval (First_Param, Env);
|
||||
|
||||
if Deref (First_Param).Sym_Type = Func then
|
||||
Rest_Params := Eval_Ast (Rest_Params, Env);
|
||||
return Call_Func (Deref_Func (First_Param).all, Rest_Params);
|
||||
elsif Deref (First_Param).Sym_Type = Lambda then
|
||||
declare
|
||||
@ -475,6 +403,16 @@ procedure StepA_Mal is
|
||||
begin
|
||||
|
||||
L := Deref_Lambda (First_Param).all;
|
||||
|
||||
if L.Get_Is_Macro then
|
||||
-- Apply to *unevaluated* arguments
|
||||
Param := L.Apply (Rest_Params);
|
||||
-- then EVAL the result.
|
||||
goto Tail_Call_Opt;
|
||||
end if;
|
||||
|
||||
Rest_Params := Eval_Ast (Rest_Params, Env);
|
||||
|
||||
E := Envs.New_Env (L.Get_Env);
|
||||
|
||||
Param_Names := Deref_List (L.Get_Params).all;
|
||||
@ -498,16 +436,12 @@ procedure StepA_Mal is
|
||||
raise Runtime_Exception with "Deref called on non-Func/Lambda";
|
||||
end if;
|
||||
|
||||
end;
|
||||
|
||||
end if;
|
||||
|
||||
else -- not a List_List
|
||||
|
||||
return Eval_Ast (Param, Env);
|
||||
|
||||
end if;
|
||||
|
||||
end case;
|
||||
when others => -- not a list, map, symbol or vector
|
||||
return Param;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
|
||||
|
@ -141,40 +141,6 @@ package body Types is
|
||||
return To_Str (T, Print_Readably);
|
||||
end To_String;
|
||||
|
||||
function Is_Macro_Call (T : Mal_Type'Class; Env : Envs.Env_Handle) return Boolean is
|
||||
L : List_Mal_Type;
|
||||
First_Elem, Func : Mal_Handle;
|
||||
begin
|
||||
|
||||
if T.Sym_Type /= List then
|
||||
return False;
|
||||
end if;
|
||||
|
||||
L := List_Mal_Type (T);
|
||||
|
||||
if Is_Null (L) then
|
||||
return False;
|
||||
end if;
|
||||
|
||||
First_Elem := Car (L);
|
||||
|
||||
if Deref (First_Elem).Sym_Type /= Sym then
|
||||
return False;
|
||||
end if;
|
||||
|
||||
Func := Envs.Get (Env, Deref_Sym (First_Elem).Get_Sym);
|
||||
|
||||
if Deref (Func).Sym_Type /= Lambda then
|
||||
return False;
|
||||
end if;
|
||||
|
||||
return Deref_Lambda (Func).Get_Is_Macro;
|
||||
|
||||
exception
|
||||
when Envs.Not_Found => return False;
|
||||
end Is_Macro_Call;
|
||||
|
||||
|
||||
-- A helper function that just view converts the smart pointer.
|
||||
function Deref (S : Mal_Handle) return Mal_Ptr is
|
||||
begin
|
||||
@ -1072,41 +1038,6 @@ package body Types is
|
||||
|
||||
end Apply;
|
||||
|
||||
|
||||
function Get_Macro (T : Mal_Handle; Env : Envs.Env_Handle) return Lambda_Ptr is
|
||||
L : List_Mal_Type;
|
||||
First_Elem, Func : Mal_Handle;
|
||||
begin
|
||||
|
||||
if Deref (T).Sym_Type /= List then
|
||||
return null;
|
||||
end if;
|
||||
|
||||
L := Deref_List (T).all;
|
||||
|
||||
if Is_Null (L) then
|
||||
return null;
|
||||
end if;
|
||||
|
||||
First_Elem := Car (L);
|
||||
|
||||
if Deref (First_Elem).Sym_Type /= Sym then
|
||||
return null;
|
||||
end if;
|
||||
|
||||
Func := Envs.Get (Env, Deref_Sym (First_Elem).Get_Sym);
|
||||
|
||||
if Deref (Func).Sym_Type /= Lambda then
|
||||
return null;
|
||||
end if;
|
||||
|
||||
return Deref_Lambda (Func);
|
||||
|
||||
exception
|
||||
when Envs.Not_Found => return null;
|
||||
end Get_Macro;
|
||||
|
||||
|
||||
overriding function To_Str
|
||||
(T : Lambda_Mal_Type; Print_Readably : Boolean := True)
|
||||
return Mal_String is
|
||||
|
@ -51,8 +51,6 @@ package Types is
|
||||
function To_String (T : Mal_Type'Class; Print_Readably : Boolean := True)
|
||||
return Mal_String;
|
||||
|
||||
function Is_Macro_Call (T : Mal_Type'Class; Env : Envs.Env_Handle) return Boolean;
|
||||
|
||||
type Mal_Ptr is access all Mal_Type'Class;
|
||||
|
||||
-- A helper function that just view converts the smart pointer to
|
||||
@ -297,8 +295,6 @@ package Types is
|
||||
|
||||
type Lambda_Ptr is access all Lambda_Mal_Type;
|
||||
|
||||
function Get_Macro (T : Mal_Handle; Env : Envs.Env_Handle) return Lambda_Ptr;
|
||||
|
||||
function Deref_Lambda (SP : Mal_Handle) return Lambda_Ptr;
|
||||
|
||||
generic
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM ubuntu:vivid
|
||||
FROM ubuntu:xenial
|
||||
MAINTAINER Joel Martin <github@martintribe.org>
|
||||
|
||||
##########################################################
|
||||
|
@ -8,18 +8,20 @@ function READ(str)
|
||||
}
|
||||
|
||||
function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
# This function has two distinct purposes.
|
||||
# non empty list: a0 a1 .. an -> list: nil (eval a1) .. (eval an)
|
||||
# vector: a0 a1 .. an -> vector: (eval a0) (eval a1) .. (eval an)
|
||||
{
|
||||
switch (ast) {
|
||||
case /^'/:
|
||||
if (ast in env) {
|
||||
return types_addref(env[ast])
|
||||
}
|
||||
return "!\"'" substr(ast, 2) "' not found"
|
||||
case /^[([]/:
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
new_idx = types_allocate()
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (ast ~ /^\(/) {
|
||||
types_heap[new_idx][0] = "#nil"
|
||||
i = 1
|
||||
} else {
|
||||
i = 0
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
ret = EVAL(types_addref(types_heap[idx][i]), env)
|
||||
if (ret ~ /^!/) {
|
||||
types_heap[new_idx]["len"] = i
|
||||
@ -30,7 +32,10 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
types_heap[new_idx]["len"] = len
|
||||
return substr(ast, 1, 1) new_idx
|
||||
case /^\{/:
|
||||
}
|
||||
|
||||
function eval_map(ast, env, i, idx, new_idx, ret)
|
||||
{
|
||||
idx = substr(ast, 2)
|
||||
new_idx = types_allocate()
|
||||
for (i in types_heap[idx]) {
|
||||
@ -44,29 +49,48 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
}
|
||||
return "{" new_idx
|
||||
default:
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
function EVAL(ast, env, new_ast, ret, idx, f, f_idx)
|
||||
{
|
||||
if (ast !~ /^\(/) {
|
||||
# print "EVAL: " printer_pr_str(ast, 1)
|
||||
|
||||
switch (ast) {
|
||||
case /^'/: # symbol
|
||||
if (ast in env) {
|
||||
ret = types_addref(env[ast])
|
||||
} else {
|
||||
ret = "!\"'" substr(ast, 2) "' not found"
|
||||
}
|
||||
types_release(ast)
|
||||
return ret
|
||||
case /^\[/: # vector
|
||||
ret = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
return ret
|
||||
case /^\{/: # map
|
||||
ret = eval_map(ast, env)
|
||||
types_release(ast)
|
||||
return ret
|
||||
case /^[^(]/: # not a list
|
||||
types_release(ast)
|
||||
return ast
|
||||
}
|
||||
idx = substr(ast, 2)
|
||||
if (types_heap[idx]["len"] == 0) {
|
||||
return ast
|
||||
}
|
||||
f = EVAL(types_addref(types_heap[idx][0]), env)
|
||||
if (f ~ /^!/) {
|
||||
types_release(ast)
|
||||
return f
|
||||
}
|
||||
new_ast = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
if (new_ast ~ /^!/) {
|
||||
return new_ast
|
||||
}
|
||||
idx = substr(new_ast, 2)
|
||||
f = types_heap[idx][0]
|
||||
if (f ~ /^&/) {
|
||||
f_idx = substr(f, 2)
|
||||
ret = @f_idx(idx)
|
||||
|
@ -9,19 +9,20 @@ function READ(str)
|
||||
}
|
||||
|
||||
function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
# This function has two distinct purposes.
|
||||
# non empty list: a0 a1 .. an -> list: nil (eval a1) .. (eval an)
|
||||
# vector: a0 a1 .. an -> vector: (eval a0) (eval a1) .. (eval an)
|
||||
{
|
||||
switch (ast) {
|
||||
case /^'/:
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
return ret
|
||||
case /^[([]/:
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
new_idx = types_allocate()
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (ast ~ /^\(/) {
|
||||
types_heap[new_idx][0] = "#nil"
|
||||
i = 1
|
||||
} else {
|
||||
i = 0
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
ret = EVAL(types_addref(types_heap[idx][i]), env)
|
||||
if (ret ~ /^!/) {
|
||||
types_heap[new_idx]["len"] = i
|
||||
@ -32,7 +33,10 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
types_heap[new_idx]["len"] = len
|
||||
return substr(ast, 1, 1) new_idx
|
||||
case /^\{/:
|
||||
}
|
||||
|
||||
function eval_map(ast, env, i, idx, new_idx, ret)
|
||||
{
|
||||
idx = substr(ast, 2)
|
||||
new_idx = types_allocate()
|
||||
for (i in types_heap[idx]) {
|
||||
@ -46,9 +50,6 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
}
|
||||
return "{" new_idx
|
||||
default:
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
function EVAL_def(ast, env, idx, sym, ret, len)
|
||||
@ -125,11 +126,39 @@ function EVAL_let(ast, env, idx, params, params_idx, params_len, new_env, i,
|
||||
function EVAL(ast, env, new_ast, ret, idx, f, f_idx)
|
||||
{
|
||||
env_addref(env)
|
||||
if (ast !~ /^\(/) {
|
||||
|
||||
switch (env_get(env, "'DEBUG-EVAL")) {
|
||||
case /^!/:
|
||||
case "#nil":
|
||||
case "#false":
|
||||
break
|
||||
default:
|
||||
print "EVAL: " printer_pr_str(ast, 1)
|
||||
}
|
||||
|
||||
switch (ast) {
|
||||
case /^'/: # symbol
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^\[/: # vector
|
||||
ret = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^\{/: # map
|
||||
ret = eval_map(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^[^(]/: # not a list
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
idx = substr(ast, 2)
|
||||
if (types_heap[idx]["len"] == 0) {
|
||||
@ -142,6 +171,12 @@ function EVAL(ast, env, new_ast, ret, idx, f, f_idx)
|
||||
case "'let*":
|
||||
return EVAL_let(ast, env)
|
||||
default:
|
||||
f = EVAL(types_addref(types_heap[idx][0]), env)
|
||||
if (f ~ /^!/) {
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return f
|
||||
}
|
||||
new_ast = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
@ -149,13 +184,13 @@ function EVAL(ast, env, new_ast, ret, idx, f, f_idx)
|
||||
return new_ast
|
||||
}
|
||||
idx = substr(new_ast, 2)
|
||||
f = types_heap[idx][0]
|
||||
if (f ~ /^&/) {
|
||||
f_idx = substr(f, 2)
|
||||
switch (f) {
|
||||
case /^&/:
|
||||
ret = @f_idx(idx)
|
||||
types_release(new_ast)
|
||||
return ret
|
||||
} else {
|
||||
default:
|
||||
types_release(new_ast)
|
||||
return "!\"First element of list must be function, supplied " types_typename(f) "."
|
||||
}
|
||||
|
@ -10,19 +10,20 @@ function READ(str)
|
||||
}
|
||||
|
||||
function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
# This function has two distinct purposes.
|
||||
# non empty list: a0 a1 .. an -> list: nil (eval a1) .. (eval an)
|
||||
# vector: a0 a1 .. an -> vector: (eval a0) (eval a1) .. (eval an)
|
||||
{
|
||||
switch (ast) {
|
||||
case /^'/:
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
return ret
|
||||
case /^[([]/:
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
new_idx = types_allocate()
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (ast ~ /^\(/) {
|
||||
types_heap[new_idx][0] = "#nil"
|
||||
i = 1
|
||||
} else {
|
||||
i = 0
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
ret = EVAL(types_addref(types_heap[idx][i]), env)
|
||||
if (ret ~ /^!/) {
|
||||
types_heap[new_idx]["len"] = i
|
||||
@ -33,7 +34,10 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
types_heap[new_idx]["len"] = len
|
||||
return substr(ast, 1, 1) new_idx
|
||||
case /^\{/:
|
||||
}
|
||||
|
||||
function eval_map(ast, env, i, idx, new_idx, ret)
|
||||
{
|
||||
idx = substr(ast, 2)
|
||||
new_idx = types_allocate()
|
||||
for (i in types_heap[idx]) {
|
||||
@ -47,9 +51,6 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
}
|
||||
return "{" new_idx
|
||||
default:
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
function EVAL_def(ast, env, idx, sym, ret, len)
|
||||
@ -225,11 +226,39 @@ function EVAL_fn(ast, env, idx, params, params_idx, params_len, i, sym, f_idx
|
||||
function EVAL(ast, env, new_ast, ret, idx, f, f_idx)
|
||||
{
|
||||
env_addref(env)
|
||||
if (ast !~ /^\(/) {
|
||||
|
||||
switch (env_get(env, "'DEBUG-EVAL")) {
|
||||
case /^!/:
|
||||
case "#nil":
|
||||
case "#false":
|
||||
break
|
||||
default:
|
||||
print "EVAL: " printer_pr_str(ast, 1)
|
||||
}
|
||||
|
||||
switch (ast) {
|
||||
case /^'/: # symbol
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^\[/: # vector
|
||||
ret = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^\{/: # map
|
||||
ret = eval_map(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^[^(]/: # not a list
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
idx = substr(ast, 2)
|
||||
if (types_heap[idx]["len"] == 0) {
|
||||
@ -248,6 +277,12 @@ function EVAL(ast, env, new_ast, ret, idx, f, f_idx)
|
||||
case "'fn*":
|
||||
return EVAL_fn(ast, env)
|
||||
default:
|
||||
f = EVAL(types_addref(types_heap[idx][0]), env)
|
||||
if (f ~ /^!/) {
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return f
|
||||
}
|
||||
new_ast = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
@ -255,7 +290,6 @@ function EVAL(ast, env, new_ast, ret, idx, f, f_idx)
|
||||
return new_ast
|
||||
}
|
||||
idx = substr(new_ast, 2)
|
||||
f = types_heap[idx][0]
|
||||
f_idx = substr(f, 2)
|
||||
switch (f) {
|
||||
case /^\$/:
|
||||
|
@ -10,19 +10,20 @@ function READ(str)
|
||||
}
|
||||
|
||||
function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
# This function has two distinct purposes.
|
||||
# non empty list: a0 a1 .. an -> list: nil (eval a1) .. (eval an)
|
||||
# vector: a0 a1 .. an -> vector: (eval a0) (eval a1) .. (eval an)
|
||||
{
|
||||
switch (ast) {
|
||||
case /^'/:
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
return ret
|
||||
case /^[([]/:
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
new_idx = types_allocate()
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (ast ~ /^\(/) {
|
||||
types_heap[new_idx][0] = "#nil"
|
||||
i = 1
|
||||
} else {
|
||||
i = 0
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
ret = EVAL(types_addref(types_heap[idx][i]), env)
|
||||
if (ret ~ /^!/) {
|
||||
types_heap[new_idx]["len"] = i
|
||||
@ -33,7 +34,10 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
types_heap[new_idx]["len"] = len
|
||||
return substr(ast, 1, 1) new_idx
|
||||
case /^\{/:
|
||||
}
|
||||
|
||||
function eval_map(ast, env, i, idx, new_idx, ret)
|
||||
{
|
||||
idx = substr(ast, 2)
|
||||
new_idx = types_allocate()
|
||||
for (i in types_heap[idx]) {
|
||||
@ -47,9 +51,6 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
}
|
||||
return "{" new_idx
|
||||
default:
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
function EVAL_def(ast, env, idx, sym, ret, len)
|
||||
@ -214,15 +215,43 @@ function EVAL_fn(ast, env, idx, params, params_idx, params_len, i, sym, f_idx
|
||||
return "$" f_idx
|
||||
}
|
||||
|
||||
function EVAL(ast, env, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_body, ret_env)
|
||||
{
|
||||
env_addref(env)
|
||||
for (;;) {
|
||||
if (ast !~ /^\(/) {
|
||||
|
||||
switch (env_get(env, "'DEBUG-EVAL")) {
|
||||
case /^!/:
|
||||
case "#nil":
|
||||
case "#false":
|
||||
break
|
||||
default:
|
||||
print "EVAL: " printer_pr_str(ast, 1)
|
||||
}
|
||||
|
||||
switch (ast) {
|
||||
case /^'/: # symbol
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^\[/: # vector
|
||||
ret = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^\{/: # map
|
||||
ret = eval_map(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^[^(]/: # not a list
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
@ -256,6 +285,12 @@ function EVAL(ast, env, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
case "'fn*":
|
||||
return EVAL_fn(ast, env)
|
||||
default:
|
||||
f = EVAL(types_addref(types_heap[idx][0]), env)
|
||||
if (f ~ /^!/) {
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return f
|
||||
}
|
||||
new_ast = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
@ -263,7 +298,6 @@ function EVAL(ast, env, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
return new_ast
|
||||
}
|
||||
idx = substr(new_ast, 2)
|
||||
f = types_heap[idx][0]
|
||||
f_idx = substr(f, 2)
|
||||
switch (f) {
|
||||
case /^\$/:
|
||||
@ -273,6 +307,7 @@ function EVAL(ast, env, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
return env
|
||||
}
|
||||
types_addref(ast = types_heap[f_idx]["body"])
|
||||
types_release(f)
|
||||
types_release(new_ast)
|
||||
continue
|
||||
case /^&/:
|
||||
@ -281,7 +316,9 @@ function EVAL(ast, env, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
return ret
|
||||
default:
|
||||
types_release(new_ast)
|
||||
return "!\"First element of list must be function, supplied " types_typename(f) "."
|
||||
ret = "!\"First element of list must be function, supplied " types_typename(f) "."
|
||||
types_release(f)
|
||||
return ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,19 +10,20 @@ function READ(str)
|
||||
}
|
||||
|
||||
function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
# This function has two distinct purposes.
|
||||
# non empty list: a0 a1 .. an -> list: nil (eval a1) .. (eval an)
|
||||
# vector: a0 a1 .. an -> vector: (eval a0) (eval a1) .. (eval an)
|
||||
{
|
||||
switch (ast) {
|
||||
case /^'/:
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
return ret
|
||||
case /^[([]/:
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
new_idx = types_allocate()
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (ast ~ /^\(/) {
|
||||
types_heap[new_idx][0] = "#nil"
|
||||
i = 1
|
||||
} else {
|
||||
i = 0
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
ret = EVAL(types_addref(types_heap[idx][i]), env)
|
||||
if (ret ~ /^!/) {
|
||||
types_heap[new_idx]["len"] = i
|
||||
@ -33,7 +34,10 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
types_heap[new_idx]["len"] = len
|
||||
return substr(ast, 1, 1) new_idx
|
||||
case /^\{/:
|
||||
}
|
||||
|
||||
function eval_map(ast, env, i, idx, new_idx, ret)
|
||||
{
|
||||
idx = substr(ast, 2)
|
||||
new_idx = types_allocate()
|
||||
for (i in types_heap[idx]) {
|
||||
@ -47,9 +51,6 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
}
|
||||
return "{" new_idx
|
||||
default:
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
function EVAL_def(ast, env, idx, sym, ret, len)
|
||||
@ -214,15 +215,43 @@ function EVAL_fn(ast, env, idx, params, params_idx, params_len, i, sym, f_idx
|
||||
return "$" f_idx
|
||||
}
|
||||
|
||||
function EVAL(ast, env, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_body, ret_env)
|
||||
{
|
||||
env_addref(env)
|
||||
for (;;) {
|
||||
if (ast !~ /^\(/) {
|
||||
|
||||
switch (env_get(env, "'DEBUG-EVAL")) {
|
||||
case /^!/:
|
||||
case "#nil":
|
||||
case "#false":
|
||||
break
|
||||
default:
|
||||
print "EVAL: " printer_pr_str(ast, 1)
|
||||
}
|
||||
|
||||
switch (ast) {
|
||||
case /^'/: # symbol
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^\[/: # vector
|
||||
ret = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^\{/: # map
|
||||
ret = eval_map(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^[^(]/: # not a list
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
@ -256,6 +285,12 @@ function EVAL(ast, env, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
case "'fn*":
|
||||
return EVAL_fn(ast, env)
|
||||
default:
|
||||
f = EVAL(types_addref(types_heap[idx][0]), env)
|
||||
if (f ~ /^!/) {
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return f
|
||||
}
|
||||
new_ast = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
@ -263,7 +298,6 @@ function EVAL(ast, env, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
return new_ast
|
||||
}
|
||||
idx = substr(new_ast, 2)
|
||||
f = types_heap[idx][0]
|
||||
f_idx = substr(f, 2)
|
||||
switch (f) {
|
||||
case /^\$/:
|
||||
@ -273,6 +307,7 @@ function EVAL(ast, env, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
return env
|
||||
}
|
||||
types_addref(ast = types_heap[f_idx]["body"])
|
||||
types_release(f)
|
||||
types_release(new_ast)
|
||||
continue
|
||||
case /^&/:
|
||||
@ -281,7 +316,9 @@ function EVAL(ast, env, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
return ret
|
||||
default:
|
||||
types_release(new_ast)
|
||||
return "!\"First element of list must be function, supplied " types_typename(f) "."
|
||||
ret = "!\"First element of list must be function, supplied " types_typename(f) "."
|
||||
types_release(f)
|
||||
return ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,19 +89,20 @@ function quasiquote(ast, new_idx, ret, ast_idx, elt_i, elt, previous)
|
||||
}
|
||||
|
||||
function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
# This function has two distinct purposes.
|
||||
# non empty list: a0 a1 .. an -> list: nil (eval a1) .. (eval an)
|
||||
# vector: a0 a1 .. an -> vector: (eval a0) (eval a1) .. (eval an)
|
||||
{
|
||||
switch (ast) {
|
||||
case /^'/:
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
return ret
|
||||
case /^[([]/:
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
new_idx = types_allocate()
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (ast ~ /^\(/) {
|
||||
types_heap[new_idx][0] = "#nil"
|
||||
i = 1
|
||||
} else {
|
||||
i = 0
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
ret = EVAL(types_addref(types_heap[idx][i]), env)
|
||||
if (ret ~ /^!/) {
|
||||
types_heap[new_idx]["len"] = i
|
||||
@ -112,7 +113,10 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
types_heap[new_idx]["len"] = len
|
||||
return substr(ast, 1, 1) new_idx
|
||||
case /^\{/:
|
||||
}
|
||||
|
||||
function eval_map(ast, env, i, idx, new_idx, ret)
|
||||
{
|
||||
idx = substr(ast, 2)
|
||||
new_idx = types_allocate()
|
||||
for (i in types_heap[idx]) {
|
||||
@ -126,9 +130,6 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
}
|
||||
return "{" new_idx
|
||||
default:
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
function EVAL_def(ast, env, idx, sym, ret, len)
|
||||
@ -293,15 +294,43 @@ function EVAL_fn(ast, env, idx, params, params_idx, params_len, i, sym, f_idx
|
||||
return "$" f_idx
|
||||
}
|
||||
|
||||
function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_body, ret_env)
|
||||
{
|
||||
env_addref(env)
|
||||
for (;;) {
|
||||
if (ast !~ /^\(/) {
|
||||
|
||||
switch (env_get(env, "'DEBUG-EVAL")) {
|
||||
case /^!/:
|
||||
case "#nil":
|
||||
case "#false":
|
||||
break
|
||||
default:
|
||||
print "EVAL: " printer_pr_str(ast, 1)
|
||||
}
|
||||
|
||||
switch (ast) {
|
||||
case /^'/: # symbol
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^\[/: # vector
|
||||
ret = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^\{/: # map
|
||||
ret = eval_map(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^[^(]/: # not a list
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
@ -329,15 +358,6 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return body
|
||||
case "'quasiquoteexpand":
|
||||
env_release(env)
|
||||
if (len != 2) {
|
||||
types_release(ast)
|
||||
return "!\"Invalid argument length for 'quasiquoteexpand'. Expects exactly 1 argument, supplied " (len - 1) "."
|
||||
}
|
||||
types_addref(body = types_heap[idx][1])
|
||||
types_release(ast)
|
||||
return quasiquote(body)
|
||||
case "'quasiquote":
|
||||
if (len != 2) {
|
||||
types_release(ast)
|
||||
@ -368,6 +388,12 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
case "'fn*":
|
||||
return EVAL_fn(ast, env)
|
||||
default:
|
||||
f = EVAL(types_addref(types_heap[idx][0]), env)
|
||||
if (f ~ /^!/) {
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return f
|
||||
}
|
||||
new_ast = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
@ -375,7 +401,6 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
return new_ast
|
||||
}
|
||||
idx = substr(new_ast, 2)
|
||||
f = types_heap[idx][0]
|
||||
f_idx = substr(f, 2)
|
||||
switch (f) {
|
||||
case /^\$/:
|
||||
@ -385,6 +410,7 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
return env
|
||||
}
|
||||
types_addref(ast = types_heap[f_idx]["body"])
|
||||
types_release(f)
|
||||
types_release(new_ast)
|
||||
continue
|
||||
case /^&/:
|
||||
@ -393,7 +419,9 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
return ret
|
||||
default:
|
||||
types_release(new_ast)
|
||||
return "!\"First element of list must be function, supplied " types_typename(f) "."
|
||||
ret = "!\"First element of list must be function, supplied " types_typename(f) "."
|
||||
types_release(f)
|
||||
return ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,52 +88,21 @@ function quasiquote(ast, new_idx, ret, ast_idx, elt_i, elt, previous)
|
||||
return "(" new_idx
|
||||
}
|
||||
|
||||
function is_macro_call(ast, env, idx, len, sym, f)
|
||||
{
|
||||
if (ast !~ /^\(/) return 0
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
if (len == 0) return 0
|
||||
sym = types_heap[idx][0]
|
||||
if (sym !~ /^'/) return 0
|
||||
f = env_get(env, sym)
|
||||
return f ~ /^\$/ && types_heap[substr(f, 2)]["is_macro"]
|
||||
}
|
||||
|
||||
function macroexpand(ast, env, idx, f_idx, new_env)
|
||||
{
|
||||
while (is_macro_call(ast, env)) {
|
||||
idx = substr(ast, 2)
|
||||
f_idx = substr(env_get(env, types_heap[idx][0]), 2)
|
||||
new_env = env_new(types_heap[f_idx]["env"], types_heap[f_idx]["params"], idx)
|
||||
types_release(ast)
|
||||
if (new_env ~ /^!/) {
|
||||
return new_env
|
||||
}
|
||||
types_addref(ast = types_heap[f_idx]["body"])
|
||||
ast = EVAL(ast, new_env)
|
||||
env_release(new_env)
|
||||
if (ast ~ /^!/) {
|
||||
return ast
|
||||
}
|
||||
}
|
||||
return ast
|
||||
}
|
||||
|
||||
function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
# This function has two distinct purposes.
|
||||
# non empty list: a0 a1 .. an -> list: nil (eval a1) .. (eval an)
|
||||
# vector: a0 a1 .. an -> vector: (eval a0) (eval a1) .. (eval an)
|
||||
{
|
||||
switch (ast) {
|
||||
case /^'/:
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
return ret
|
||||
case /^[([]/:
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
new_idx = types_allocate()
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (ast ~ /^\(/) {
|
||||
types_heap[new_idx][0] = "#nil"
|
||||
i = 1
|
||||
} else {
|
||||
i = 0
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
ret = EVAL(types_addref(types_heap[idx][i]), env)
|
||||
if (ret ~ /^!/) {
|
||||
types_heap[new_idx]["len"] = i
|
||||
@ -144,7 +113,10 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
types_heap[new_idx]["len"] = len
|
||||
return substr(ast, 1, 1) new_idx
|
||||
case /^\{/:
|
||||
}
|
||||
|
||||
function eval_map(ast, env, i, idx, new_idx, ret)
|
||||
{
|
||||
idx = substr(ast, 2)
|
||||
new_idx = types_allocate()
|
||||
for (i in types_heap[idx]) {
|
||||
@ -158,9 +130,6 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
}
|
||||
return "{" new_idx
|
||||
default:
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
function EVAL_def(ast, env, idx, sym, ret, len)
|
||||
@ -368,33 +337,50 @@ function EVAL_fn(ast, env, idx, params, params_idx, params_len, i, sym, f_idx
|
||||
return "$" f_idx
|
||||
}
|
||||
|
||||
function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_body, ret_env)
|
||||
{
|
||||
env_addref(env)
|
||||
for (;;) {
|
||||
if (ast !~ /^\(/) {
|
||||
|
||||
switch (env_get(env, "'DEBUG-EVAL")) {
|
||||
case /^!/:
|
||||
case "#nil":
|
||||
case "#false":
|
||||
break
|
||||
default:
|
||||
print "EVAL: " printer_pr_str(ast, 1)
|
||||
}
|
||||
|
||||
switch (ast) {
|
||||
case /^'/: # symbol
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^\[/: # vector
|
||||
ret = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
}
|
||||
if (types_heap[substr(ast, 2)]["len"] == 0) {
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
ast = macroexpand(ast, env)
|
||||
if (ast ~ /^!/) {
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
if (ast !~ /^\(/) {
|
||||
ret = eval_ast(ast, env)
|
||||
case /^\{/: # map
|
||||
ret = eval_map(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^[^(]/: # not a list
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
if (len == 0) {
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
switch (types_heap[idx][0]) {
|
||||
case "'def!":
|
||||
return EVAL_def(ast, env)
|
||||
@ -415,15 +401,6 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return body
|
||||
case "'quasiquoteexpand":
|
||||
env_release(env)
|
||||
if (len != 2) {
|
||||
types_release(ast)
|
||||
return "!\"Invalid argument length for 'quasiquoteexpand'. Expects exactly 1 argument, supplied " (len - 1) "."
|
||||
}
|
||||
types_addref(body = types_heap[idx][1])
|
||||
types_release(ast)
|
||||
return quasiquote(body)
|
||||
case "'quasiquote":
|
||||
if (len != 2) {
|
||||
types_release(ast)
|
||||
@ -440,17 +417,6 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
continue
|
||||
case "'defmacro!":
|
||||
return EVAL_defmacro(ast, env)
|
||||
case "'macroexpand":
|
||||
if (len != 2) {
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return "!\"Invalid argument length for 'macroexpand'. Expects exactly 1 argument, supplied " (len - 1) "."
|
||||
}
|
||||
types_addref(body = types_heap[idx][1])
|
||||
types_release(ast)
|
||||
ret = macroexpand(body, env)
|
||||
env_release(env)
|
||||
return ret
|
||||
case "'do":
|
||||
ast = EVAL_do(ast, env)
|
||||
if (ast ~ /^!/) {
|
||||
@ -467,6 +433,29 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
case "'fn*":
|
||||
return EVAL_fn(ast, env)
|
||||
default:
|
||||
f = EVAL(types_addref(types_heap[idx][0]), env)
|
||||
if (f ~ /^!/) {
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return f
|
||||
}
|
||||
f_idx = substr(f, 2)
|
||||
switch (f) {
|
||||
case /^\$/:
|
||||
if (types_heap[f_idx]["is_macro"]) {
|
||||
idx = substr(ast, 2)
|
||||
ret = env_new(types_heap[f_idx]["env"], types_heap[f_idx]["params"], idx)
|
||||
types_release(ast)
|
||||
if (ret ~ /^!/) {
|
||||
types_release(f)
|
||||
types_release(env)
|
||||
return ret
|
||||
}
|
||||
ast = EVAL(types_addref(types_heap[f_idx]["body"]), ret)
|
||||
types_release(ret)
|
||||
types_release(f)
|
||||
continue
|
||||
}
|
||||
new_ast = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
@ -474,25 +463,31 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_env)
|
||||
return new_ast
|
||||
}
|
||||
idx = substr(new_ast, 2)
|
||||
f = types_heap[idx][0]
|
||||
f_idx = substr(f, 2)
|
||||
switch (f) {
|
||||
case /^\$/:
|
||||
env = env_new(types_heap[f_idx]["env"], types_heap[f_idx]["params"], idx)
|
||||
if (env ~ /^!/) {
|
||||
types_release(new_ast)
|
||||
return env
|
||||
}
|
||||
types_addref(ast = types_heap[f_idx]["body"])
|
||||
types_release(f)
|
||||
types_release(new_ast)
|
||||
continue
|
||||
case /^&/:
|
||||
new_ast = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
if (new_ast ~ /^!/) {
|
||||
return new_ast
|
||||
}
|
||||
idx = substr(new_ast, 2)
|
||||
ret = @f_idx(idx)
|
||||
types_release(new_ast)
|
||||
return ret
|
||||
default:
|
||||
types_release(new_ast)
|
||||
return "!\"First element of list must be function, supplied " types_typename(f) "."
|
||||
ret = "!\"First element of list must be function, supplied " types_typename(f) "."
|
||||
types_release(f)
|
||||
return ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,52 +88,21 @@ function quasiquote(ast, new_idx, ret, ast_idx, elt_i, elt, previous)
|
||||
return "(" new_idx
|
||||
}
|
||||
|
||||
function is_macro_call(ast, env, idx, len, sym, f)
|
||||
{
|
||||
if (ast !~ /^\(/) return 0
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
if (len == 0) return 0
|
||||
sym = types_heap[idx][0]
|
||||
if (sym !~ /^'/) return 0
|
||||
f = env_get(env, sym)
|
||||
return f ~ /^\$/ && types_heap[substr(f, 2)]["is_macro"]
|
||||
}
|
||||
|
||||
function macroexpand(ast, env, idx, f_idx, new_env)
|
||||
{
|
||||
while (is_macro_call(ast, env)) {
|
||||
idx = substr(ast, 2)
|
||||
f_idx = substr(env_get(env, types_heap[idx][0]), 2)
|
||||
new_env = env_new(types_heap[f_idx]["env"], types_heap[f_idx]["params"], idx)
|
||||
types_release(ast)
|
||||
if (new_env ~ /^!/) {
|
||||
return new_env
|
||||
}
|
||||
types_addref(ast = types_heap[f_idx]["body"])
|
||||
ast = EVAL(ast, new_env)
|
||||
env_release(new_env)
|
||||
if (ast ~ /^!/) {
|
||||
return ast
|
||||
}
|
||||
}
|
||||
return ast
|
||||
}
|
||||
|
||||
function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
# This function has two distinct purposes.
|
||||
# non empty list: a0 a1 .. an -> list: nil (eval a1) .. (eval an)
|
||||
# vector: a0 a1 .. an -> vector: (eval a0) (eval a1) .. (eval an)
|
||||
{
|
||||
switch (ast) {
|
||||
case /^'/:
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
return ret
|
||||
case /^[([]/:
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
new_idx = types_allocate()
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (ast ~ /^\(/) {
|
||||
types_heap[new_idx][0] = "#nil"
|
||||
i = 1
|
||||
} else {
|
||||
i = 0
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
ret = EVAL(types_addref(types_heap[idx][i]), env)
|
||||
if (ret ~ /^!/) {
|
||||
types_heap[new_idx]["len"] = i
|
||||
@ -144,7 +113,10 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
types_heap[new_idx]["len"] = len
|
||||
return substr(ast, 1, 1) new_idx
|
||||
case /^\{/:
|
||||
}
|
||||
|
||||
function eval_map(ast, env, i, idx, new_idx, ret)
|
||||
{
|
||||
idx = substr(ast, 2)
|
||||
new_idx = types_allocate()
|
||||
for (i in types_heap[idx]) {
|
||||
@ -158,9 +130,6 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
}
|
||||
return "{" new_idx
|
||||
default:
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
function EVAL_def(ast, env, idx, sym, ret, len)
|
||||
@ -426,29 +395,46 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_body, ret
|
||||
{
|
||||
env_addref(env)
|
||||
for (;;) {
|
||||
if (ast !~ /^\(/) {
|
||||
|
||||
switch (env_get(env, "'DEBUG-EVAL")) {
|
||||
case /^!/:
|
||||
case "#nil":
|
||||
case "#false":
|
||||
break
|
||||
default:
|
||||
print "EVAL: " printer_pr_str(ast, 1)
|
||||
}
|
||||
|
||||
switch (ast) {
|
||||
case /^'/: # symbol
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^\[/: # vector
|
||||
ret = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
}
|
||||
if (types_heap[substr(ast, 2)]["len"] == 0) {
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
ast = macroexpand(ast, env)
|
||||
if (ast ~ /^!/) {
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
if (ast !~ /^\(/) {
|
||||
ret = eval_ast(ast, env)
|
||||
case /^\{/: # map
|
||||
ret = eval_map(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^[^(]/: # not a list
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
if (len == 0) {
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
switch (types_heap[idx][0]) {
|
||||
case "'def!":
|
||||
return EVAL_def(ast, env)
|
||||
@ -469,15 +455,6 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_body, ret
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return body
|
||||
case "'quasiquoteexpand":
|
||||
env_release(env)
|
||||
if (len != 2) {
|
||||
types_release(ast)
|
||||
return "!\"Invalid argument length for 'quasiquoteexpand'. Expects exactly 1 argument, supplied " (len - 1) "."
|
||||
}
|
||||
types_addref(body = types_heap[idx][1])
|
||||
types_release(ast)
|
||||
return quasiquote(body)
|
||||
case "'quasiquote":
|
||||
if (len != 2) {
|
||||
types_release(ast)
|
||||
@ -494,17 +471,6 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_body, ret
|
||||
continue
|
||||
case "'defmacro!":
|
||||
return EVAL_defmacro(ast, env)
|
||||
case "'macroexpand":
|
||||
if (len != 2) {
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return "!\"Invalid argument length for 'macroexpand'. Expects exactly 1 argument, supplied " (len - 1) "."
|
||||
}
|
||||
types_addref(body = types_heap[idx][1])
|
||||
types_release(ast)
|
||||
ret = macroexpand(body, env)
|
||||
env_release(env)
|
||||
return ret
|
||||
case "'try*":
|
||||
ret = EVAL_try(ast, env, ret_body, ret_env)
|
||||
if (ret != "") {
|
||||
@ -529,6 +495,29 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_body, ret
|
||||
case "'fn*":
|
||||
return EVAL_fn(ast, env)
|
||||
default:
|
||||
f = EVAL(types_addref(types_heap[idx][0]), env)
|
||||
if (f ~ /^!/) {
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return f
|
||||
}
|
||||
f_idx = substr(f, 2)
|
||||
switch (f) {
|
||||
case /^\$/:
|
||||
if (types_heap[f_idx]["is_macro"]) {
|
||||
idx = substr(ast, 2)
|
||||
ret = env_new(types_heap[f_idx]["env"], types_heap[f_idx]["params"], idx)
|
||||
types_release(ast)
|
||||
if (ret ~ /^!/) {
|
||||
types_release(f)
|
||||
types_release(env)
|
||||
return ret
|
||||
}
|
||||
ast = EVAL(types_addref(types_heap[f_idx]["body"]), ret)
|
||||
types_release(ret)
|
||||
types_release(f)
|
||||
continue
|
||||
}
|
||||
new_ast = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
@ -536,25 +525,31 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_body, ret
|
||||
return new_ast
|
||||
}
|
||||
idx = substr(new_ast, 2)
|
||||
f = types_heap[idx][0]
|
||||
f_idx = substr(f, 2)
|
||||
switch (f) {
|
||||
case /^\$/:
|
||||
env = env_new(types_heap[f_idx]["env"], types_heap[f_idx]["params"], idx)
|
||||
if (env ~ /^!/) {
|
||||
types_release(new_ast)
|
||||
return env
|
||||
}
|
||||
types_addref(ast = types_heap[f_idx]["body"])
|
||||
types_release(f)
|
||||
types_release(new_ast)
|
||||
continue
|
||||
case /^&/:
|
||||
new_ast = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
if (new_ast ~ /^!/) {
|
||||
return new_ast
|
||||
}
|
||||
idx = substr(new_ast, 2)
|
||||
ret = @f_idx(idx)
|
||||
types_release(new_ast)
|
||||
return ret
|
||||
default:
|
||||
types_release(new_ast)
|
||||
return "!\"First element of list must be function, supplied " types_typename(f) "."
|
||||
ret = "!\"First element of list must be function, supplied " types_typename(f) "."
|
||||
types_release(f)
|
||||
return ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,52 +88,21 @@ function quasiquote(ast, new_idx, ret, ast_idx, elt_i, elt, previous)
|
||||
return "(" new_idx
|
||||
}
|
||||
|
||||
function is_macro_call(ast, env, idx, len, sym, f)
|
||||
{
|
||||
if (ast !~ /^\(/) return 0
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
if (len == 0) return 0
|
||||
sym = types_heap[idx][0]
|
||||
if (sym !~ /^'/) return 0
|
||||
f = env_get(env, sym)
|
||||
return f ~ /^\$/ && types_heap[substr(f, 2)]["is_macro"]
|
||||
}
|
||||
|
||||
function macroexpand(ast, env, idx, f_idx, new_env)
|
||||
{
|
||||
while (is_macro_call(ast, env)) {
|
||||
idx = substr(ast, 2)
|
||||
f_idx = substr(env_get(env, types_heap[idx][0]), 2)
|
||||
new_env = env_new(types_heap[f_idx]["env"], types_heap[f_idx]["params"], idx)
|
||||
types_release(ast)
|
||||
if (new_env ~ /^!/) {
|
||||
return new_env
|
||||
}
|
||||
types_addref(ast = types_heap[f_idx]["body"])
|
||||
ast = EVAL(ast, new_env)
|
||||
env_release(new_env)
|
||||
if (ast ~ /^!/) {
|
||||
return ast
|
||||
}
|
||||
}
|
||||
return ast
|
||||
}
|
||||
|
||||
function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
# This function has two distinct purposes.
|
||||
# non empty list: a0 a1 .. an -> list: nil (eval a1) .. (eval an)
|
||||
# vector: a0 a1 .. an -> vector: (eval a0) (eval a1) .. (eval an)
|
||||
{
|
||||
switch (ast) {
|
||||
case /^'/:
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
return ret
|
||||
case /^[([]/:
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
new_idx = types_allocate()
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (ast ~ /^\(/) {
|
||||
types_heap[new_idx][0] = "#nil"
|
||||
i = 1
|
||||
} else {
|
||||
i = 0
|
||||
}
|
||||
for (; i < len; ++i) {
|
||||
ret = EVAL(types_addref(types_heap[idx][i]), env)
|
||||
if (ret ~ /^!/) {
|
||||
types_heap[new_idx]["len"] = i
|
||||
@ -144,7 +113,10 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
types_heap[new_idx]["len"] = len
|
||||
return substr(ast, 1, 1) new_idx
|
||||
case /^\{/:
|
||||
}
|
||||
|
||||
function eval_map(ast, env, i, idx, new_idx, ret)
|
||||
{
|
||||
idx = substr(ast, 2)
|
||||
new_idx = types_allocate()
|
||||
for (i in types_heap[idx]) {
|
||||
@ -158,9 +130,6 @@ function eval_ast(ast, env, i, idx, len, new_idx, ret)
|
||||
}
|
||||
}
|
||||
return "{" new_idx
|
||||
default:
|
||||
return ast
|
||||
}
|
||||
}
|
||||
|
||||
function EVAL_def(ast, env, idx, sym, ret, len)
|
||||
@ -426,29 +395,46 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_body, ret
|
||||
{
|
||||
env_addref(env)
|
||||
for (;;) {
|
||||
if (ast !~ /^\(/) {
|
||||
|
||||
switch (env_get(env, "'DEBUG-EVAL")) {
|
||||
case /^!/:
|
||||
case "#nil":
|
||||
case "#false":
|
||||
break
|
||||
default:
|
||||
print "EVAL: " printer_pr_str(ast, 1)
|
||||
}
|
||||
|
||||
switch (ast) {
|
||||
case /^'/: # symbol
|
||||
ret = env_get(env, ast)
|
||||
if (ret !~ /^!/) {
|
||||
types_addref(ret)
|
||||
}
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^\[/: # vector
|
||||
ret = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
}
|
||||
if (types_heap[substr(ast, 2)]["len"] == 0) {
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
ast = macroexpand(ast, env)
|
||||
if (ast ~ /^!/) {
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
if (ast !~ /^\(/) {
|
||||
ret = eval_ast(ast, env)
|
||||
case /^\{/: # map
|
||||
ret = eval_map(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ret
|
||||
case /^[^(]/: # not a list
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
idx = substr(ast, 2)
|
||||
len = types_heap[idx]["len"]
|
||||
if (len == 0) {
|
||||
env_release(env)
|
||||
return ast
|
||||
}
|
||||
switch (types_heap[idx][0]) {
|
||||
case "'def!":
|
||||
return EVAL_def(ast, env)
|
||||
@ -469,15 +455,6 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_body, ret
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return body
|
||||
case "'quasiquoteexpand":
|
||||
env_release(env)
|
||||
if (len != 2) {
|
||||
types_release(ast)
|
||||
return "!\"Invalid argument length for 'quasiquoteexpand'. Expects exactly 1 argument, supplied " (len - 1) "."
|
||||
}
|
||||
types_addref(body = types_heap[idx][1])
|
||||
types_release(ast)
|
||||
return quasiquote(body)
|
||||
case "'quasiquote":
|
||||
if (len != 2) {
|
||||
types_release(ast)
|
||||
@ -494,17 +471,6 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_body, ret
|
||||
continue
|
||||
case "'defmacro!":
|
||||
return EVAL_defmacro(ast, env)
|
||||
case "'macroexpand":
|
||||
if (len != 2) {
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return "!\"Invalid argument length for 'macroexpand'. Expects exactly 1 argument, supplied " (len - 1) "."
|
||||
}
|
||||
types_addref(body = types_heap[idx][1])
|
||||
types_release(ast)
|
||||
ret = macroexpand(body, env)
|
||||
env_release(env)
|
||||
return ret
|
||||
case "'try*":
|
||||
ret = EVAL_try(ast, env, ret_body, ret_env)
|
||||
if (ret != "") {
|
||||
@ -529,6 +495,29 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_body, ret
|
||||
case "'fn*":
|
||||
return EVAL_fn(ast, env)
|
||||
default:
|
||||
f = EVAL(types_addref(types_heap[idx][0]), env)
|
||||
if (f ~ /^!/) {
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
return f
|
||||
}
|
||||
f_idx = substr(f, 2)
|
||||
switch (f) {
|
||||
case /^\$/:
|
||||
if (types_heap[f_idx]["is_macro"]) {
|
||||
idx = substr(ast, 2)
|
||||
ret = env_new(types_heap[f_idx]["env"], types_heap[f_idx]["params"], idx)
|
||||
types_release(ast)
|
||||
if (ret ~ /^!/) {
|
||||
types_release(f)
|
||||
types_release(env)
|
||||
return ret
|
||||
}
|
||||
ast = EVAL(types_addref(types_heap[f_idx]["body"]), ret)
|
||||
types_release(ret)
|
||||
types_release(f)
|
||||
continue
|
||||
}
|
||||
new_ast = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
@ -536,27 +525,34 @@ function EVAL(ast, env, body, new_ast, ret, idx, len, f, f_idx, ret_body, ret
|
||||
return new_ast
|
||||
}
|
||||
idx = substr(new_ast, 2)
|
||||
f = types_heap[idx][0]
|
||||
f_idx = substr(f, 2)
|
||||
switch (f) {
|
||||
case /^\$/:
|
||||
env = env_new(types_heap[f_idx]["env"], types_heap[f_idx]["params"], idx)
|
||||
if (env ~ /^!/) {
|
||||
types_release(new_ast)
|
||||
return env
|
||||
}
|
||||
types_addref(ast = types_heap[f_idx]["body"])
|
||||
types_release(f)
|
||||
types_release(new_ast)
|
||||
continue
|
||||
case /^%/:
|
||||
f_idx = types_heap[f_idx]["func"]
|
||||
types_release(f)
|
||||
case /^&/:
|
||||
new_ast = eval_ast(ast, env)
|
||||
types_release(ast)
|
||||
env_release(env)
|
||||
if (new_ast ~ /^!/) {
|
||||
return new_ast
|
||||
}
|
||||
idx = substr(new_ast, 2)
|
||||
ret = @f_idx(idx)
|
||||
types_release(new_ast)
|
||||
return ret
|
||||
default:
|
||||
types_release(new_ast)
|
||||
return "!\"First element of list must be function, supplied " types_typename(f) "."
|
||||
ret = "!\"First element of list must be function, supplied " types_typename(f) "."
|
||||
types_release(f)
|
||||
return ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM ubuntu:vivid
|
||||
FROM ubuntu:xenial
|
||||
MAINTAINER Joel Martin <github@martintribe.org>
|
||||
|
||||
##########################################################
|
||||
|
@ -10,7 +10,7 @@ READ () {
|
||||
}
|
||||
|
||||
# eval
|
||||
EVAL_AST () {
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
#_pr_str "${ast}"; echo "EVAL_AST '${ast}:${r} / ${env}'"
|
||||
_obj_type "${ast}"; local ot="${r}"
|
||||
@ -18,11 +18,13 @@ EVAL_AST () {
|
||||
symbol)
|
||||
local val="${ANON["${ast}"]}"
|
||||
eval r="\${${env}["${val}"]}"
|
||||
[ "${r}" ] || _error "'${val}' not found" ;;
|
||||
[ "${r}" ] || _error "'${val}' not found"
|
||||
return ;;
|
||||
list)
|
||||
_map_with_type _list EVAL "${ast}" "${env}" ;;
|
||||
;;
|
||||
vector)
|
||||
_map_with_type _vector EVAL "${ast}" "${env}" ;;
|
||||
_map_with_type _vector EVAL "${ast}" "${env}"
|
||||
return ;;
|
||||
hash_map)
|
||||
local res="" key= val="" hm="${ANON["${ast}"]}"
|
||||
_hash_map; local new_hm="${r}"
|
||||
@ -32,27 +34,17 @@ EVAL_AST () {
|
||||
EVAL "${val}" "${env}"
|
||||
_assoc! "${new_hm}" "${key}" "${r}"
|
||||
done
|
||||
r="${new_hm}" ;;
|
||||
r="${new_hm}"
|
||||
return ;;
|
||||
*)
|
||||
r="${ast}" ;;
|
||||
r="${ast}"
|
||||
return ;;
|
||||
esac
|
||||
}
|
||||
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
r=
|
||||
[[ "${__ERROR}" ]] && return 1
|
||||
#_pr_str "${ast}"; echo "EVAL '${r} / ${env}'"
|
||||
_obj_type "${ast}"; local ot="${r}"
|
||||
if [[ "${ot}" != "list" ]]; then
|
||||
EVAL_AST "${ast}" "${env}"
|
||||
return
|
||||
fi
|
||||
|
||||
# apply list
|
||||
_empty? "${ast}" && r="${ast}" && return
|
||||
|
||||
EVAL_AST "${ast}" "${env}"
|
||||
_map_with_type _list EVAL "${ast}" "${env}"
|
||||
[[ "${__ERROR}" ]] && return 1
|
||||
local el="${r}"
|
||||
_first "${el}"; local f="${r}"
|
||||
|
@ -11,18 +11,28 @@ READ () {
|
||||
}
|
||||
|
||||
# eval
|
||||
EVAL_AST () {
|
||||
_symbol DEBUG-EVAL; debug_eval="$r"
|
||||
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
#_pr_str "${ast}"; echo "EVAL_AST '${ast}:${r} / ${env}'"
|
||||
|
||||
ENV_GET "$env" "$debug_eval"
|
||||
if [ -n "$__ERROR" ]; then
|
||||
__ERROR=
|
||||
elif [ "$r" != "$__false" -a "$r" != "$__nil" ]; then
|
||||
_pr_str "$ast" yes; echo "EVAL: $r / $env"
|
||||
fi
|
||||
|
||||
_obj_type "${ast}"; local ot="${r}"
|
||||
case "${ot}" in
|
||||
symbol)
|
||||
ENV_GET "${env}" "${ast}"
|
||||
return ;;
|
||||
list)
|
||||
_map_with_type _list EVAL "${ast}" "${env}" ;;
|
||||
;;
|
||||
vector)
|
||||
_map_with_type _vector EVAL "${ast}" "${env}" ;;
|
||||
_map_with_type _vector EVAL "${ast}" "${env}"
|
||||
return ;;
|
||||
hash_map)
|
||||
local res="" key= val="" hm="${ANON["${ast}"]}"
|
||||
_hash_map; local new_hm="${r}"
|
||||
@ -32,22 +42,12 @@ EVAL_AST () {
|
||||
EVAL "${val}" "${env}"
|
||||
_assoc! "${new_hm}" "${key}" "${r}"
|
||||
done
|
||||
r="${new_hm}" ;;
|
||||
r="${new_hm}"
|
||||
return ;;
|
||||
*)
|
||||
r="${ast}" ;;
|
||||
r="${ast}"
|
||||
return ;;
|
||||
esac
|
||||
}
|
||||
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
r=
|
||||
[[ "${__ERROR}" ]] && return 1
|
||||
#_pr_str "${ast}"; echo "EVAL '${r} / ${env}'"
|
||||
_obj_type "${ast}"; local ot="${r}"
|
||||
if [[ "${ot}" != "list" ]]; then
|
||||
EVAL_AST "${ast}" "${env}"
|
||||
return
|
||||
fi
|
||||
|
||||
# apply list
|
||||
_empty? "${ast}" && r="${ast}" && return
|
||||
@ -71,7 +71,7 @@ EVAL () {
|
||||
done
|
||||
EVAL "${a2}" "${let_env}"
|
||||
return ;;
|
||||
*) EVAL_AST "${ast}" "${env}"
|
||||
*) _map_with_type _list EVAL "${ast}" "${env}"
|
||||
[[ "${__ERROR}" ]] && r= && return 1
|
||||
local el="${r}"
|
||||
_first "${el}"; local f="${r}"
|
||||
|
@ -12,18 +12,28 @@ READ () {
|
||||
}
|
||||
|
||||
# eval
|
||||
EVAL_AST () {
|
||||
_symbol DEBUG-EVAL; debug_eval="$r"
|
||||
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
#_pr_str "${ast}"; echo "EVAL_AST '${ast}:${r} / ${env}'"
|
||||
|
||||
ENV_GET "$env" "$debug_eval"
|
||||
if [ -n "$__ERROR" ]; then
|
||||
__ERROR=
|
||||
elif [ "$r" != "$__false" -a "$r" != "$__nil" ]; then
|
||||
_pr_str "$ast" yes; echo "EVAL: $r / $env"
|
||||
fi
|
||||
|
||||
_obj_type "${ast}"; local ot="${r}"
|
||||
case "${ot}" in
|
||||
symbol)
|
||||
ENV_GET "${env}" "${ast}"
|
||||
return ;;
|
||||
list)
|
||||
_map_with_type _list EVAL "${ast}" "${env}" ;;
|
||||
;;
|
||||
vector)
|
||||
_map_with_type _vector EVAL "${ast}" "${env}" ;;
|
||||
_map_with_type _vector EVAL "${ast}" "${env}"
|
||||
return ;;
|
||||
hash_map)
|
||||
local res="" key= val="" hm="${ANON["${ast}"]}"
|
||||
_hash_map; local new_hm="${r}"
|
||||
@ -33,22 +43,12 @@ EVAL_AST () {
|
||||
EVAL "${val}" "${env}"
|
||||
_assoc! "${new_hm}" "${key}" "${r}"
|
||||
done
|
||||
r="${new_hm}" ;;
|
||||
r="${new_hm}"
|
||||
return ;;
|
||||
*)
|
||||
r="${ast}" ;;
|
||||
r="${ast}"
|
||||
return ;;
|
||||
esac
|
||||
}
|
||||
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
r=
|
||||
[[ "${__ERROR}" ]] && return 1
|
||||
#_pr_str "${ast}"; echo "EVAL '${r} / ${env}'"
|
||||
_obj_type "${ast}"; local ot="${r}"
|
||||
if [[ "${ot}" != "list" ]]; then
|
||||
EVAL_AST "${ast}" "${env}"
|
||||
return
|
||||
fi
|
||||
|
||||
# apply list
|
||||
_empty? "${ast}" && r="${ast}" && return
|
||||
@ -73,7 +73,7 @@ EVAL () {
|
||||
EVAL "${a2}" "${let_env}"
|
||||
return ;;
|
||||
do) _rest "${ast}"
|
||||
EVAL_AST "${r}" "${env}"
|
||||
_map_with_type _list EVAL "${r}" "${env}"
|
||||
[[ "${__ERROR}" ]] && r= && return 1
|
||||
_last "${r}"
|
||||
return ;;
|
||||
@ -95,7 +95,7 @@ EVAL () {
|
||||
fn__STAR__) _function "ENV \"${env}\" \"${a1}\" \"\${@}\"; \
|
||||
EVAL \"${a2}\" \"\${r}\""
|
||||
return ;;
|
||||
*) EVAL_AST "${ast}" "${env}"
|
||||
*) _map_with_type _list EVAL "${ast}" "${env}"
|
||||
[[ "${__ERROR}" ]] && r= && return 1
|
||||
local el="${r}"
|
||||
_first "${el}"; local f="${ANON["${r}"]}"
|
||||
|
@ -12,18 +12,30 @@ READ () {
|
||||
}
|
||||
|
||||
# eval
|
||||
EVAL_AST () {
|
||||
_symbol DEBUG-EVAL; debug_eval="$r"
|
||||
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
#_pr_str "${ast}"; echo "EVAL_AST '${ast}:${r} / ${env}'"
|
||||
while true; do
|
||||
r=
|
||||
|
||||
ENV_GET "$env" "$debug_eval"
|
||||
if [ -n "$__ERROR" ]; then
|
||||
__ERROR=
|
||||
elif [ "$r" != "$__false" -a "$r" != "$__nil" ]; then
|
||||
_pr_str "$ast" yes; echo "EVAL: $r / $env"
|
||||
fi
|
||||
|
||||
_obj_type "${ast}"; local ot="${r}"
|
||||
case "${ot}" in
|
||||
symbol)
|
||||
ENV_GET "${env}" "${ast}"
|
||||
return ;;
|
||||
list)
|
||||
_map_with_type _list EVAL "${ast}" "${env}" ;;
|
||||
;;
|
||||
vector)
|
||||
_map_with_type _vector EVAL "${ast}" "${env}" ;;
|
||||
_map_with_type _vector EVAL "${ast}" "${env}"
|
||||
return ;;
|
||||
hash_map)
|
||||
local res="" key= val="" hm="${ANON["${ast}"]}"
|
||||
_hash_map; local new_hm="${r}"
|
||||
@ -33,23 +45,12 @@ EVAL_AST () {
|
||||
EVAL "${val}" "${env}"
|
||||
_assoc! "${new_hm}" "${key}" "${r}"
|
||||
done
|
||||
r="${new_hm}" ;;
|
||||
r="${new_hm}"
|
||||
return ;;
|
||||
*)
|
||||
r="${ast}" ;;
|
||||
r="${ast}"
|
||||
return ;;
|
||||
esac
|
||||
}
|
||||
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
while true; do
|
||||
r=
|
||||
[[ "${__ERROR}" ]] && return 1
|
||||
#_pr_str "${ast}"; echo "EVAL '${r} / ${env}'"
|
||||
_obj_type "${ast}"; local ot="${r}"
|
||||
if [[ "${ot}" != "list" ]]; then
|
||||
EVAL_AST "${ast}" "${env}"
|
||||
return
|
||||
fi
|
||||
|
||||
# apply list
|
||||
_empty? "${ast}" && r="${ast}" && return
|
||||
@ -77,7 +78,7 @@ EVAL () {
|
||||
;;
|
||||
do) _count "${ast}"
|
||||
_slice "${ast}" 1 $(( ${r} - 2 ))
|
||||
EVAL_AST "${r}" "${env}"
|
||||
_map_with_type _list EVAL "${r}" "${env}"
|
||||
[[ "${__ERROR}" ]] && r= && return 1
|
||||
_last "${ast}"
|
||||
ast="${r}"
|
||||
@ -104,7 +105,7 @@ EVAL () {
|
||||
EVAL \"${a2}\" \"\${r}\"" \
|
||||
"${a2}" "${env}" "${a1}"
|
||||
return ;;
|
||||
*) EVAL_AST "${ast}" "${env}"
|
||||
*) _map_with_type _list EVAL "${ast}" "${env}"
|
||||
[[ "${__ERROR}" ]] && r= && return 1
|
||||
local el="${r}"
|
||||
_first "${el}"; local f="${ANON["${r}"]}"
|
||||
|
@ -12,18 +12,30 @@ READ () {
|
||||
}
|
||||
|
||||
# eval
|
||||
EVAL_AST () {
|
||||
_symbol DEBUG-EVAL; debug_eval="$r"
|
||||
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
#_pr_str "${ast}"; echo "EVAL_AST '${ast}:${r} / ${env}'"
|
||||
while true; do
|
||||
r=
|
||||
|
||||
ENV_GET "$env" "$debug_eval"
|
||||
if [ -n "$__ERROR" ]; then
|
||||
__ERROR=
|
||||
elif [ "$r" != "$__false" -a "$r" != "$__nil" ]; then
|
||||
_pr_str "$ast" yes; echo "EVAL: $r / $env"
|
||||
fi
|
||||
|
||||
_obj_type "${ast}"; local ot="${r}"
|
||||
case "${ot}" in
|
||||
symbol)
|
||||
ENV_GET "${env}" "${ast}"
|
||||
return ;;
|
||||
list)
|
||||
_map_with_type _list EVAL "${ast}" "${env}" ;;
|
||||
;;
|
||||
vector)
|
||||
_map_with_type _vector EVAL "${ast}" "${env}" ;;
|
||||
_map_with_type _vector EVAL "${ast}" "${env}"
|
||||
return ;;
|
||||
hash_map)
|
||||
local res="" key= val="" hm="${ANON["${ast}"]}"
|
||||
_hash_map; local new_hm="${r}"
|
||||
@ -33,23 +45,12 @@ EVAL_AST () {
|
||||
EVAL "${val}" "${env}"
|
||||
_assoc! "${new_hm}" "${key}" "${r}"
|
||||
done
|
||||
r="${new_hm}" ;;
|
||||
r="${new_hm}"
|
||||
return ;;
|
||||
*)
|
||||
r="${ast}" ;;
|
||||
r="${ast}"
|
||||
return ;;
|
||||
esac
|
||||
}
|
||||
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
while true; do
|
||||
r=
|
||||
[[ "${__ERROR}" ]] && return 1
|
||||
#_pr_str "${ast}"; echo "EVAL '${r} / ${env}'"
|
||||
_obj_type "${ast}"; local ot="${r}"
|
||||
if [[ "${ot}" != "list" ]]; then
|
||||
EVAL_AST "${ast}" "${env}"
|
||||
return
|
||||
fi
|
||||
|
||||
# apply list
|
||||
_empty? "${ast}" && r="${ast}" && return
|
||||
@ -77,7 +78,7 @@ EVAL () {
|
||||
;;
|
||||
do) _count "${ast}"
|
||||
_slice "${ast}" 1 $(( ${r} - 2 ))
|
||||
EVAL_AST "${r}" "${env}"
|
||||
_map_with_type _list EVAL "${r}" "${env}"
|
||||
[[ "${__ERROR}" ]] && r= && return 1
|
||||
_last "${ast}"
|
||||
ast="${r}"
|
||||
@ -104,7 +105,7 @@ EVAL () {
|
||||
EVAL \"${a2}\" \"\${r}\"" \
|
||||
"${a2}" "${env}" "${a1}"
|
||||
return ;;
|
||||
*) EVAL_AST "${ast}" "${env}"
|
||||
*) _map_with_type _list EVAL "${ast}" "${env}"
|
||||
[[ "${__ERROR}" ]] && r= && return 1
|
||||
local el="${r}"
|
||||
_first "${el}"; local f="${ANON["${r}"]}"
|
||||
|
@ -55,18 +55,30 @@ qqIter () {
|
||||
fi
|
||||
}
|
||||
|
||||
EVAL_AST () {
|
||||
_symbol DEBUG-EVAL; debug_eval="$r"
|
||||
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
#_pr_str "${ast}"; echo "EVAL_AST '${ast}:${r} / ${env}'"
|
||||
while true; do
|
||||
r=
|
||||
|
||||
ENV_GET "$env" "$debug_eval"
|
||||
if [ -n "$__ERROR" ]; then
|
||||
__ERROR=
|
||||
elif [ "$r" != "$__false" -a "$r" != "$__nil" ]; then
|
||||
_pr_str "$ast" yes; echo "EVAL: $r / $env"
|
||||
fi
|
||||
|
||||
_obj_type "${ast}"; local ot="${r}"
|
||||
case "${ot}" in
|
||||
symbol)
|
||||
ENV_GET "${env}" "${ast}"
|
||||
return ;;
|
||||
list)
|
||||
_map_with_type _list EVAL "${ast}" "${env}" ;;
|
||||
;;
|
||||
vector)
|
||||
_map_with_type _vector EVAL "${ast}" "${env}" ;;
|
||||
_map_with_type _vector EVAL "${ast}" "${env}"
|
||||
return ;;
|
||||
hash_map)
|
||||
local res="" key= val="" hm="${ANON["${ast}"]}"
|
||||
_hash_map; local new_hm="${r}"
|
||||
@ -76,22 +88,12 @@ EVAL_AST () {
|
||||
EVAL "${val}" "${env}"
|
||||
_assoc! "${new_hm}" "${key}" "${r}"
|
||||
done
|
||||
r="${new_hm}" ;;
|
||||
r="${new_hm}"
|
||||
return ;;
|
||||
*)
|
||||
r="${ast}" ;;
|
||||
r="${ast}"
|
||||
return ;;
|
||||
esac
|
||||
}
|
||||
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
while true; do
|
||||
r=
|
||||
[[ "${__ERROR}" ]] && return 1
|
||||
#_pr_str "${ast}"; echo "EVAL '${r} / ${env}'"
|
||||
if ! _list? "${ast}"; then
|
||||
EVAL_AST "${ast}" "${env}"
|
||||
return
|
||||
fi
|
||||
|
||||
# apply list
|
||||
_empty? "${ast}" && r="${ast}" && return
|
||||
@ -120,9 +122,6 @@ EVAL () {
|
||||
quote)
|
||||
r="${a1}"
|
||||
return ;;
|
||||
quasiquoteexpand)
|
||||
QUASIQUOTE "${a1}"
|
||||
return ;;
|
||||
quasiquote)
|
||||
QUASIQUOTE "${a1}"
|
||||
ast="${r}"
|
||||
@ -130,7 +129,7 @@ EVAL () {
|
||||
;;
|
||||
do) _count "${ast}"
|
||||
_slice "${ast}" 1 $(( ${r} - 2 ))
|
||||
EVAL_AST "${r}" "${env}"
|
||||
_map_with_type _list EVAL "${r}" "${env}"
|
||||
[[ "${__ERROR}" ]] && r= && return 1
|
||||
_last "${ast}"
|
||||
ast="${r}"
|
||||
@ -157,7 +156,7 @@ EVAL () {
|
||||
EVAL \"${a2}\" \"\${r}\"" \
|
||||
"${a2}" "${env}" "${a1}"
|
||||
return ;;
|
||||
*) EVAL_AST "${ast}" "${env}"
|
||||
*) _map_with_type _list EVAL "${ast}" "${env}"
|
||||
[[ "${__ERROR}" ]] && r= && return 1
|
||||
local el="${r}"
|
||||
_first "${el}"; local f="${ANON["${r}"]}"
|
||||
|
@ -55,45 +55,30 @@ qqIter () {
|
||||
fi
|
||||
}
|
||||
|
||||
IS_MACRO_CALL () {
|
||||
if ! _list? "${1}"; then return 1; fi
|
||||
_nth "${1}" 0; local a0="${r}"
|
||||
if _symbol? "${a0}"; then
|
||||
ENV_FIND "${2}" "${a0}"
|
||||
if [[ "${r}" ]]; then
|
||||
ENV_GET "${2}" "${a0}"
|
||||
[ "${ANON["${r}_ismacro_"]}" ]
|
||||
return $?
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
_symbol DEBUG-EVAL; debug_eval="$r"
|
||||
|
||||
MACROEXPAND () {
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
while IS_MACRO_CALL "${ast}" "${env}"; do
|
||||
_nth "${ast}" 0; local a0="${r}"
|
||||
ENV_GET "${env}" "${a0}"; local mac="${ANON["${r}"]}"
|
||||
_rest "${ast}"
|
||||
${mac%%@*} ${ANON["${r}"]}
|
||||
ast="${r}"
|
||||
done
|
||||
r="${ast}"
|
||||
}
|
||||
while true; do
|
||||
r=
|
||||
|
||||
ENV_GET "$env" "$debug_eval"
|
||||
if [ -n "$__ERROR" ]; then
|
||||
__ERROR=
|
||||
elif [ "$r" != "$__false" -a "$r" != "$__nil" ]; then
|
||||
_pr_str "$ast" yes; echo "EVAL: $r / $env"
|
||||
fi
|
||||
|
||||
EVAL_AST () {
|
||||
local ast="${1}" env="${2}"
|
||||
#_pr_str "${ast}"; echo "EVAL_AST '${ast}:${r} / ${env}'"
|
||||
_obj_type "${ast}"; local ot="${r}"
|
||||
case "${ot}" in
|
||||
symbol)
|
||||
ENV_GET "${env}" "${ast}"
|
||||
return ;;
|
||||
list)
|
||||
_map_with_type _list EVAL "${ast}" "${env}" ;;
|
||||
;;
|
||||
vector)
|
||||
_map_with_type _vector EVAL "${ast}" "${env}" ;;
|
||||
_map_with_type _vector EVAL "${ast}" "${env}"
|
||||
return ;;
|
||||
hash_map)
|
||||
local res="" key= val="" hm="${ANON["${ast}"]}"
|
||||
_hash_map; local new_hm="${r}"
|
||||
@ -103,30 +88,14 @@ EVAL_AST () {
|
||||
EVAL "${val}" "${env}"
|
||||
_assoc! "${new_hm}" "${key}" "${r}"
|
||||
done
|
||||
r="${new_hm}" ;;
|
||||
r="${new_hm}"
|
||||
return ;;
|
||||
*)
|
||||
r="${ast}" ;;
|
||||
r="${ast}"
|
||||
return ;;
|
||||
esac
|
||||
}
|
||||
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
while true; do
|
||||
r=
|
||||
[[ "${__ERROR}" ]] && return 1
|
||||
#_pr_str "${ast}"; echo "EVAL '${r} / ${env}'"
|
||||
if ! _list? "${ast}"; then
|
||||
EVAL_AST "${ast}" "${env}"
|
||||
return
|
||||
fi
|
||||
|
||||
# apply list
|
||||
MACROEXPAND "${ast}" "${env}"
|
||||
ast="${r}"
|
||||
if ! _list? "${ast}"; then
|
||||
EVAL_AST "${ast}" "${env}"
|
||||
return
|
||||
fi
|
||||
_empty? "${ast}" && r="${ast}" && return
|
||||
|
||||
_nth "${ast}" 0; local a0="${r}"
|
||||
@ -153,9 +122,6 @@ EVAL () {
|
||||
quote)
|
||||
r="${a1}"
|
||||
return ;;
|
||||
quasiquoteexpand)
|
||||
QUASIQUOTE "${a1}"
|
||||
return ;;
|
||||
quasiquote)
|
||||
QUASIQUOTE "${a1}"
|
||||
ast="${r}"
|
||||
@ -170,12 +136,9 @@ EVAL () {
|
||||
ANON["${r}_ismacro_"]="yes"
|
||||
ENV_SET "${env}" "${a1}" "${r}"
|
||||
return ;;
|
||||
macroexpand)
|
||||
MACROEXPAND "${a1}" "${env}"
|
||||
return ;;
|
||||
do) _count "${ast}"
|
||||
_slice "${ast}" 1 $(( ${r} - 2 ))
|
||||
EVAL_AST "${r}" "${env}"
|
||||
_map_with_type _list EVAL "${r}" "${env}"
|
||||
[[ "${__ERROR}" ]] && r= && return 1
|
||||
_last "${ast}"
|
||||
ast="${r}"
|
||||
@ -202,11 +165,27 @@ EVAL () {
|
||||
EVAL \"${a2}\" \"\${r}\"" \
|
||||
"${a2}" "${env}" "${a1}"
|
||||
return ;;
|
||||
*) EVAL_AST "${ast}" "${env}"
|
||||
*) EVAL "${a0}" "${env}"
|
||||
[[ "${__ERROR}" ]] && return 1
|
||||
local f="${r}"
|
||||
|
||||
_rest "${ast}"
|
||||
# Should cause no error as ast is not empty.
|
||||
local args="${r}"
|
||||
|
||||
if [ "${ANON["${f}_ismacro_"]}" ]; then
|
||||
f="${ANON["${f}"]}"
|
||||
${f%%@*} ${ANON["${args}"]}
|
||||
ast="${r}"
|
||||
continue
|
||||
fi
|
||||
|
||||
f="${ANON["${f}"]}"
|
||||
|
||||
_map_with_type _list EVAL "${args}" "${env}"
|
||||
[[ "${__ERROR}" ]] && r= && return 1
|
||||
local el="${r}"
|
||||
_first "${el}"; local f="${ANON["${r}"]}"
|
||||
_rest "${el}"; local args="${ANON["${r}"]}"
|
||||
args="${ANON["${r}"]}"
|
||||
|
||||
#echo "invoke: [${f}] ${args}"
|
||||
if [[ "${f//@/ }" != "${f}" ]]; then
|
||||
set -- ${f//@/ }
|
||||
|
@ -55,45 +55,30 @@ qqIter () {
|
||||
fi
|
||||
}
|
||||
|
||||
IS_MACRO_CALL () {
|
||||
if ! _list? "${1}"; then return 1; fi
|
||||
_nth "${1}" 0; local a0="${r}"
|
||||
if _symbol? "${a0}"; then
|
||||
ENV_FIND "${2}" "${a0}"
|
||||
if [[ "${r}" ]]; then
|
||||
ENV_GET "${2}" "${a0}"
|
||||
[ "${ANON["${r}_ismacro_"]}" ]
|
||||
return $?
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
_symbol DEBUG-EVAL; debug_eval="$r"
|
||||
|
||||
MACROEXPAND () {
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
while IS_MACRO_CALL "${ast}" "${env}"; do
|
||||
_nth "${ast}" 0; local a0="${r}"
|
||||
ENV_GET "${env}" "${a0}"; local mac="${ANON["${r}"]}"
|
||||
_rest "${ast}"
|
||||
${mac%%@*} ${ANON["${r}"]}
|
||||
ast="${r}"
|
||||
done
|
||||
r="${ast}"
|
||||
}
|
||||
while true; do
|
||||
r=
|
||||
|
||||
ENV_GET "$env" "$debug_eval"
|
||||
if [ -n "$__ERROR" ]; then
|
||||
__ERROR=
|
||||
elif [ "$r" != "$__false" -a "$r" != "$__nil" ]; then
|
||||
_pr_str "$ast" yes; echo "EVAL: $r / $env"
|
||||
fi
|
||||
|
||||
EVAL_AST () {
|
||||
local ast="${1}" env="${2}"
|
||||
#_pr_str "${ast}"; echo "EVAL_AST '${ast}:${r} / ${env}'"
|
||||
_obj_type "${ast}"; local ot="${r}"
|
||||
case "${ot}" in
|
||||
symbol)
|
||||
ENV_GET "${env}" "${ast}"
|
||||
return ;;
|
||||
list)
|
||||
_map_with_type _list EVAL "${ast}" "${env}" ;;
|
||||
;;
|
||||
vector)
|
||||
_map_with_type _vector EVAL "${ast}" "${env}" ;;
|
||||
_map_with_type _vector EVAL "${ast}" "${env}"
|
||||
return ;;
|
||||
hash_map)
|
||||
local res="" key= val="" hm="${ANON["${ast}"]}"
|
||||
_hash_map; local new_hm="${r}"
|
||||
@ -103,30 +88,14 @@ EVAL_AST () {
|
||||
EVAL "${val}" "${env}"
|
||||
_assoc! "${new_hm}" "${key}" "${r}"
|
||||
done
|
||||
r="${new_hm}" ;;
|
||||
r="${new_hm}"
|
||||
return ;;
|
||||
*)
|
||||
r="${ast}" ;;
|
||||
r="${ast}"
|
||||
return ;;
|
||||
esac
|
||||
}
|
||||
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
while true; do
|
||||
r=
|
||||
[[ "${__ERROR}" ]] && return 1
|
||||
#_pr_str "${ast}"; echo "EVAL '${r} / ${env}'"
|
||||
if ! _list? "${ast}"; then
|
||||
EVAL_AST "${ast}" "${env}"
|
||||
return
|
||||
fi
|
||||
|
||||
# apply list
|
||||
MACROEXPAND "${ast}" "${env}"
|
||||
ast="${r}"
|
||||
if ! _list? "${ast}"; then
|
||||
EVAL_AST "${ast}" "${env}"
|
||||
return
|
||||
fi
|
||||
_empty? "${ast}" && r="${ast}" && return
|
||||
|
||||
_nth "${ast}" 0; local a0="${r}"
|
||||
@ -153,9 +122,6 @@ EVAL () {
|
||||
quote)
|
||||
r="${a1}"
|
||||
return ;;
|
||||
quasiquoteexpand)
|
||||
QUASIQUOTE "${a1}"
|
||||
return ;;
|
||||
quasiquote)
|
||||
QUASIQUOTE "${a1}"
|
||||
ast="${r}"
|
||||
@ -170,9 +136,6 @@ EVAL () {
|
||||
ANON["${r}_ismacro_"]="yes"
|
||||
ENV_SET "${env}" "${a1}" "${r}"
|
||||
return ;;
|
||||
macroexpand)
|
||||
MACROEXPAND "${a1}" "${env}"
|
||||
return ;;
|
||||
try__STAR__) EVAL "${a1}" "${env}"
|
||||
[[ -z "${__ERROR}" ]] && return
|
||||
_nth "${a2}" 0; local a20="${r}"
|
||||
@ -188,7 +151,7 @@ EVAL () {
|
||||
return ;;
|
||||
do) _count "${ast}"
|
||||
_slice "${ast}" 1 $(( ${r} - 2 ))
|
||||
EVAL_AST "${r}" "${env}"
|
||||
_map_with_type _list EVAL "${r}" "${env}"
|
||||
[[ "${__ERROR}" ]] && r= && return 1
|
||||
_last "${ast}"
|
||||
ast="${r}"
|
||||
@ -215,11 +178,27 @@ EVAL () {
|
||||
EVAL \"${a2}\" \"\${r}\"" \
|
||||
"${a2}" "${env}" "${a1}"
|
||||
return ;;
|
||||
*) EVAL_AST "${ast}" "${env}"
|
||||
*) EVAL "${a0}" "${env}"
|
||||
[[ "${__ERROR}" ]] && return 1
|
||||
local f="${r}"
|
||||
|
||||
_rest "${ast}"
|
||||
# Should cause no error as ast is not empty.
|
||||
local args="${r}"
|
||||
|
||||
if [ "${ANON["${f}_ismacro_"]}" ]; then
|
||||
f="${ANON["${f}"]}"
|
||||
${f%%@*} ${ANON["${args}"]}
|
||||
ast="${r}"
|
||||
continue
|
||||
fi
|
||||
|
||||
f="${ANON["${f}"]}"
|
||||
|
||||
_map_with_type _list EVAL "${args}" "${env}"
|
||||
[[ "${__ERROR}" ]] && r= && return 1
|
||||
local el="${r}"
|
||||
_first "${el}"; local f="${ANON["${r}"]}"
|
||||
_rest "${el}"; local args="${ANON["${r}"]}"
|
||||
args="${ANON["${r}"]}"
|
||||
|
||||
#echo "invoke: [${f}] ${args}"
|
||||
if [[ "${f//@/ }" != "${f}" ]]; then
|
||||
set -- ${f//@/ }
|
||||
|
@ -55,45 +55,30 @@ qqIter () {
|
||||
fi
|
||||
}
|
||||
|
||||
IS_MACRO_CALL () {
|
||||
if ! _list? "${1}"; then return 1; fi
|
||||
_nth "${1}" 0; local a0="${r}"
|
||||
if _symbol? "${a0}"; then
|
||||
ENV_FIND "${2}" "${a0}"
|
||||
if [[ "${r}" ]]; then
|
||||
ENV_GET "${2}" "${a0}"
|
||||
[ "${ANON["${r}_ismacro_"]}" ]
|
||||
return $?
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
_symbol DEBUG-EVAL; debug_eval="$r"
|
||||
|
||||
MACROEXPAND () {
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
while IS_MACRO_CALL "${ast}" "${env}"; do
|
||||
_nth "${ast}" 0; local a0="${r}"
|
||||
ENV_GET "${env}" "${a0}"; local mac="${ANON["${r}"]}"
|
||||
_rest "${ast}"
|
||||
${mac%%@*} ${ANON["${r}"]}
|
||||
ast="${r}"
|
||||
done
|
||||
r="${ast}"
|
||||
}
|
||||
while true; do
|
||||
r=
|
||||
|
||||
ENV_GET "$env" "$debug_eval"
|
||||
if [ -n "$__ERROR" ]; then
|
||||
__ERROR=
|
||||
elif [ "$r" != "$__false" -a "$r" != "$__nil" ]; then
|
||||
_pr_str "$ast" yes; echo "EVAL: $r / $env"
|
||||
fi
|
||||
|
||||
EVAL_AST () {
|
||||
local ast="${1}" env="${2}"
|
||||
#_pr_str "${ast}"; echo "EVAL_AST '${ast}:${r} / ${env}'"
|
||||
_obj_type "${ast}"; local ot="${r}"
|
||||
case "${ot}" in
|
||||
symbol)
|
||||
ENV_GET "${env}" "${ast}"
|
||||
return ;;
|
||||
list)
|
||||
_map_with_type _list EVAL "${ast}" "${env}" ;;
|
||||
;;
|
||||
vector)
|
||||
_map_with_type _vector EVAL "${ast}" "${env}" ;;
|
||||
_map_with_type _vector EVAL "${ast}" "${env}"
|
||||
return ;;
|
||||
hash_map)
|
||||
local res="" key= val="" hm="${ANON["${ast}"]}"
|
||||
_hash_map; local new_hm="${r}"
|
||||
@ -103,30 +88,14 @@ EVAL_AST () {
|
||||
EVAL "${val}" "${env}"
|
||||
_assoc! "${new_hm}" "${key}" "${r}"
|
||||
done
|
||||
r="${new_hm}" ;;
|
||||
r="${new_hm}"
|
||||
return ;;
|
||||
*)
|
||||
r="${ast}" ;;
|
||||
r="${ast}"
|
||||
return ;;
|
||||
esac
|
||||
}
|
||||
|
||||
EVAL () {
|
||||
local ast="${1}" env="${2}"
|
||||
while true; do
|
||||
r=
|
||||
[[ "${__ERROR}" ]] && return 1
|
||||
#_pr_str "${ast}"; echo "EVAL '${r} / ${env}'"
|
||||
if ! _list? "${ast}"; then
|
||||
EVAL_AST "${ast}" "${env}"
|
||||
return
|
||||
fi
|
||||
|
||||
# apply list
|
||||
MACROEXPAND "${ast}" "${env}"
|
||||
ast="${r}"
|
||||
if ! _list? "${ast}"; then
|
||||
EVAL_AST "${ast}" "${env}"
|
||||
return
|
||||
fi
|
||||
_empty? "${ast}" && r="${ast}" && return
|
||||
|
||||
_nth "${ast}" 0; local a0="${r}"
|
||||
@ -153,9 +122,6 @@ EVAL () {
|
||||
quote)
|
||||
r="${a1}"
|
||||
return ;;
|
||||
quasiquoteexpand)
|
||||
QUASIQUOTE "${a1}"
|
||||
return ;;
|
||||
quasiquote)
|
||||
QUASIQUOTE "${a1}"
|
||||
ast="${r}"
|
||||
@ -170,9 +136,6 @@ EVAL () {
|
||||
ANON["${r}_ismacro_"]="yes"
|
||||
ENV_SET "${env}" "${a1}" "${r}"
|
||||
return ;;
|
||||
macroexpand)
|
||||
MACROEXPAND "${a1}" "${env}"
|
||||
return ;;
|
||||
sh__STAR__) EVAL "${a1}" "${env}"
|
||||
local output=""
|
||||
local line=""
|
||||
@ -198,7 +161,7 @@ EVAL () {
|
||||
return ;;
|
||||
do) _count "${ast}"
|
||||
_slice "${ast}" 1 $(( ${r} - 2 ))
|
||||
EVAL_AST "${r}" "${env}"
|
||||
_map_with_type _list EVAL "${r}" "${env}"
|
||||
[[ "${__ERROR}" ]] && r= && return 1
|
||||
_last "${ast}"
|
||||
ast="${r}"
|
||||
@ -225,11 +188,27 @@ EVAL () {
|
||||
EVAL \"${a2}\" \"\${r}\"" \
|
||||
"${a2}" "${env}" "${a1}"
|
||||
return ;;
|
||||
*) EVAL_AST "${ast}" "${env}"
|
||||
*) EVAL "${a0}" "${env}"
|
||||
[[ "${__ERROR}" ]] && return 1
|
||||
local f="${r}"
|
||||
|
||||
_rest "${ast}"
|
||||
# Should cause no error as ast is not empty.
|
||||
local args="${r}"
|
||||
|
||||
if [ "${ANON["${f}_ismacro_"]}" ]; then
|
||||
f="${ANON["${f}"]}"
|
||||
${f%%@*} ${ANON["${args}"]}
|
||||
ast="${r}"
|
||||
continue
|
||||
fi
|
||||
|
||||
f="${ANON["${f}"]}"
|
||||
|
||||
_map_with_type _list EVAL "${args}" "${env}"
|
||||
[[ "${__ERROR}" ]] && r= && return 1
|
||||
local el="${r}"
|
||||
_first "${el}"; local f="${ANON["${r}"]}"
|
||||
_rest "${el}"; local args="${ANON["${r}"]}"
|
||||
args="${ANON["${r}"]}"
|
||||
|
||||
#echo "invoke: [${f}] ${args}"
|
||||
if [[ "${f//@/ }" != "${f}" ]]; then
|
||||
set -- ${f//@/ }
|
||||
|
@ -1,39 +1,36 @@
|
||||
REM > env library for mal in BBC BASIC
|
||||
|
||||
DEF FNnew_env(outer%, binds%, exprs%)
|
||||
LOCAL env%
|
||||
LOCAL env%, key$
|
||||
env% = FNalloc_environment(outer%)
|
||||
WHILE NOT FNis_empty(binds%)
|
||||
IF FNunbox_symbol(FNfirst(binds%)) = "&" THEN
|
||||
PROCenv_set(env%, FNnth(binds%, 1), FNas_list(exprs%))
|
||||
key$ = FNunbox_symbol(FNfirst(binds%))
|
||||
IF key$ = "&" THEN
|
||||
PROCenv_set(env%, FNunbox_symbol(FNnth(binds%, 1)), FNas_list(exprs%))
|
||||
binds% = FNempty
|
||||
ELSE
|
||||
PROCenv_set(env%, FNfirst(binds%), FNfirst(exprs%))
|
||||
PROCenv_set(env%, key$, FNfirst(exprs%))
|
||||
binds% = FNrest(binds%) : exprs% = FNrest(exprs%)
|
||||
ENDIF
|
||||
ENDWHILE
|
||||
=env%
|
||||
|
||||
DEF PROCenv_set(env%, keysym%, val%)
|
||||
DEF PROCenv_set(env%, key$, val%)
|
||||
LOCAL data%
|
||||
data% = FNenvironment_data(env%)
|
||||
data% = FNhashmap_set(data%, FNunbox_symbol(keysym%), val%)
|
||||
data% = FNhashmap_set(data%, key$, val%)
|
||||
PROCenvironment_set_data(env%, data%)
|
||||
ENDPROC
|
||||
|
||||
DEF FNenv_find(env%, keysym%)
|
||||
LOCAL val%, outer%, key$
|
||||
key$ = FNunbox_symbol(keysym%)
|
||||
DEF FNenv_find(env%, key$)
|
||||
WHILE NOT FNis_nil(env%)
|
||||
IF FNhashmap_contains(FNenvironment_data(env%), key$) THEN =env%
|
||||
env% = FNenvironment_outer(env%)
|
||||
ENDWHILE
|
||||
=FNnil
|
||||
|
||||
DEF FNenv_get(env%, keysym%)
|
||||
LOCAL key$
|
||||
env% = FNenv_find(env%, keysym%)
|
||||
key$ = FNunbox_symbol(keysym%)
|
||||
DEF FNenv_get(env%, key$)
|
||||
env% = FNenv_find(env%, key$)
|
||||
IF FNis_nil(env%) THEN ERROR &40E80922, "'"+key$+"' not found"
|
||||
=FNhashmap_get(FNenvironment_data(env%), key$)
|
||||
|
||||
|
@ -30,10 +30,28 @@ DEF FNREAD(a$)
|
||||
=FNread_str(FNalloc_string(a$))
|
||||
|
||||
DEF FNEVAL(ast%, env%)
|
||||
IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%)
|
||||
LOCAL car%, val%, key$
|
||||
REM PRINT "EVAL: " + FNunbox_string(FNpr_str(ast%, TRUE))
|
||||
IF FNis_symbol(ast%) THEN
|
||||
val% = FNhashmap_get(env%, FNunbox_symbol(ast%))
|
||||
IF val% = FNnil THEN ERROR &40E80922, "Symbol not in environment"
|
||||
=val%
|
||||
ENDIF
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
val% = FNempty_hashmap
|
||||
bindings% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
key$ = FNunbox_string(FNfirst(bindings%))
|
||||
val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
bindings% = FNrest(bindings%)
|
||||
ENDWHILE
|
||||
=val%
|
||||
ENDIF
|
||||
IF NOT FNis_seq(ast%) THEN =ast%
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
ast% = FNeval_ast(ast%, env%)
|
||||
=FNcore_call(FNunbox_corefn(FNfirst(ast%)), FNrest(ast%))
|
||||
car% = FNEVAL(FNfirst(ast%), env%)
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(car%, FNeval_ast(FNrest(ast%), env%))
|
||||
=FNcore_call(FNunbox_corefn(car%), FNeval_ast(FNrest(ast%), env%))
|
||||
|
||||
DEF FNPRINT(a%)
|
||||
=FNunbox_string(FNpr_str(a%, TRUE))
|
||||
@ -42,30 +60,8 @@ DEF FNrep(a$)
|
||||
=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))
|
||||
|
||||
DEF FNeval_ast(ast%, env%)
|
||||
LOCAL val%, car%, cdr%, map%, keys%, key$
|
||||
IF FNis_symbol(ast%) THEN
|
||||
val% = FNhashmap_get(env%, FNunbox_symbol(ast%))
|
||||
IF val% = FNnil THEN ERROR &40E80922, "Symbol not in environment"
|
||||
=val%
|
||||
ENDIF
|
||||
IF FNis_seq(ast%) THEN
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
car% = FNEVAL(FNfirst(ast%), env%)
|
||||
cdr% = FNeval_ast(FNrest(ast%), env%)
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(car%, cdr%)
|
||||
=FNalloc_pair(car%, cdr%)
|
||||
ENDIF
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
map% = FNempty_hashmap
|
||||
keys% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(keys%)
|
||||
key$ = FNunbox_string(FNfirst(keys%))
|
||||
map% = FNhashmap_set(map%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
keys% = FNrest(keys%)
|
||||
ENDWHILE
|
||||
=map%
|
||||
ENDIF
|
||||
=ast%
|
||||
=FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))
|
||||
|
||||
REM Call a core function, taking the function number and a mal list of
|
||||
REM objects to pass as arguments.
|
||||
|
@ -9,10 +9,10 @@ PROCtypes_init
|
||||
|
||||
REM These correspond with the CASE statement in FNcore_call
|
||||
repl_env% = FNalloc_environment(FNnil)
|
||||
PROCenv_set(repl_env%, FNalloc_symbol("+"), FNalloc_corefn(0))
|
||||
PROCenv_set(repl_env%, FNalloc_symbol("-"), FNalloc_corefn(1))
|
||||
PROCenv_set(repl_env%, FNalloc_symbol("*"), FNalloc_corefn(2))
|
||||
PROCenv_set(repl_env%, FNalloc_symbol("/"), FNalloc_corefn(3))
|
||||
PROCenv_set(repl_env%, "+", FNalloc_corefn(0))
|
||||
PROCenv_set(repl_env%, "-", FNalloc_corefn(1))
|
||||
PROCenv_set(repl_env%, "*", FNalloc_corefn(2))
|
||||
PROCenv_set(repl_env%, "/", FNalloc_corefn(3))
|
||||
|
||||
sav% = FNgc_save
|
||||
REPEAT
|
||||
@ -31,31 +31,53 @@ DEF FNREAD(a$)
|
||||
=FNread_str(FNalloc_string(a$))
|
||||
|
||||
DEF FNEVAL(ast%, env%)
|
||||
LOCAL car%
|
||||
IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%)
|
||||
LOCAL car%, val%, bindings%, key$
|
||||
val% = FNenv_find(env%, "DEBUG-EVAL")
|
||||
IF NOT FNis_nil(val%) THEN
|
||||
IF FNis_truish(FNenv_get(val%, "DEBUG-EVAL")) THEN
|
||||
PRINT "EVAL: " + FNunbox_string(FNpr_str(ast%, TRUE))
|
||||
ENDIF
|
||||
ENDIF
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
val% = FNempty_hashmap
|
||||
bindings% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
key$ = FNunbox_string(FNfirst(bindings%))
|
||||
val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
bindings% = FNrest(bindings%)
|
||||
ENDWHILE
|
||||
=val%
|
||||
ENDIF
|
||||
IF NOT FNis_seq(ast%) THEN =ast%
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
car% = FNfirst(ast%)
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))
|
||||
IF FNis_symbol(car%) THEN
|
||||
CASE FNunbox_symbol(car%) OF
|
||||
key$ = FNunbox_symbol(car%)
|
||||
CASE key$ OF
|
||||
REM Special forms
|
||||
WHEN "def!"
|
||||
LOCAL val%
|
||||
val% = FNEVAL(FNnth(ast%, 2), env%)
|
||||
PROCenv_set(env%, FNnth(ast%, 1), val%)
|
||||
PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)
|
||||
=val%
|
||||
WHEN "let*"
|
||||
LOCAL bindings%
|
||||
env% = FNalloc_environment(env%)
|
||||
bindings% = FNnth(ast%, 1)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
PROCenv_set(env%, FNfirst(bindings%), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
bindings% = FNrest(FNrest(bindings%))
|
||||
ENDWHILE
|
||||
=FNEVAL(FNnth(ast%, 2), env%)
|
||||
OTHERWISE
|
||||
car% = FNenv_get(env%, key$)
|
||||
ENDCASE
|
||||
ELSE
|
||||
car% = FNEVAL(car%, env%)
|
||||
ENDIF
|
||||
ast% = FNeval_ast(ast%, env%)
|
||||
=FNcore_call(FNunbox_corefn(FNfirst(ast%)), FNrest(ast%))
|
||||
REM This is the "apply" part.
|
||||
ast% = FNeval_ast(FNrest(ast%), env%)
|
||||
=FNcore_call(FNunbox_corefn(car%), ast%)
|
||||
|
||||
DEF FNPRINT(a%)
|
||||
=FNunbox_string(FNpr_str(a%, TRUE))
|
||||
@ -64,26 +86,8 @@ DEF FNrep(a$)
|
||||
=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))
|
||||
|
||||
DEF FNeval_ast(ast%, env%)
|
||||
LOCAL val%, car%, cdr%, map%, keys%, key$
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, ast%)
|
||||
IF FNis_seq(ast%) THEN
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
car% = FNEVAL(FNfirst(ast%), env%)
|
||||
cdr% = FNeval_ast(FNrest(ast%), env%)
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(car%, cdr%)
|
||||
=FNalloc_pair(car%, cdr%)
|
||||
ENDIF
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
map% = FNempty_hashmap
|
||||
keys% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(keys%)
|
||||
key$ = FNunbox_string(FNfirst(keys%))
|
||||
map% = FNhashmap_set(map%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
keys% = FNrest(keys%)
|
||||
ENDWHILE
|
||||
=map%
|
||||
ENDIF
|
||||
=ast%
|
||||
=FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))
|
||||
|
||||
REM Call a core function, taking the function number and a mal list of
|
||||
REM objects to pass as arguments.
|
||||
|
@ -13,7 +13,7 @@ PROCcore_ns : REM This sets the data pointer
|
||||
REPEAT
|
||||
READ sym$, i%
|
||||
IF sym$ <> "" THEN
|
||||
PROCenv_set(repl_env%, FNalloc_symbol(sym$), FNalloc_corefn(i%))
|
||||
PROCenv_set(repl_env%, sym$, FNalloc_corefn(i%))
|
||||
ENDIF
|
||||
UNTIL sym$ = ""
|
||||
|
||||
@ -41,53 +41,71 @@ DEF FNEVAL(ast%, env%)
|
||||
=FNgc_exit(FNEVAL_(ast%, env%))
|
||||
|
||||
DEF FNEVAL_(ast%, env%)
|
||||
LOCAL car%
|
||||
IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%)
|
||||
LOCAL car%, val%, bindings%, key$
|
||||
PROCgc_keep_only2(ast%, env%)
|
||||
val% = FNenv_find(env%, "DEBUG-EVAL")
|
||||
IF NOT FNis_nil(val%) THEN
|
||||
IF FNis_truish(FNenv_get(val%, "DEBUG-EVAL")) THEN
|
||||
PRINT "EVAL: " + FNunbox_string(FNpr_str(ast%, TRUE))
|
||||
ENDIF
|
||||
ENDIF
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
val% = FNempty_hashmap
|
||||
bindings% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
key$ = FNunbox_string(FNfirst(bindings%))
|
||||
val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
bindings% = FNrest(bindings%)
|
||||
ENDWHILE
|
||||
=val%
|
||||
ENDIF
|
||||
IF NOT FNis_seq(ast%) THEN =ast%
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
car% = FNfirst(ast%)
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))
|
||||
IF FNis_symbol(car%) THEN
|
||||
CASE FNunbox_symbol(car%) OF
|
||||
key$ = FNunbox_symbol(car%)
|
||||
CASE key$ OF
|
||||
REM Special forms
|
||||
WHEN "def!"
|
||||
LOCAL val%
|
||||
val% = FNEVAL(FNnth(ast%, 2), env%)
|
||||
PROCenv_set(env%, FNnth(ast%, 1), val%)
|
||||
PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)
|
||||
=val%
|
||||
WHEN "let*"
|
||||
LOCAL bindings%
|
||||
env% = FNalloc_environment(env%)
|
||||
bindings% = FNnth(ast%, 1)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
PROCenv_set(env%, FNfirst(bindings%), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
bindings% = FNrest(FNrest(bindings%))
|
||||
ENDWHILE
|
||||
=FNEVAL(FNnth(ast%, 2), env%)
|
||||
WHEN "do"
|
||||
LOCAL val%
|
||||
ast% = FNeval_ast(FNrest(ast%), env%)
|
||||
REPEAT
|
||||
val% = FNfirst(ast%)
|
||||
WHILE TRUE
|
||||
ast% = FNrest(ast%)
|
||||
UNTIL FNis_empty(ast%)
|
||||
=val%
|
||||
IF FNis_empty(ast%) THEN = val%
|
||||
val% = FNEVAL(FNfirst(ast%), env%)
|
||||
ENDWHILE
|
||||
WHEN "if"
|
||||
IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN
|
||||
=FNEVAL(FNnth(ast%, 2), env%)
|
||||
ENDIF
|
||||
IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN =FNEVAL(FNnth(ast%, 2), env%)
|
||||
IF FNcount(ast%) = 3 THEN =FNnil
|
||||
=FNEVAL(FNnth(ast%, 3), env%)
|
||||
WHEN "fn*"
|
||||
=FNalloc_fn(FNnth(ast%, 2), FNnth(ast%, 1), env%)
|
||||
OTHERWISE
|
||||
car% = FNenv_get(env%, key$)
|
||||
ENDCASE
|
||||
ELSE
|
||||
car% = FNEVAL(car%, env%)
|
||||
ENDIF
|
||||
ast% = FNeval_ast(ast%, env%)
|
||||
car% = FNfirst(ast%)
|
||||
IF FNis_fn(car%) THEN
|
||||
env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), FNrest(ast%))
|
||||
=FNEVAL(FNfn_ast(car%), env%)
|
||||
ENDIF
|
||||
REM This is the "apply" part.
|
||||
ast% = FNeval_ast(FNrest(ast%), env%)
|
||||
IF FNis_corefn(car%) THEN
|
||||
=FNcore_call(FNunbox_corefn(car%), FNrest(ast%))
|
||||
=FNcore_call(FNunbox_corefn(car%), ast%)
|
||||
ENDIF
|
||||
IF FNis_fn(car%) THEN
|
||||
env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%)
|
||||
=FNEVAL(FNfn_ast(car%), env%)
|
||||
ENDIF
|
||||
ERROR &40E80918, "Not a function"
|
||||
|
||||
@ -98,26 +116,8 @@ DEF FNrep(a$)
|
||||
=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))
|
||||
|
||||
DEF FNeval_ast(ast%, env%)
|
||||
LOCAL val%, car%, cdr%, map%, keys%, key$
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, ast%)
|
||||
IF FNis_seq(ast%) THEN
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
car% = FNEVAL(FNfirst(ast%), env%)
|
||||
cdr% = FNeval_ast(FNrest(ast%), env%)
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(car%, cdr%)
|
||||
=FNalloc_pair(car%, cdr%)
|
||||
ENDIF
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
map% = FNempty_hashmap
|
||||
keys% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(keys%)
|
||||
key$ = FNunbox_string(FNfirst(keys%))
|
||||
map% = FNhashmap_set(map%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
keys% = FNrest(keys%)
|
||||
ENDWHILE
|
||||
=map%
|
||||
ENDIF
|
||||
=ast%
|
||||
=FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))
|
||||
|
||||
REM Local Variables:
|
||||
REM indent-tabs-mode: nil
|
||||
|
@ -13,7 +13,7 @@ PROCcore_ns : REM This sets the data pointer
|
||||
REPEAT
|
||||
READ sym$, i%
|
||||
IF sym$ <> "" THEN
|
||||
PROCenv_set(repl_env%, FNalloc_symbol(sym$), FNalloc_corefn(i%))
|
||||
PROCenv_set(repl_env%, sym$, FNalloc_corefn(i%))
|
||||
ENDIF
|
||||
UNTIL sym$ = ""
|
||||
|
||||
@ -40,30 +40,47 @@ DEF FNEVAL(ast%, env%)
|
||||
=FNgc_exit(FNEVAL_(ast%, env%))
|
||||
|
||||
DEF FNEVAL_(ast%, env%)
|
||||
LOCAL car%, specialform%, val%, bindings%
|
||||
REPEAT
|
||||
LOCAL car%, val%, bindings%, key$
|
||||
31416 REM tail call optimization loop
|
||||
PROCgc_keep_only2(ast%, env%)
|
||||
IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%)
|
||||
val% = FNenv_find(env%, "DEBUG-EVAL")
|
||||
IF NOT FNis_nil(val%) THEN
|
||||
IF FNis_truish(FNenv_get(val%, "DEBUG-EVAL")) THEN
|
||||
PRINT "EVAL: " + FNunbox_string(FNpr_str(ast%, TRUE))
|
||||
ENDIF
|
||||
ENDIF
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
val% = FNempty_hashmap
|
||||
bindings% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
key$ = FNunbox_string(FNfirst(bindings%))
|
||||
val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
bindings% = FNrest(bindings%)
|
||||
ENDWHILE
|
||||
=val%
|
||||
ENDIF
|
||||
IF NOT FNis_seq(ast%) THEN =ast%
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
car% = FNfirst(ast%)
|
||||
specialform% = FALSE
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))
|
||||
IF FNis_symbol(car%) THEN
|
||||
specialform% = TRUE
|
||||
CASE FNunbox_symbol(car%) OF
|
||||
key$ = FNunbox_symbol(car%)
|
||||
CASE key$ OF
|
||||
REM Special forms
|
||||
WHEN "def!"
|
||||
val% = FNEVAL(FNnth(ast%, 2), env%)
|
||||
PROCenv_set(env%, FNnth(ast%, 1), val%)
|
||||
PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)
|
||||
=val%
|
||||
WHEN "let*"
|
||||
env% = FNalloc_environment(env%)
|
||||
bindings% = FNnth(ast%, 1)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
PROCenv_set(env%, FNfirst(bindings%), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
bindings% = FNrest(FNrest(bindings%))
|
||||
ENDWHILE
|
||||
ast% = FNnth(ast%, 2)
|
||||
REM Loop round for tail-call optimisation.
|
||||
GOTO 31416
|
||||
WHEN "do"
|
||||
REM The guide has us call FNeval_ast on the sub-list that excludes
|
||||
REM the last element of ast%, but that's a bit painful without
|
||||
@ -75,35 +92,34 @@ DEF FNEVAL_(ast%, env%)
|
||||
ast% = FNrest(ast%)
|
||||
ENDWHILE
|
||||
ast% = FNfirst(ast%)
|
||||
GOTO 31416
|
||||
WHEN "if"
|
||||
IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN
|
||||
ast% = FNnth(ast%, 2)
|
||||
ELSE
|
||||
IF FNcount(ast%) = 3 THEN =FNnil ELSE ast% = FNnth(ast%, 3)
|
||||
IF FNcount(ast%) = 3 THEN =FNnil
|
||||
ast% = FNnth(ast%, 3)
|
||||
ENDIF
|
||||
REM Loop round for tail-call optimisation.
|
||||
GOTO 31416
|
||||
WHEN "fn*"
|
||||
=FNalloc_fn(FNnth(ast%, 2), FNnth(ast%, 1), env%)
|
||||
OTHERWISE
|
||||
specialform% = FALSE
|
||||
car% = FNenv_get(env%, key$)
|
||||
ENDCASE
|
||||
ELSE
|
||||
car% = FNEVAL(car%, env%)
|
||||
ENDIF
|
||||
IF NOT specialform% THEN
|
||||
REM This is the "apply" part.
|
||||
ast% = FNeval_ast(ast%, env%)
|
||||
car% = FNfirst(ast%)
|
||||
ast% = FNeval_ast(FNrest(ast%), env%)
|
||||
IF FNis_corefn(car%) THEN
|
||||
=FNcore_call(FNunbox_corefn(car%), FNrest(ast%))
|
||||
=FNcore_call(FNunbox_corefn(car%), ast%)
|
||||
ENDIF
|
||||
IF FNis_fn(car%) THEN
|
||||
env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), FNrest(ast%))
|
||||
env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%)
|
||||
ast% = FNfn_ast(car%)
|
||||
REM Loop round for tail-call optimisation.
|
||||
ELSE
|
||||
GOTO 31416
|
||||
ENDIF
|
||||
ERROR &40E80918, "Not a function"
|
||||
ENDIF
|
||||
ENDIF
|
||||
UNTIL FALSE
|
||||
|
||||
DEF FNPRINT(a%)
|
||||
=FNunbox_string(FNpr_str(a%, TRUE))
|
||||
@ -112,26 +128,8 @@ DEF FNrep(a$)
|
||||
=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))
|
||||
|
||||
DEF FNeval_ast(ast%, env%)
|
||||
LOCAL val%, car%, cdr%, map%, keys%, key$
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, ast%)
|
||||
IF FNis_seq(ast%) THEN
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
car% = FNEVAL(FNfirst(ast%), env%)
|
||||
cdr% = FNeval_ast(FNrest(ast%), env%)
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(car%, cdr%)
|
||||
=FNalloc_pair(car%, cdr%)
|
||||
ENDIF
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
map% = FNempty_hashmap
|
||||
keys% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(keys%)
|
||||
key$ = FNunbox_string(FNfirst(keys%))
|
||||
map% = FNhashmap_set(map%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
keys% = FNrest(keys%)
|
||||
ENDWHILE
|
||||
=map%
|
||||
ENDIF
|
||||
=ast%
|
||||
=FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))
|
||||
|
||||
REM Local Variables:
|
||||
REM indent-tabs-mode: nil
|
||||
|
@ -13,7 +13,7 @@ PROCcore_ns : REM This sets the data pointer
|
||||
REPEAT
|
||||
READ sym$, i%
|
||||
IF sym$ <> "" THEN
|
||||
PROCenv_set(repl_env%, FNalloc_symbol(sym$), FNalloc_corefn(i%))
|
||||
PROCenv_set(repl_env%, sym$, FNalloc_corefn(i%))
|
||||
ENDIF
|
||||
UNTIL sym$ = ""
|
||||
|
||||
@ -30,9 +30,9 @@ UNTIL form$ = ""
|
||||
argv% = FNget_argv
|
||||
|
||||
IF FNis_empty(argv%) THEN
|
||||
PROCenv_set(repl_env%, FNalloc_symbol("*ARGV*"), FNempty)
|
||||
PROCenv_set(repl_env%, "*ARGV*", FNempty)
|
||||
ELSE
|
||||
PROCenv_set(repl_env%, FNalloc_symbol("*ARGV*"), FNrest(argv%))
|
||||
PROCenv_set(repl_env%, "*ARGV*", FNrest(argv%))
|
||||
val$ = FNrep("(load-file " + FNunbox_string(FNpr_str(FNfirst(argv%), TRUE)) + ")")
|
||||
END
|
||||
ENDIF
|
||||
@ -58,30 +58,47 @@ DEF FNEVAL(ast%, env%)
|
||||
=FNgc_exit(FNEVAL_(ast%, env%))
|
||||
|
||||
DEF FNEVAL_(ast%, env%)
|
||||
LOCAL car%, specialform%, val%, bindings%
|
||||
REPEAT
|
||||
LOCAL car%, val%, bindings%, key$
|
||||
31416 REM tail call optimization loop
|
||||
PROCgc_keep_only2(ast%, env%)
|
||||
IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%)
|
||||
val% = FNenv_find(env%, "DEBUG-EVAL")
|
||||
IF NOT FNis_nil(val%) THEN
|
||||
IF FNis_truish(FNenv_get(val%, "DEBUG-EVAL")) THEN
|
||||
PRINT "EVAL: " + FNunbox_string(FNpr_str(ast%, TRUE))
|
||||
ENDIF
|
||||
ENDIF
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
val% = FNempty_hashmap
|
||||
bindings% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
key$ = FNunbox_string(FNfirst(bindings%))
|
||||
val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
bindings% = FNrest(bindings%)
|
||||
ENDWHILE
|
||||
=val%
|
||||
ENDIF
|
||||
IF NOT FNis_seq(ast%) THEN =ast%
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
car% = FNfirst(ast%)
|
||||
specialform% = FALSE
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))
|
||||
IF FNis_symbol(car%) THEN
|
||||
specialform% = TRUE
|
||||
CASE FNunbox_symbol(car%) OF
|
||||
key$ = FNunbox_symbol(car%)
|
||||
CASE key$ OF
|
||||
REM Special forms
|
||||
WHEN "def!"
|
||||
val% = FNEVAL(FNnth(ast%, 2), env%)
|
||||
PROCenv_set(env%, FNnth(ast%, 1), val%)
|
||||
PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)
|
||||
=val%
|
||||
WHEN "let*"
|
||||
env% = FNalloc_environment(env%)
|
||||
bindings% = FNnth(ast%, 1)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
PROCenv_set(env%, FNfirst(bindings%), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
bindings% = FNrest(FNrest(bindings%))
|
||||
ENDWHILE
|
||||
ast% = FNnth(ast%, 2)
|
||||
REM Loop round for tail-call optimisation.
|
||||
GOTO 31416
|
||||
WHEN "do"
|
||||
REM The guide has us call FNeval_ast on the sub-list that excludes
|
||||
REM the last element of ast%, but that's a bit painful without
|
||||
@ -93,35 +110,34 @@ DEF FNEVAL_(ast%, env%)
|
||||
ast% = FNrest(ast%)
|
||||
ENDWHILE
|
||||
ast% = FNfirst(ast%)
|
||||
GOTO 31416
|
||||
WHEN "if"
|
||||
IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN
|
||||
ast% = FNnth(ast%, 2)
|
||||
ELSE
|
||||
IF FNcount(ast%) = 3 THEN =FNnil ELSE ast% = FNnth(ast%, 3)
|
||||
IF FNcount(ast%) = 3 THEN =FNnil
|
||||
ast% = FNnth(ast%, 3)
|
||||
ENDIF
|
||||
REM Loop round for tail-call optimisation.
|
||||
GOTO 31416
|
||||
WHEN "fn*"
|
||||
=FNalloc_fn(FNnth(ast%, 2), FNnth(ast%, 1), env%)
|
||||
OTHERWISE
|
||||
specialform% = FALSE
|
||||
car% = FNenv_get(env%, key$)
|
||||
ENDCASE
|
||||
ELSE
|
||||
car% = FNEVAL(car%, env%)
|
||||
ENDIF
|
||||
IF NOT specialform% THEN
|
||||
REM This is the "apply" part.
|
||||
ast% = FNeval_ast(ast%, env%)
|
||||
car% = FNfirst(ast%)
|
||||
ast% = FNeval_ast(FNrest(ast%), env%)
|
||||
IF FNis_corefn(car%) THEN
|
||||
=FNcore_call(FNunbox_corefn(car%), FNrest(ast%))
|
||||
=FNcore_call(FNunbox_corefn(car%), ast%)
|
||||
ENDIF
|
||||
IF FNis_fn(car%) THEN
|
||||
env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), FNrest(ast%))
|
||||
env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%)
|
||||
ast% = FNfn_ast(car%)
|
||||
REM Loop round for tail-call optimisation.
|
||||
ELSE
|
||||
GOTO 31416
|
||||
ENDIF
|
||||
ERROR &40E80918, "Not a function"
|
||||
ENDIF
|
||||
ENDIF
|
||||
UNTIL FALSE
|
||||
|
||||
DEF FNPRINT(a%)
|
||||
=FNunbox_string(FNpr_str(a%, TRUE))
|
||||
@ -130,26 +146,8 @@ DEF FNrep(a$)
|
||||
=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))
|
||||
|
||||
DEF FNeval_ast(ast%, env%)
|
||||
LOCAL val%, car%, cdr%, map%, keys%, key$
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, ast%)
|
||||
IF FNis_seq(ast%) THEN
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
car% = FNEVAL(FNfirst(ast%), env%)
|
||||
cdr% = FNeval_ast(FNrest(ast%), env%)
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(car%, cdr%)
|
||||
=FNalloc_pair(car%, cdr%)
|
||||
ENDIF
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
map% = FNempty_hashmap
|
||||
keys% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(keys%)
|
||||
key$ = FNunbox_string(FNfirst(keys%))
|
||||
map% = FNhashmap_set(map%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
keys% = FNrest(keys%)
|
||||
ENDWHILE
|
||||
=map%
|
||||
ENDIF
|
||||
=ast%
|
||||
=FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))
|
||||
|
||||
DEF FNget_argv
|
||||
PROCgc_enter
|
||||
|
@ -13,7 +13,7 @@ PROCcore_ns : REM This sets the data pointer
|
||||
REPEAT
|
||||
READ sym$, i%
|
||||
IF sym$ <> "" THEN
|
||||
PROCenv_set(repl_env%, FNalloc_symbol(sym$), FNalloc_corefn(i%))
|
||||
PROCenv_set(repl_env%, sym$, FNalloc_corefn(i%))
|
||||
ENDIF
|
||||
UNTIL sym$ = ""
|
||||
|
||||
@ -30,9 +30,9 @@ UNTIL form$ = ""
|
||||
argv% = FNget_argv
|
||||
|
||||
IF FNis_empty(argv%) THEN
|
||||
PROCenv_set(repl_env%, FNalloc_symbol("*ARGV*"), FNempty)
|
||||
PROCenv_set(repl_env%, "*ARGV*", FNempty)
|
||||
ELSE
|
||||
PROCenv_set(repl_env%, FNalloc_symbol("*ARGV*"), FNrest(argv%))
|
||||
PROCenv_set(repl_env%, "*ARGV*", FNrest(argv%))
|
||||
val$ = FNrep("(load-file " + FNunbox_string(FNpr_str(FNfirst(argv%), TRUE)) + ")")
|
||||
END
|
||||
ENDIF
|
||||
@ -86,30 +86,47 @@ DEF FNEVAL(ast%, env%)
|
||||
=FNgc_exit(FNEVAL_(ast%, env%))
|
||||
|
||||
DEF FNEVAL_(ast%, env%)
|
||||
LOCAL car%, specialform%, val%, bindings%
|
||||
REPEAT
|
||||
LOCAL car%, val%, bindings%, key$
|
||||
31416 REM tail call optimization loop
|
||||
PROCgc_keep_only2(ast%, env%)
|
||||
IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%)
|
||||
val% = FNenv_find(env%, "DEBUG-EVAL")
|
||||
IF NOT FNis_nil(val%) THEN
|
||||
IF FNis_truish(FNenv_get(val%, "DEBUG-EVAL")) THEN
|
||||
PRINT "EVAL: " + FNunbox_string(FNpr_str(ast%, TRUE))
|
||||
ENDIF
|
||||
ENDIF
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
val% = FNempty_hashmap
|
||||
bindings% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
key$ = FNunbox_string(FNfirst(bindings%))
|
||||
val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
bindings% = FNrest(bindings%)
|
||||
ENDWHILE
|
||||
=val%
|
||||
ENDIF
|
||||
IF NOT FNis_seq(ast%) THEN =ast%
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
car% = FNfirst(ast%)
|
||||
specialform% = FALSE
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))
|
||||
IF FNis_symbol(car%) THEN
|
||||
specialform% = TRUE
|
||||
CASE FNunbox_symbol(car%) OF
|
||||
key$ = FNunbox_symbol(car%)
|
||||
CASE key$ OF
|
||||
REM Special forms
|
||||
WHEN "def!"
|
||||
val% = FNEVAL(FNnth(ast%, 2), env%)
|
||||
PROCenv_set(env%, FNnth(ast%, 1), val%)
|
||||
PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)
|
||||
=val%
|
||||
WHEN "let*"
|
||||
env% = FNalloc_environment(env%)
|
||||
bindings% = FNnth(ast%, 1)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
PROCenv_set(env%, FNfirst(bindings%), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
bindings% = FNrest(FNrest(bindings%))
|
||||
ENDWHILE
|
||||
ast% = FNnth(ast%, 2)
|
||||
REM Loop round for tail-call optimisation.
|
||||
GOTO 31416
|
||||
WHEN "do"
|
||||
REM The guide has us call FNeval_ast on the sub-list that excludes
|
||||
REM the last element of ast%, but that's a bit painful without
|
||||
@ -121,42 +138,39 @@ DEF FNEVAL_(ast%, env%)
|
||||
ast% = FNrest(ast%)
|
||||
ENDWHILE
|
||||
ast% = FNfirst(ast%)
|
||||
GOTO 31416
|
||||
WHEN "if"
|
||||
IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN
|
||||
ast% = FNnth(ast%, 2)
|
||||
ELSE
|
||||
IF FNcount(ast%) = 3 THEN =FNnil ELSE ast% = FNnth(ast%, 3)
|
||||
IF FNcount(ast%) = 3 THEN =FNnil
|
||||
ast% = FNnth(ast%, 3)
|
||||
ENDIF
|
||||
REM Loop round for tail-call optimisation.
|
||||
GOTO 31416
|
||||
WHEN "fn*"
|
||||
=FNalloc_fn(FNnth(ast%, 2), FNnth(ast%, 1), env%)
|
||||
WHEN "quote"
|
||||
=FNnth(ast%, 1)
|
||||
WHEN "quasiquoteexpand"
|
||||
= FNquasiquote(FNnth(ast%, 1))
|
||||
WHEN "quasiquote"
|
||||
ast% = FNquasiquote(FNnth(ast%, 1))
|
||||
REM Loop round for tail-call optimisation
|
||||
GOTO 31416
|
||||
OTHERWISE
|
||||
specialform% = FALSE
|
||||
car% = FNenv_get(env%, key$)
|
||||
ENDCASE
|
||||
ELSE
|
||||
car% = FNEVAL(car%, env%)
|
||||
ENDIF
|
||||
IF NOT specialform% THEN
|
||||
REM This is the "apply" part.
|
||||
ast% = FNeval_ast(ast%, env%)
|
||||
car% = FNfirst(ast%)
|
||||
ast% = FNeval_ast(FNrest(ast%), env%)
|
||||
IF FNis_corefn(car%) THEN
|
||||
=FNcore_call(FNunbox_corefn(car%), FNrest(ast%))
|
||||
=FNcore_call(FNunbox_corefn(car%), ast%)
|
||||
ENDIF
|
||||
IF FNis_fn(car%) THEN
|
||||
env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), FNrest(ast%))
|
||||
env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%)
|
||||
ast% = FNfn_ast(car%)
|
||||
REM Loop round for tail-call optimisation.
|
||||
ELSE
|
||||
GOTO 31416
|
||||
ENDIF
|
||||
ERROR &40E80918, "Not a function"
|
||||
ENDIF
|
||||
ENDIF
|
||||
UNTIL FALSE
|
||||
|
||||
DEF FNPRINT(a%)
|
||||
=FNunbox_string(FNpr_str(a%, TRUE))
|
||||
@ -165,26 +179,8 @@ DEF FNrep(a$)
|
||||
=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))
|
||||
|
||||
DEF FNeval_ast(ast%, env%)
|
||||
LOCAL val%, car%, cdr%, map%, keys%, key$
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, ast%)
|
||||
IF FNis_seq(ast%) THEN
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
car% = FNEVAL(FNfirst(ast%), env%)
|
||||
cdr% = FNeval_ast(FNrest(ast%), env%)
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(car%, cdr%)
|
||||
=FNalloc_pair(car%, cdr%)
|
||||
ENDIF
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
map% = FNempty_hashmap
|
||||
keys% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(keys%)
|
||||
key$ = FNunbox_string(FNfirst(keys%))
|
||||
map% = FNhashmap_set(map%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
keys% = FNrest(keys%)
|
||||
ENDWHILE
|
||||
=map%
|
||||
ENDIF
|
||||
=ast%
|
||||
=FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))
|
||||
|
||||
DEF FNget_argv
|
||||
PROCgc_enter
|
||||
|
@ -13,7 +13,7 @@ PROCcore_ns : REM This sets the data pointer
|
||||
REPEAT
|
||||
READ sym$, i%
|
||||
IF sym$ <> "" THEN
|
||||
PROCenv_set(repl_env%, FNalloc_symbol(sym$), FNalloc_corefn(i%))
|
||||
PROCenv_set(repl_env%, sym$, FNalloc_corefn(i%))
|
||||
ENDIF
|
||||
UNTIL sym$ = ""
|
||||
|
||||
@ -31,9 +31,9 @@ UNTIL form$ = ""
|
||||
argv% = FNget_argv
|
||||
|
||||
IF FNis_empty(argv%) THEN
|
||||
PROCenv_set(repl_env%, FNalloc_symbol("*ARGV*"), FNempty)
|
||||
PROCenv_set(repl_env%, "*ARGV*", FNempty)
|
||||
ELSE
|
||||
PROCenv_set(repl_env%, FNalloc_symbol("*ARGV*"), FNrest(argv%))
|
||||
PROCenv_set(repl_env%, "*ARGV*", FNrest(argv%))
|
||||
val$ = FNrep("(load-file " + FNunbox_string(FNpr_str(FNfirst(argv%), TRUE)) + ")")
|
||||
END
|
||||
ENDIF
|
||||
@ -82,63 +82,57 @@ DEF FNquasiquote(ast%)
|
||||
ENDIF
|
||||
=ast%
|
||||
|
||||
DEF FNis_macro_call(ast%, env%)
|
||||
LOCAL car%, val%
|
||||
IF NOT FNis_list(ast%) THEN =FALSE
|
||||
car% = FNfirst(ast%)
|
||||
IF NOT FNis_symbol(car%) THEN =FALSE
|
||||
IF FNis_nil(FNenv_find(env%, car%)) THEN =FALSE
|
||||
val% = FNenv_get(env%, car%)
|
||||
=FNis_macro(val%)
|
||||
|
||||
DEF FNmacroexpand(ast%, env%)
|
||||
LOCAL mac%, macenv%, macast%
|
||||
WHILE FNis_macro_call(ast%, env%)
|
||||
REM PRINT "expanded ";FNpr_str(ast%, TRUE);
|
||||
mac% = FNenv_get(env%, FNfirst(ast%))
|
||||
macenv% = FNnew_env(FNfn_env(mac%), FNfn_params(mac%), FNrest(ast%))
|
||||
macast% = FNfn_ast(mac%)
|
||||
ast% = FNEVAL(macast%, macenv%)
|
||||
REM PRINT " to ";FNpr_str(ast%, TRUE)
|
||||
ENDWHILE
|
||||
=ast%
|
||||
|
||||
DEF FNEVAL(ast%, env%)
|
||||
PROCgc_enter
|
||||
=FNgc_exit(FNEVAL_(ast%, env%))
|
||||
|
||||
DEF FNEVAL_(ast%, env%)
|
||||
LOCAL car%, specialform%, val%, bindings%
|
||||
REPEAT
|
||||
LOCAL car%, val%, bindings%, key$
|
||||
31416 REM tail call optimization loop
|
||||
PROCgc_keep_only2(ast%, env%)
|
||||
IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%)
|
||||
val% = FNenv_find(env%, "DEBUG-EVAL")
|
||||
IF NOT FNis_nil(val%) THEN
|
||||
IF FNis_truish(FNenv_get(val%, "DEBUG-EVAL")) THEN
|
||||
PRINT "EVAL: " + FNunbox_string(FNpr_str(ast%, TRUE))
|
||||
ENDIF
|
||||
ENDIF
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
val% = FNempty_hashmap
|
||||
bindings% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
key$ = FNunbox_string(FNfirst(bindings%))
|
||||
val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
bindings% = FNrest(bindings%)
|
||||
ENDWHILE
|
||||
=val%
|
||||
ENDIF
|
||||
IF NOT FNis_seq(ast%) THEN =ast%
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
ast% = FNmacroexpand(ast%, env%)
|
||||
IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%)
|
||||
car% = FNfirst(ast%)
|
||||
specialform% = FALSE
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))
|
||||
IF FNis_symbol(car%) THEN
|
||||
specialform% = TRUE
|
||||
CASE FNunbox_symbol(car%) OF
|
||||
key$ = FNunbox_symbol(car%)
|
||||
CASE key$ OF
|
||||
REM Special forms
|
||||
WHEN "def!"
|
||||
val% = FNEVAL(FNnth(ast%, 2), env%)
|
||||
PROCenv_set(env%, FNnth(ast%, 1), val%)
|
||||
PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)
|
||||
=val%
|
||||
WHEN "defmacro!"
|
||||
val% = FNEVAL(FNnth(ast%, 2), env%)
|
||||
IF FNis_fn(val%) THEN val% = FNas_macro(val%)
|
||||
PROCenv_set(env%, FNnth(ast%, 1), val%)
|
||||
PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)
|
||||
=val%
|
||||
WHEN "let*"
|
||||
env% = FNalloc_environment(env%)
|
||||
bindings% = FNnth(ast%, 1)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
PROCenv_set(env%, FNfirst(bindings%), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
bindings% = FNrest(FNrest(bindings%))
|
||||
ENDWHILE
|
||||
ast% = FNnth(ast%, 2)
|
||||
REM Loop round for tail-call optimisation.
|
||||
GOTO 31416
|
||||
WHEN "do"
|
||||
REM The guide has us call FNeval_ast on the sub-list that excludes
|
||||
REM the last element of ast%, but that's a bit painful without
|
||||
@ -150,44 +144,44 @@ DEF FNEVAL_(ast%, env%)
|
||||
ast% = FNrest(ast%)
|
||||
ENDWHILE
|
||||
ast% = FNfirst(ast%)
|
||||
GOTO 31416
|
||||
WHEN "if"
|
||||
IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN
|
||||
ast% = FNnth(ast%, 2)
|
||||
ELSE
|
||||
IF FNcount(ast%) = 3 THEN =FNnil ELSE ast% = FNnth(ast%, 3)
|
||||
IF FNcount(ast%) = 3 THEN =FNnil
|
||||
ast% = FNnth(ast%, 3)
|
||||
ENDIF
|
||||
REM Loop round for tail-call optimisation.
|
||||
GOTO 31416
|
||||
WHEN "fn*"
|
||||
=FNalloc_fn(FNnth(ast%, 2), FNnth(ast%, 1), env%)
|
||||
WHEN "quote"
|
||||
=FNnth(ast%, 1)
|
||||
WHEN "quasiquoteexpand"
|
||||
= FNquasiquote(FNnth(ast%, 1))
|
||||
WHEN "quasiquote"
|
||||
ast% = FNquasiquote(FNnth(ast%, 1))
|
||||
REM Loop round for tail-call optimisation
|
||||
WHEN "macroexpand"
|
||||
=FNmacroexpand(FNnth(ast%, 1), env%)
|
||||
GOTO 31416
|
||||
OTHERWISE
|
||||
specialform% = FALSE
|
||||
car% = FNenv_get(env%, key$)
|
||||
ENDCASE
|
||||
ELSE
|
||||
car% = FNEVAL(car%, env%)
|
||||
ENDIF
|
||||
IF NOT specialform% THEN
|
||||
REM This is the "apply" part.
|
||||
ast% = FNrest(ast%)
|
||||
IF FNis_macro(car%) THEN
|
||||
ast% = FNEVAL(FNfn_ast(car%), FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%))
|
||||
GOTO 31416
|
||||
ENDIF
|
||||
ast% = FNeval_ast(ast%, env%)
|
||||
car% = FNfirst(ast%)
|
||||
IF FNis_corefn(car%) THEN
|
||||
=FNcore_call(FNunbox_corefn(car%), FNrest(ast%))
|
||||
=FNcore_call(FNunbox_corefn(car%), ast%)
|
||||
ENDIF
|
||||
IF FNis_fn(car%) THEN
|
||||
env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), FNrest(ast%))
|
||||
env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%)
|
||||
ast% = FNfn_ast(car%)
|
||||
REM Loop round for tail-call optimisation.
|
||||
ELSE
|
||||
GOTO 31416
|
||||
ENDIF
|
||||
ERROR &40E80918, "Not a function"
|
||||
ENDIF
|
||||
ENDIF
|
||||
UNTIL FALSE
|
||||
|
||||
DEF FNPRINT(a%)
|
||||
=FNunbox_string(FNpr_str(a%, TRUE))
|
||||
@ -196,26 +190,8 @@ DEF FNrep(a$)
|
||||
=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))
|
||||
|
||||
DEF FNeval_ast(ast%, env%)
|
||||
LOCAL val%, car%, cdr%, map%, keys%, key$
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, ast%)
|
||||
IF FNis_seq(ast%) THEN
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
car% = FNEVAL(FNfirst(ast%), env%)
|
||||
cdr% = FNeval_ast(FNrest(ast%), env%)
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(car%, cdr%)
|
||||
=FNalloc_pair(car%, cdr%)
|
||||
ENDIF
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
map% = FNempty_hashmap
|
||||
keys% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(keys%)
|
||||
key$ = FNunbox_string(FNfirst(keys%))
|
||||
map% = FNhashmap_set(map%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
keys% = FNrest(keys%)
|
||||
ENDWHILE
|
||||
=map%
|
||||
ENDIF
|
||||
=ast%
|
||||
=FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))
|
||||
|
||||
DEF FNget_argv
|
||||
PROCgc_enter
|
||||
|
@ -13,7 +13,7 @@ PROCcore_ns : REM This sets the data pointer
|
||||
REPEAT
|
||||
READ sym$, i%
|
||||
IF sym$ <> "" THEN
|
||||
PROCenv_set(repl_env%, FNalloc_symbol(sym$), FNalloc_corefn(i%))
|
||||
PROCenv_set(repl_env%, sym$, FNalloc_corefn(i%))
|
||||
ENDIF
|
||||
UNTIL sym$ = ""
|
||||
|
||||
@ -31,9 +31,9 @@ UNTIL form$ = ""
|
||||
argv% = FNget_argv
|
||||
|
||||
IF FNis_empty(argv%) THEN
|
||||
PROCenv_set(repl_env%, FNalloc_symbol("*ARGV*"), FNempty)
|
||||
PROCenv_set(repl_env%, "*ARGV*", FNempty)
|
||||
ELSE
|
||||
PROCenv_set(repl_env%, FNalloc_symbol("*ARGV*"), FNrest(argv%))
|
||||
PROCenv_set(repl_env%, "*ARGV*", FNrest(argv%))
|
||||
val$ = FNrep("(load-file " + FNunbox_string(FNpr_str(FNfirst(argv%), TRUE)) + ")")
|
||||
END
|
||||
ENDIF
|
||||
@ -82,27 +82,6 @@ DEF FNquasiquote(ast%)
|
||||
ENDIF
|
||||
=ast%
|
||||
|
||||
DEF FNis_macro_call(ast%, env%)
|
||||
LOCAL car%, val%
|
||||
IF NOT FNis_list(ast%) THEN =FALSE
|
||||
car% = FNfirst(ast%)
|
||||
IF NOT FNis_symbol(car%) THEN =FALSE
|
||||
IF FNis_nil(FNenv_find(env%, car%)) THEN =FALSE
|
||||
val% = FNenv_get(env%, car%)
|
||||
=FNis_macro(val%)
|
||||
|
||||
DEF FNmacroexpand(ast%, env%)
|
||||
LOCAL mac%, macenv%, macast%
|
||||
WHILE FNis_macro_call(ast%, env%)
|
||||
REM PRINT "expanded ";FNpr_str(ast%, TRUE);
|
||||
mac% = FNenv_get(env%, FNfirst(ast%))
|
||||
macenv% = FNnew_env(FNfn_env(mac%), FNfn_params(mac%), FNrest(ast%))
|
||||
macast% = FNfn_ast(mac%)
|
||||
ast% = FNEVAL(macast%, macenv%)
|
||||
REM PRINT " to ";FNpr_str(ast%, TRUE)
|
||||
ENDWHILE
|
||||
=ast%
|
||||
|
||||
DEF FNtry_catch(ast%, env%)
|
||||
LOCAL is_error%, ret%
|
||||
REM If there's no 'catch*' clause then we just evaluate the 'try*'.
|
||||
@ -150,37 +129,52 @@ DEF FNEVAL(ast%, env%)
|
||||
=FNgc_exit(FNEVAL_(ast%, env%))
|
||||
|
||||
DEF FNEVAL_(ast%, env%)
|
||||
LOCAL car%, specialform%, val%, bindings%
|
||||
REPEAT
|
||||
LOCAL car%, val%, bindings%, key$
|
||||
31416 REM tail call optimization loop
|
||||
PROCgc_keep_only2(ast%, env%)
|
||||
IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%)
|
||||
val% = FNenv_find(env%, "DEBUG-EVAL")
|
||||
IF NOT FNis_nil(val%) THEN
|
||||
IF FNis_truish(FNenv_get(val%, "DEBUG-EVAL")) THEN
|
||||
PRINT "EVAL: " + FNunbox_string(FNpr_str(ast%, TRUE))
|
||||
ENDIF
|
||||
ENDIF
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
val% = FNempty_hashmap
|
||||
bindings% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
key$ = FNunbox_string(FNfirst(bindings%))
|
||||
val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
bindings% = FNrest(bindings%)
|
||||
ENDWHILE
|
||||
=val%
|
||||
ENDIF
|
||||
IF NOT FNis_seq(ast%) THEN =ast%
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
ast% = FNmacroexpand(ast%, env%)
|
||||
IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%)
|
||||
car% = FNfirst(ast%)
|
||||
specialform% = FALSE
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))
|
||||
IF FNis_symbol(car%) THEN
|
||||
specialform% = TRUE
|
||||
CASE FNunbox_symbol(car%) OF
|
||||
key$ = FNunbox_symbol(car%)
|
||||
CASE key$ OF
|
||||
REM Special forms
|
||||
WHEN "def!"
|
||||
val% = FNEVAL(FNnth(ast%, 2), env%)
|
||||
PROCenv_set(env%, FNnth(ast%, 1), val%)
|
||||
PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)
|
||||
=val%
|
||||
WHEN "defmacro!"
|
||||
val% = FNEVAL(FNnth(ast%, 2), env%)
|
||||
IF FNis_fn(val%) THEN val% = FNas_macro(val%)
|
||||
PROCenv_set(env%, FNnth(ast%, 1), val%)
|
||||
PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)
|
||||
=val%
|
||||
WHEN "let*"
|
||||
env% = FNalloc_environment(env%)
|
||||
bindings% = FNnth(ast%, 1)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
PROCenv_set(env%, FNfirst(bindings%), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
bindings% = FNrest(FNrest(bindings%))
|
||||
ENDWHILE
|
||||
ast% = FNnth(ast%, 2)
|
||||
REM Loop round for tail-call optimisation.
|
||||
GOTO 31416
|
||||
WHEN "do"
|
||||
REM The guide has us call FNeval_ast on the sub-list that excludes
|
||||
REM the last element of ast%, but that's a bit painful without
|
||||
@ -192,46 +186,46 @@ DEF FNEVAL_(ast%, env%)
|
||||
ast% = FNrest(ast%)
|
||||
ENDWHILE
|
||||
ast% = FNfirst(ast%)
|
||||
GOTO 31416
|
||||
WHEN "if"
|
||||
IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN
|
||||
ast% = FNnth(ast%, 2)
|
||||
ELSE
|
||||
IF FNcount(ast%) = 3 THEN =FNnil ELSE ast% = FNnth(ast%, 3)
|
||||
IF FNcount(ast%) = 3 THEN =FNnil
|
||||
ast% = FNnth(ast%, 3)
|
||||
ENDIF
|
||||
REM Loop round for tail-call optimisation.
|
||||
GOTO 31416
|
||||
WHEN "fn*"
|
||||
=FNalloc_fn(FNnth(ast%, 2), FNnth(ast%, 1), env%)
|
||||
WHEN "quote"
|
||||
=FNnth(ast%, 1)
|
||||
WHEN "quasiquoteexpand"
|
||||
= FNquasiquote(FNnth(ast%, 1))
|
||||
WHEN "quasiquote"
|
||||
ast% = FNquasiquote(FNnth(ast%, 1))
|
||||
REM Loop round for tail-call optimisation
|
||||
WHEN "macroexpand"
|
||||
=FNmacroexpand(FNnth(ast%, 1), env%)
|
||||
GOTO 31416
|
||||
WHEN "try*"
|
||||
=FNtry_catch(ast%, env%)
|
||||
OTHERWISE
|
||||
specialform% = FALSE
|
||||
car% = FNenv_get(env%, key$)
|
||||
ENDCASE
|
||||
ELSE
|
||||
car% = FNEVAL(car%, env%)
|
||||
ENDIF
|
||||
IF NOT specialform% THEN
|
||||
REM This is the "apply" part.
|
||||
ast% = FNrest(ast%)
|
||||
IF FNis_macro(car%) THEN
|
||||
ast% = FNEVAL(FNfn_ast(car%), FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%))
|
||||
GOTO 31416
|
||||
ENDIF
|
||||
ast% = FNeval_ast(ast%, env%)
|
||||
car% = FNfirst(ast%)
|
||||
IF FNis_corefn(car%) THEN
|
||||
=FNcore_call(FNunbox_corefn(car%), FNrest(ast%))
|
||||
=FNcore_call(FNunbox_corefn(car%), ast%)
|
||||
ENDIF
|
||||
IF FNis_fn(car%) THEN
|
||||
env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), FNrest(ast%))
|
||||
env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%)
|
||||
ast% = FNfn_ast(car%)
|
||||
REM Loop round for tail-call optimisation.
|
||||
ELSE
|
||||
GOTO 31416
|
||||
ENDIF
|
||||
ERROR &40E80918, "Not a function"
|
||||
ENDIF
|
||||
ENDIF
|
||||
UNTIL FALSE
|
||||
|
||||
DEF FNPRINT(a%)
|
||||
=FNunbox_string(FNpr_str(a%, TRUE))
|
||||
@ -240,26 +234,8 @@ DEF FNrep(a$)
|
||||
=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))
|
||||
|
||||
DEF FNeval_ast(ast%, env%)
|
||||
LOCAL val%, car%, cdr%, map%, keys%, key$
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, ast%)
|
||||
IF FNis_seq(ast%) THEN
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
car% = FNEVAL(FNfirst(ast%), env%)
|
||||
cdr% = FNeval_ast(FNrest(ast%), env%)
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(car%, cdr%)
|
||||
=FNalloc_pair(car%, cdr%)
|
||||
ENDIF
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
map% = FNempty_hashmap
|
||||
keys% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(keys%)
|
||||
key$ = FNunbox_string(FNfirst(keys%))
|
||||
map% = FNhashmap_set(map%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
keys% = FNrest(keys%)
|
||||
ENDWHILE
|
||||
=map%
|
||||
ENDIF
|
||||
=ast%
|
||||
=FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))
|
||||
|
||||
DEF FNget_argv
|
||||
PROCgc_enter
|
||||
|
@ -13,7 +13,7 @@ PROCcore_ns : REM This sets the data pointer
|
||||
REPEAT
|
||||
READ sym$, i%
|
||||
IF sym$ <> "" THEN
|
||||
PROCenv_set(repl_env%, FNalloc_symbol(sym$), FNalloc_corefn(i%))
|
||||
PROCenv_set(repl_env%, sym$, FNalloc_corefn(i%))
|
||||
ENDIF
|
||||
UNTIL sym$ = ""
|
||||
|
||||
@ -32,9 +32,9 @@ UNTIL form$ = ""
|
||||
argv% = FNget_argv
|
||||
|
||||
IF FNis_empty(argv%) THEN
|
||||
PROCenv_set(repl_env%, FNalloc_symbol("*ARGV*"), FNempty)
|
||||
PROCenv_set(repl_env%, "*ARGV*", FNempty)
|
||||
ELSE
|
||||
PROCenv_set(repl_env%, FNalloc_symbol("*ARGV*"), FNrest(argv%))
|
||||
PROCenv_set(repl_env%, "*ARGV*", FNrest(argv%))
|
||||
val$ = FNrep("(load-file " + FNunbox_string(FNpr_str(FNfirst(argv%), TRUE)) + ")")
|
||||
END
|
||||
ENDIF
|
||||
@ -84,27 +84,6 @@ DEF FNquasiquote(ast%)
|
||||
ENDIF
|
||||
=ast%
|
||||
|
||||
DEF FNis_macro_call(ast%, env%)
|
||||
LOCAL car%, val%
|
||||
IF NOT FNis_list(ast%) THEN =FALSE
|
||||
car% = FNfirst(ast%)
|
||||
IF NOT FNis_symbol(car%) THEN =FALSE
|
||||
IF FNis_nil(FNenv_find(env%, car%)) THEN =FALSE
|
||||
val% = FNenv_get(env%, car%)
|
||||
=FNis_macro(val%)
|
||||
|
||||
DEF FNmacroexpand(ast%, env%)
|
||||
LOCAL mac%, macenv%, macast%
|
||||
WHILE FNis_macro_call(ast%, env%)
|
||||
REM PRINT "expanded ";FNpr_str(ast%, TRUE);
|
||||
mac% = FNenv_get(env%, FNfirst(ast%))
|
||||
macenv% = FNnew_env(FNfn_env(mac%), FNfn_params(mac%), FNrest(ast%))
|
||||
macast% = FNfn_ast(mac%)
|
||||
ast% = FNEVAL(macast%, macenv%)
|
||||
REM PRINT " to ";FNpr_str(ast%, TRUE)
|
||||
ENDWHILE
|
||||
=ast%
|
||||
|
||||
DEF FNtry_catch(ast%, env%)
|
||||
LOCAL is_error%, ret%
|
||||
REM If there's no 'catch*' clause then we just evaluate the 'try*'.
|
||||
@ -152,37 +131,52 @@ DEF FNEVAL(ast%, env%)
|
||||
=FNgc_exit(FNEVAL_(ast%, env%))
|
||||
|
||||
DEF FNEVAL_(ast%, env%)
|
||||
LOCAL car%, specialform%, val%, bindings%
|
||||
REPEAT
|
||||
LOCAL car%, val%, bindings%, key$
|
||||
31416 REM tail call optimization loop
|
||||
PROCgc_keep_only2(ast%, env%)
|
||||
IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%)
|
||||
val% = FNenv_find(env%, "DEBUG-EVAL")
|
||||
IF NOT FNis_nil(val%) THEN
|
||||
IF FNis_truish(FNenv_get(val%, "DEBUG-EVAL")) THEN
|
||||
PRINT "EVAL: " + FNunbox_string(FNpr_str(ast%, TRUE))
|
||||
ENDIF
|
||||
ENDIF
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, FNunbox_symbol(ast%))
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
val% = FNempty_hashmap
|
||||
bindings% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
key$ = FNunbox_string(FNfirst(bindings%))
|
||||
val% = FNhashmap_set(val%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
bindings% = FNrest(bindings%)
|
||||
ENDWHILE
|
||||
=val%
|
||||
ENDIF
|
||||
IF NOT FNis_seq(ast%) THEN =ast%
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
ast% = FNmacroexpand(ast%, env%)
|
||||
IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%)
|
||||
car% = FNfirst(ast%)
|
||||
specialform% = FALSE
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(FNEVAL(car%, env%), FNeval_ast(FNrest(ast%), env%))
|
||||
IF FNis_symbol(car%) THEN
|
||||
specialform% = TRUE
|
||||
CASE FNunbox_symbol(car%) OF
|
||||
key$ = FNunbox_symbol(car%)
|
||||
CASE key$ OF
|
||||
REM Special forms
|
||||
WHEN "def!"
|
||||
val% = FNEVAL(FNnth(ast%, 2), env%)
|
||||
PROCenv_set(env%, FNnth(ast%, 1), val%)
|
||||
PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)
|
||||
=val%
|
||||
WHEN "defmacro!"
|
||||
val% = FNEVAL(FNnth(ast%, 2), env%)
|
||||
IF FNis_fn(val%) THEN val% = FNas_macro(val%)
|
||||
PROCenv_set(env%, FNnth(ast%, 1), val%)
|
||||
PROCenv_set(env%, FNunbox_symbol(FNnth(ast%, 1)), val%)
|
||||
=val%
|
||||
WHEN "let*"
|
||||
env% = FNalloc_environment(env%)
|
||||
bindings% = FNnth(ast%, 1)
|
||||
WHILE NOT FNis_empty(bindings%)
|
||||
PROCenv_set(env%, FNfirst(bindings%), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
PROCenv_set(env%, FNunbox_symbol(FNfirst(bindings%)), FNEVAL(FNnth(bindings%, 1), env%))
|
||||
bindings% = FNrest(FNrest(bindings%))
|
||||
ENDWHILE
|
||||
ast% = FNnth(ast%, 2)
|
||||
REM Loop round for tail-call optimisation.
|
||||
GOTO 31416
|
||||
WHEN "do"
|
||||
REM The guide has us call FNeval_ast on the sub-list that excludes
|
||||
REM the last element of ast%, but that's a bit painful without
|
||||
@ -194,46 +188,46 @@ DEF FNEVAL_(ast%, env%)
|
||||
ast% = FNrest(ast%)
|
||||
ENDWHILE
|
||||
ast% = FNfirst(ast%)
|
||||
GOTO 31416
|
||||
WHEN "if"
|
||||
IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN
|
||||
ast% = FNnth(ast%, 2)
|
||||
ELSE
|
||||
IF FNcount(ast%) = 3 THEN =FNnil ELSE ast% = FNnth(ast%, 3)
|
||||
IF FNcount(ast%) = 3 THEN =FNnil
|
||||
ast% = FNnth(ast%, 3)
|
||||
ENDIF
|
||||
REM Loop round for tail-call optimisation.
|
||||
GOTO 31416
|
||||
WHEN "fn*"
|
||||
=FNalloc_fn(FNnth(ast%, 2), FNnth(ast%, 1), env%)
|
||||
WHEN "quote"
|
||||
=FNnth(ast%, 1)
|
||||
WHEN "quasiquoteexpand"
|
||||
= FNquasiquote(FNnth(ast%, 1))
|
||||
WHEN "quasiquote"
|
||||
ast% = FNquasiquote(FNnth(ast%, 1))
|
||||
REM Loop round for tail-call optimisation
|
||||
WHEN "macroexpand"
|
||||
=FNmacroexpand(FNnth(ast%, 1), env%)
|
||||
GOTO 31416
|
||||
WHEN "try*"
|
||||
=FNtry_catch(ast%, env%)
|
||||
OTHERWISE
|
||||
specialform% = FALSE
|
||||
car% = FNenv_get(env%, key$)
|
||||
ENDCASE
|
||||
ELSE
|
||||
car% = FNEVAL(car%, env%)
|
||||
ENDIF
|
||||
IF NOT specialform% THEN
|
||||
REM This is the "apply" part.
|
||||
ast% = FNrest(ast%)
|
||||
IF FNis_macro(car%) THEN
|
||||
ast% = FNEVAL(FNfn_ast(car%), FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%))
|
||||
GOTO 31416
|
||||
ENDIF
|
||||
ast% = FNeval_ast(ast%, env%)
|
||||
car% = FNfirst(ast%)
|
||||
IF FNis_corefn(car%) THEN
|
||||
=FNcore_call(FNunbox_corefn(car%), FNrest(ast%))
|
||||
=FNcore_call(FNunbox_corefn(car%), ast%)
|
||||
ENDIF
|
||||
IF FNis_fn(car%) THEN
|
||||
env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), FNrest(ast%))
|
||||
env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), ast%)
|
||||
ast% = FNfn_ast(car%)
|
||||
REM Loop round for tail-call optimisation.
|
||||
ELSE
|
||||
GOTO 31416
|
||||
ENDIF
|
||||
ERROR &40E80918, "Not a function"
|
||||
ENDIF
|
||||
ENDIF
|
||||
UNTIL FALSE
|
||||
|
||||
DEF FNPRINT(a%)
|
||||
=FNunbox_string(FNpr_str(a%, TRUE))
|
||||
@ -242,26 +236,8 @@ DEF FNrep(a$)
|
||||
=FNPRINT(FNEVAL(FNREAD(a$), repl_env%))
|
||||
|
||||
DEF FNeval_ast(ast%, env%)
|
||||
LOCAL val%, car%, cdr%, map%, keys%, key$
|
||||
IF FNis_symbol(ast%) THEN =FNenv_get(env%, ast%)
|
||||
IF FNis_seq(ast%) THEN
|
||||
IF FNis_empty(ast%) THEN =ast%
|
||||
car% = FNEVAL(FNfirst(ast%), env%)
|
||||
cdr% = FNeval_ast(FNrest(ast%), env%)
|
||||
IF FNis_vector(ast%) THEN =FNalloc_vector_pair(car%, cdr%)
|
||||
=FNalloc_pair(car%, cdr%)
|
||||
ENDIF
|
||||
IF FNis_hashmap(ast%) THEN
|
||||
map% = FNempty_hashmap
|
||||
keys% = FNhashmap_keys(ast%)
|
||||
WHILE NOT FNis_empty(keys%)
|
||||
key$ = FNunbox_string(FNfirst(keys%))
|
||||
map% = FNhashmap_set(map%, key$, FNEVAL(FNhashmap_get(ast%, key$), env%))
|
||||
keys% = FNrest(keys%)
|
||||
ENDWHILE
|
||||
=map%
|
||||
ENDIF
|
||||
=ast%
|
||||
=FNalloc_pair(FNEVAL(FNfirst(ast%), env%), FNeval_ast(FNrest(ast%), env%))
|
||||
|
||||
DEF FNget_argv
|
||||
PROCgc_enter
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM ubuntu:bionic
|
||||
FROM ubuntu:24.04
|
||||
MAINTAINER Duncan Watts <fungiblecog@gmail.com>
|
||||
|
||||
##########################################################
|
||||
@ -9,10 +9,8 @@ MAINTAINER Duncan Watts <fungiblecog@gmail.com>
|
||||
RUN apt-get -y update
|
||||
|
||||
# Required for running tests
|
||||
RUN apt-get -y install make python
|
||||
|
||||
# Some typical implementation and test requirements
|
||||
#RUN apt-get -y install curl
|
||||
RUN apt-get -y install make python3
|
||||
RUN ln -fs /usr/bin/python3 /usr/local/bin/python
|
||||
|
||||
RUN mkdir -p /mal
|
||||
WORKDIR /mal
|
||||
|
@ -15,7 +15,7 @@ Env* env_make(Env* outer, list symbol_list, list exprs_list, MalType* more_symbo
|
||||
|
||||
while (symbol_list) {
|
||||
|
||||
env = env_set(env, symbol_list->data, exprs_list->data);
|
||||
env_set(env, ((MalType*)symbol_list->data)->value.mal_symbol, exprs_list->data);
|
||||
|
||||
symbol_list = symbol_list->next;
|
||||
exprs_list = exprs_list->next;
|
||||
@ -23,45 +23,28 @@ Env* env_make(Env* outer, list symbol_list, list exprs_list, MalType* more_symbo
|
||||
|
||||
/* set the 'more' symbol if there is one */
|
||||
if (more_symbol) {
|
||||
env = env_set(env, more_symbol, make_list(exprs_list));
|
||||
env_set(env, more_symbol->value.mal_symbol, make_list(exprs_list));
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
Env* env_set(Env* current, MalType* symbol, MalType* value) {
|
||||
void env_set(Env* current, char* symbol, MalType* value) {
|
||||
|
||||
current->data = hashmap_put(current->data, symbol, value);
|
||||
|
||||
current->data = hashmap_put(current->data, symbol->value.mal_symbol, value);
|
||||
return current;
|
||||
}
|
||||
|
||||
Env* env_find(Env* current, MalType* symbol) {
|
||||
MalType* env_get(Env* current, char* symbol) {
|
||||
|
||||
MalType* val = hashmap_get(current->data, symbol->value.mal_symbol);
|
||||
MalType* val = hashmap_get(current->data, symbol);
|
||||
|
||||
if (val) {
|
||||
return current;
|
||||
return val;
|
||||
}
|
||||
else if (current->outer) {
|
||||
return env_find(current->outer, symbol);
|
||||
return env_get(current->outer, symbol);
|
||||
}
|
||||
else {
|
||||
return NULL; /* not found */
|
||||
}
|
||||
}
|
||||
|
||||
MalType* env_get(Env* current, MalType* symbol) {
|
||||
|
||||
Env* env = env_find(current, symbol);
|
||||
|
||||
if (env) {
|
||||
return hashmap_get(env->data, symbol->value.mal_symbol);
|
||||
}
|
||||
else {
|
||||
return make_error_fmt("'%s' not found", symbol->value.mal_symbol);
|
||||
}
|
||||
}
|
||||
|
||||
Env* env_set_C_fn(Env* current, char* symbol_name, MalType*(*fn)(list)) {
|
||||
|
||||
return env_set(current, make_symbol(symbol_name), make_function(fn));
|
||||
}
|
||||
|
@ -15,9 +15,7 @@ struct Env_s {
|
||||
};
|
||||
|
||||
Env* env_make(Env* outer, list binds, list exprs, MalType* variadic_symbol);
|
||||
Env* env_set(Env* current, MalType* symbol, MalType* value);
|
||||
Env* env_set_C_fn(Env* current, char* symbol_name, MalType*(*fn)(list));
|
||||
MalType* env_get(Env* current, MalType* symbol);
|
||||
Env* env_find(Env* current, MalType* symbol);
|
||||
void env_set(Env* current, char* symbol, MalType* value);
|
||||
MalType* env_get(Env* current, char* symbol);
|
||||
|
||||
#endif
|
||||
|
@ -21,13 +21,41 @@ MalType* READ(char* str) {
|
||||
MalType* EVAL(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
MalType* eval_ast(MalType* ast, Env* env);
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
|
||||
/* printf("EVAL: %s\n", pr_str(ast, READABLY)); */
|
||||
|
||||
/* NULL */
|
||||
if (!ast) { return make_nil(); }
|
||||
|
||||
if (is_symbol(ast)) {
|
||||
MalType* symbol_value = hashmap_get(env->data, ast->value.mal_symbol);
|
||||
if (symbol_value)
|
||||
return symbol_value;
|
||||
else
|
||||
return make_error_fmt("'%s' not found", ast->value.mal_symbol);
|
||||
}
|
||||
|
||||
if (is_vector(ast)) {
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_vector(result);
|
||||
}
|
||||
|
||||
if (is_hashmap(ast)) {
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_hashmap(result);
|
||||
}
|
||||
|
||||
/* not a list */
|
||||
if (!is_list(ast)) { return eval_ast(ast, env); }
|
||||
if (!is_list(ast)) { return ast; }
|
||||
|
||||
/* empty list */
|
||||
if (ast->value.mal_list == NULL) { return ast; }
|
||||
@ -35,12 +63,10 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
/* list */
|
||||
|
||||
/* evaluate the list */
|
||||
MalType* evaluated_list = eval_ast(ast, env);
|
||||
|
||||
if (is_error(evaluated_list)) { return evaluated_list; }
|
||||
list evlst = evaluate_list(ast->value.mal_list, env);
|
||||
if (is_error(evlst->data)) return evlst->data;
|
||||
|
||||
/* apply the first element of the list to the arguments */
|
||||
list evlst = evaluated_list->value.mal_list;
|
||||
MalType* func = evlst->data;
|
||||
|
||||
if (is_function(func)) {
|
||||
@ -112,58 +138,6 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MalType* eval_ast(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
|
||||
if (is_symbol(ast)) {
|
||||
|
||||
MalType* symbol_value = hashmap_get(env->data, ast->value.mal_symbol);
|
||||
|
||||
if (symbol_value) {
|
||||
return symbol_value;
|
||||
} else {
|
||||
return make_error_fmt("var '%s' not found", pr_str(ast, UNREADABLY));
|
||||
}
|
||||
}
|
||||
else if (is_list(ast)) {
|
||||
|
||||
list result = evaluate_list(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_list(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_vector(ast)) {
|
||||
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_vector(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_hashmap(ast)) {
|
||||
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_hashmap(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
list evaluate_list(list lst, Env* env) {
|
||||
|
||||
list evlst = NULL;
|
||||
|
@ -24,15 +24,45 @@ MalType* READ(char* str) {
|
||||
MalType* EVAL(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
MalType* eval_ast(MalType* ast, Env* env);
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
MalType* eval_defbang(MalType* ast, Env* env);
|
||||
MalType* eval_letstar(MalType* ast, Env* env);
|
||||
|
||||
MalType* dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && ! is_false(dbgeval) && ! is_nil(dbgeval))
|
||||
printf("EVAL: %s\n", pr_str(ast, READABLY));
|
||||
|
||||
/* NULL */
|
||||
if (!ast) { return make_nil(); }
|
||||
|
||||
if (is_symbol(ast)) {
|
||||
MalType* symbol_value = env_get(env, ast->value.mal_symbol);
|
||||
if (symbol_value)
|
||||
return symbol_value;
|
||||
else
|
||||
return make_error_fmt("'%s' not found", ast->value.mal_symbol);
|
||||
}
|
||||
|
||||
if (is_vector(ast)) {
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_vector(result);
|
||||
}
|
||||
|
||||
if (is_hashmap(ast)) {
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_hashmap(result);
|
||||
}
|
||||
|
||||
/* not a list */
|
||||
if (!is_list(ast)) { return eval_ast(ast, env); }
|
||||
if (!is_list(ast)) { return ast; }
|
||||
|
||||
/* empty list */
|
||||
if (ast->value.mal_list == NULL) { return ast; }
|
||||
@ -52,12 +82,10 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
}
|
||||
}
|
||||
/* first element is not a special symbol */
|
||||
MalType* evaluated_list = eval_ast(ast, env);
|
||||
|
||||
if (is_error(evaluated_list)) { return evaluated_list; }
|
||||
list evlst = evaluate_list(ast->value.mal_list, env);
|
||||
if (is_error(evlst->data)) return evlst->data;
|
||||
|
||||
/* apply the first element of the list to the arguments */
|
||||
list evlst = evaluated_list->value.mal_list;
|
||||
MalType* func = evlst->data;
|
||||
|
||||
if (is_function(func)) {
|
||||
@ -92,10 +120,10 @@ int main(int argc, char** argv) {
|
||||
puts("Press Ctrl+d to exit\n");
|
||||
|
||||
Env* repl_env = env_make(NULL, NULL, NULL, NULL);
|
||||
repl_env = env_set_C_fn(repl_env, "+", mal_add);
|
||||
repl_env = env_set_C_fn(repl_env, "-", mal_sub);
|
||||
repl_env = env_set_C_fn(repl_env, "*", mal_mul);
|
||||
repl_env = env_set_C_fn(repl_env, "/", mal_div);
|
||||
env_set(repl_env, "+", make_function(mal_add));
|
||||
env_set(repl_env, "-", make_function(mal_sub));
|
||||
env_set(repl_env, "*", make_function(mal_mul));
|
||||
env_set(repl_env, "/", make_function(mal_div));
|
||||
|
||||
while (1) {
|
||||
|
||||
@ -122,58 +150,6 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MalType* eval_ast(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
|
||||
if (is_symbol(ast)) {
|
||||
|
||||
MalType* symbol_value = env_get(env, ast);
|
||||
|
||||
if (symbol_value) {
|
||||
return symbol_value;
|
||||
} else {
|
||||
return make_error_fmt("var '%s' not found", pr_str(ast, UNREADABLY));
|
||||
}
|
||||
}
|
||||
else if (is_list(ast)) {
|
||||
|
||||
list result = evaluate_list(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_list(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_vector(ast)) {
|
||||
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_vector(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_hashmap(ast)) {
|
||||
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_hashmap(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalType* eval_defbang(MalType* ast, Env* env) {
|
||||
|
||||
list lst = (ast->value.mal_list)->next;
|
||||
@ -185,7 +161,7 @@ MalType* eval_defbang(MalType* ast, Env* env) {
|
||||
MalType* result = EVAL(defbang_value, env);
|
||||
|
||||
if (!is_error(result)) {
|
||||
env_set(env, defbang_symbol, result);
|
||||
env_set(env, defbang_symbol->value.mal_symbol, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -206,7 +182,7 @@ MalType* eval_letstar(MalType* ast, Env* env) {
|
||||
|
||||
MalType* symbol = letstar_bindings_list->data;
|
||||
MalType* value = letstar_bindings_list->next->data;
|
||||
letstar_env = env_set(letstar_env, symbol, EVAL(value, letstar_env));
|
||||
env_set(letstar_env, symbol->value.mal_symbol, EVAL(value, letstar_env));
|
||||
|
||||
letstar_bindings_list = letstar_bindings_list->next->next; /* pop symbol and value*/
|
||||
}
|
||||
|
@ -28,18 +28,48 @@ MalType* READ(char* str) {
|
||||
MalType* EVAL(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
MalType* eval_ast(MalType* ast, Env* env);
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
MalType* eval_defbang(MalType* ast, Env* env);
|
||||
MalType* eval_letstar(MalType* ast, Env* env);
|
||||
MalType* eval_if(MalType* ast, Env* env);
|
||||
MalType* eval_fnstar(MalType* ast, Env* env);
|
||||
MalType* eval_do(MalType* ast, Env* env);
|
||||
|
||||
MalType* dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && ! is_false(dbgeval) && ! is_nil(dbgeval))
|
||||
printf("EVAL: %s\n", pr_str(ast, READABLY));
|
||||
|
||||
/* NULL */
|
||||
if (!ast) { return make_nil(); }
|
||||
|
||||
if (is_symbol(ast)) {
|
||||
MalType* symbol_value = env_get(env, ast->value.mal_symbol);
|
||||
if (symbol_value)
|
||||
return symbol_value;
|
||||
else
|
||||
return make_error_fmt("'%s' not found", ast->value.mal_symbol);
|
||||
}
|
||||
|
||||
if (is_vector(ast)) {
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_vector(result);
|
||||
}
|
||||
|
||||
if (is_hashmap(ast)) {
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_hashmap(result);
|
||||
}
|
||||
|
||||
/* not a list */
|
||||
if (!is_list(ast)) { return eval_ast(ast, env); }
|
||||
if (!is_list(ast)) { return ast; }
|
||||
|
||||
/* empty list */
|
||||
if (ast->value.mal_list == NULL) { return ast; }
|
||||
@ -68,12 +98,10 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
}
|
||||
}
|
||||
/* first element is not a special symbol */
|
||||
MalType* evaluated_list = eval_ast(ast, env);
|
||||
|
||||
if (is_error(evaluated_list)) { return evaluated_list; }
|
||||
list evlst = evaluate_list(ast->value.mal_list, env);
|
||||
if (is_error(evlst->data)) return evlst->data;
|
||||
|
||||
/* apply the first element of the list to the arguments */
|
||||
list evlst = evaluated_list->value.mal_list;
|
||||
MalType* func = evlst->data;
|
||||
|
||||
if (is_function(func)) {
|
||||
@ -131,7 +159,7 @@ int main(int argc, char** argv) {
|
||||
char* symbol = mappings->data;
|
||||
MalType*(*function)(list) = (MalType*(*)(list))mappings->next->data;
|
||||
|
||||
env_set_C_fn(repl_env, symbol, function);
|
||||
env_set(repl_env, symbol, make_function(function));
|
||||
|
||||
/* pop symbol and function from hashmap/list */
|
||||
mappings = mappings->next->next;
|
||||
@ -166,58 +194,6 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MalType* eval_ast(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
|
||||
if (is_symbol(ast)) {
|
||||
|
||||
MalType* symbol_value = env_get(env, ast);
|
||||
|
||||
if (symbol_value) {
|
||||
return symbol_value;
|
||||
} else {
|
||||
return make_error_fmt("var '%s' not found", pr_str(ast, UNREADABLY));
|
||||
}
|
||||
}
|
||||
else if (is_list(ast)) {
|
||||
|
||||
list result = evaluate_list(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_list(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_vector(ast)) {
|
||||
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_vector(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_hashmap(ast)) {
|
||||
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_hashmap(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalType* eval_defbang(MalType* ast, Env* env) {
|
||||
|
||||
list lst = (ast->value.mal_list)->next;
|
||||
@ -236,7 +212,7 @@ MalType* eval_defbang(MalType* ast, Env* env) {
|
||||
MalType* result = EVAL(defbang_value, env);
|
||||
|
||||
if (!is_error(result)){
|
||||
env = env_set(env, defbang_symbol, result);
|
||||
env_set(env, defbang_symbol->value.mal_symbol, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -272,7 +248,7 @@ MalType* eval_letstar(MalType* ast, Env* env) {
|
||||
/* early return from error */
|
||||
if (is_error(value)) { return value; }
|
||||
|
||||
env_set(letstar_env, symbol, value);
|
||||
env_set(letstar_env, symbol->value.mal_symbol, value);
|
||||
bindings_list = bindings_list->next->next;
|
||||
}
|
||||
return EVAL(forms, letstar_env);
|
||||
|
@ -28,7 +28,9 @@ MalType* READ(char* str) {
|
||||
MalType* EVAL(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
MalType* eval_ast(MalType* ast, Env* env);
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
MalType* eval_defbang(MalType* ast, Env** env);
|
||||
void eval_letstar(MalType** ast, Env** env);
|
||||
void eval_if(MalType** ast, Env** env);
|
||||
@ -38,11 +40,39 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
/* Use goto to jump here rather than calling eval for tail-call elimination */
|
||||
TCE_entry_point:
|
||||
|
||||
MalType* dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && ! is_false(dbgeval) && ! is_nil(dbgeval))
|
||||
printf("EVAL: %s\n", pr_str(ast, READABLY));
|
||||
|
||||
/* NULL */
|
||||
if (!ast) { return make_nil(); }
|
||||
|
||||
if (is_symbol(ast)) {
|
||||
MalType* symbol_value = env_get(env, ast->value.mal_symbol);
|
||||
if (symbol_value)
|
||||
return symbol_value;
|
||||
else
|
||||
return make_error_fmt("'%s' not found", ast->value.mal_symbol);
|
||||
}
|
||||
|
||||
if (is_vector(ast)) {
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_vector(result);
|
||||
}
|
||||
|
||||
if (is_hashmap(ast)) {
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_hashmap(result);
|
||||
}
|
||||
|
||||
/* not a list */
|
||||
if (!is_list(ast)) { return eval_ast(ast, env); }
|
||||
if (!is_list(ast)) { return ast; }
|
||||
|
||||
/* empty list */
|
||||
if (ast->value.mal_list == NULL) { return ast; }
|
||||
@ -86,10 +116,10 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
}
|
||||
}
|
||||
/* first element is not a special symbol */
|
||||
MalType* evaluated_list = eval_ast(ast, env);
|
||||
list evlst = evaluate_list(ast->value.mal_list, env);
|
||||
if (is_error(evlst->data)) return evlst->data;
|
||||
|
||||
/* apply the first element of the list to the arguments */
|
||||
list evlst = evaluated_list->value.mal_list;
|
||||
MalType* func = evlst->data;
|
||||
|
||||
if (is_function(func)) {
|
||||
@ -151,7 +181,7 @@ int main(int argc, char** argv) {
|
||||
char* symbol = mappings->data;
|
||||
MalType*(*function)(list) = (MalType*(*)(list))mappings->next->data;
|
||||
|
||||
env_set_C_fn(repl_env, symbol, function);
|
||||
env_set(repl_env, symbol, make_function(function));
|
||||
|
||||
/* pop symbol and function from hashmap/list */
|
||||
mappings = mappings->next->next;
|
||||
@ -186,58 +216,6 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MalType* eval_ast(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
|
||||
if (is_symbol(ast)) {
|
||||
|
||||
MalType* symbol_value = env_get(env, ast);
|
||||
|
||||
if (symbol_value) {
|
||||
return symbol_value;
|
||||
} else {
|
||||
return make_error_fmt("var '%s' not found", pr_str(ast, UNREADABLY));
|
||||
}
|
||||
}
|
||||
else if (is_list(ast)) {
|
||||
|
||||
list result = evaluate_list(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_list(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_vector(ast)) {
|
||||
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_vector(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_hashmap(ast)) {
|
||||
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_hashmap(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalType* eval_defbang(MalType* ast, Env** env) {
|
||||
|
||||
list lst = (ast->value.mal_list)->next;
|
||||
@ -256,7 +234,7 @@ MalType* eval_defbang(MalType* ast, Env** env) {
|
||||
MalType* result = EVAL(defbang_value, *env);
|
||||
|
||||
if (!is_error(result)){
|
||||
*env = env_set(*env, defbang_symbol, result);
|
||||
env_set(*env, defbang_symbol->value.mal_symbol, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -298,7 +276,7 @@ void eval_letstar(MalType** ast, Env** env) {
|
||||
return;
|
||||
}
|
||||
|
||||
env_set(letstar_env, symbol, value);
|
||||
env_set(letstar_env, symbol->value.mal_symbol, value);
|
||||
bindings_list = bindings_list->next->next;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,9 @@ MalType* READ(char* str) {
|
||||
MalType* EVAL(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
MalType* eval_ast(MalType* ast, Env* env);
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
MalType* eval_defbang(MalType* ast, Env** env);
|
||||
void eval_letstar(MalType** ast, Env** env);
|
||||
void eval_if(MalType** ast, Env** env);
|
||||
@ -38,11 +40,39 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
/* Use goto to jump here rather than calling eval for tail-call elimination */
|
||||
TCE_entry_point:
|
||||
|
||||
MalType* dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && ! is_false(dbgeval) && ! is_nil(dbgeval))
|
||||
printf("EVAL: %s\n", pr_str(ast, READABLY));
|
||||
|
||||
/* NULL */
|
||||
if (!ast) { return make_nil(); }
|
||||
|
||||
if (is_symbol(ast)) {
|
||||
MalType* symbol_value = env_get(env, ast->value.mal_symbol);
|
||||
if (symbol_value)
|
||||
return symbol_value;
|
||||
else
|
||||
return make_error_fmt("'%s' not found", ast->value.mal_symbol);
|
||||
}
|
||||
|
||||
if (is_vector(ast)) {
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_vector(result);
|
||||
}
|
||||
|
||||
if (is_hashmap(ast)) {
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_hashmap(result);
|
||||
}
|
||||
|
||||
/* not a list */
|
||||
if (!is_list(ast)) { return eval_ast(ast, env); }
|
||||
if (!is_list(ast)) { return ast; }
|
||||
|
||||
/* empty list */
|
||||
if (ast->value.mal_list == NULL) { return ast; }
|
||||
@ -86,12 +116,10 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
}
|
||||
}
|
||||
/* first element is not a special symbol */
|
||||
MalType* evaluated_list = eval_ast(ast, env);
|
||||
|
||||
if (is_error(evaluated_list)) { return evaluated_list; }
|
||||
list evlst = evaluate_list(ast->value.mal_list, env);
|
||||
if (is_error(evlst->data)) return evlst->data;
|
||||
|
||||
/* apply the first element of the list to the arguments */
|
||||
list evlst = evaluated_list->value.mal_list;
|
||||
MalType* func = evlst->data;
|
||||
|
||||
if (is_function(func)) {
|
||||
@ -159,13 +187,13 @@ int main(int argc, char** argv) {
|
||||
char* symbol = mappings->data;
|
||||
MalType*(*function)(list) = (MalType*(*)(list))mappings->next->data;
|
||||
|
||||
env_set_C_fn(repl_env, symbol, function);
|
||||
env_set(repl_env, symbol, make_function(function));
|
||||
|
||||
/* pop symbol and function from hashmap/list */
|
||||
mappings = mappings->next->next;
|
||||
}
|
||||
|
||||
env_set_C_fn(repl_env, "eval", mal_eval);
|
||||
env_set(repl_env, "eval", make_function(mal_eval));
|
||||
|
||||
/* add functions written in mal - not using rep as it prints the result */
|
||||
EVAL(READ("(def! not (fn* (a) (if a false true)))"), repl_env);
|
||||
@ -177,7 +205,7 @@ int main(int argc, char** argv) {
|
||||
for (int i = 2; i < argc; i++) {
|
||||
lst = list_push(lst, make_string(argv[i]));
|
||||
}
|
||||
env_set(repl_env, make_symbol("*ARGV*"), make_list(list_reverse(lst)));
|
||||
env_set(repl_env, "*ARGV*", make_list(list_reverse(lst)));
|
||||
|
||||
/* run in script mode if a filename is given */
|
||||
if (argc > 1) {
|
||||
@ -218,58 +246,6 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MalType* eval_ast(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
|
||||
if (is_symbol(ast)) {
|
||||
|
||||
MalType* symbol_value = env_get(env, ast);
|
||||
|
||||
if (symbol_value) {
|
||||
return symbol_value;
|
||||
} else {
|
||||
return make_error_fmt("var '%s' not found", pr_str(ast, UNREADABLY));
|
||||
}
|
||||
}
|
||||
else if (is_list(ast)) {
|
||||
|
||||
list result = evaluate_list(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_list(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_vector(ast)) {
|
||||
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_vector(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_hashmap(ast)) {
|
||||
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_hashmap(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalType* eval_defbang(MalType* ast, Env** env) {
|
||||
|
||||
list lst = (ast->value.mal_list)->next;
|
||||
@ -288,7 +264,7 @@ MalType* eval_defbang(MalType* ast, Env** env) {
|
||||
MalType* result = EVAL(defbang_value, *env);
|
||||
|
||||
if (!is_error(result)){
|
||||
*env = env_set(*env, defbang_symbol, result);
|
||||
env_set(*env, defbang_symbol->value.mal_symbol, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -330,7 +306,7 @@ void eval_letstar(MalType** ast, Env** env) {
|
||||
return;
|
||||
}
|
||||
|
||||
env_set(letstar_env, symbol, value);
|
||||
env_set(letstar_env, symbol->value.mal_symbol, value);
|
||||
bindings_list = bindings_list->next->next;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,6 @@
|
||||
#define SYMBOL_FNSTAR "fn*"
|
||||
#define SYMBOL_QUOTE "quote"
|
||||
#define SYMBOL_QUASIQUOTE "quasiquote"
|
||||
#define SYMBOL_QUASIQUOTEEXPAND "quasiquoteexpand"
|
||||
#define SYMBOL_UNQUOTE "unquote"
|
||||
#define SYMBOL_SPLICE_UNQUOTE "splice-unquote"
|
||||
|
||||
@ -33,7 +32,9 @@ MalType* READ(char* str) {
|
||||
MalType* EVAL(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
MalType* eval_ast(MalType* ast, Env* env);
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
MalType* eval_defbang(MalType* ast, Env** env);
|
||||
void eval_letstar(MalType** ast, Env** env);
|
||||
void eval_if(MalType** ast, Env** env);
|
||||
@ -41,16 +42,43 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
MalType* eval_do(MalType* ast, Env* env);
|
||||
MalType* eval_quote(MalType* ast);
|
||||
MalType* eval_quasiquote(MalType* ast);
|
||||
MalType* eval_quasiquoteexpand(MalType* ast);
|
||||
|
||||
/* Use goto to jump here rather than calling eval for tail-call elimination */
|
||||
TCE_entry_point:
|
||||
|
||||
MalType* dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && ! is_false(dbgeval) && ! is_nil(dbgeval))
|
||||
printf("EVAL: %s\n", pr_str(ast, READABLY));
|
||||
|
||||
/* NULL */
|
||||
if (!ast) { return make_nil(); }
|
||||
|
||||
if (is_symbol(ast)) {
|
||||
MalType* symbol_value = env_get(env, ast->value.mal_symbol);
|
||||
if (symbol_value)
|
||||
return symbol_value;
|
||||
else
|
||||
return make_error_fmt("'%s' not found", ast->value.mal_symbol);
|
||||
}
|
||||
|
||||
if (is_vector(ast)) {
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_vector(result);
|
||||
}
|
||||
|
||||
if (is_hashmap(ast)) {
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_hashmap(result);
|
||||
}
|
||||
|
||||
/* not a list */
|
||||
if (!is_list(ast)) { return eval_ast(ast, env); }
|
||||
if (!is_list(ast)) { return ast; }
|
||||
|
||||
/* empty list */
|
||||
if (ast->value.mal_list == NULL) { return ast; }
|
||||
@ -102,20 +130,13 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
if (is_error(ast)) { return ast; }
|
||||
goto TCE_entry_point;
|
||||
}
|
||||
else if (strcmp(symbol, SYMBOL_QUASIQUOTEEXPAND) == 0) {
|
||||
|
||||
list lst = ast->value.mal_list;
|
||||
return eval_quasiquote(make_list(lst));
|
||||
}
|
||||
}
|
||||
|
||||
/* first element is not a special symbol */
|
||||
MalType* evaluated_list = eval_ast(ast, env);
|
||||
|
||||
if (is_error(evaluated_list)) { return evaluated_list; }
|
||||
list evlst = evaluate_list(ast->value.mal_list, env);
|
||||
if (is_error(evlst->data)) return evlst->data;
|
||||
|
||||
/* apply the first element of the list to the arguments */
|
||||
list evlst = evaluated_list->value.mal_list;
|
||||
MalType* func = evlst->data;
|
||||
|
||||
if (is_function(func)) {
|
||||
@ -183,13 +204,13 @@ int main(int argc, char** argv) {
|
||||
char* symbol = mappings->data;
|
||||
MalType*(*function)(list) = (MalType*(*)(list))mappings->next->data;
|
||||
|
||||
env_set_C_fn(repl_env, symbol, function);
|
||||
env_set(repl_env, symbol, make_function(function));
|
||||
|
||||
/* pop symbol and function from hashmap/list */
|
||||
mappings = mappings->next->next;
|
||||
}
|
||||
|
||||
env_set_C_fn(repl_env, "eval", mal_eval);
|
||||
env_set(repl_env, "eval", make_function(mal_eval));
|
||||
|
||||
/* add functions written in mal - not using rep as it prints the result */
|
||||
EVAL(READ("(def! not (fn* (a) (if a false true)))"), repl_env);
|
||||
@ -201,7 +222,7 @@ int main(int argc, char** argv) {
|
||||
for (long i = 2; i < argc; i++) {
|
||||
lst = list_push(lst, make_string(argv[i]));
|
||||
}
|
||||
env_set(repl_env, make_symbol("*ARGV*"), make_list(list_reverse(lst)));
|
||||
env_set(repl_env, "*ARGV*", make_list(list_reverse(lst)));
|
||||
|
||||
/* run in script mode if a filename is given */
|
||||
if (argc > 1) {
|
||||
@ -242,58 +263,6 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MalType* eval_ast(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
|
||||
if (is_symbol(ast)) {
|
||||
|
||||
MalType* symbol_value = env_get(env, ast);
|
||||
|
||||
if (symbol_value) {
|
||||
return symbol_value;
|
||||
} else {
|
||||
return make_error_fmt("var '%s' not found", pr_str(ast, UNREADABLY));
|
||||
}
|
||||
}
|
||||
else if (is_list(ast)) {
|
||||
|
||||
list result = evaluate_list(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_list(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_vector(ast)) {
|
||||
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_vector(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_hashmap(ast)) {
|
||||
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_hashmap(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalType* eval_defbang(MalType* ast, Env** env) {
|
||||
|
||||
list lst = (ast->value.mal_list)->next;
|
||||
@ -312,7 +281,7 @@ MalType* eval_defbang(MalType* ast, Env** env) {
|
||||
MalType* result = EVAL(defbang_value, *env);
|
||||
|
||||
if (!is_error(result)){
|
||||
*env = env_set(*env, defbang_symbol, result);
|
||||
env_set(*env, defbang_symbol->value.mal_symbol, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -354,7 +323,7 @@ void eval_letstar(MalType** ast, Env** env) {
|
||||
return;
|
||||
}
|
||||
|
||||
env_set(letstar_env, symbol, value);
|
||||
env_set(letstar_env, symbol->value.mal_symbol, value);
|
||||
bindings_list = bindings_list->next->next;
|
||||
}
|
||||
|
||||
|
@ -19,11 +19,9 @@
|
||||
#define SYMBOL_FNSTAR "fn*"
|
||||
#define SYMBOL_QUOTE "quote"
|
||||
#define SYMBOL_QUASIQUOTE "quasiquote"
|
||||
#define SYMBOL_QUASIQUOTEEXPAND "quasiquoteexpand"
|
||||
#define SYMBOL_UNQUOTE "unquote"
|
||||
#define SYMBOL_SPLICE_UNQUOTE "splice-unquote"
|
||||
#define SYMBOL_DEFMACROBANG "defmacro!"
|
||||
#define SYMBOL_MACROEXPAND "macroexpand"
|
||||
|
||||
#define PROMPT_STRING "user> "
|
||||
|
||||
@ -35,7 +33,10 @@ MalType* READ(char* str) {
|
||||
MalType* EVAL(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
MalType* eval_ast(MalType* ast, Env* env);
|
||||
MalType* apply(MalType* fn, list args);
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
MalType* eval_defbang(MalType* ast, Env** env);
|
||||
void eval_letstar(MalType** ast, Env** env);
|
||||
void eval_if(MalType** ast, Env** env);
|
||||
@ -43,23 +44,44 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
MalType* eval_do(MalType* ast, Env* env);
|
||||
MalType* eval_quote(MalType* ast);
|
||||
MalType* eval_quasiquote(MalType* ast);
|
||||
MalType* eval_quasiquoteexpand(MalType* ast);
|
||||
MalType* eval_defmacrobang(MalType*, Env** env);
|
||||
MalType* eval_macroexpand(MalType* ast, Env* env);
|
||||
MalType* macroexpand(MalType* ast, Env* env);
|
||||
|
||||
/* Use goto to jump here rather than calling eval for tail-call elimination */
|
||||
TCE_entry_point:
|
||||
|
||||
MalType* dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && ! is_false(dbgeval) && ! is_nil(dbgeval))
|
||||
printf("EVAL: %s\n", pr_str(ast, READABLY));
|
||||
|
||||
/* NULL */
|
||||
if (!ast) { return make_nil(); }
|
||||
|
||||
/* macroexpansion */
|
||||
ast = macroexpand(ast, env);
|
||||
if (is_error(ast)) { return ast; }
|
||||
if (is_symbol(ast)) {
|
||||
MalType* symbol_value = env_get(env, ast->value.mal_symbol);
|
||||
if (symbol_value)
|
||||
return symbol_value;
|
||||
else
|
||||
return make_error_fmt("'%s' not found", ast->value.mal_symbol);
|
||||
}
|
||||
|
||||
if (is_vector(ast)) {
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_vector(result);
|
||||
}
|
||||
|
||||
if (is_hashmap(ast)) {
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_hashmap(result);
|
||||
}
|
||||
|
||||
/* not a list */
|
||||
if (!is_list(ast)) { return eval_ast(ast, env); }
|
||||
if (!is_list(ast)) { return ast; }
|
||||
|
||||
/* empty list */
|
||||
if (ast->value.mal_list == NULL) { return ast; }
|
||||
@ -111,30 +133,25 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
if (is_error(ast)) { return ast; }
|
||||
goto TCE_entry_point;
|
||||
}
|
||||
else if (strcmp(symbol, SYMBOL_QUASIQUOTEEXPAND) == 0) {
|
||||
|
||||
list lst = ast->value.mal_list;
|
||||
return eval_quasiquote(make_list(lst));
|
||||
}
|
||||
else if (strcmp(symbol, SYMBOL_DEFMACROBANG) == 0) {
|
||||
return eval_defmacrobang(ast, &env);
|
||||
}
|
||||
else if (strcmp(symbol, SYMBOL_MACROEXPAND) == 0) {
|
||||
return eval_macroexpand(ast, env);
|
||||
}
|
||||
}
|
||||
|
||||
/* first element is not a special symbol */
|
||||
MalType* evaluated_list = eval_ast(ast, env);
|
||||
|
||||
if (is_error(evaluated_list)) { return evaluated_list; }
|
||||
MalType* func = EVAL(first, env);
|
||||
if (is_error(func)) { return func; }
|
||||
if (func->is_macro) {
|
||||
ast = apply(func, ast->value.mal_list->next);
|
||||
if (is_error(ast)) { return ast; }
|
||||
goto TCE_entry_point;
|
||||
}
|
||||
list evlst = evaluate_list(ast->value.mal_list->next, env);
|
||||
if (evlst && is_error(evlst->data)) { return evlst->data; }
|
||||
|
||||
/* apply the first element of the list to the arguments */
|
||||
list evlst = evaluated_list->value.mal_list;
|
||||
MalType* func = evlst->data;
|
||||
|
||||
if (is_function(func)) {
|
||||
return (*func->value.mal_function)(evlst->next);
|
||||
return (*func->value.mal_function)(evlst);
|
||||
}
|
||||
else if (is_closure(func)) {
|
||||
|
||||
@ -142,7 +159,7 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
list params = (closure->parameters)->value.mal_list;
|
||||
|
||||
long param_count = list_count(params);
|
||||
long arg_count = list_count(evlst->next);
|
||||
long arg_count = list_count(evlst);
|
||||
|
||||
if (param_count > arg_count) {
|
||||
return make_error("too few arguments supplied to function");
|
||||
@ -153,7 +170,7 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
else {
|
||||
|
||||
/* TCE - modify ast and env directly and jump back to eval */
|
||||
env = env_make(closure->env, params, evlst->next, closure->more_symbol);
|
||||
env = env_make(closure->env, params, evlst, closure->more_symbol);
|
||||
ast = func->value.mal_closure->definition;
|
||||
|
||||
if (is_error(ast)) { return ast; }
|
||||
@ -198,13 +215,13 @@ int main(int argc, char** argv) {
|
||||
char* symbol = mappings->data;
|
||||
MalType*(*function)(list) = (MalType*(*)(list))mappings->next->data;
|
||||
|
||||
env_set_C_fn(repl_env, symbol, function);
|
||||
env_set(repl_env, symbol, make_function(function));
|
||||
|
||||
/* pop symbol and function from hashmap/list */
|
||||
mappings = mappings->next->next;
|
||||
}
|
||||
|
||||
env_set_C_fn(repl_env, "eval", mal_eval);
|
||||
env_set(repl_env, "eval", make_function(mal_eval));
|
||||
|
||||
/* add functions written in mal - not using rep as it prints the result */
|
||||
EVAL(READ("(def! not (fn* (a) (if a false true)))"), repl_env);
|
||||
@ -216,7 +233,7 @@ int main(int argc, char** argv) {
|
||||
for (long i = 2; i < argc; i++) {
|
||||
lst = list_push(lst, make_string(argv[i]));
|
||||
}
|
||||
env_set(repl_env, make_symbol("*ARGV*"), make_list(list_reverse(lst)));
|
||||
env_set(repl_env, "*ARGV*", make_list(list_reverse(lst)));
|
||||
|
||||
/* run in script mode if a filename is given */
|
||||
if (argc > 1) {
|
||||
@ -257,58 +274,6 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MalType* eval_ast(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
|
||||
if (is_symbol(ast)) {
|
||||
|
||||
MalType* symbol_value = env_get(env, ast);
|
||||
|
||||
if (symbol_value) {
|
||||
return symbol_value;
|
||||
} else {
|
||||
return make_error_fmt("var '%s' not found", pr_str(ast, UNREADABLY));
|
||||
}
|
||||
}
|
||||
else if (is_list(ast)) {
|
||||
|
||||
list result = evaluate_list(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_list(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_vector(ast)) {
|
||||
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_vector(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_hashmap(ast)) {
|
||||
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_hashmap(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalType* eval_defbang(MalType* ast, Env** env) {
|
||||
|
||||
list lst = (ast->value.mal_list)->next;
|
||||
@ -327,7 +292,7 @@ MalType* eval_defbang(MalType* ast, Env** env) {
|
||||
MalType* result = EVAL(defbang_value, *env);
|
||||
|
||||
if (!is_error(result)){
|
||||
*env = env_set(*env, defbang_symbol, result);
|
||||
env_set(*env, defbang_symbol->value.mal_symbol, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -369,7 +334,7 @@ void eval_letstar(MalType** ast, Env** env) {
|
||||
return;
|
||||
}
|
||||
|
||||
env_set(letstar_env, symbol, value);
|
||||
env_set(letstar_env, symbol->value.mal_symbol, value);
|
||||
bindings_list = bindings_list->next->next;
|
||||
}
|
||||
|
||||
@ -668,51 +633,11 @@ MalType* eval_defmacrobang(MalType* ast, Env** env) {
|
||||
if (!is_error(result)) {
|
||||
result = copy_type(result);
|
||||
result->is_macro = 1;
|
||||
*env = env_set(*env, defbang_symbol, result);
|
||||
env_set(*env, defbang_symbol->value.mal_symbol, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MalType* eval_macroexpand(MalType* ast, Env* env) {
|
||||
|
||||
/* forward reference */
|
||||
MalType* macroexpand(MalType* ast, Env* env);
|
||||
|
||||
list lst = ast->value.mal_list;
|
||||
|
||||
if (!lst->next) {
|
||||
return make_nil();
|
||||
}
|
||||
else if (lst->next->next) {
|
||||
return make_error("'macroexpand': expected exactly one argument");
|
||||
}
|
||||
else {
|
||||
return macroexpand(lst->next->data, env);
|
||||
}
|
||||
}
|
||||
|
||||
MalType* macroexpand(MalType* ast, Env* env) {
|
||||
|
||||
/* forward reference */
|
||||
int is_macro_call(MalType* ast, Env* env);
|
||||
|
||||
while(is_macro_call(ast, env)) {
|
||||
|
||||
list lst = ast->value.mal_list;
|
||||
|
||||
MalType* macro_fn = env_get(env, lst->data);
|
||||
MalClosure* cls = macro_fn->value.mal_closure;
|
||||
MalType* more_symbol = cls->more_symbol;
|
||||
|
||||
list params_list = (cls->parameters)->value.mal_list;
|
||||
list args_list = lst->next;
|
||||
|
||||
env = env_make(cls->env, params_list, args_list, more_symbol);
|
||||
ast = EVAL(cls->definition, env);
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
list evaluate_list(list lst, Env* env) {
|
||||
|
||||
list evlst = NULL;
|
||||
@ -866,32 +791,3 @@ MalType* apply(MalType* fn, list args) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int is_macro_call(MalType* ast, Env* env) {
|
||||
|
||||
/* not a list */
|
||||
if (!is_list(ast)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* empty list */
|
||||
list lst = ast->value.mal_list;
|
||||
if (!lst) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* first item not a symbol */
|
||||
MalType* first = lst->data;
|
||||
if (!is_symbol(first)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* lookup symbol */
|
||||
MalType* val = env_get(env, first);
|
||||
if (is_error(val)) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return (val->is_macro);
|
||||
}
|
||||
}
|
||||
|
@ -19,11 +19,9 @@
|
||||
#define SYMBOL_FNSTAR "fn*"
|
||||
#define SYMBOL_QUOTE "quote"
|
||||
#define SYMBOL_QUASIQUOTE "quasiquote"
|
||||
#define SYMBOL_QUASIQUOTEEXPAND "quasiquoteexpand"
|
||||
#define SYMBOL_UNQUOTE "unquote"
|
||||
#define SYMBOL_SPLICE_UNQUOTE "splice-unquote"
|
||||
#define SYMBOL_DEFMACROBANG "defmacro!"
|
||||
#define SYMBOL_MACROEXPAND "macroexpand"
|
||||
#define SYMBOL_TRYSTAR "try*"
|
||||
#define SYMBOL_CATCHSTAR "catch*"
|
||||
|
||||
@ -37,7 +35,10 @@ MalType* READ(char* str) {
|
||||
MalType* EVAL(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
MalType* eval_ast(MalType* ast, Env* env);
|
||||
MalType* apply(MalType* fn, list args);
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
MalType* eval_defbang(MalType* ast, Env** env);
|
||||
void eval_letstar(MalType** ast, Env** env);
|
||||
void eval_if(MalType** ast, Env** env);
|
||||
@ -45,24 +46,45 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
MalType* eval_do(MalType* ast, Env* env);
|
||||
MalType* eval_quote(MalType* ast);
|
||||
MalType* eval_quasiquote(MalType* ast);
|
||||
MalType* eval_quasiquoteexpand(MalType* ast);
|
||||
MalType* eval_defmacrobang(MalType*, Env** env);
|
||||
MalType* eval_macroexpand(MalType* ast, Env* env);
|
||||
MalType* macroexpand(MalType* ast, Env* env);
|
||||
void eval_try(MalType** ast, Env** env);
|
||||
|
||||
/* Use goto to jump here rather than calling eval for tail-call elimination */
|
||||
TCE_entry_point:
|
||||
|
||||
MalType* dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && ! is_false(dbgeval) && ! is_nil(dbgeval))
|
||||
printf("EVAL: %s\n", pr_str(ast, READABLY));
|
||||
|
||||
/* NULL */
|
||||
if (!ast) { return make_nil(); }
|
||||
|
||||
/* macroexpansion */
|
||||
ast = macroexpand(ast, env);
|
||||
if (is_error(ast)) { return ast; }
|
||||
if (is_symbol(ast)) {
|
||||
MalType* symbol_value = env_get(env, ast->value.mal_symbol);
|
||||
if (symbol_value)
|
||||
return symbol_value;
|
||||
else
|
||||
return make_error_fmt("'%s' not found", ast->value.mal_symbol);
|
||||
}
|
||||
|
||||
if (is_vector(ast)) {
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_vector(result);
|
||||
}
|
||||
|
||||
if (is_hashmap(ast)) {
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_hashmap(result);
|
||||
}
|
||||
|
||||
/* not a list */
|
||||
if (!is_list(ast)) { return eval_ast(ast, env); }
|
||||
if (!is_list(ast)) { return ast; }
|
||||
|
||||
/* empty list */
|
||||
if (ast->value.mal_list == NULL) { return ast; }
|
||||
@ -114,17 +136,9 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
if (is_error(ast)) { return ast; }
|
||||
goto TCE_entry_point;
|
||||
}
|
||||
else if (strcmp(symbol, SYMBOL_QUASIQUOTEEXPAND) == 0) {
|
||||
|
||||
list lst = ast->value.mal_list;
|
||||
return eval_quasiquote(make_list(lst));
|
||||
}
|
||||
else if (strcmp(symbol, SYMBOL_DEFMACROBANG) == 0) {
|
||||
return eval_defmacrobang(ast, &env);
|
||||
}
|
||||
else if (strcmp(symbol, SYMBOL_MACROEXPAND) == 0) {
|
||||
return eval_macroexpand(ast, env);
|
||||
}
|
||||
else if (strcmp(symbol, SYMBOL_TRYSTAR) == 0) {
|
||||
|
||||
/* TCE - modify ast and env directly and jump back to eval */
|
||||
@ -135,16 +149,19 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
}
|
||||
}
|
||||
/* first element is not a special symbol */
|
||||
MalType* evaluated_list = eval_ast(ast, env);
|
||||
|
||||
if (is_error(evaluated_list)) { return evaluated_list; }
|
||||
MalType* func = EVAL(first, env);
|
||||
if (is_error(func)) { return func; }
|
||||
if (func->is_macro) {
|
||||
ast = apply(func, ast->value.mal_list->next);
|
||||
if (is_error(ast)) { return ast; }
|
||||
goto TCE_entry_point;
|
||||
}
|
||||
list evlst = evaluate_list(ast->value.mal_list->next, env);
|
||||
if (evlst && is_error(evlst->data)) { return evlst->data; }
|
||||
|
||||
/* apply the first element of the list to the arguments */
|
||||
list evlst = evaluated_list->value.mal_list;
|
||||
MalType* func = evlst->data;
|
||||
|
||||
if (is_function(func)) {
|
||||
return (*func->value.mal_function)(evlst->next);
|
||||
return (*func->value.mal_function)(evlst);
|
||||
}
|
||||
else if (is_closure(func)) {
|
||||
|
||||
@ -152,7 +169,7 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
list params = (closure->parameters)->value.mal_list;
|
||||
|
||||
long param_count = list_count(params);
|
||||
long arg_count = list_count(evlst->next);
|
||||
long arg_count = list_count(evlst);
|
||||
|
||||
if (param_count > arg_count) {
|
||||
return make_error("too few arguments supplied to function");
|
||||
@ -163,7 +180,7 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
else {
|
||||
|
||||
/* TCE - modify ast and env directly and jump back to eval */
|
||||
env = env_make(closure->env, params, evlst->next, closure->more_symbol);
|
||||
env = env_make(closure->env, params, evlst, closure->more_symbol);
|
||||
ast = func->value.mal_closure->definition;
|
||||
|
||||
if (is_error(ast)) { return ast; }
|
||||
@ -209,13 +226,13 @@ int main(int argc, char** argv) {
|
||||
char* symbol = mappings->data;
|
||||
MalType*(*function)(list) = (MalType*(*)(list))mappings->next->data;
|
||||
|
||||
env_set_C_fn(repl_env, symbol, function);
|
||||
env_set(repl_env, symbol, make_function(function));
|
||||
|
||||
/* pop symbol and function from hashmap/list */
|
||||
mappings = mappings->next->next;
|
||||
}
|
||||
|
||||
env_set_C_fn(repl_env, "eval", mal_eval);
|
||||
env_set(repl_env, "eval", make_function(mal_eval));
|
||||
|
||||
/* add functions written in mal - not using rep as it prints the result */
|
||||
EVAL(READ("(def! not (fn* (a) (if a false true)))"), repl_env);
|
||||
@ -227,7 +244,7 @@ int main(int argc, char** argv) {
|
||||
for (long i = 2; i < argc; i++) {
|
||||
lst = list_push(lst, make_string(argv[i]));
|
||||
}
|
||||
env_set(repl_env, make_symbol("*ARGV*"), make_list(list_reverse(lst)));
|
||||
env_set(repl_env, "*ARGV*", make_list(list_reverse(lst)));
|
||||
|
||||
/* run in script mode if a filename is given */
|
||||
if (argc > 1) {
|
||||
@ -268,58 +285,6 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MalType* eval_ast(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
|
||||
if (is_symbol(ast)) {
|
||||
|
||||
MalType* symbol_value = env_get(env, ast);
|
||||
|
||||
if (symbol_value) {
|
||||
return symbol_value;
|
||||
} else {
|
||||
return make_error_fmt("var '%s' not found", pr_str(ast, UNREADABLY));
|
||||
}
|
||||
}
|
||||
else if (is_list(ast)) {
|
||||
|
||||
list result = evaluate_list(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_list(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_vector(ast)) {
|
||||
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_vector(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_hashmap(ast)) {
|
||||
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_hashmap(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalType* eval_defbang(MalType* ast, Env** env) {
|
||||
|
||||
list lst = (ast->value.mal_list)->next;
|
||||
@ -338,7 +303,7 @@ MalType* eval_defbang(MalType* ast, Env** env) {
|
||||
MalType* result = EVAL(defbang_value, *env);
|
||||
|
||||
if (!is_error(result)){
|
||||
*env = env_set(*env, defbang_symbol, result);
|
||||
env_set(*env, defbang_symbol->value.mal_symbol, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -380,7 +345,7 @@ void eval_letstar(MalType** ast, Env** env) {
|
||||
return;
|
||||
}
|
||||
|
||||
env_set(letstar_env, symbol, value);
|
||||
env_set(letstar_env, symbol->value.mal_symbol, value);
|
||||
bindings_list = bindings_list->next->next;
|
||||
}
|
||||
|
||||
@ -679,51 +644,11 @@ MalType* eval_defmacrobang(MalType* ast, Env** env) {
|
||||
if (!is_error(result)) {
|
||||
result = copy_type(result);
|
||||
result->is_macro = 1;
|
||||
*env = env_set(*env, defbang_symbol, result);
|
||||
env_set(*env, defbang_symbol->value.mal_symbol, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MalType* eval_macroexpand(MalType* ast, Env* env) {
|
||||
|
||||
/* forward reference */
|
||||
MalType* macroexpand(MalType* ast, Env* env);
|
||||
|
||||
list lst = ast->value.mal_list;
|
||||
|
||||
if (!lst->next) {
|
||||
return make_nil();
|
||||
}
|
||||
else if (lst->next->next) {
|
||||
return make_error("'macroexpand': expected exactly one argument");
|
||||
}
|
||||
else {
|
||||
return macroexpand(lst->next->data, env);
|
||||
}
|
||||
}
|
||||
|
||||
MalType* macroexpand(MalType* ast, Env* env) {
|
||||
|
||||
/* forward reference */
|
||||
int is_macro_call(MalType* ast, Env* env);
|
||||
|
||||
while(is_macro_call(ast, env)) {
|
||||
|
||||
list lst = ast->value.mal_list;
|
||||
|
||||
MalType* macro_fn = env_get(env, lst->data);
|
||||
MalClosure* cls = macro_fn->value.mal_closure;
|
||||
MalType* more_symbol = cls->more_symbol;
|
||||
|
||||
list params_list = (cls->parameters)->value.mal_list;
|
||||
list args_list = lst->next;
|
||||
|
||||
env = env_make(cls->env, params_list, args_list, more_symbol);
|
||||
ast = EVAL(cls->definition, env);
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
void eval_try(MalType** ast, Env** env) {
|
||||
|
||||
list lst = (*ast)->value.mal_list;
|
||||
@ -773,11 +698,10 @@ void eval_try(MalType** ast, Env** env) {
|
||||
}
|
||||
|
||||
/* bind the symbol to the exception */
|
||||
list symbol_list = list_make(catch_list->next->data);
|
||||
list expr_list = list_make(try_result->value.mal_error);
|
||||
|
||||
/* TODO: validate symbols and exprs match before calling env_make */
|
||||
Env* catch_env = env_make(*env, symbol_list, expr_list, NULL);
|
||||
Env* catch_env = env_make(*env, NULL, NULL, NULL);
|
||||
env_set(catch_env,
|
||||
((MalType*)catch_list->next->data)->value.mal_symbol,
|
||||
try_result->value.mal_error);
|
||||
*ast = catch_list->next->next->data;
|
||||
*env = catch_env;
|
||||
|
||||
@ -937,32 +861,3 @@ MalType* apply(MalType* fn, list args) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int is_macro_call(MalType* ast, Env* env) {
|
||||
|
||||
/* not a list */
|
||||
if (!is_list(ast)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* empty list */
|
||||
list lst = ast->value.mal_list;
|
||||
if (!lst) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* first item not a symbol */
|
||||
MalType* first = lst->data;
|
||||
if (!is_symbol(first)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* lookup symbol */
|
||||
MalType* val = env_get(env, first);
|
||||
if (is_error(val)) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return (val->is_macro);
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,9 @@
|
||||
#define SYMBOL_FNSTAR "fn*"
|
||||
#define SYMBOL_QUOTE "quote"
|
||||
#define SYMBOL_QUASIQUOTE "quasiquote"
|
||||
#define SYMBOL_QUASIQUOTEEXPAND "quasiquoteexpand"
|
||||
#define SYMBOL_UNQUOTE "unquote"
|
||||
#define SYMBOL_SPLICE_UNQUOTE "splice-unquote"
|
||||
#define SYMBOL_DEFMACROBANG "defmacro!"
|
||||
#define SYMBOL_MACROEXPAND "macroexpand"
|
||||
#define SYMBOL_TRYSTAR "try*"
|
||||
#define SYMBOL_CATCHSTAR "catch*"
|
||||
|
||||
@ -38,7 +36,10 @@ MalType* READ(char* str) {
|
||||
MalType* EVAL(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
MalType* eval_ast(MalType* ast, Env* env);
|
||||
MalType* apply(MalType* fn, list args);
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
MalType* eval_defbang(MalType* ast, Env** env);
|
||||
void eval_letstar(MalType** ast, Env** env);
|
||||
void eval_if(MalType** ast, Env** env);
|
||||
@ -46,24 +47,45 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
MalType* eval_do(MalType* ast, Env* env);
|
||||
MalType* eval_quote(MalType* ast);
|
||||
MalType* eval_quasiquote(MalType* ast);
|
||||
MalType* eval_quasiquoteexpand(MalType* ast);
|
||||
MalType* eval_defmacrobang(MalType*, Env** env);
|
||||
MalType* eval_macroexpand(MalType* ast, Env* env);
|
||||
MalType* macroexpand(MalType* ast, Env* env);
|
||||
void eval_try(MalType** ast, Env** env);
|
||||
|
||||
/* Use goto to jump here rather than calling eval for tail-call elimination */
|
||||
TCE_entry_point:
|
||||
|
||||
MalType* dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && ! is_false(dbgeval) && ! is_nil(dbgeval))
|
||||
printf("EVAL: %s\n", pr_str(ast, READABLY));
|
||||
|
||||
/* NULL */
|
||||
if (!ast) { return make_nil(); }
|
||||
|
||||
/* macroexpansion */
|
||||
ast = macroexpand(ast, env);
|
||||
if (is_error(ast)) { return ast; }
|
||||
if (is_symbol(ast)) {
|
||||
MalType* symbol_value = env_get(env, ast->value.mal_symbol);
|
||||
if (symbol_value)
|
||||
return symbol_value;
|
||||
else
|
||||
return make_error_fmt("'%s' not found", ast->value.mal_symbol);
|
||||
}
|
||||
|
||||
if (is_vector(ast)) {
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_vector(result);
|
||||
}
|
||||
|
||||
if (is_hashmap(ast)) {
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
if (result && is_error(result->data))
|
||||
return result->data;
|
||||
else
|
||||
return make_hashmap(result);
|
||||
}
|
||||
|
||||
/* not a list */
|
||||
if (!is_list(ast)) { return eval_ast(ast, env); }
|
||||
if (!is_list(ast)) { return ast; }
|
||||
|
||||
/* empty list */
|
||||
if (ast->value.mal_list == NULL) { return ast; }
|
||||
@ -115,17 +137,9 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
if (is_error(ast)) { return ast; }
|
||||
goto TCE_entry_point;
|
||||
}
|
||||
else if (strcmp(symbol, SYMBOL_QUASIQUOTEEXPAND) == 0) {
|
||||
|
||||
list lst = ast->value.mal_list;
|
||||
return eval_quasiquote(make_list(lst));
|
||||
}
|
||||
else if (strcmp(symbol, SYMBOL_DEFMACROBANG) == 0) {
|
||||
return eval_defmacrobang(ast, &env);
|
||||
}
|
||||
else if (strcmp(symbol, SYMBOL_MACROEXPAND) == 0) {
|
||||
return eval_macroexpand(ast, env);
|
||||
}
|
||||
else if (strcmp(symbol, SYMBOL_TRYSTAR) == 0) {
|
||||
|
||||
/* TCE - modify ast and env directly and jump back to eval */
|
||||
@ -136,16 +150,19 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
}
|
||||
}
|
||||
/* first element is not a special symbol */
|
||||
MalType* evaluated_list = eval_ast(ast, env);
|
||||
|
||||
if (is_error(evaluated_list)) { return evaluated_list; }
|
||||
MalType* func = EVAL(first, env);
|
||||
if (is_error(func)) { return func; }
|
||||
if (func->is_macro) {
|
||||
ast = apply(func, ast->value.mal_list->next);
|
||||
if (is_error(ast)) { return ast; }
|
||||
goto TCE_entry_point;
|
||||
}
|
||||
list evlst = evaluate_list(ast->value.mal_list->next, env);
|
||||
if (evlst && is_error(evlst->data)) { return evlst->data; }
|
||||
|
||||
/* apply the first element of the list to the arguments */
|
||||
list evlst = evaluated_list->value.mal_list;
|
||||
MalType* func = evlst->data;
|
||||
|
||||
if (is_function(func)) {
|
||||
return (*func->value.mal_function)(evlst->next);
|
||||
return (*func->value.mal_function)(evlst);
|
||||
}
|
||||
else if (is_closure(func)) {
|
||||
|
||||
@ -153,7 +170,7 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
list params = (closure->parameters)->value.mal_list;
|
||||
|
||||
long param_count = list_count(params);
|
||||
long arg_count = list_count(evlst->next);
|
||||
long arg_count = list_count(evlst);
|
||||
|
||||
if (param_count > arg_count) {
|
||||
return make_error("too few arguments supplied to function");
|
||||
@ -164,7 +181,7 @@ MalType* EVAL(MalType* ast, Env* env) {
|
||||
else {
|
||||
|
||||
/* TCE - modify ast and env directly and jump back to eval */
|
||||
env = env_make(closure->env, params, evlst->next, closure->more_symbol);
|
||||
env = env_make(closure->env, params, evlst, closure->more_symbol);
|
||||
ast = func->value.mal_closure->definition;
|
||||
|
||||
if (is_error(ast)) { return ast; }
|
||||
@ -233,14 +250,14 @@ int main(int argc, char** argv) {
|
||||
char* symbol = mappings->data;
|
||||
MalType*(*function)(list) = mappings->next->data;
|
||||
|
||||
env_set_C_fn(repl_env, symbol, function);
|
||||
env_set(repl_env, symbol, make_function(function));
|
||||
|
||||
/* pop symbol and function from hashmap/list */
|
||||
mappings = mappings->next->next;
|
||||
}
|
||||
|
||||
env_set_C_fn(repl_env, "eval", mal_eval);
|
||||
env_set_C_fn(repl_env, "readline", mal_readline);
|
||||
env_set(repl_env, "eval", make_function(mal_eval));
|
||||
env_set(repl_env, "readline", make_function(mal_readline));
|
||||
|
||||
/* add functions written in mal - not using rep as it prints the result */
|
||||
EVAL(READ("(def! not (fn* (a) (if a false true)))"), repl_env);
|
||||
@ -252,8 +269,8 @@ int main(int argc, char** argv) {
|
||||
for (long i = 2; i < argc; i++) {
|
||||
lst = list_push(lst, make_string(argv[i]));
|
||||
}
|
||||
env_set(repl_env, make_symbol("*ARGV*"), make_list(list_reverse(lst)));
|
||||
env_set(repl_env, make_symbol("*host-language*"), make_string("c.2"));
|
||||
env_set(repl_env, "*ARGV*", make_list(list_reverse(lst)));
|
||||
env_set(repl_env, "*host-language*", make_string("c.2"));
|
||||
|
||||
/* run in script mode if a filename is given */
|
||||
if (argc > 1) {
|
||||
@ -293,58 +310,6 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MalType* eval_ast(MalType* ast, Env* env) {
|
||||
|
||||
/* forward references */
|
||||
list evaluate_list(list lst, Env* env);
|
||||
list evaluate_vector(list lst, Env* env);
|
||||
list evaluate_hashmap(list lst, Env* env);
|
||||
|
||||
if (is_symbol(ast)) {
|
||||
|
||||
MalType* symbol_value = env_get(env, ast);
|
||||
|
||||
if (symbol_value) {
|
||||
return symbol_value;
|
||||
} else {
|
||||
return make_error_fmt("var '%s' not found", pr_str(ast, UNREADABLY));
|
||||
}
|
||||
}
|
||||
else if (is_list(ast)) {
|
||||
|
||||
list result = evaluate_list(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_list(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_vector(ast)) {
|
||||
|
||||
list result = evaluate_vector(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_vector(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else if (is_hashmap(ast)) {
|
||||
|
||||
list result = evaluate_hashmap(ast->value.mal_list, env);
|
||||
|
||||
if (!result || !is_error(result->data)) {
|
||||
return make_hashmap(result);
|
||||
} else {
|
||||
return result->data;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalType* eval_defbang(MalType* ast, Env** env) {
|
||||
|
||||
list lst = (ast->value.mal_list)->next;
|
||||
@ -363,7 +328,7 @@ MalType* eval_defbang(MalType* ast, Env** env) {
|
||||
MalType* result = EVAL(defbang_value, *env);
|
||||
|
||||
if (!is_error(result)){
|
||||
*env = env_set(*env, defbang_symbol, result);
|
||||
env_set(*env, defbang_symbol->value.mal_symbol, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -405,7 +370,7 @@ void eval_letstar(MalType** ast, Env** env) {
|
||||
return;
|
||||
}
|
||||
|
||||
env_set(letstar_env, symbol, value);
|
||||
env_set(letstar_env, symbol->value.mal_symbol, value);
|
||||
bindings_list = bindings_list->next->next;
|
||||
}
|
||||
|
||||
@ -704,51 +669,11 @@ MalType* eval_defmacrobang(MalType* ast, Env** env) {
|
||||
if (!is_error(result)) {
|
||||
result = copy_type(result);
|
||||
result->is_macro = 1;
|
||||
*env = env_set(*env, defbang_symbol, result);
|
||||
env_set(*env, defbang_symbol->value.mal_symbol, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MalType* eval_macroexpand(MalType* ast, Env* env) {
|
||||
|
||||
/* forward reference */
|
||||
MalType* macroexpand(MalType* ast, Env* env);
|
||||
|
||||
list lst = ast->value.mal_list;
|
||||
|
||||
if (!lst->next) {
|
||||
return make_nil();
|
||||
}
|
||||
else if (lst->next->next) {
|
||||
return make_error("'macroexpand': expected exactly one argument");
|
||||
}
|
||||
else {
|
||||
return macroexpand(lst->next->data, env);
|
||||
}
|
||||
}
|
||||
|
||||
MalType* macroexpand(MalType* ast, Env* env) {
|
||||
|
||||
/* forward reference */
|
||||
int is_macro_call(MalType* ast, Env* env);
|
||||
|
||||
while(is_macro_call(ast, env)) {
|
||||
|
||||
list lst = ast->value.mal_list;
|
||||
|
||||
MalType* macro_fn = env_get(env, lst->data);
|
||||
MalClosure* cls = macro_fn->value.mal_closure;
|
||||
MalType* more_symbol = cls->more_symbol;
|
||||
|
||||
list params_list = (cls->parameters)->value.mal_list;
|
||||
list args_list = lst->next;
|
||||
|
||||
env = env_make(cls->env, params_list, args_list, more_symbol);
|
||||
ast = EVAL(cls->definition, env);
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
void eval_try(MalType** ast, Env** env) {
|
||||
|
||||
list lst = (*ast)->value.mal_list;
|
||||
@ -798,10 +723,10 @@ void eval_try(MalType** ast, Env** env) {
|
||||
}
|
||||
|
||||
/* bind the symbol to the exception */
|
||||
list symbol_list = list_make(catch_list->next->data);
|
||||
list expr_list = list_make(try_result->value.mal_error);
|
||||
|
||||
Env* catch_env = env_make(*env, symbol_list, expr_list, NULL);
|
||||
Env* catch_env = env_make(*env, NULL, NULL, NULL);
|
||||
env_set(catch_env,
|
||||
((MalType*)catch_list->next->data)->value.mal_symbol,
|
||||
try_result->value.mal_error);
|
||||
*ast = catch_list->next->next->data;
|
||||
*env = catch_env;
|
||||
|
||||
@ -961,32 +886,3 @@ MalType* apply(MalType* fn, list args) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int is_macro_call(MalType* ast, Env* env) {
|
||||
|
||||
/* not a list */
|
||||
if (!is_list(ast)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* empty list */
|
||||
list lst = ast->value.mal_list;
|
||||
if (!lst) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* first item not a symbol */
|
||||
MalType* first = lst->data;
|
||||
if (!is_symbol(first)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* lookup symbol */
|
||||
MalType* val = env_get(env, first);
|
||||
if (is_error(val)) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return (val->is_macro);
|
||||
}
|
||||
}
|
||||
|
@ -20,10 +20,11 @@ Env *new_env(Env *outer, MalVal* binds, MalVal *exprs) {
|
||||
if (i > exprs_len) { break; }
|
||||
if (_nth(binds, i)->val.string[0] == '&') {
|
||||
varargs = 1;
|
||||
env_set(e, _nth(binds, i+1), _slice(exprs, i, _count(exprs)));
|
||||
env_set(e, _nth(binds, i+1)->val.string,
|
||||
_slice(exprs, i, _count(exprs)));
|
||||
break;
|
||||
} else {
|
||||
env_set(e, _nth(binds, i), _nth(exprs, i));
|
||||
env_set(e, _nth(binds, i)->val.string, _nth(exprs, i));
|
||||
}
|
||||
}
|
||||
assert(varargs || (binds_len == exprs_len),
|
||||
@ -34,24 +35,17 @@ Env *new_env(Env *outer, MalVal* binds, MalVal *exprs) {
|
||||
return e;
|
||||
}
|
||||
|
||||
Env *env_find(Env *env, MalVal *key) {
|
||||
void *val = g_hash_table_lookup(env->table, key->val.string);
|
||||
MalVal *env_get(Env *env, const char *key) {
|
||||
MalVal *val = g_hash_table_lookup(env->table, key);
|
||||
if (val) {
|
||||
return env;
|
||||
return val;
|
||||
} else if (env->outer) {
|
||||
return env_find(env->outer, key);
|
||||
return env_get(env->outer, key);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MalVal *env_get(Env *env, MalVal *key) {
|
||||
Env *e = env_find(env, key);
|
||||
assert(e, "'%s' not found", key->val.string);
|
||||
return g_hash_table_lookup(e->table, key->val.string);
|
||||
}
|
||||
|
||||
Env *env_set(Env *env, MalVal *key, MalVal *val) {
|
||||
g_hash_table_insert(env->table, key->val.string, val);
|
||||
return env;
|
||||
void env_set(Env *env, char *key, MalVal *val) {
|
||||
g_hash_table_insert(env->table, key, val);
|
||||
}
|
||||
|
@ -29,15 +29,19 @@ MalVal *READ(char prompt[], char *str) {
|
||||
}
|
||||
|
||||
// eval
|
||||
MalVal *eval_ast(MalVal *ast, GHashTable *env) {
|
||||
MalVal *EVAL(MalVal *ast, GHashTable *env) {
|
||||
if (!ast || mal_error) return NULL;
|
||||
//g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
|
||||
if (ast->type == MAL_SYMBOL) {
|
||||
//g_print("EVAL symbol: %s\n", ast->val.string);
|
||||
// TODO: check if not found
|
||||
MalVal *res = g_hash_table_lookup(env, ast->val.string);
|
||||
assert(res, "'%s' not found", ast->val.string);
|
||||
return res;
|
||||
} else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) {
|
||||
} else if (ast->type == MAL_LIST) {
|
||||
// Proceed after this conditional.
|
||||
} else if (ast->type == MAL_VECTOR) {
|
||||
//g_print("EVAL sequential: %s\n", _pr_str(ast,1));
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);
|
||||
if (!el || mal_error) return NULL;
|
||||
@ -62,22 +66,12 @@ MalVal *eval_ast(MalVal *ast, GHashTable *env) {
|
||||
//g_print("EVAL scalar: %s\n", _pr_str(ast,1));
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalVal *EVAL(MalVal *ast, GHashTable *env) {
|
||||
if (!ast || mal_error) return NULL;
|
||||
//g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
if (ast->type != MAL_LIST) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
if (!ast || mal_error) return NULL;
|
||||
|
||||
// apply list
|
||||
//g_print("EVAL apply list: %s\n", _pr_str(ast,1));
|
||||
if (_count(ast) == 0) { return ast; }
|
||||
MalVal *a0 = _nth(ast, 0);
|
||||
assert_type(a0, MAL_SYMBOL, "Cannot invoke %s", _pr_str(a0,1));
|
||||
MalVal *el = eval_ast(ast, env);
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);
|
||||
if (!el || mal_error) { return NULL; }
|
||||
MalVal *(*f)(void *, void*) = (MalVal *(*)(void*, void*))_first(el);
|
||||
//g_print("eval_invoke el: %s\n", _pr_str(el,1));
|
||||
|
@ -29,12 +29,22 @@ MalVal *READ(char prompt[], char *str) {
|
||||
}
|
||||
|
||||
// eval
|
||||
MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
if (!ast || mal_error) return NULL;
|
||||
|
||||
MalVal *dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {
|
||||
g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
}
|
||||
|
||||
if (ast->type == MAL_SYMBOL) {
|
||||
//g_print("EVAL symbol: %s\n", ast->val.string);
|
||||
return env_get(env, ast);
|
||||
} else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) {
|
||||
MalVal *res = env_get(env, ast->val.string);
|
||||
assert(res, "'%s' not found", ast->val.string);
|
||||
return res;
|
||||
} else if (ast->type == MAL_LIST) {
|
||||
// Proceed after this conditional.
|
||||
} else if (ast->type == MAL_VECTOR) {
|
||||
//g_print("EVAL sequential: %s\n", _pr_str(ast,1));
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);
|
||||
if (!el || mal_error) return NULL;
|
||||
@ -59,15 +69,6 @@ MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
//g_print("EVAL scalar: %s\n", _pr_str(ast,1));
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
if (!ast || mal_error) return NULL;
|
||||
//g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
if (ast->type != MAL_LIST) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
if (!ast || mal_error) return NULL;
|
||||
|
||||
// apply list
|
||||
//g_print("EVAL apply list: %s\n", _pr_str(ast,1));
|
||||
@ -81,7 +82,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
*a2 = _nth(ast, 2);
|
||||
MalVal *res = EVAL(a2, env);
|
||||
if (mal_error) return NULL;
|
||||
env_set(env, a1, res);
|
||||
env_set(env, a1->val.string, res);
|
||||
return res;
|
||||
} else if (strcmp("let*", a0->val.string) == 0) {
|
||||
//g_print("eval apply let*\n");
|
||||
@ -97,12 +98,12 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
key = g_array_index(a1->val.array, MalVal*, i);
|
||||
val = g_array_index(a1->val.array, MalVal*, i+1);
|
||||
assert_type(key, MAL_SYMBOL, "let* bind to non-symbol");
|
||||
env_set(let_env, key, EVAL(val, let_env));
|
||||
env_set(let_env, key->val.string, EVAL(val, let_env));
|
||||
}
|
||||
return EVAL(a2, let_env);
|
||||
} else {
|
||||
//g_print("eval apply\n");
|
||||
MalVal *el = eval_ast(ast, env);
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);
|
||||
if (!el || mal_error) { return NULL; }
|
||||
MalVal *(*f)(void *, void*) = (MalVal *(*)(void*, void*))_first(el);
|
||||
return f(_nth(el, 1), _nth(el, 2));
|
||||
@ -142,10 +143,10 @@ WRAP_INTEGER_OP(divide,/)
|
||||
void init_repl_env() {
|
||||
repl_env = new_env(NULL, NULL, NULL);
|
||||
|
||||
env_set(repl_env, malval_new_symbol("+"), (MalVal *)int_plus);
|
||||
env_set(repl_env, malval_new_symbol("-"), (MalVal *)int_minus);
|
||||
env_set(repl_env, malval_new_symbol("*"), (MalVal *)int_multiply);
|
||||
env_set(repl_env, malval_new_symbol("/"), (MalVal *)int_divide);
|
||||
env_set(repl_env, "+", (MalVal *)int_plus);
|
||||
env_set(repl_env, "-", (MalVal *)int_minus);
|
||||
env_set(repl_env, "*", (MalVal *)int_multiply);
|
||||
env_set(repl_env, "/", (MalVal *)int_divide);
|
||||
}
|
||||
|
||||
int main()
|
||||
|
@ -30,12 +30,22 @@ MalVal *READ(char prompt[], char *str) {
|
||||
}
|
||||
|
||||
// eval
|
||||
MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
if (!ast || mal_error) return NULL;
|
||||
|
||||
MalVal *dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {
|
||||
g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
}
|
||||
|
||||
if (ast->type == MAL_SYMBOL) {
|
||||
//g_print("EVAL symbol: %s\n", ast->val.string);
|
||||
return env_get(env, ast);
|
||||
} else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) {
|
||||
MalVal *res = env_get(env, ast->val.string);
|
||||
assert(res, "'%s' not found", ast->val.string);
|
||||
return res;
|
||||
} else if (ast->type == MAL_LIST) {
|
||||
// Proceed after this conditional.
|
||||
} else if (ast->type == MAL_VECTOR) {
|
||||
//g_print("EVAL sequential: %s\n", _pr_str(ast,1));
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);
|
||||
if (!el || mal_error) return NULL;
|
||||
@ -60,15 +70,6 @@ MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
//g_print("EVAL scalar: %s\n", _pr_str(ast,1));
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
if (!ast || mal_error) return NULL;
|
||||
//g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
if (ast->type != MAL_LIST) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
if (!ast || mal_error) return NULL;
|
||||
|
||||
// apply list
|
||||
//g_print("EVAL apply list: %s\n", _pr_str(ast,1));
|
||||
@ -82,7 +83,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
*a2 = _nth(ast, 2);
|
||||
MalVal *res = EVAL(a2, env);
|
||||
if (mal_error) return NULL;
|
||||
env_set(env, a1, res);
|
||||
env_set(env, a1->val.string, res);
|
||||
return res;
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("let*", a0->val.string) == 0) {
|
||||
@ -99,13 +100,13 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
key = g_array_index(a1->val.array, MalVal*, i);
|
||||
val = g_array_index(a1->val.array, MalVal*, i+1);
|
||||
assert_type(key, MAL_SYMBOL, "let* bind to non-symbol");
|
||||
env_set(let_env, key, EVAL(val, let_env));
|
||||
env_set(let_env, key->val.string, EVAL(val, let_env));
|
||||
}
|
||||
return EVAL(a2, let_env);
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("do", a0->val.string) == 0) {
|
||||
//g_print("eval apply do\n");
|
||||
MalVal *el = eval_ast(_rest(ast), env);
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, _rest(ast), env);
|
||||
return _last(el);
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("if", a0->val.string) == 0) {
|
||||
@ -136,7 +137,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
return mf;
|
||||
} else {
|
||||
//g_print("eval apply\n");
|
||||
MalVal *el = eval_ast(ast, env);
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);
|
||||
if (!el || mal_error) { return NULL; }
|
||||
MalVal *f = _first(el),
|
||||
*args = _rest(el);
|
||||
@ -177,8 +178,7 @@ void init_repl_env() {
|
||||
// core.c: defined using C
|
||||
int i;
|
||||
for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) {
|
||||
env_set(repl_env,
|
||||
malval_new_symbol(core_ns[i].name),
|
||||
env_set(repl_env, core_ns[i].name,
|
||||
malval_new_function(core_ns[i].func, core_ns[i].arg_cnt));
|
||||
}
|
||||
|
||||
|
@ -30,12 +30,24 @@ MalVal *READ(char prompt[], char *str) {
|
||||
}
|
||||
|
||||
// eval
|
||||
MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
while (TRUE) {
|
||||
|
||||
if (!ast || mal_error) return NULL;
|
||||
|
||||
MalVal *dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {
|
||||
g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
}
|
||||
|
||||
if (ast->type == MAL_SYMBOL) {
|
||||
//g_print("EVAL symbol: %s\n", ast->val.string);
|
||||
return env_get(env, ast);
|
||||
} else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) {
|
||||
MalVal *res = env_get(env, ast->val.string);
|
||||
assert(res, "'%s' not found", ast->val.string);
|
||||
return res;
|
||||
} else if (ast->type == MAL_LIST) {
|
||||
// Proceed after this conditional.
|
||||
} else if (ast->type == MAL_VECTOR) {
|
||||
//g_print("EVAL sequential: %s\n", _pr_str(ast,1));
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);
|
||||
if (!el || mal_error) return NULL;
|
||||
@ -60,17 +72,6 @@ MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
//g_print("EVAL scalar: %s\n", _pr_str(ast,1));
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
while (TRUE) {
|
||||
|
||||
if (!ast || mal_error) return NULL;
|
||||
//g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
if (ast->type != MAL_LIST) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
if (!ast || mal_error) return NULL;
|
||||
|
||||
// apply list
|
||||
//g_print("EVAL apply list: %s\n", _pr_str(ast,1));
|
||||
@ -84,7 +85,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
*a2 = _nth(ast, 2);
|
||||
MalVal *res = EVAL(a2, env);
|
||||
if (mal_error) return NULL;
|
||||
env_set(env, a1, res);
|
||||
env_set(env, a1->val.string, res);
|
||||
return res;
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("let*", a0->val.string) == 0) {
|
||||
@ -101,7 +102,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
key = g_array_index(a1->val.array, MalVal*, i);
|
||||
val = g_array_index(a1->val.array, MalVal*, i+1);
|
||||
assert_type(key, MAL_SYMBOL, "let* bind to non-symbol");
|
||||
env_set(let_env, key, EVAL(val, let_env));
|
||||
env_set(let_env, key->val.string, EVAL(val, let_env));
|
||||
}
|
||||
ast = a2;
|
||||
env = let_env;
|
||||
@ -109,7 +110,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("do", a0->val.string) == 0) {
|
||||
//g_print("eval apply do\n");
|
||||
eval_ast(_slice(ast, 1, _count(ast)-1), env);
|
||||
_map2((MalVal *(*)(void*, void*))EVAL, _slice(ast, 1, _count(ast) - 1), env);
|
||||
ast = _last(ast);
|
||||
// Continue loop
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
@ -141,7 +142,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
return mf;
|
||||
} else {
|
||||
//g_print("eval apply\n");
|
||||
MalVal *el = eval_ast(ast, env);
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);
|
||||
if (!el || mal_error) { return NULL; }
|
||||
MalVal *f = _first(el),
|
||||
*args = _rest(el);
|
||||
@ -190,8 +191,7 @@ void init_repl_env() {
|
||||
// core.c: defined using C
|
||||
int i;
|
||||
for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) {
|
||||
env_set(repl_env,
|
||||
malval_new_symbol(core_ns[i].name),
|
||||
env_set(repl_env, core_ns[i].name,
|
||||
malval_new_function(core_ns[i].func, core_ns[i].arg_cnt));
|
||||
}
|
||||
|
||||
|
@ -30,12 +30,24 @@ MalVal *READ(char prompt[], char *str) {
|
||||
}
|
||||
|
||||
// eval
|
||||
MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
while (TRUE) {
|
||||
|
||||
if (!ast || mal_error) return NULL;
|
||||
|
||||
MalVal *dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {
|
||||
g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
}
|
||||
|
||||
if (ast->type == MAL_SYMBOL) {
|
||||
//g_print("EVAL symbol: %s\n", ast->val.string);
|
||||
return env_get(env, ast);
|
||||
} else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) {
|
||||
MalVal *res = env_get(env, ast->val.string);
|
||||
assert(res, "'%s' not found", ast->val.string);
|
||||
return res;
|
||||
} else if (ast->type == MAL_LIST) {
|
||||
// Proceed after this conditional.
|
||||
} else if (ast->type == MAL_VECTOR) {
|
||||
//g_print("EVAL sequential: %s\n", _pr_str(ast,1));
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);
|
||||
if (!el || mal_error) return NULL;
|
||||
@ -60,17 +72,6 @@ MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
//g_print("EVAL scalar: %s\n", _pr_str(ast,1));
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
while (TRUE) {
|
||||
|
||||
if (!ast || mal_error) return NULL;
|
||||
//g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
if (ast->type != MAL_LIST) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
if (!ast || mal_error) return NULL;
|
||||
|
||||
// apply list
|
||||
//g_print("EVAL apply list: %s\n", _pr_str(ast,1));
|
||||
@ -84,7 +85,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
*a2 = _nth(ast, 2);
|
||||
MalVal *res = EVAL(a2, env);
|
||||
if (mal_error) return NULL;
|
||||
env_set(env, a1, res);
|
||||
env_set(env, a1->val.string, res);
|
||||
return res;
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("let*", a0->val.string) == 0) {
|
||||
@ -101,7 +102,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
key = g_array_index(a1->val.array, MalVal*, i);
|
||||
val = g_array_index(a1->val.array, MalVal*, i+1);
|
||||
assert_type(key, MAL_SYMBOL, "let* bind to non-symbol");
|
||||
env_set(let_env, key, EVAL(val, let_env));
|
||||
env_set(let_env, key->val.string, EVAL(val, let_env));
|
||||
}
|
||||
ast = a2;
|
||||
env = let_env;
|
||||
@ -109,7 +110,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("do", a0->val.string) == 0) {
|
||||
//g_print("eval apply do\n");
|
||||
eval_ast(_slice(ast, 1, _count(ast)-1), env);
|
||||
_map2((MalVal *(*)(void*, void*))EVAL, _slice(ast, 1, _count(ast)-1), env);
|
||||
ast = _last(ast);
|
||||
// Continue loop
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
@ -141,7 +142,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
return mf;
|
||||
} else {
|
||||
//g_print("eval apply\n");
|
||||
MalVal *el = eval_ast(ast, env);
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);
|
||||
if (!el || mal_error) { return NULL; }
|
||||
MalVal *f = _first(el),
|
||||
*args = _rest(el);
|
||||
@ -192,12 +193,10 @@ void init_repl_env(int argc, char *argv[]) {
|
||||
// core.c: defined using C
|
||||
int i;
|
||||
for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) {
|
||||
env_set(repl_env,
|
||||
malval_new_symbol(core_ns[i].name),
|
||||
env_set(repl_env, core_ns[i].name,
|
||||
malval_new_function(core_ns[i].func, core_ns[i].arg_cnt));
|
||||
}
|
||||
env_set(repl_env,
|
||||
malval_new_symbol("eval"),
|
||||
env_set(repl_env, "eval",
|
||||
malval_new_function((void*(*)(void *))do_eval, 1));
|
||||
|
||||
MalVal *_argv = _listX(0);
|
||||
@ -205,7 +204,7 @@ void init_repl_env(int argc, char *argv[]) {
|
||||
MalVal *arg = malval_new_string(argv[i]);
|
||||
g_array_append_val(_argv->val.array, arg);
|
||||
}
|
||||
env_set(repl_env, malval_new_symbol("*ARGV*"), _argv);
|
||||
env_set(repl_env, "*ARGV*", _argv);
|
||||
|
||||
// core.mal: defined using the language itself
|
||||
RE(repl_env, "", "(def! not (fn* (a) (if a false true)))");
|
||||
|
@ -68,12 +68,24 @@ MalVal *quasiquote(MalVal *ast) {
|
||||
}
|
||||
}
|
||||
|
||||
MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
while (TRUE) {
|
||||
|
||||
if (!ast || mal_error) return NULL;
|
||||
|
||||
MalVal *dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {
|
||||
g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
}
|
||||
|
||||
if (ast->type == MAL_SYMBOL) {
|
||||
//g_print("EVAL symbol: %s\n", ast->val.string);
|
||||
return env_get(env, ast);
|
||||
} else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) {
|
||||
MalVal *res = env_get(env, ast->val.string);
|
||||
assert(res, "'%s' not found", ast->val.string);
|
||||
return res;
|
||||
} else if (ast->type == MAL_LIST) {
|
||||
// Proceed after this conditional.
|
||||
} else if (ast->type == MAL_VECTOR) {
|
||||
//g_print("EVAL sequential: %s\n", _pr_str(ast,1));
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);
|
||||
if (!el || mal_error) return NULL;
|
||||
@ -98,17 +110,6 @@ MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
//g_print("EVAL scalar: %s\n", _pr_str(ast,1));
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
while (TRUE) {
|
||||
|
||||
if (!ast || mal_error) return NULL;
|
||||
//g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
if (ast->type != MAL_LIST) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
if (!ast || mal_error) return NULL;
|
||||
|
||||
// apply list
|
||||
//g_print("EVAL apply list: %s\n", _pr_str(ast,1));
|
||||
@ -122,7 +123,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
*a2 = _nth(ast, 2);
|
||||
MalVal *res = EVAL(a2, env);
|
||||
if (mal_error) return NULL;
|
||||
env_set(env, a1, res);
|
||||
env_set(env, a1->val.string, res);
|
||||
return res;
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("let*", a0->val.string) == 0) {
|
||||
@ -139,7 +140,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
key = g_array_index(a1->val.array, MalVal*, i);
|
||||
val = g_array_index(a1->val.array, MalVal*, i+1);
|
||||
assert_type(key, MAL_SYMBOL, "let* bind to non-symbol");
|
||||
env_set(let_env, key, EVAL(val, let_env));
|
||||
env_set(let_env, key->val.string, EVAL(val, let_env));
|
||||
}
|
||||
ast = a2;
|
||||
env = let_env;
|
||||
@ -148,9 +149,6 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
strcmp("quote", a0->val.string) == 0) {
|
||||
//g_print("eval apply quote\n");
|
||||
return _nth(ast, 1);
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("quasiquoteexpand", a0->val.string) == 0) {
|
||||
return quasiquote(_nth(ast, 1));
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("quasiquote", a0->val.string) == 0) {
|
||||
//g_print("eval apply quasiquote\n");
|
||||
@ -160,7 +158,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("do", a0->val.string) == 0) {
|
||||
//g_print("eval apply do\n");
|
||||
eval_ast(_slice(ast, 1, _count(ast)-1), env);
|
||||
_map2((MalVal *(*)(void*, void*))EVAL, _slice(ast, 1, _count(ast)-1), env);
|
||||
ast = _last(ast);
|
||||
// Continue loop
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
@ -192,7 +190,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
return mf;
|
||||
} else {
|
||||
//g_print("eval apply\n");
|
||||
MalVal *el = eval_ast(ast, env);
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);
|
||||
if (!el || mal_error) { return NULL; }
|
||||
MalVal *f = _first(el),
|
||||
*args = _rest(el);
|
||||
@ -243,12 +241,10 @@ void init_repl_env(int argc, char *argv[]) {
|
||||
// core.c: defined using C
|
||||
int i;
|
||||
for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) {
|
||||
env_set(repl_env,
|
||||
malval_new_symbol(core_ns[i].name),
|
||||
env_set(repl_env, core_ns[i].name,
|
||||
malval_new_function(core_ns[i].func, core_ns[i].arg_cnt));
|
||||
}
|
||||
env_set(repl_env,
|
||||
malval_new_symbol("eval"),
|
||||
env_set(repl_env, "eval",
|
||||
malval_new_function((void*(*)(void *))do_eval, 1));
|
||||
|
||||
MalVal *_argv = _listX(0);
|
||||
@ -256,7 +252,7 @@ void init_repl_env(int argc, char *argv[]) {
|
||||
MalVal *arg = malval_new_string(argv[i]);
|
||||
g_array_append_val(_argv->val.array, arg);
|
||||
}
|
||||
env_set(repl_env, malval_new_symbol("*ARGV*"), _argv);
|
||||
env_set(repl_env, "*ARGV*", _argv);
|
||||
|
||||
// core.mal: defined using the language itself
|
||||
RE(repl_env, "", "(def! not (fn* (a) (if a false true)))");
|
||||
|
@ -11,7 +11,6 @@
|
||||
// Declarations
|
||||
MalVal *EVAL(MalVal *ast, Env *env);
|
||||
MalVal *quasiquote(MalVal *ast);
|
||||
MalVal *macroexpand(MalVal *ast, Env *env);
|
||||
|
||||
// read
|
||||
MalVal *READ(char prompt[], char *str) {
|
||||
@ -69,31 +68,24 @@ MalVal *quasiquote(MalVal *ast) {
|
||||
}
|
||||
}
|
||||
|
||||
int is_macro_call(MalVal *ast, Env *env) {
|
||||
if (!ast || ast->type != MAL_LIST || _count(ast) == 0) { return 0; }
|
||||
MalVal *a0 = _nth(ast, 0);
|
||||
return (a0->type & MAL_SYMBOL) &&
|
||||
env_find(env, a0) &&
|
||||
env_get(env, a0)->ismacro;
|
||||
}
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
while (TRUE) {
|
||||
|
||||
MalVal *macroexpand(MalVal *ast, Env *env) {
|
||||
if (!ast || mal_error) return NULL;
|
||||
while (is_macro_call(ast, env)) {
|
||||
MalVal *a0 = _nth(ast, 0);
|
||||
MalVal *mac = env_get(env, a0);
|
||||
// TODO: this is weird and limits it to 20. FIXME
|
||||
ast = _apply(mac, _rest(ast));
|
||||
|
||||
MalVal *dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {
|
||||
g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
if (!ast || mal_error) return NULL;
|
||||
if (ast->type == MAL_SYMBOL) {
|
||||
//g_print("EVAL symbol: %s\n", ast->val.string);
|
||||
return env_get(env, ast);
|
||||
} else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) {
|
||||
MalVal *res = env_get(env, ast->val.string);
|
||||
assert(res, "'%s' not found", ast->val.string);
|
||||
return res;
|
||||
} else if (ast->type == MAL_LIST) {
|
||||
// Proceed after this conditional.
|
||||
} else if (ast->type == MAL_VECTOR) {
|
||||
//g_print("EVAL sequential: %s\n", _pr_str(ast,1));
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);
|
||||
if (!el || mal_error) return NULL;
|
||||
@ -118,25 +110,8 @@ MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
//g_print("EVAL scalar: %s\n", _pr_str(ast,1));
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
while (TRUE) {
|
||||
|
||||
if (!ast || mal_error) return NULL;
|
||||
//g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
if (ast->type != MAL_LIST) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
if (!ast || mal_error) return NULL;
|
||||
|
||||
// apply list
|
||||
//g_print("EVAL apply list: %s\n", _pr_str(ast,1));
|
||||
ast = macroexpand(ast, env);
|
||||
if (!ast || mal_error) return NULL;
|
||||
if (ast->type != MAL_LIST) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
if (_count(ast) == 0) { return ast; }
|
||||
|
||||
int i, len;
|
||||
@ -148,7 +123,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
*a2 = _nth(ast, 2);
|
||||
MalVal *res = EVAL(a2, env);
|
||||
if (mal_error) return NULL;
|
||||
env_set(env, a1, res);
|
||||
env_set(env, a1->val.string, res);
|
||||
return res;
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("let*", a0->val.string) == 0) {
|
||||
@ -165,7 +140,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
key = g_array_index(a1->val.array, MalVal*, i);
|
||||
val = g_array_index(a1->val.array, MalVal*, i+1);
|
||||
assert_type(key, MAL_SYMBOL, "let* bind to non-symbol");
|
||||
env_set(let_env, key, EVAL(val, let_env));
|
||||
env_set(let_env, key->val.string, EVAL(val, let_env));
|
||||
}
|
||||
ast = a2;
|
||||
env = let_env;
|
||||
@ -174,9 +149,6 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
strcmp("quote", a0->val.string) == 0) {
|
||||
//g_print("eval apply quote\n");
|
||||
return _nth(ast, 1);
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("quasiquoteexpand", a0->val.string) == 0) {
|
||||
return quasiquote(_nth(ast, 1));
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("quasiquote", a0->val.string) == 0) {
|
||||
//g_print("eval apply quasiquote\n");
|
||||
@ -188,20 +160,17 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
//g_print("eval apply defmacro!\n");
|
||||
MalVal *a1 = _nth(ast, 1),
|
||||
*a2 = _nth(ast, 2);
|
||||
MalVal *res = EVAL(a2, env);
|
||||
MalVal *old = EVAL(a2, env);
|
||||
if (mal_error) return NULL;
|
||||
MalVal *res = malval_new(MAL_FUNCTION_MAL, NULL);
|
||||
res->val.func = old->val.func;
|
||||
res->ismacro = TRUE;
|
||||
env_set(env, a1, res);
|
||||
env_set(env, a1->val.string, res);
|
||||
return res;
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("macroexpand", a0->val.string) == 0) {
|
||||
//g_print("eval apply macroexpand\n");
|
||||
MalVal *a1 = _nth(ast, 1);
|
||||
return macroexpand(a1, env);
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("do", a0->val.string) == 0) {
|
||||
//g_print("eval apply do\n");
|
||||
eval_ast(_slice(ast, 1, _count(ast)-1), env);
|
||||
_map2((MalVal *(*)(void*, void*))EVAL, _slice(ast, 1, _count(ast)-1), env);
|
||||
ast = _last(ast);
|
||||
// Continue loop
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
@ -234,10 +203,15 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
return mf;
|
||||
} else {
|
||||
//g_print("eval apply\n");
|
||||
MalVal *el = eval_ast(ast, env);
|
||||
if (!el || mal_error) { return NULL; }
|
||||
MalVal *f = _first(el),
|
||||
*args = _rest(el);
|
||||
MalVal *f = EVAL(a0, env);
|
||||
if (!f || mal_error) { return NULL; }
|
||||
MalVal *rest = _rest(ast);
|
||||
if (f->ismacro) {
|
||||
ast = _apply(f, rest);
|
||||
continue;
|
||||
}
|
||||
MalVal *args = _map2((MalVal *(*)(void*, void*))EVAL, rest, env);
|
||||
if (!args || mal_error) { return NULL; }
|
||||
assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL,
|
||||
"cannot apply '%s'", _pr_str(f,1));
|
||||
if (f->type & MAL_FUNCTION_MAL) {
|
||||
@ -285,12 +259,10 @@ void init_repl_env(int argc, char *argv[]) {
|
||||
// core.c: defined using C
|
||||
int i;
|
||||
for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) {
|
||||
env_set(repl_env,
|
||||
malval_new_symbol(core_ns[i].name),
|
||||
env_set(repl_env, core_ns[i].name,
|
||||
malval_new_function(core_ns[i].func, core_ns[i].arg_cnt));
|
||||
}
|
||||
env_set(repl_env,
|
||||
malval_new_symbol("eval"),
|
||||
env_set(repl_env, "eval",
|
||||
malval_new_function((void*(*)(void *))do_eval, 1));
|
||||
|
||||
MalVal *_argv = _listX(0);
|
||||
@ -298,7 +270,7 @@ void init_repl_env(int argc, char *argv[]) {
|
||||
MalVal *arg = malval_new_string(argv[i]);
|
||||
g_array_append_val(_argv->val.array, arg);
|
||||
}
|
||||
env_set(repl_env, malval_new_symbol("*ARGV*"), _argv);
|
||||
env_set(repl_env, "*ARGV*", _argv);
|
||||
|
||||
// core.mal: defined using the language itself
|
||||
RE(repl_env, "", "(def! not (fn* (a) (if a false true)))");
|
||||
|
@ -12,7 +12,6 @@
|
||||
// Declarations
|
||||
MalVal *EVAL(MalVal *ast, Env *env);
|
||||
MalVal *quasiquote(MalVal *ast);
|
||||
MalVal *macroexpand(MalVal *ast, Env *env);
|
||||
|
||||
// read
|
||||
MalVal *READ(char prompt[], char *str) {
|
||||
@ -70,31 +69,24 @@ MalVal *quasiquote(MalVal *ast) {
|
||||
}
|
||||
}
|
||||
|
||||
int is_macro_call(MalVal *ast, Env *env) {
|
||||
if (!ast || ast->type != MAL_LIST || _count(ast) == 0) { return 0; }
|
||||
MalVal *a0 = _nth(ast, 0);
|
||||
return (a0->type & MAL_SYMBOL) &&
|
||||
env_find(env, a0) &&
|
||||
env_get(env, a0)->ismacro;
|
||||
}
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
while (TRUE) {
|
||||
|
||||
MalVal *macroexpand(MalVal *ast, Env *env) {
|
||||
if (!ast || mal_error) return NULL;
|
||||
while (is_macro_call(ast, env)) {
|
||||
MalVal *a0 = _nth(ast, 0);
|
||||
MalVal *mac = env_get(env, a0);
|
||||
// TODO: this is weird and limits it to 20. FIXME
|
||||
ast = _apply(mac, _rest(ast));
|
||||
|
||||
MalVal *dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {
|
||||
g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
if (!ast || mal_error) return NULL;
|
||||
if (ast->type == MAL_SYMBOL) {
|
||||
//g_print("EVAL symbol: %s\n", ast->val.string);
|
||||
return env_get(env, ast);
|
||||
} else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) {
|
||||
MalVal *res = env_get(env, ast->val.string);
|
||||
assert(res, "'%s' not found", ast->val.string);
|
||||
return res;
|
||||
} else if (ast->type == MAL_LIST) {
|
||||
// Proceed after this conditional.
|
||||
} else if (ast->type == MAL_VECTOR) {
|
||||
//g_print("EVAL sequential: %s\n", _pr_str(ast,1));
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);
|
||||
if (!el || mal_error) return NULL;
|
||||
@ -119,25 +111,8 @@ MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
//g_print("EVAL scalar: %s\n", _pr_str(ast,1));
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
while (TRUE) {
|
||||
|
||||
if (!ast || mal_error) return NULL;
|
||||
//g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
if (ast->type != MAL_LIST) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
if (!ast || mal_error) return NULL;
|
||||
|
||||
// apply list
|
||||
//g_print("EVAL apply list: %s\n", _pr_str(ast,1));
|
||||
ast = macroexpand(ast, env);
|
||||
if (!ast || mal_error) return NULL;
|
||||
if (ast->type != MAL_LIST) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
if (_count(ast) == 0) { return ast; }
|
||||
|
||||
int i, len;
|
||||
@ -149,7 +124,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
*a2 = _nth(ast, 2);
|
||||
MalVal *res = EVAL(a2, env);
|
||||
if (mal_error) return NULL;
|
||||
env_set(env, a1, res);
|
||||
env_set(env, a1->val.string, res);
|
||||
return res;
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("let*", a0->val.string) == 0) {
|
||||
@ -166,7 +141,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
key = g_array_index(a1->val.array, MalVal*, i);
|
||||
val = g_array_index(a1->val.array, MalVal*, i+1);
|
||||
assert_type(key, MAL_SYMBOL, "let* bind to non-symbol");
|
||||
env_set(let_env, key, EVAL(val, let_env));
|
||||
env_set(let_env, key->val.string, EVAL(val, let_env));
|
||||
}
|
||||
ast = a2;
|
||||
env = let_env;
|
||||
@ -175,9 +150,6 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
strcmp("quote", a0->val.string) == 0) {
|
||||
//g_print("eval apply quote\n");
|
||||
return _nth(ast, 1);
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("quasiquoteexpand", a0->val.string) == 0) {
|
||||
return quasiquote(_nth(ast, 1));
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("quasiquote", a0->val.string) == 0) {
|
||||
//g_print("eval apply quasiquote\n");
|
||||
@ -189,16 +161,13 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
//g_print("eval apply defmacro!\n");
|
||||
MalVal *a1 = _nth(ast, 1),
|
||||
*a2 = _nth(ast, 2);
|
||||
MalVal *res = EVAL(a2, env);
|
||||
MalVal *old = EVAL(a2, env);
|
||||
if (mal_error) return NULL;
|
||||
MalVal *res = malval_new(MAL_FUNCTION_MAL, NULL);
|
||||
res->val.func = old->val.func;
|
||||
res->ismacro = TRUE;
|
||||
env_set(env, a1, res);
|
||||
env_set(env, a1->val.string, res);
|
||||
return res;
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("macroexpand", a0->val.string) == 0) {
|
||||
//g_print("eval apply macroexpand\n");
|
||||
MalVal *a1 = _nth(ast, 1);
|
||||
return macroexpand(a1, env);
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("try*", a0->val.string) == 0) {
|
||||
//g_print("eval apply try*\n");
|
||||
@ -226,7 +195,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("do", a0->val.string) == 0) {
|
||||
//g_print("eval apply do\n");
|
||||
eval_ast(_slice(ast, 1, _count(ast)-1), env);
|
||||
_map2((MalVal *(*)(void*, void*))EVAL, _slice(ast, 1, _count(ast)-1), env);
|
||||
ast = _last(ast);
|
||||
// Continue loop
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
@ -259,10 +228,15 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
return mf;
|
||||
} else {
|
||||
//g_print("eval apply\n");
|
||||
MalVal *el = eval_ast(ast, env);
|
||||
if (!el || mal_error) { return NULL; }
|
||||
MalVal *f = _first(el),
|
||||
*args = _rest(el);
|
||||
MalVal *f = EVAL(a0, env);
|
||||
if (!f || mal_error) { return NULL; }
|
||||
MalVal *rest = _rest(ast);
|
||||
if (f->ismacro) {
|
||||
ast = _apply(f, rest);
|
||||
continue;
|
||||
}
|
||||
MalVal *args = _map2((MalVal *(*)(void*, void*))EVAL, rest, env);
|
||||
if (!args || mal_error) { return NULL; }
|
||||
assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL,
|
||||
"cannot apply '%s'", _pr_str(f,1));
|
||||
if (f->type & MAL_FUNCTION_MAL) {
|
||||
@ -310,12 +284,10 @@ void init_repl_env(int argc, char *argv[]) {
|
||||
// core.c: defined using C
|
||||
int i;
|
||||
for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) {
|
||||
env_set(repl_env,
|
||||
malval_new_symbol(core_ns[i].name),
|
||||
env_set(repl_env, core_ns[i].name,
|
||||
malval_new_function(core_ns[i].func, core_ns[i].arg_cnt));
|
||||
}
|
||||
env_set(repl_env,
|
||||
malval_new_symbol("eval"),
|
||||
env_set(repl_env, "eval",
|
||||
malval_new_function((void*(*)(void *))do_eval, 1));
|
||||
|
||||
MalVal *_argv = _listX(0);
|
||||
@ -323,7 +295,7 @@ void init_repl_env(int argc, char *argv[]) {
|
||||
MalVal *arg = malval_new_string(argv[i]);
|
||||
g_array_append_val(_argv->val.array, arg);
|
||||
}
|
||||
env_set(repl_env, malval_new_symbol("*ARGV*"), _argv);
|
||||
env_set(repl_env, "*ARGV*", _argv);
|
||||
|
||||
// core.mal: defined using the language itself
|
||||
RE(repl_env, "", "(def! not (fn* (a) (if a false true)))");
|
||||
|
@ -12,7 +12,6 @@
|
||||
// Declarations
|
||||
MalVal *EVAL(MalVal *ast, Env *env);
|
||||
MalVal *quasiquote(MalVal *ast);
|
||||
MalVal *macroexpand(MalVal *ast, Env *env);
|
||||
|
||||
// read
|
||||
MalVal *READ(char prompt[], char *str) {
|
||||
@ -70,31 +69,24 @@ MalVal *quasiquote(MalVal *ast) {
|
||||
}
|
||||
}
|
||||
|
||||
int is_macro_call(MalVal *ast, Env *env) {
|
||||
if (!ast || ast->type != MAL_LIST || _count(ast) == 0) { return 0; }
|
||||
MalVal *a0 = _nth(ast, 0);
|
||||
return (a0->type & MAL_SYMBOL) &&
|
||||
env_find(env, a0) &&
|
||||
env_get(env, a0)->ismacro;
|
||||
}
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
while (TRUE) {
|
||||
|
||||
MalVal *macroexpand(MalVal *ast, Env *env) {
|
||||
if (!ast || mal_error) return NULL;
|
||||
while (is_macro_call(ast, env)) {
|
||||
MalVal *a0 = _nth(ast, 0);
|
||||
MalVal *mac = env_get(env, a0);
|
||||
// TODO: this is weird and limits it to 20. FIXME
|
||||
ast = _apply(mac, _rest(ast));
|
||||
|
||||
MalVal *dbgeval = env_get(env, "DEBUG-EVAL");
|
||||
if (dbgeval && !(dbgeval->type & (MAL_FALSE|MAL_NIL))) {
|
||||
g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
if (!ast || mal_error) return NULL;
|
||||
if (ast->type == MAL_SYMBOL) {
|
||||
//g_print("EVAL symbol: %s\n", ast->val.string);
|
||||
return env_get(env, ast);
|
||||
} else if ((ast->type == MAL_LIST) || (ast->type == MAL_VECTOR)) {
|
||||
MalVal *res = env_get(env, ast->val.string);
|
||||
assert(res, "'%s' not found", ast->val.string);
|
||||
return res;
|
||||
} else if (ast->type == MAL_LIST) {
|
||||
// Proceed after this conditional.
|
||||
} else if (ast->type == MAL_VECTOR) {
|
||||
//g_print("EVAL sequential: %s\n", _pr_str(ast,1));
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, ast, env);
|
||||
if (!el || mal_error) return NULL;
|
||||
@ -119,25 +111,8 @@ MalVal *eval_ast(MalVal *ast, Env *env) {
|
||||
//g_print("EVAL scalar: %s\n", _pr_str(ast,1));
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
while (TRUE) {
|
||||
|
||||
if (!ast || mal_error) return NULL;
|
||||
//g_print("EVAL: %s\n", _pr_str(ast,1));
|
||||
if (ast->type != MAL_LIST) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
if (!ast || mal_error) return NULL;
|
||||
|
||||
// apply list
|
||||
//g_print("EVAL apply list: %s\n", _pr_str(ast,1));
|
||||
ast = macroexpand(ast, env);
|
||||
if (!ast || mal_error) return NULL;
|
||||
if (ast->type != MAL_LIST) {
|
||||
return eval_ast(ast, env);
|
||||
}
|
||||
if (_count(ast) == 0) { return ast; }
|
||||
|
||||
int i, len;
|
||||
@ -149,7 +124,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
*a2 = _nth(ast, 2);
|
||||
MalVal *res = EVAL(a2, env);
|
||||
if (mal_error) return NULL;
|
||||
env_set(env, a1, res);
|
||||
env_set(env, a1->val.string, res);
|
||||
return res;
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("let*", a0->val.string) == 0) {
|
||||
@ -166,7 +141,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
key = g_array_index(a1->val.array, MalVal*, i);
|
||||
val = g_array_index(a1->val.array, MalVal*, i+1);
|
||||
assert_type(key, MAL_SYMBOL, "let* bind to non-symbol");
|
||||
env_set(let_env, key, EVAL(val, let_env));
|
||||
env_set(let_env, key->val.string, EVAL(val, let_env));
|
||||
}
|
||||
ast = a2;
|
||||
env = let_env;
|
||||
@ -175,9 +150,6 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
strcmp("quote", a0->val.string) == 0) {
|
||||
//g_print("eval apply quote\n");
|
||||
return _nth(ast, 1);
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("quasiquoteexpand", a0->val.string) == 0) {
|
||||
return quasiquote(_nth(ast, 1));
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("quasiquote", a0->val.string) == 0) {
|
||||
//g_print("eval apply quasiquote\n");
|
||||
@ -189,20 +161,18 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
//g_print("eval apply defmacro!\n");
|
||||
MalVal *a1 = _nth(ast, 1),
|
||||
*a2 = _nth(ast, 2);
|
||||
MalVal *res = EVAL(a2, env);
|
||||
MalVal *old = EVAL(a2, env);
|
||||
if (mal_error) return NULL;
|
||||
MalVal *res = malval_new(MAL_FUNCTION_MAL, NULL);
|
||||
res->val.func = old->val.func;
|
||||
res->ismacro = TRUE;
|
||||
env_set(env, a1, res);
|
||||
env_set(env, a1->val.string, res);
|
||||
return res;
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("macroexpand", a0->val.string) == 0) {
|
||||
//g_print("eval apply macroexpand\n");
|
||||
MalVal *a1 = _nth(ast, 1);
|
||||
return macroexpand(a1, env);
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp(".", a0->val.string) == 0) {
|
||||
//g_print("eval apply .\n");
|
||||
MalVal *el = eval_ast(_slice(ast, 1, _count(ast)), env);
|
||||
MalVal *el = _map2((MalVal *(*)(void*, void*))EVAL, _slice(ast, 1, _count(ast)), env);
|
||||
if (!el || mal_error) return NULL;
|
||||
return invoke_native(el);
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("try*", a0->val.string) == 0) {
|
||||
@ -231,7 +201,7 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
strcmp("do", a0->val.string) == 0) {
|
||||
//g_print("eval apply do\n");
|
||||
eval_ast(_slice(ast, 1, _count(ast)-1), env);
|
||||
_map2((MalVal *(*)(void*, void*))EVAL, _slice(ast, 1, _count(ast)-1), env);
|
||||
ast = _last(ast);
|
||||
// Continue loop
|
||||
} else if ((a0->type & MAL_SYMBOL) &&
|
||||
@ -264,10 +234,15 @@ MalVal *EVAL(MalVal *ast, Env *env) {
|
||||
return mf;
|
||||
} else {
|
||||
//g_print("eval apply\n");
|
||||
MalVal *el = eval_ast(ast, env);
|
||||
if (!el || mal_error) { return NULL; }
|
||||
MalVal *f = _first(el),
|
||||
*args = _rest(el);
|
||||
MalVal *f = EVAL(a0, env);
|
||||
if (!f || mal_error) { return NULL; }
|
||||
MalVal *rest = _rest(ast);
|
||||
if (f->ismacro) {
|
||||
ast = _apply(f, rest);
|
||||
continue;
|
||||
}
|
||||
MalVal *args = _map2((MalVal *(*)(void*, void*))EVAL, rest, env);
|
||||
if (!args || mal_error) { return NULL; }
|
||||
assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL,
|
||||
"cannot apply '%s'", _pr_str(f,1));
|
||||
if (f->type & MAL_FUNCTION_MAL) {
|
||||
@ -315,12 +290,10 @@ void init_repl_env(int argc, char *argv[]) {
|
||||
// core.c: defined using C
|
||||
int i;
|
||||
for(i=0; i < (sizeof(core_ns) / sizeof(core_ns[0])); i++) {
|
||||
env_set(repl_env,
|
||||
malval_new_symbol(core_ns[i].name),
|
||||
env_set(repl_env, core_ns[i].name,
|
||||
malval_new_function(core_ns[i].func, core_ns[i].arg_cnt));
|
||||
}
|
||||
env_set(repl_env,
|
||||
malval_new_symbol("eval"),
|
||||
env_set(repl_env, "eval",
|
||||
malval_new_function((void*(*)(void *))do_eval, 1));
|
||||
|
||||
MalVal *_argv = _listX(0);
|
||||
@ -328,7 +301,7 @@ void init_repl_env(int argc, char *argv[]) {
|
||||
MalVal *arg = malval_new_string(argv[i]);
|
||||
g_array_append_val(_argv->val.array, arg);
|
||||
}
|
||||
env_set(repl_env, malval_new_symbol("*ARGV*"), _argv);
|
||||
env_set(repl_env, "*ARGV*", _argv);
|
||||
|
||||
// core.mal: defined using the language itself
|
||||
RE(repl_env, "", "(def! *host-language* \"c\")");
|
||||
|
@ -35,9 +35,9 @@ typedef struct Env {
|
||||
} Env;
|
||||
|
||||
Env *new_env(Env *outer, struct MalVal* binds, struct MalVal *exprs);
|
||||
Env *env_find(Env *env, struct MalVal *key);
|
||||
struct MalVal *env_get(Env *env, struct MalVal *key);
|
||||
Env *env_set(Env *env, struct MalVal *key, struct MalVal *val);
|
||||
struct MalVal *env_get(Env *env, const char *key);
|
||||
// Returns NULL if the key is missing.
|
||||
void env_set(Env *env, char *key, struct MalVal *val);
|
||||
|
||||
|
||||
// Utility functiosn
|
||||
|
@ -10,36 +10,32 @@
|
||||
(reader/read-string strng))
|
||||
|
||||
;; eval
|
||||
(declare EVAL)
|
||||
(defn eval-ast [ast env]
|
||||
(defn EVAL [ast env]
|
||||
|
||||
;; (println "EVAL:" (printer/pr-str ast) (keys @env))
|
||||
;; (flush)
|
||||
|
||||
(cond
|
||||
(symbol? ast) (or (get env ast)
|
||||
(throw (#?(:clj Error.
|
||||
:cljs js/Error.) (str ast " not found"))))
|
||||
|
||||
(seq? ast) (doall (map #(EVAL % env) ast))
|
||||
(vector? ast) (vec (map #(EVAL % env) ast))
|
||||
|
||||
(vector? ast) (vec (doall (map #(EVAL % env) ast)))
|
||||
|
||||
(map? ast) (apply hash-map (doall (map #(EVAL % env)
|
||||
(mapcat identity ast))))
|
||||
|
||||
:else ast))
|
||||
|
||||
(defn EVAL [ast env]
|
||||
;; indented to match later steps
|
||||
;;(prn "EVAL" ast (keys @env)) (flush)
|
||||
(if (not (seq? ast))
|
||||
(eval-ast ast env)
|
||||
(map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))
|
||||
|
||||
(seq? ast)
|
||||
;; apply list
|
||||
;; indented to match later steps
|
||||
(if (empty? ast)
|
||||
ast
|
||||
(let [el (eval-ast ast env)
|
||||
(let [el (map #(EVAL % env) ast)
|
||||
f (first el)
|
||||
args (rest el)]
|
||||
(apply f args)))))
|
||||
(apply f args)))
|
||||
|
||||
:else ;; not a list, map, symbol or vector
|
||||
ast))
|
||||
|
||||
;; print
|
||||
(defn PRINT [exp] (printer/pr-str exp))
|
||||
|
@ -11,26 +11,24 @@
|
||||
(reader/read-string strng))
|
||||
|
||||
;; eval
|
||||
(declare EVAL)
|
||||
(defn eval-ast [ast env]
|
||||
(defn EVAL [ast env]
|
||||
|
||||
(let [e (env/env-find env 'DEBUG-EVAL)]
|
||||
(when e
|
||||
(let [v (env/env-get e 'DEBUG-EVAL)]
|
||||
(when (and (not= v nil)
|
||||
(not= v false))
|
||||
(println "EVAL:" (printer/pr-str ast) (keys @env))
|
||||
(flush)))))
|
||||
|
||||
(cond
|
||||
(symbol? ast) (env/env-get env ast)
|
||||
|
||||
(seq? ast) (doall (map #(EVAL % env) ast))
|
||||
(vector? ast) (vec (map #(EVAL % env) ast))
|
||||
|
||||
(vector? ast) (vec (doall (map #(EVAL % env) ast)))
|
||||
|
||||
(map? ast) (apply hash-map (doall (map #(EVAL % env)
|
||||
(mapcat identity ast))))
|
||||
|
||||
:else ast))
|
||||
|
||||
(defn EVAL [ast env]
|
||||
;; indented to match later steps
|
||||
;;(prn "EVAL" ast (keys @env)) (flush)
|
||||
(if (not (seq? ast))
|
||||
(eval-ast ast env)
|
||||
(map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))
|
||||
|
||||
(seq? ast)
|
||||
;; apply list
|
||||
;; indented to match later steps
|
||||
(let [[a0 a1 a2 a3] ast]
|
||||
@ -48,10 +46,13 @@
|
||||
(EVAL a2 let-env))
|
||||
|
||||
;; apply
|
||||
(let [el (eval-ast ast env)
|
||||
(let [el (map #(EVAL % env) ast)
|
||||
f (first el)
|
||||
args (rest el)]
|
||||
(apply f args))))))
|
||||
(apply f args))))
|
||||
|
||||
:else ;; not a list, map, symbol or vector
|
||||
ast))
|
||||
|
||||
;; print
|
||||
(defn PRINT [exp] (printer/pr-str exp))
|
||||
|
@ -12,26 +12,24 @@
|
||||
(reader/read-string strng))
|
||||
|
||||
;; eval
|
||||
(declare EVAL)
|
||||
(defn eval-ast [ast env]
|
||||
(defn EVAL [ast env]
|
||||
|
||||
(let [e (env/env-find env 'DEBUG-EVAL)]
|
||||
(when e
|
||||
(let [v (env/env-get e 'DEBUG-EVAL)]
|
||||
(when (and (not= v nil)
|
||||
(not= v false))
|
||||
(println "EVAL:" (printer/pr-str ast) (keys @env))
|
||||
(flush)))))
|
||||
|
||||
(cond
|
||||
(symbol? ast) (env/env-get env ast)
|
||||
|
||||
(seq? ast) (doall (map #(EVAL % env) ast))
|
||||
(vector? ast) (vec (map #(EVAL % env) ast))
|
||||
|
||||
(vector? ast) (vec (doall (map #(EVAL % env) ast)))
|
||||
|
||||
(map? ast) (apply hash-map (doall (map #(EVAL % env)
|
||||
(mapcat identity ast))))
|
||||
|
||||
:else ast))
|
||||
|
||||
(defn EVAL [ast env]
|
||||
;; indented to match later steps
|
||||
;;(prn "EVAL" ast (keys @env)) (flush)
|
||||
(if (not (seq? ast))
|
||||
(eval-ast ast env)
|
||||
(map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))
|
||||
|
||||
(seq? ast)
|
||||
;; apply list
|
||||
;; indented to match later steps
|
||||
(let [[a0 a1 a2 a3] ast]
|
||||
@ -49,7 +47,7 @@
|
||||
(EVAL a2 let-env))
|
||||
|
||||
'do
|
||||
(last (eval-ast (rest ast) env))
|
||||
(last (doall (map #(EVAL % env) (rest ast))))
|
||||
|
||||
'if
|
||||
(let [cond (EVAL a1 env)]
|
||||
@ -64,10 +62,13 @@
|
||||
(EVAL a2 (env/env env a1 (or args '()))))
|
||||
|
||||
;; apply
|
||||
(let [el (eval-ast ast env)
|
||||
(let [el (map #(EVAL % env) ast)
|
||||
f (first el)
|
||||
args (rest el)]
|
||||
(apply f args))))))
|
||||
(apply f args))))
|
||||
|
||||
:else ;; not a list, map, symbol or vector
|
||||
ast))
|
||||
|
||||
;; print
|
||||
(defn PRINT [exp] (printer/pr-str exp))
|
||||
|
@ -12,27 +12,26 @@
|
||||
(reader/read-string strng))
|
||||
|
||||
;; eval
|
||||
(declare EVAL)
|
||||
(defn eval-ast [ast env]
|
||||
(cond
|
||||
(symbol? ast) (env/env-get env ast)
|
||||
|
||||
(seq? ast) (doall (map #(EVAL % env) ast))
|
||||
|
||||
(vector? ast) (vec (doall (map #(EVAL % env) ast)))
|
||||
|
||||
(map? ast) (apply hash-map (doall (map #(EVAL % env)
|
||||
(mapcat identity ast))))
|
||||
|
||||
:else ast))
|
||||
|
||||
(defn EVAL [ast env]
|
||||
(loop [ast ast
|
||||
env env]
|
||||
;;(prn "EVAL" ast (keys @env)) (flush)
|
||||
(if (not (seq? ast))
|
||||
(eval-ast ast env)
|
||||
|
||||
(let [e (env/env-find env 'DEBUG-EVAL)]
|
||||
(when e
|
||||
(let [v (env/env-get e 'DEBUG-EVAL)]
|
||||
(when (and (not= v nil)
|
||||
(not= v false))
|
||||
(println "EVAL:" (printer/pr-str ast) (keys @env))
|
||||
(flush)))))
|
||||
|
||||
(cond
|
||||
(symbol? ast) (env/env-get env ast)
|
||||
|
||||
(vector? ast) (vec (map #(EVAL % env) ast))
|
||||
|
||||
(map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))
|
||||
|
||||
(seq? ast)
|
||||
;; apply list
|
||||
;; indented to match later steps
|
||||
(let [[a0 a1 a2 a3] ast]
|
||||
@ -50,7 +49,7 @@
|
||||
(recur a2 let-env))
|
||||
|
||||
'do
|
||||
(do (eval-ast (->> ast (drop-last) (drop 1)) env)
|
||||
(do (doall (map #(EVAL % env) (->> ast (drop-last) (drop 1))))
|
||||
(recur (last ast) env))
|
||||
|
||||
'if
|
||||
@ -70,13 +69,17 @@
|
||||
:parameters a1})
|
||||
|
||||
;; apply
|
||||
(let [el (eval-ast ast env)
|
||||
(let [el (map #(EVAL % env) ast)
|
||||
f (first el)
|
||||
args (rest el)
|
||||
{:keys [expression environment parameters]} (meta f)]
|
||||
(if expression
|
||||
(recur expression (env/env environment parameters args))
|
||||
(apply f args))))))))
|
||||
(apply f args)))))
|
||||
|
||||
:else ;; not a list, map, symbol or vector
|
||||
ast)))
|
||||
|
||||
|
||||
;; print
|
||||
(defn PRINT [exp] (printer/pr-str exp))
|
||||
|
@ -12,27 +12,26 @@
|
||||
(reader/read-string strng))
|
||||
|
||||
;; eval
|
||||
(declare EVAL)
|
||||
(defn eval-ast [ast env]
|
||||
(cond
|
||||
(symbol? ast) (env/env-get env ast)
|
||||
|
||||
(seq? ast) (doall (map #(EVAL % env) ast))
|
||||
|
||||
(vector? ast) (vec (doall (map #(EVAL % env) ast)))
|
||||
|
||||
(map? ast) (apply hash-map (doall (map #(EVAL % env)
|
||||
(mapcat identity ast))))
|
||||
|
||||
:else ast))
|
||||
|
||||
(defn EVAL [ast env]
|
||||
(loop [ast ast
|
||||
env env]
|
||||
;;(prn "EVAL" ast (keys @env)) (flush)
|
||||
(if (not (seq? ast))
|
||||
(eval-ast ast env)
|
||||
|
||||
(let [e (env/env-find env 'DEBUG-EVAL)]
|
||||
(when e
|
||||
(let [v (env/env-get e 'DEBUG-EVAL)]
|
||||
(when (and (not= v nil)
|
||||
(not= v false))
|
||||
(println "EVAL:" (printer/pr-str ast) (keys @env))
|
||||
(flush)))))
|
||||
|
||||
(cond
|
||||
(symbol? ast) (env/env-get env ast)
|
||||
|
||||
(vector? ast) (vec (map #(EVAL % env) ast))
|
||||
|
||||
(map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))
|
||||
|
||||
(seq? ast)
|
||||
;; apply list
|
||||
;; indented to match later steps
|
||||
(let [[a0 a1 a2 a3] ast]
|
||||
@ -50,7 +49,7 @@
|
||||
(recur a2 let-env))
|
||||
|
||||
'do
|
||||
(do (eval-ast (->> ast (drop-last) (drop 1)) env)
|
||||
(do (doall (map #(EVAL % env) (->> ast (drop-last) (drop 1))))
|
||||
(recur (last ast) env))
|
||||
|
||||
'if
|
||||
@ -70,13 +69,17 @@
|
||||
:parameters a1})
|
||||
|
||||
;; apply
|
||||
(let [el (eval-ast ast env)
|
||||
(let [el (map #(EVAL % env) ast)
|
||||
f (first el)
|
||||
args (rest el)
|
||||
{:keys [expression environment parameters]} (meta f)]
|
||||
(if expression
|
||||
(recur expression (env/env environment parameters args))
|
||||
(apply f args))))))))
|
||||
(apply f args)))))
|
||||
|
||||
:else ;; not a list, map, symbol or vector
|
||||
ast)))
|
||||
|
||||
|
||||
;; print
|
||||
(defn PRINT [exp] (printer/pr-str exp))
|
||||
|
@ -12,8 +12,6 @@
|
||||
(reader/read-string strng))
|
||||
|
||||
;; eval
|
||||
(declare EVAL)
|
||||
|
||||
(declare quasiquote)
|
||||
(defn starts_with [ast sym]
|
||||
(and (seq? ast)
|
||||
@ -33,26 +31,26 @@
|
||||
(or (symbol? ast) (map? ast)) (list 'quote ast)
|
||||
:else ast))
|
||||
|
||||
(defn eval-ast [ast env]
|
||||
(cond
|
||||
(symbol? ast) (env/env-get env ast)
|
||||
|
||||
(seq? ast) (doall (map #(EVAL % env) ast))
|
||||
|
||||
(vector? ast) (vec (doall (map #(EVAL % env) ast)))
|
||||
|
||||
(map? ast) (apply hash-map (doall (map #(EVAL % env)
|
||||
(mapcat identity ast))))
|
||||
|
||||
:else ast))
|
||||
|
||||
(defn EVAL [ast env]
|
||||
(loop [ast ast
|
||||
env env]
|
||||
;;(prn "EVAL" ast (keys @env)) (flush)
|
||||
(if (not (seq? ast))
|
||||
(eval-ast ast env)
|
||||
|
||||
(let [e (env/env-find env 'DEBUG-EVAL)]
|
||||
(when e
|
||||
(let [v (env/env-get e 'DEBUG-EVAL)]
|
||||
(when (and (not= v nil)
|
||||
(not= v false))
|
||||
(println "EVAL:" (printer/pr-str ast) (keys @env))
|
||||
(flush)))))
|
||||
|
||||
(cond
|
||||
(symbol? ast) (env/env-get env ast)
|
||||
|
||||
(vector? ast) (vec (map #(EVAL % env) ast))
|
||||
|
||||
(map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))
|
||||
|
||||
(seq? ast)
|
||||
;; apply list
|
||||
;; indented to match later steps
|
||||
(let [[a0 a1 a2 a3] ast]
|
||||
@ -72,14 +70,11 @@
|
||||
'quote
|
||||
a1
|
||||
|
||||
'quasiquoteexpand
|
||||
(quasiquote a1)
|
||||
|
||||
'quasiquote
|
||||
(recur (quasiquote a1) env)
|
||||
|
||||
'do
|
||||
(do (eval-ast (->> ast (drop-last) (drop 1)) env)
|
||||
(do (doall (map #(EVAL % env) (->> ast (drop-last) (drop 1))))
|
||||
(recur (last ast) env))
|
||||
|
||||
'if
|
||||
@ -99,13 +94,17 @@
|
||||
:parameters a1})
|
||||
|
||||
;; apply
|
||||
(let [el (eval-ast ast env)
|
||||
(let [el (map #(EVAL % env) ast)
|
||||
f (first el)
|
||||
args (rest el)
|
||||
{:keys [expression environment parameters]} (meta f)]
|
||||
(if expression
|
||||
(recur expression (env/env environment parameters args))
|
||||
(apply f args))))))))
|
||||
(apply f args)))))
|
||||
|
||||
:else ;; not a list, map, symbol or vector
|
||||
ast)))
|
||||
|
||||
|
||||
;; print
|
||||
(defn PRINT [exp] (printer/pr-str exp))
|
||||
|
@ -1,5 +1,4 @@
|
||||
(ns mal.step8-macros
|
||||
(:refer-clojure :exclude [macroexpand])
|
||||
(:require [mal.readline :as readline]
|
||||
#?(:clj [clojure.repl])
|
||||
[mal.reader :as reader]
|
||||
@ -13,8 +12,6 @@
|
||||
(reader/read-string strng))
|
||||
|
||||
;; eval
|
||||
(declare EVAL)
|
||||
|
||||
(declare quasiquote)
|
||||
(defn starts_with [ast sym]
|
||||
(and (seq? ast)
|
||||
@ -34,46 +31,28 @@
|
||||
(or (symbol? ast) (map? ast)) (list 'quote ast)
|
||||
:else ast))
|
||||
|
||||
(defn is-macro-call [ast env]
|
||||
(and (seq? ast)
|
||||
(symbol? (first ast))
|
||||
(env/env-find env (first ast))
|
||||
(:ismacro (meta (env/env-get env (first ast))))))
|
||||
|
||||
(defn macroexpand [ast env]
|
||||
(loop [ast ast]
|
||||
(if (is-macro-call ast env)
|
||||
;; Get original unadorned function because ClojureScript (1.10)
|
||||
;; limits functions with meta on them to arity 20
|
||||
(let [mac (:orig (meta (env/env-get env (first ast))))]
|
||||
(recur (apply mac (rest ast))))
|
||||
ast)))
|
||||
|
||||
(defn eval-ast [ast env]
|
||||
(cond
|
||||
(symbol? ast) (env/env-get env ast)
|
||||
|
||||
(seq? ast) (doall (map #(EVAL % env) ast))
|
||||
|
||||
(vector? ast) (vec (doall (map #(EVAL % env) ast)))
|
||||
|
||||
(map? ast) (apply hash-map (doall (map #(EVAL % env)
|
||||
(mapcat identity ast))))
|
||||
|
||||
:else ast))
|
||||
|
||||
(defn EVAL [ast env]
|
||||
(loop [ast ast
|
||||
env env]
|
||||
;;(prn "EVAL" ast (keys @env)) (flush)
|
||||
(if (not (seq? ast))
|
||||
(eval-ast ast env)
|
||||
|
||||
(let [e (env/env-find env 'DEBUG-EVAL)]
|
||||
(when e
|
||||
(let [v (env/env-get e 'DEBUG-EVAL)]
|
||||
(when (and (not= v nil)
|
||||
(not= v false))
|
||||
(println "EVAL:" (printer/pr-str ast) (keys @env))
|
||||
(flush)))))
|
||||
|
||||
(cond
|
||||
(symbol? ast) (env/env-get env ast)
|
||||
|
||||
(vector? ast) (vec (map #(EVAL % env) ast))
|
||||
|
||||
(map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))
|
||||
|
||||
(seq? ast)
|
||||
;; apply list
|
||||
(let [ast (macroexpand ast env)]
|
||||
(if (not (seq? ast))
|
||||
(eval-ast ast env)
|
||||
|
||||
;; indented to match later steps
|
||||
(let [[a0 a1 a2 a3] ast]
|
||||
(condp = a0
|
||||
nil
|
||||
@ -91,9 +70,6 @@
|
||||
'quote
|
||||
a1
|
||||
|
||||
'quasiquoteexpand
|
||||
(quasiquote a1)
|
||||
|
||||
'quasiquote
|
||||
(recur (quasiquote a1) env)
|
||||
|
||||
@ -105,11 +81,8 @@
|
||||
:ismacro true})]
|
||||
(env/env-set env a1 mac))
|
||||
|
||||
'macroexpand
|
||||
(macroexpand a1 env)
|
||||
|
||||
'do
|
||||
(do (eval-ast (->> ast (drop-last) (drop 1)) env)
|
||||
(do (doall (map #(EVAL % env) (->> ast (drop-last) (drop 1))))
|
||||
(recur (last ast) env))
|
||||
|
||||
'if
|
||||
@ -133,13 +106,19 @@
|
||||
:parameters a1}))
|
||||
|
||||
;; apply
|
||||
(let [el (eval-ast ast env)
|
||||
f (first el)
|
||||
args (rest el)
|
||||
(let [f (EVAL a0 env)
|
||||
unevaluated_args (rest ast)]
|
||||
(if (:ismacro (meta f))
|
||||
(recur (apply (:orig (meta f)) unevaluated_args) env)
|
||||
(let [args (map #(EVAL % env) unevaluated_args)
|
||||
{:keys [expression environment parameters]} (meta f)]
|
||||
(if expression
|
||||
(recur expression (env/env environment parameters args))
|
||||
(apply f args))))))))))
|
||||
(apply f args)))))))
|
||||
|
||||
:else ;; not a list, map, symbol or vector
|
||||
ast)))
|
||||
|
||||
|
||||
;; print
|
||||
(defn PRINT [exp] (printer/pr-str exp))
|
||||
|
@ -1,5 +1,4 @@
|
||||
(ns mal.step9-try
|
||||
(:refer-clojure :exclude [macroexpand])
|
||||
(:require [mal.readline :as readline]
|
||||
#?(:clj [clojure.repl])
|
||||
[mal.reader :as reader]
|
||||
@ -13,8 +12,6 @@
|
||||
(reader/read-string strng))
|
||||
|
||||
;; eval
|
||||
(declare EVAL)
|
||||
|
||||
(declare quasiquote)
|
||||
(defn starts_with [ast sym]
|
||||
(and (seq? ast)
|
||||
@ -34,46 +31,28 @@
|
||||
(or (symbol? ast) (map? ast)) (list 'quote ast)
|
||||
:else ast))
|
||||
|
||||
(defn is-macro-call [ast env]
|
||||
(and (seq? ast)
|
||||
(symbol? (first ast))
|
||||
(env/env-find env (first ast))
|
||||
(:ismacro (meta (env/env-get env (first ast))))))
|
||||
|
||||
(defn macroexpand [ast env]
|
||||
(loop [ast ast]
|
||||
(if (is-macro-call ast env)
|
||||
;; Get original unadorned function because ClojureScript (1.10)
|
||||
;; limits functions with meta on them to arity 20
|
||||
(let [mac (:orig (meta (env/env-get env (first ast))))]
|
||||
(recur (apply mac (rest ast))))
|
||||
ast)))
|
||||
|
||||
(defn eval-ast [ast env]
|
||||
(cond
|
||||
(symbol? ast) (env/env-get env ast)
|
||||
|
||||
(seq? ast) (doall (map #(EVAL % env) ast))
|
||||
|
||||
(vector? ast) (vec (doall (map #(EVAL % env) ast)))
|
||||
|
||||
(map? ast) (apply hash-map (doall (map #(EVAL % env)
|
||||
(mapcat identity ast))))
|
||||
|
||||
:else ast))
|
||||
|
||||
(defn EVAL [ast env]
|
||||
(loop [ast ast
|
||||
env env]
|
||||
;;(prn "EVAL" ast (keys @env)) (flush)
|
||||
(if (not (seq? ast))
|
||||
(eval-ast ast env)
|
||||
|
||||
(let [e (env/env-find env 'DEBUG-EVAL)]
|
||||
(when e
|
||||
(let [v (env/env-get e 'DEBUG-EVAL)]
|
||||
(when (and (not= v nil)
|
||||
(not= v false))
|
||||
(println "EVAL:" (printer/pr-str ast) (keys @env))
|
||||
(flush)))))
|
||||
|
||||
(cond
|
||||
(symbol? ast) (env/env-get env ast)
|
||||
|
||||
(vector? ast) (vec (map #(EVAL % env) ast))
|
||||
|
||||
(map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))
|
||||
|
||||
(seq? ast)
|
||||
;; apply list
|
||||
(let [ast (macroexpand ast env)]
|
||||
(if (not (seq? ast))
|
||||
(eval-ast ast env)
|
||||
|
||||
;; indented to match later steps
|
||||
(let [[a0 a1 a2 a3] ast]
|
||||
(condp = a0
|
||||
nil
|
||||
@ -91,9 +70,6 @@
|
||||
'quote
|
||||
a1
|
||||
|
||||
'quasiquoteexpand
|
||||
(quasiquote a1)
|
||||
|
||||
'quasiquote
|
||||
(recur (quasiquote a1) env)
|
||||
|
||||
@ -105,9 +81,6 @@
|
||||
:ismacro true})]
|
||||
(env/env-set env a1 mac))
|
||||
|
||||
'macroexpand
|
||||
(macroexpand a1 env)
|
||||
|
||||
'try*
|
||||
(if (= 'catch* (nth a2 0))
|
||||
(try
|
||||
@ -126,7 +99,7 @@
|
||||
(EVAL a1 env))
|
||||
|
||||
'do
|
||||
(do (eval-ast (->> ast (drop-last) (drop 1)) env)
|
||||
(do (doall (map #(EVAL % env) (->> ast (drop-last) (drop 1))))
|
||||
(recur (last ast) env))
|
||||
|
||||
'if
|
||||
@ -150,13 +123,19 @@
|
||||
:parameters a1}))
|
||||
|
||||
;; apply
|
||||
(let [el (eval-ast ast env)
|
||||
f (first el)
|
||||
args (rest el)
|
||||
(let [f (EVAL a0 env)
|
||||
unevaluated_args (rest ast)]
|
||||
(if (:ismacro (meta f))
|
||||
(recur (apply (:orig (meta f)) unevaluated_args) env)
|
||||
(let [args (map #(EVAL % env) unevaluated_args)
|
||||
{:keys [expression environment parameters]} (meta f)]
|
||||
(if expression
|
||||
(recur expression (env/env environment parameters args))
|
||||
(apply f args))))))))))
|
||||
(apply f args)))))))
|
||||
|
||||
:else ;; not a list, map, symbol or vector
|
||||
ast)))
|
||||
|
||||
|
||||
;; print
|
||||
(defn PRINT [exp] (printer/pr-str exp))
|
||||
|
@ -1,5 +1,4 @@
|
||||
(ns mal.stepA-mal
|
||||
(:refer-clojure :exclude [macroexpand])
|
||||
(:require [mal.readline :as readline]
|
||||
#?(:clj [clojure.repl])
|
||||
[mal.reader :as reader]
|
||||
@ -13,8 +12,6 @@
|
||||
(reader/read-string strng))
|
||||
|
||||
;; eval
|
||||
(declare EVAL)
|
||||
|
||||
(declare quasiquote)
|
||||
(defn starts_with [ast sym]
|
||||
(and (seq? ast)
|
||||
@ -34,46 +31,28 @@
|
||||
(or (symbol? ast) (map? ast)) (list 'quote ast)
|
||||
:else ast))
|
||||
|
||||
(defn is-macro-call [ast env]
|
||||
(and (seq? ast)
|
||||
(symbol? (first ast))
|
||||
(env/env-find env (first ast))
|
||||
(:ismacro (meta (env/env-get env (first ast))))))
|
||||
|
||||
(defn macroexpand [ast env]
|
||||
(loop [ast ast]
|
||||
(if (is-macro-call ast env)
|
||||
;; Get original unadorned function because ClojureScript (1.10)
|
||||
;; limits functions with meta on them to arity 20
|
||||
(let [mac (:orig (meta (env/env-get env (first ast))))]
|
||||
(recur (apply mac (rest ast))))
|
||||
ast)))
|
||||
|
||||
(defn eval-ast [ast env]
|
||||
(cond
|
||||
(symbol? ast) (env/env-get env ast)
|
||||
|
||||
(seq? ast) (doall (map #(EVAL % env) ast))
|
||||
|
||||
(vector? ast) (vec (doall (map #(EVAL % env) ast)))
|
||||
|
||||
(map? ast) (apply hash-map (doall (map #(EVAL % env)
|
||||
(mapcat identity ast))))
|
||||
|
||||
:else ast))
|
||||
|
||||
(defn EVAL [ast env]
|
||||
(loop [ast ast
|
||||
env env]
|
||||
;;(prn "EVAL" ast (keys @env)) (flush)
|
||||
(if (not (seq? ast))
|
||||
(eval-ast ast env)
|
||||
|
||||
(let [e (env/env-find env 'DEBUG-EVAL)]
|
||||
(when e
|
||||
(let [v (env/env-get e 'DEBUG-EVAL)]
|
||||
(when (and (not= v nil)
|
||||
(not= v false))
|
||||
(println "EVAL:" (printer/pr-str ast) (keys @env))
|
||||
(flush)))))
|
||||
|
||||
(cond
|
||||
(symbol? ast) (env/env-get env ast)
|
||||
|
||||
(vector? ast) (vec (map #(EVAL % env) ast))
|
||||
|
||||
(map? ast) (apply hash-map (map #(EVAL % env) (mapcat identity ast)))
|
||||
|
||||
(seq? ast)
|
||||
;; apply list
|
||||
(let [ast (macroexpand ast env)]
|
||||
(if (not (seq? ast))
|
||||
(eval-ast ast env)
|
||||
|
||||
;; indented to match later steps
|
||||
(let [[a0 a1 a2 a3] ast]
|
||||
(condp = a0
|
||||
nil
|
||||
@ -91,9 +70,6 @@
|
||||
'quote
|
||||
a1
|
||||
|
||||
'quasiquoteexpand
|
||||
(quasiquote a1)
|
||||
|
||||
'quasiquote
|
||||
(recur (quasiquote a1) env)
|
||||
|
||||
@ -105,9 +81,6 @@
|
||||
:ismacro true})]
|
||||
(env/env-set env a1 mac))
|
||||
|
||||
'macroexpand
|
||||
(macroexpand a1 env)
|
||||
|
||||
'clj*
|
||||
#?(:clj (eval (reader/read-string a1))
|
||||
:cljs (throw (ex-info "clj* unsupported in ClojureScript mode" {})))
|
||||
@ -134,7 +107,7 @@
|
||||
(EVAL a1 env))
|
||||
|
||||
'do
|
||||
(do (eval-ast (->> ast (drop-last) (drop 1)) env)
|
||||
(do (doall (map #(EVAL % env) (->> ast (drop-last) (drop 1))))
|
||||
(recur (last ast) env))
|
||||
|
||||
'if
|
||||
@ -158,13 +131,19 @@
|
||||
:parameters a1}))
|
||||
|
||||
;; apply
|
||||
(let [el (eval-ast ast env)
|
||||
f (first el)
|
||||
args (rest el)
|
||||
(let [f (EVAL a0 env)
|
||||
unevaluated_args (rest ast)]
|
||||
(if (:ismacro (meta f))
|
||||
(recur (apply (:orig (meta f)) unevaluated_args) env)
|
||||
(let [args (map #(EVAL % env) unevaluated_args)
|
||||
{:keys [expression environment parameters]} (meta f)]
|
||||
(if expression
|
||||
(recur expression (env/env environment parameters args))
|
||||
(apply f args))))))))))
|
||||
(apply f args)))))))
|
||||
|
||||
:else ;; not a list, map, symbol or vector
|
||||
ast)))
|
||||
|
||||
|
||||
;; print
|
||||
(defn PRINT [exp] (printer/pr-str exp))
|
||||
|
@ -12,9 +12,7 @@ exports.Env = class Env
|
||||
else
|
||||
@data[b.name] = @exprs[i]
|
||||
find: (key) ->
|
||||
if not types._symbol_Q(key)
|
||||
throw new Error("env.find key must be symbol")
|
||||
if key.name of @data then @
|
||||
if key of @data then @
|
||||
else if @outer then @outer.find(key)
|
||||
else null
|
||||
set: (key, value) ->
|
||||
@ -22,10 +20,8 @@ exports.Env = class Env
|
||||
throw new Error("env.set key must be symbol")
|
||||
@data[key.name] = value
|
||||
get: (key) ->
|
||||
if not types._symbol_Q(key)
|
||||
throw new Error("env.get key must be symbol")
|
||||
env = @find(key)
|
||||
throw new Error("'" + key.name + "' not found") if !env
|
||||
env.data[key.name]
|
||||
throw new Error("'" + key + "' not found") if !env
|
||||
env.data[key]
|
||||
|
||||
# vim: ts=2:sw=2
|
||||
|
@ -7,24 +7,24 @@ printer = require "./printer.coffee"
|
||||
READ = (str) -> reader.read_str str
|
||||
|
||||
# eval
|
||||
eval_ast = (ast, env) ->
|
||||
if types._symbol_Q(ast) then env[ast.name]
|
||||
else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env))
|
||||
EVAL = (ast, env) ->
|
||||
# console.log "EVAL:", printer._pr_str ast
|
||||
|
||||
if types._symbol_Q(ast) then return env[ast.name]
|
||||
else if types._list_Q(ast) then # exit this switch
|
||||
else if types._list_Q(ast) then # exit this switch
|
||||
else if types._vector_Q(ast)
|
||||
types._vector(ast.map((a) -> EVAL(a, env))...)
|
||||
return types._vector(ast.map((a) -> EVAL(a, env))...)
|
||||
else if types._hash_map_Q(ast)
|
||||
new_hm = {}
|
||||
new_hm[k] = EVAL(v, env) for k,v of ast
|
||||
new_hm
|
||||
else ast
|
||||
return new_hm
|
||||
else return ast
|
||||
|
||||
EVAL = (ast, env) ->
|
||||
#console.log "EVAL:", printer._pr_str ast
|
||||
if !types._list_Q ast then return eval_ast ast, env
|
||||
if ast.length == 0 then return ast
|
||||
|
||||
# apply list
|
||||
[f, args...] = eval_ast ast, env
|
||||
[f, args...] = ast.map((a) -> EVAL(a, env))
|
||||
f(args...)
|
||||
|
||||
|
||||
|
@ -8,20 +8,23 @@ Env = require("./env.coffee").Env
|
||||
READ = (str) -> reader.read_str str
|
||||
|
||||
# eval
|
||||
eval_ast = (ast, env) ->
|
||||
if types._symbol_Q(ast) then env.get ast
|
||||
else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env))
|
||||
EVAL = (ast, env) ->
|
||||
dbgenv = env.find("DEBUG-EVAL")
|
||||
if dbgenv
|
||||
dbgeval = dbgenv.get("DEBUG-EVAL")
|
||||
if dbgeval != null and dbgeval != false
|
||||
console.log "EVAL:", printer._pr_str ast
|
||||
|
||||
if types._symbol_Q(ast) then return env.get ast.name
|
||||
else if types._list_Q(ast) then # exit this switch
|
||||
else if types._vector_Q(ast)
|
||||
types._vector(ast.map((a) -> EVAL(a, env))...)
|
||||
return types._vector(ast.map((a) -> EVAL(a, env))...)
|
||||
else if types._hash_map_Q(ast)
|
||||
new_hm = {}
|
||||
new_hm[k] = EVAL(v, env) for k,v of ast
|
||||
new_hm
|
||||
else ast
|
||||
return new_hm
|
||||
else return ast
|
||||
|
||||
EVAL = (ast, env) ->
|
||||
#console.log "EVAL:", printer._pr_str ast
|
||||
if !types._list_Q ast then return eval_ast ast, env
|
||||
if ast.length == 0 then return ast
|
||||
|
||||
# apply list
|
||||
@ -35,7 +38,7 @@ EVAL = (ast, env) ->
|
||||
let_env.set(a1[i], EVAL(a1[i+1], let_env))
|
||||
EVAL(a2, let_env)
|
||||
else
|
||||
[f, args...] = eval_ast ast, env
|
||||
[f, args...] = ast.map((a) -> EVAL(a, env))
|
||||
f(args...)
|
||||
|
||||
|
||||
|
@ -9,20 +9,23 @@ core = require("./core.coffee")
|
||||
READ = (str) -> reader.read_str str
|
||||
|
||||
# eval
|
||||
eval_ast = (ast, env) ->
|
||||
if types._symbol_Q(ast) then env.get ast
|
||||
else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env))
|
||||
EVAL = (ast, env) ->
|
||||
dbgenv = env.find("DEBUG-EVAL")
|
||||
if dbgenv
|
||||
dbgeval = dbgenv.get("DEBUG-EVAL")
|
||||
if dbgeval != null and dbgeval != false
|
||||
console.log "EVAL:", printer._pr_str ast
|
||||
|
||||
if types._symbol_Q(ast) then return env.get ast.name
|
||||
else if types._list_Q(ast) then # exit this switch
|
||||
else if types._vector_Q(ast)
|
||||
types._vector(ast.map((a) -> EVAL(a, env))...)
|
||||
return types._vector(ast.map((a) -> EVAL(a, env))...)
|
||||
else if types._hash_map_Q(ast)
|
||||
new_hm = {}
|
||||
new_hm[k] = EVAL(v, env) for k,v of ast
|
||||
new_hm
|
||||
else ast
|
||||
return new_hm
|
||||
else return ast
|
||||
|
||||
EVAL = (ast, env) ->
|
||||
#console.log "EVAL:", printer._pr_str ast
|
||||
if !types._list_Q ast then return eval_ast ast, env
|
||||
if ast.length == 0 then return ast
|
||||
|
||||
# apply list
|
||||
@ -36,7 +39,7 @@ EVAL = (ast, env) ->
|
||||
let_env.set(a1[i], EVAL(a1[i+1], let_env))
|
||||
EVAL(a2, let_env)
|
||||
when "do"
|
||||
el = eval_ast(ast[1..], env)
|
||||
el = ast[1..].map((a) -> EVAL(a, env))
|
||||
el[el.length-1]
|
||||
when "if"
|
||||
cond = EVAL(a1, env)
|
||||
@ -47,7 +50,7 @@ EVAL = (ast, env) ->
|
||||
when "fn*"
|
||||
(args...) -> EVAL(a2, new Env(env, a1, args))
|
||||
else
|
||||
[f, args...] = eval_ast ast, env
|
||||
[f, args...] = ast.map((a) -> EVAL(a, env))
|
||||
f(args...)
|
||||
|
||||
|
||||
|
@ -9,21 +9,24 @@ core = require("./core.coffee")
|
||||
READ = (str) -> reader.read_str str
|
||||
|
||||
# eval
|
||||
eval_ast = (ast, env) ->
|
||||
if types._symbol_Q(ast) then env.get ast
|
||||
else if types._list_Q(ast) then ast.map((a) -> EVAL(a, env))
|
||||
EVAL = (ast, env) ->
|
||||
loop
|
||||
dbgenv = env.find("DEBUG-EVAL")
|
||||
if dbgenv
|
||||
dbgeval = dbgenv.get("DEBUG-EVAL")
|
||||
if dbgeval != null and dbgeval != false
|
||||
console.log "EVAL:", printer._pr_str ast
|
||||
|
||||
if types._symbol_Q(ast) then return env.get ast.name
|
||||
else if types._list_Q(ast) then # exit this switch
|
||||
else if types._vector_Q(ast)
|
||||
types._vector(ast.map((a) -> EVAL(a, env))...)
|
||||
return types._vector(ast.map((a) -> EVAL(a, env))...)
|
||||
else if types._hash_map_Q(ast)
|
||||
new_hm = {}
|
||||
new_hm[k] = EVAL(v, env) for k,v of ast
|
||||
new_hm
|
||||
else ast
|
||||
return new_hm
|
||||
else return ast
|
||||
|
||||
EVAL = (ast, env) ->
|
||||
loop
|
||||
#console.log "EVAL:", printer._pr_str ast
|
||||
if !types._list_Q ast then return eval_ast ast, env
|
||||
if ast.length == 0 then return ast
|
||||
|
||||
# apply list
|
||||
@ -38,7 +41,7 @@ EVAL = (ast, env) ->
|
||||
ast = a2
|
||||
env = let_env
|
||||
when "do"
|
||||
eval_ast(ast[1..-2], env)
|
||||
ast[1..-2].map((a) -> EVAL(a, env))
|
||||
ast = ast[ast.length-1]
|
||||
when "if"
|
||||
cond = EVAL(a1, env)
|
||||
@ -49,7 +52,7 @@ EVAL = (ast, env) ->
|
||||
when "fn*"
|
||||
return types._function(EVAL, a2, env, a1)
|
||||
else
|
||||
[f, args...] = eval_ast ast, env
|
||||
[f, args...] = ast.map((a) -> EVAL(a, env))
|
||||
if types._function_Q(f)
|
||||
ast = f.__ast__
|
||||
env = f.__gen_env__(args)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user