1
1
mirror of https://github.com/kanaka/mal.git synced 2024-10-04 01:17:33 +03:00

Compare commits

...

36 Commits

Author SHA1 Message Date
Name
4cf41f2911
Merge branch 'kanaka:master' into update-elm 2024-08-05 22:44:21 +02:00
Joel Martin
a0fe8e4355 c.2,make,perl6,ocaml,objpascal: update to ubuntu:24.04
c.2:
    By updating to 24.04 we get around an error with declarations after
    labels which is allowed in more recent compiler versions.

make:
    With older make, the newer make code results in this error:
    ../make/readline.mk:13: *** unterminated call to function 'eval': missing ')'.  Stop.

    Newer versions of make work fine.

perl6:
    With older rakudo, this error occurs:
        user> (let* (DEBUG-EVAL false) (- 3 1))
        Cannot resolve caller Numeric(types::MalFalse: ); none of these signatures match:

ocaml:
    Fixes build error that happens with older versions of ocaml:
        ocamlopt -a types.ml reader.ml printer.ml env.ml core.ml -o mal_lib.cmxa
        File "env.ml", line 15, characters 8-21:
        Error: Unbound value Data.find_opt

objpascal:
    With older compiler, the following error occurs:
        user> (apply str (seq "this is a test"))
        *** Error in `../objpascal/stepA_mal': corrupted size vs. prev_size: 0x000000000246c360 ***
2024-08-05 11:55:55 -05:00
Nicolas Boulenguez
a60581e0a2 test: check that a: is a valid symbol.
This is required by the current mal implementation of DBG-EVAL.

Fix resulting errors in powershell, prolog, swift5
2024-08-05 11:54:37 -05:00
Nicolas Boulenguez
033892777a Merge eval-ast and macro expansion into EVAL, add DEBUG-EVAL
See issue #587.
* Merge eval-ast and eval into a single conditional.
* Expand macros during the apply phase, removing lots of duplicate
  tests, and increasing the overall consistency by allowing the macro
  to be computed instead of referenced by name (`((defmacro! cond
  (...)))` is currently illegal for example).
* Print "EVAL: $ast" at the top of EVAL if DEBUG-EVAL exists in the
  MAL environment.
* Remove macroexpand and quasiquoteexpand special forms.
* Use pattern-matching style in process/step*.txt.

Unresolved issues:
c.2: unable to reproduce with gcc 11.12.0.
elm: the directory is unchanged.
groovy: sometimes fail, but not on each rebuild.
nasm: fails some new soft tests, but the issue is unreproducible when
  running the interpreter manually.
objpascal: unreproducible with fpc 3.2.2.
ocaml: unreproducible with 4.11.1.
perl6: unreproducible with rakudo 2021.09.

Unrelated changes:
Reduce diff betweens steps.
Prevent defmacro! from mutating functions: c forth logo miniMAL vb.
dart: fix recent errors and warnings
ocaml: remove metadata from symbols.

Improve the logo implementation.
Encapsulate all representation in types.lg and env.lg, unwrap numbers.
Replace some manual iterations with logo control structures.
Reduce the diff between steps.
Use native iteration in env_get and env_map
Rewrite the reader with less temporary strings.
Reduce the number of temporary lists (for example, reverse iteration
with butlast requires O(n^2) allocations).
It seems possible to remove a few exceptions: GC settings
(Dockerfile), NO_SELF_HOSTING (IMPLS.yml) and step5_EXCLUDES
(Makefile.impls) .
2024-08-05 11:40:49 -05:00
Nicolas Boulenguez
cb333f1387 ruby.2: backport a fix for step6 tests from stepA source to step9 source 2024-08-03 11:32:58 -05:00
Nicolas Boulenguez
6a6bc8cb73 python.2: fix argv in step9 2024-08-03 11:32:58 -05:00
Nicolas Boulenguez
10e8854c04 python.2: add with-meta reader macro 2024-08-03 11:32:58 -05:00
Nicolas Boulenguez
d0375ec9f8 vb: allow keyword argument to keyword function 2024-08-03 11:32:58 -05:00
Nicolas Boulenguez
3233e7821a make: fix enconding of hash and dollar signs
Some tests were failing with Make 4.3.
2024-08-03 11:32:58 -05:00
Nicolas Boulenguez
3bd752c979 make: prevent defmacro! from mutating functions 2024-08-03 11:32:58 -05:00
Nicolas Boulenguez
b4843b602c make: allow keyword argument to keyword functions 2024-08-03 11:32:58 -05:00
Joel Martin
ee6c11da3b GHA: use default_base for get-changed-files
This is needed for new branches.
2024-08-03 11:32:58 -05:00
Joel Martin
d4f6b1e6dd ci.sh: restore tr lower-casing to fix macos. 2024-08-03 11:15:55 -05:00
Joel Martin
dd16a7d023 ci.sh: remove pulled step summary.
This should be the default if things are working correctly so logging
this now is just noise that causes the summaries that are relevant to
get folded in the UI.
2024-08-03 11:08:32 -05:00
Joel Martin
f3903a7141 docs: docker image/CI info. Replace IRC with Discord links. 2024-08-03 11:06:43 -05:00
Joel Martin
6bf89ea118 miniMAL: update to miniMAL-1.2.2
- Update to ubuntu 24.04 Dockerfile and add labels.
- update to working version of ffi-napi
- Remove from miniMAL-core.json the things that overlap with the builtin
  core functionality that miniMAL now provides in 1.2.2.
- Update ARGS variable to argv to align with how 1.2.2 now does command
  line parameters.
2024-08-03 11:06:43 -05:00
Joel Martin
e6ec37a468 ruby: fix stackoverflow printing in steps 6-A 2024-08-02 11:10:48 -05:00
Joel Martin
f63d2e4672 ruby: update vivid->xenial 2024-08-02 11:10:48 -05:00
Joel Martin
b3759ab63b php: update vivid->xenial and php5 -> php7.0 2024-08-02 11:10:48 -05:00
Joel Martin
a1a784c572 groovy,kotlin: vivid->xenial and jdk-7->jdk-8 2024-08-02 11:10:48 -05:00
Joel Martin
714b718bd0 Update VB/c#/f# from vivid to xenial and add tzdata. 2024-08-02 11:10:48 -05:00
Joel Martin
fe218df3d2 GHA,ci.sh: add step summaries for docker actions.
Add docker build-push summary statements for the workflow if the
GITHUB_STEP_SUMMARY variable is set.
2024-08-02 11:10:46 -05:00
Joel Martin
3f7b28652b GHA: switch from set-output to env file.
The set-output mechanism is deprecated for security reasons so set
environment variable outputs in GITHUB_OUTPUT instead. Change
get-ci-matrix.py to output the env file settings to stdout and move all
debug output to stderr.
2024-08-02 11:10:12 -05:00
Joel Martin
69e570aeca GHA: Update versions of checkout/artifact/login actions 2024-08-02 11:10:12 -05:00
Joel Martin
5c979b5a19 GHA: add packages write permission. 2024-08-02 11:10:12 -05:00
Joel Martin
7d49797697 ada: Dockerfile labels for repo linkage/visibility 2024-08-02 11:10:12 -05:00
Joel Martin
3e90e42e94 GHA: Update to ubuntu-24.04 runner/image. 2024-08-02 11:10:12 -05:00
Joel Martin
7a2bc6f066 Update Dockerfiles using ubuntu:vivid to xenial.
vivid packages are no longer available.

There are still implementations that use vivid because updating to
xenial was not sufficient to get them working again.
2024-08-02 11:10:12 -05:00
Joel Martin
0a34968695 GHA: Update macos image version from 10.15 to 12
10.15 has been removed since Aug 2022 and 11 has also been removed.
2024-08-02 11:10:12 -05:00
Joel Martin
eef959fb82 Update get-changed-files action to v2. 2024-08-02 11:10:12 -05:00
Joel Martin
8f8608a989 ci.sh/GHA: Add docker-build-push step/action
A major maintainability issue for years has been that the CI assumes
that a docker image for the implementation exists in Docker's registry
(named kanaka/mal-test-IMPL). This means the upstream maintainers have
to be involved in the PR loop to build the implementation Dockerfile,
push to the docker registry and then have the PR submitter re-run CI.

To address this, in ci.sh, the docker-build-push action will try to pull
the image and then continue as normal. If the pull fails then it will
build the image and push it (if the build is running in the context of
the upstream repo's main branch) and then continue.

Also, this switches to using ghcr.io as the default repo for images
which will make image transfer more local (during CI) and hopefully
a fair bit faster (and avoid potential docker pull limits).

Add a steps to the GHA main workflow that do a docker login to ghcr.io
and then call `ci.sh docker-build-push ${IMPL}`.
2024-08-02 11:10:06 -05:00
Joel Martin
78d6dabcaf Makefile,ci.sh: use versioned images
Get a voom style git hash of the impl Dockerfile and use that as the
testing docker image version (rather than blank which implies "latest").
2024-08-01 12:49:51 -05:00
Joel Martin
96e6d9c81a Don't pass MAKE into dockerized calls.
The outer and inner MAKE path may be unrelated.
2024-07-30 14:08:50 -05:00
Dov Murik
dcf8f4d7b9
Merge pull request #610 from Aegwenia/patch-1
Correcting a typo.
2022-03-06 11:54:15 +02:00
Bezděk Miroslav
c7d437c6d2
Merge branch 'kanaka:master' into patch-1 2022-03-04 14:33:23 +01:00
Bezděk Miroslav
f20d62fb35
Update guide.md
Corrected one spelling error.
2021-11-22 21:51:47 +01:00
678 changed files with 16086 additions and 20591 deletions

View File

@ -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

View File

@ -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}

View File

@ -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))))

View File

@ -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

View File

@ -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
View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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

View File

@ -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;

View File

@ -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 =>

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,4 @@
FROM ubuntu:vivid
FROM ubuntu:xenial
MAINTAINER Joel Martin <github@martintribe.org>
##########################################################

View File

@ -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)

View File

@ -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) "."
}

View File

@ -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 /^\$/:

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -1,4 +1,4 @@
FROM ubuntu:vivid
FROM ubuntu:xenial
MAINTAINER Joel Martin <github@martintribe.org>
##########################################################

View File

@ -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}"

View File

@ -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}"

View File

@ -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}"]}"

View File

@ -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}"]}"

View File

@ -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}"]}"

View File

@ -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}"]}"

View File

@ -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//@/ }

View File

@ -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//@/ }

View File

@ -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//@/ }

View File

@ -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$)

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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));
}

View File

@ -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

View File

@ -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;

View File

@ -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*/
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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));

View File

@ -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()

View File

@ -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));
}

View File

@ -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));
}

View File

@ -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)))");

View File

@ -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)))");

View File

@ -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)))");

View File

@ -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)))");

View File

@ -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\")");

View File

@ -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

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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

View File

@ -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...)

View File

@ -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...)

View File

@ -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...)

View File

@ -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