]> rethought.computer Git - sorel-lang.git/commitdiff
de-monorepo keep/dca335d4b6e4842b4552332e49479fd1ff1b0c21
authorBryan English <bryan@rethought.computer>
Tue, 10 Feb 2026 04:09:09 +0000 (04:09 +0000)
committerBryan English <bryan@rethought.computer>
Tue, 10 Feb 2026 04:16:16 +0000 (04:16 +0000)
Moves all the language stuff to the top level, since
this repo is now just for the Sorel language.

Website commits were removed, and preserved in a separate
repo.

68 files changed:
.gitignore [new file with mode: 0644]
Cargo.lock [new file with mode: 0644]
Cargo.toml [new file with mode: 0644]
README.md [new file with mode: 0644]
docs/hacking.md [new file with mode: 0644]
docs/language_overview.md [new file with mode: 0644]
examples/alloc.sorel [new file with mode: 0644]
examples/fib.sorel [new file with mode: 0644]
examples/full-fib/compile.sh [new file with mode: 0644]
examples/full-fib/fib.rel [new file with mode: 0644]
examples/full-fib/putn.c [new file with mode: 0644]
examples/put2.sorel [new file with mode: 0644]
examples/syscalls.sorel [new file with mode: 0644]
flake.lock [new file with mode: 0644]
flake.nix [new file with mode: 0644]
qemu/run.sh [new file with mode: 0644]
qemu/setup.sh [new file with mode: 0644]
rel-lang/.gitignore [deleted file]
rel-lang/Cargo.lock [deleted file]
rel-lang/Cargo.toml [deleted file]
rel-lang/README.md [deleted file]
rel-lang/docs/hacking.md [deleted file]
rel-lang/docs/language_overview.md [deleted file]
rel-lang/examples/alloc.sorel [deleted file]
rel-lang/examples/fib.sorel [deleted file]
rel-lang/examples/full-fib/compile.sh [deleted file]
rel-lang/examples/full-fib/fib.rel [deleted file]
rel-lang/examples/full-fib/putn.c [deleted file]
rel-lang/examples/put2.sorel [deleted file]
rel-lang/examples/syscalls.sorel [deleted file]
rel-lang/flake.lock [deleted file]
rel-lang/flake.nix [deleted file]
rel-lang/qemu/run.sh [deleted file]
rel-lang/qemu/setup.sh [deleted file]
rel-lang/sorel-ir/Cargo.toml [deleted file]
rel-lang/sorel-ir/src/lib.rs [deleted file]
rel-lang/sorelc/Cargo.toml [deleted file]
rel-lang/sorelc/src/ir.rs [deleted file]
rel-lang/sorelc/src/main.rs [deleted file]
rel-lang/sorelc/src/parser.rs [deleted file]
rel-lang/sorelc/src/riscv_asm_codegen.rs [deleted file]
rel-lang/sorelc/src/tokenizer.rs [deleted file]
rel-lang/stdlib/mem.sorel [deleted file]
rel-lang/stdlib/out.sorel [deleted file]
rel-lang/stdlib/process.sorel [deleted file]
rel-lang/stdlib/string.sorel [deleted file]
rel-lang/tests/assert.sorel [deleted file]
rel-lang/tests/putstack.c [deleted file]
rel-lang/tests/test.sh [deleted file]
rel-lang/tests/test1.sorel [deleted file]
sorel-ir/Cargo.toml [new file with mode: 0644]
sorel-ir/src/lib.rs [new file with mode: 0644]
sorelc/Cargo.toml [new file with mode: 0644]
sorelc/src/ir.rs [new file with mode: 0644]
sorelc/src/main.rs [new file with mode: 0644]
sorelc/src/parser.rs [new file with mode: 0644]
sorelc/src/riscv_asm_codegen.rs [new file with mode: 0644]
sorelc/src/tokenizer.rs [new file with mode: 0644]
stdlib/mem.sorel [new file with mode: 0644]
stdlib/out.sorel [new file with mode: 0644]
stdlib/process.sorel [new file with mode: 0644]
stdlib/string.sorel [new file with mode: 0644]
target/rust-analyzer/flycheck0/stderr [deleted file]
target/rust-analyzer/flycheck0/stdout [deleted file]
tests/assert.sorel [new file with mode: 0644]
tests/putstack.c [new file with mode: 0644]
tests/test.sh [new file with mode: 0644]
tests/test1.sorel [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a41520b
--- /dev/null
@@ -0,0 +1,5 @@
+target
+qemu/machine
+*.o
+*.asm
+*.out
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644 (file)
index 0000000..47524a9
--- /dev/null
@@ -0,0 +1,143 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "anyhow"
+version = "1.0.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+
+[[package]]
+name = "indexmap"
+version = "2.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_yaml"
+version = "0.9.34+deprecated"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
+dependencies = [
+ "indexmap",
+ "itoa",
+ "ryu",
+ "serde",
+ "unsafe-libyaml",
+]
+
+[[package]]
+name = "sorel-ir"
+version = "0.1.0"
+dependencies = [
+ "serde",
+ "serde_derive",
+ "serde_yaml",
+]
+
+[[package]]
+name = "sorelc"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "sorel-ir",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.111"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
+
+[[package]]
+name = "unsafe-libyaml"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644 (file)
index 0000000..fae2a78
--- /dev/null
@@ -0,0 +1,8 @@
+[workspace]
+
+resolver = "3"
+members = ["sorel-ir","sorelc"]
+
+
+[workspace.dependencies]
+sorel-ir = { path = "./sorel-ir", version = "0.1.0" }
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..40b5544
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+# sorel
+
+The name means "Stack-Oriented Rethought Language".
+
+
+## TODO
+
+* [x] Imports
+* [x] Syscalls
+* [x] Loops
+* [ ] Structs
+* [ ] many, many more things
+
+## Helpful External Resources
+
+* https://gpages.juszkiewicz.com.pl/syscalls-table/syscalls.html
+* https://godbolt.org/
+* https://projectf.io/posts/riscv-cheat-sheet/
+* https://github.com/dvoytik/riscv-cheats
diff --git a/docs/hacking.md b/docs/hacking.md
new file mode 100644 (file)
index 0000000..fe7323d
--- /dev/null
@@ -0,0 +1,74 @@
+## Developer setup
+
+### Nix / NixOS
+
+If you're using `Nix` or `NixOS`, the provided `flake.nix` should give you a good start.
+You'll need a recent Rust toolchain, for which you can use [`rustup`](https://rustup.sh).
+Currently that's assumed to be included on the system.
+Also, the test scripts currently assume binfmt is set up for RISC-V 64 (or that you're running on RISC-V 64).
+Assuming you're not on RISC-V 64 (most people aren't), you can add the following to your Nix `configuration.nix`:
+
+```
+
+  boot.binfmt.emulatedSystems = [
+    "riscv64-linux"
+    # ....
+  ];
+```
+
+### Ubuntu
+
+```
+# On non RISC-V 64 systems:
+apt install -y build-essential gdb crossbuild-essential-riscv64 qemu-system qemu-user qemu-user-binfmt
+```
+
+## Building the Compiler
+
+The compiler is written in Rust, and can be compiled with 
+
+```
+cargo build
+```
+
+This puts the compiler binary at `target/debug/sorelc`.
+Release builds can also be created in the usual Rust/`cargo` fasion.
+
+Note that if you change any stdlib files, you'll need to rebuild.
+
+## Compiling a Sorel Program
+
+Currently, `sorelc` takes only one argument, and that's an entrypoint for your program.
+Any words in that file outside word definitions will constitute the "main" of your program.
+
+This will create a file of the same name alongside it, with the extension changed to `asm`.
+This is a RISC-V 64 assembly file, suitable for use with GNU Assembler.
+You can assemble this as normal, and run your program.
+
+## Testing
+
+The `tests` directory contains a shell script you can invoke with `sh test.sh`.
+It should run just fine given the above setup, provided you've done a `cargo build` to build the compiler.
+This will compile the test binary using `sorelc` and your local `riscv64` assembler.
+It will run the binary using binfmt, which instructs the kernel to run your binary using `qemu-user`.
+
+### Debugging
+
+You can use `gdb`!
+Unfortunately, with `qemu-user` we can't just load the binary directly into `gdb`.
+Instead, we'll need to run `qemu-user` on a RISC-V 64 binary with a debugger port open:
+
+```
+qemu-riscv64 -g 4567 ./test1.out
+```
+
+Then, you can fire up `gdb`, and once inside it's prompt, connect to the "remote" port.
+
+```
+target remote :4567
+```
+
+Then you can use normal gdb commands!
+The `tui layout asm` is highly recommended.
+Note that if you'l linking `putstack.c`, that will use libc, which drastically inflates the binary and hijack's init.
+This can make things quite a pain in `gdb`, so try to build without it (i.e. uncomment the LD line and comment out the CC line in `test.sh`, and remove all references to `putstack` in code).
diff --git a/docs/language_overview.md b/docs/language_overview.md
new file mode 100644 (file)
index 0000000..cb94446
--- /dev/null
@@ -0,0 +1,68 @@
+# Language Overview
+
+**Sorel** is a concatenative stack language, similar in form to Forth.
+Unlike most Forth variants, it's strictly a compiled language, with no interpreter provided at run time.
+Today, it targets only 64-bit RISC-V architectures.
+It has a small standard library, that does not use libc.
+Instead it makes system calls directly, currently only supporting Linux.
+
+Like in Forth, code is written in Reverse Polish Notation.
+Every "word" corresponds to a function that manipulates the stack in some way.
+Unlike in Forth, strings count as a single word, and are written much like they are in C.
+
+## The Stack
+
+All Forths have a stack, and Sorel is no exception.
+Since Sorel's function calls follow the C ABI, there's no need for the traditional Forth return stack.
+There's only the data stack.
+Much like call stacks, it grows downward.
+Each stack cell is exactly 64 bits wide, regardless of type.
+For smaller types, the values are padded with 0s.
+Note that RISC-V systems are little-endian.
+
+## Type System
+
+All values are treated as 64-bit unsigned integers unles otherwise indicated.
+Number literals can provide type information after a colon.
+For example, `-37:i32` will add -37 as a signed 32-bit integer onto the stack.
+
+Values themselves don't have types.
+Operations have types.
+In other words, values are always just bits in memory, and only have a "type" when they are treated as such by a word manipulating the stack or heap memory.
+
+> This is, of course, very dangerous.
+> A planned improvement is to add static type-checking support via type comments.
+
+Complex types like arrays and structs don't exist (yet), but they can be maniplulated through direct memory operations.
+See `std:mem` for details.
+
+String literals, when pushed onto the stack, appear as pointers to null-terminated UTF-8 representations of them.
+Other than that, strings are just like any other complex type, in that there's no special support (yet).
+
+## Module System
+
+Sorel supports import statements of the form `import "./relative/path/to/file.sorel"`, with the exception of standard library imports (see below).
+Note that `import` is _not_ executed at run-time, and instead is evaluated as a pre-processing step.
+To make that clear, note that the import specifier comes _after_ the keyword.
+This indicates that `import` is not operating on a value in the stack.
+
+Similarly, words may be exported via `export word_name`.
+Only words deliberately exported will be made available to other files via `import`.
+
+Word invocations _outside_ word defintions will be executed as the program's entrypoint function (i.e. "main"), but _only_ if those invocations are in the entrypoint module.
+This means any module can have word invocations inside it that aren't executed when it's not the entrypoint.
+This is useful for writing tests.
+
+## Built-in Words
+
+<!-- TODO -->
+
+## Standard Library
+
+Built-in words are insufficient to create most programs.
+A standard library is provided with exported words to enable higher level operations.
+It's implemented primarily by wrapping system calls.
+
+The following standard library modules are provided, and can be imported by specificying their name as the import specifier:
+* **`std:mem`** : This module contains words to allocate and free memory.
+* **`std:out`** : This module contains words to print numbers and strings to stdout.
diff --git a/examples/alloc.sorel b/examples/alloc.sorel
new file mode 100644 (file)
index 0000000..ab014eb
--- /dev/null
@@ -0,0 +1,18 @@
+\ vim: filetype=forth
+
+: mmap 9 sys6 ;
+
+: PROT_READ 1 ;
+: PROT_WRITE 2 ;
+: MAP_PRIVATE 2 ;
+: MAP_ANONYMOUS 32 ;
+
+: ALLOC_PROT PROT_READ PROT_WRITE | ;
+: ALLOC_MAP MAP_PRIVATE MAP_ANONYMOUS | ;
+
+: alloc 0 swap ALLOC_PROT ALLOC_MAP -1:i16 0 mmap ;
+
+1024 alloc
+putn
+swap
+putn
diff --git a/examples/fib.sorel b/examples/fib.sorel
new file mode 100644 (file)
index 0000000..ef22ee9
--- /dev/null
@@ -0,0 +1,25 @@
+\ vim: filetype=forth
+
+import "./put2.rel"
+
+: fib 
+    dup 1 > if
+        dup 1 - fib
+        swap 2 - fib
+        +
+    endif
+;
+
+0 fib putn
+1 fib putn
+2 fib putn
+3 fib putn
+4 fib putn
+5 fib putn
+6 fib putn
+7 fib putn
+8 fib putn
+9 fib putn
+10 fib putn
+
+5 fib 6 fib put2
diff --git a/examples/full-fib/compile.sh b/examples/full-fib/compile.sh
new file mode 100644 (file)
index 0000000..6ef003e
--- /dev/null
@@ -0,0 +1,4 @@
+../target/debug/sorelc fib.sorel
+riscv64-unknown-linux-gnu-as -o fib.o fib.asm
+riscv64-unknown-linux-gnu-cc -O1 -no-pie -o test.out fib.o putn.c -nostartfiles 
+./test.out
diff --git a/examples/full-fib/fib.rel b/examples/full-fib/fib.rel
new file mode 100644 (file)
index 0000000..2407f15
--- /dev/null
@@ -0,0 +1,24 @@
+\ foo bar
+: fib
+    dup
+    1
+    >
+    if
+        dup
+        1 - fib
+        swap
+        2
+        -
+        fib
+        +
+    endif
+;
+
+0 fib putn drop
+1 fib putn drop
+2 fib putn drop
+3 fib putn drop
+4 fib putn drop
+5 fib putn drop
+6 fib putn drop
+7 fib putn drop
diff --git a/examples/full-fib/putn.c b/examples/full-fib/putn.c
new file mode 100644 (file)
index 0000000..201050c
--- /dev/null
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+extern unsigned long data_stack_end;
+register unsigned long * stack_pointer asm("s2");
+
+void putn() {
+  unsigned long * stack_index = &data_stack_end;
+  printf("stack: ");
+  while (stack_index != stack_pointer) {
+         printf("%ld ", *stack_index);
+    stack_index -= 1;
+  }
+       printf("%ld\n", *stack_pointer);
+}
+
+
diff --git a/examples/put2.sorel b/examples/put2.sorel
new file mode 100644 (file)
index 0000000..4f756c2
--- /dev/null
@@ -0,0 +1,7 @@
+\ vim: filetype=forth
+
+: put2 putn putn ; 
+
+: foobar dup dup ;
+
+export put2
diff --git a/examples/syscalls.sorel b/examples/syscalls.sorel
new file mode 100644 (file)
index 0000000..9d7b88c
--- /dev/null
@@ -0,0 +1,8 @@
+\ vim: filetype=forth
+
+: getpid
+  39 sys0
+  drop
+;
+
+getpid putn
diff --git a/flake.lock b/flake.lock
new file mode 100644 (file)
index 0000000..1384c7f
--- /dev/null
@@ -0,0 +1,27 @@
+{
+  "nodes": {
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1765425892,
+        "narHash": "sha256-jlQpSkg2sK6IJVzTQBDyRxQZgKADC2HKMRfGCSgNMHo=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "5d6bdbddb4695a62f0d00a3620b37a15275a5093",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nixos",
+        "ref": "nixpkgs-unstable",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "root": {
+      "inputs": {
+        "nixpkgs": "nixpkgs"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644 (file)
index 0000000..8aacadf
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,23 @@
+{
+  description = "uxn11";
+
+  inputs = {
+    nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
+  };
+
+  outputs = {nixpkgs, ...}: let
+    system = "x86_64-linux";
+    pkgs = import nixpkgs {
+      inherit system;
+    };
+  in {
+    devShells.${system}.default = pkgs.mkShell {
+      packages = [
+        pkgs.pkgsCross.riscv64.buildPackages.gcc
+        pkgs.pkgsCross.riscv64.buildPackages.gdb
+        pkgs.gdb
+        pkgs.qemu
+      ];
+    };
+  };
+}
diff --git a/qemu/run.sh b/qemu/run.sh
new file mode 100644 (file)
index 0000000..a9d3a7e
--- /dev/null
@@ -0,0 +1,17 @@
+
+qemu-system-riscv64 \
+    -machine virt \
+    -cpu rv64 \
+    -m 1G \
+    -device virtio-blk-device,drive=hd \
+    -drive file=machine/overlay.qcow2,if=none,id=hd \
+    -device virtio-net-device,netdev=net \
+    -netdev user,id=net,hostfwd=tcp::2222-:22 \
+    -kernel machine/u-boot-qemu/uboot.elf \
+    -object rng-random,filename=/dev/urandom,id=rng \
+    -device virtio-rng-device,rng=rng \
+    -append "root=LABEL=rootfs console=ttyS0" \
+    -nographic \
+
+echo "now do:"
+echo "  ssh debian@localhost -p 2222"
diff --git a/qemu/setup.sh b/qemu/setup.sh
new file mode 100644 (file)
index 0000000..b206ac6
--- /dev/null
@@ -0,0 +1,18 @@
+
+mkdir machine
+cd machine
+wget "https://gitlab.com/api/v4/projects/giomasce%2Fdqib/jobs/artifacts/master/download?job=convert_riscv64-virt" -O debian-rv64.zip
+
+unzip ./debian-rv64.zip
+
+# Grab the URL from https://packages.debian.org/sid/u-boot-qemu
+wget "http://ftp.us.debian.org/debian/pool/main/u/u-boot/u-boot-qemu_2025.01-3.1_all.deb" -O u-boot-qemu.deb
+mkdir u-boot-qemu
+cd u-boot-qemu
+ar -x ../u-boot-qemu.deb
+tar xvf data.tar.xz
+cp ./usr/lib/u-boot/qemu-riscv64_smode/uboot.elf ./uboot.elf
+
+cd ..
+qemu-img create -o backing_file=./dqib_riscv64-virt/image.qcow2,backing_fmt=qcow2 -f qcow2 overlay.qcow2
+
diff --git a/rel-lang/.gitignore b/rel-lang/.gitignore
deleted file mode 100644 (file)
index a41520b..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-target
-qemu/machine
-*.o
-*.asm
-*.out
diff --git a/rel-lang/Cargo.lock b/rel-lang/Cargo.lock
deleted file mode 100644 (file)
index 47524a9..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 4
-
-[[package]]
-name = "anyhow"
-version = "1.0.100"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
-
-[[package]]
-name = "equivalent"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
-
-[[package]]
-name = "hashbrown"
-version = "0.16.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
-
-[[package]]
-name = "indexmap"
-version = "2.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
-dependencies = [
- "equivalent",
- "hashbrown",
-]
-
-[[package]]
-name = "itoa"
-version = "1.0.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.103"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.42"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "ryu"
-version = "1.0.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
-
-[[package]]
-name = "serde"
-version = "1.0.228"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
-dependencies = [
- "serde_core",
-]
-
-[[package]]
-name = "serde_core"
-version = "1.0.228"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.228"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "serde_yaml"
-version = "0.9.34+deprecated"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
-dependencies = [
- "indexmap",
- "itoa",
- "ryu",
- "serde",
- "unsafe-libyaml",
-]
-
-[[package]]
-name = "sorel-ir"
-version = "0.1.0"
-dependencies = [
- "serde",
- "serde_derive",
- "serde_yaml",
-]
-
-[[package]]
-name = "sorelc"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "sorel-ir",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.111"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
-
-[[package]]
-name = "unsafe-libyaml"
-version = "0.2.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
diff --git a/rel-lang/Cargo.toml b/rel-lang/Cargo.toml
deleted file mode 100644 (file)
index fae2a78..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[workspace]
-
-resolver = "3"
-members = ["sorel-ir","sorelc"]
-
-
-[workspace.dependencies]
-sorel-ir = { path = "./sorel-ir", version = "0.1.0" }
diff --git a/rel-lang/README.md b/rel-lang/README.md
deleted file mode 100644 (file)
index 40b5544..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-# sorel
-
-The name means "Stack-Oriented Rethought Language".
-
-
-## TODO
-
-* [x] Imports
-* [x] Syscalls
-* [x] Loops
-* [ ] Structs
-* [ ] many, many more things
-
-## Helpful External Resources
-
-* https://gpages.juszkiewicz.com.pl/syscalls-table/syscalls.html
-* https://godbolt.org/
-* https://projectf.io/posts/riscv-cheat-sheet/
-* https://github.com/dvoytik/riscv-cheats
diff --git a/rel-lang/docs/hacking.md b/rel-lang/docs/hacking.md
deleted file mode 100644 (file)
index fe7323d..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-## Developer setup
-
-### Nix / NixOS
-
-If you're using `Nix` or `NixOS`, the provided `flake.nix` should give you a good start.
-You'll need a recent Rust toolchain, for which you can use [`rustup`](https://rustup.sh).
-Currently that's assumed to be included on the system.
-Also, the test scripts currently assume binfmt is set up for RISC-V 64 (or that you're running on RISC-V 64).
-Assuming you're not on RISC-V 64 (most people aren't), you can add the following to your Nix `configuration.nix`:
-
-```
-
-  boot.binfmt.emulatedSystems = [
-    "riscv64-linux"
-    # ....
-  ];
-```
-
-### Ubuntu
-
-```
-# On non RISC-V 64 systems:
-apt install -y build-essential gdb crossbuild-essential-riscv64 qemu-system qemu-user qemu-user-binfmt
-```
-
-## Building the Compiler
-
-The compiler is written in Rust, and can be compiled with 
-
-```
-cargo build
-```
-
-This puts the compiler binary at `target/debug/sorelc`.
-Release builds can also be created in the usual Rust/`cargo` fasion.
-
-Note that if you change any stdlib files, you'll need to rebuild.
-
-## Compiling a Sorel Program
-
-Currently, `sorelc` takes only one argument, and that's an entrypoint for your program.
-Any words in that file outside word definitions will constitute the "main" of your program.
-
-This will create a file of the same name alongside it, with the extension changed to `asm`.
-This is a RISC-V 64 assembly file, suitable for use with GNU Assembler.
-You can assemble this as normal, and run your program.
-
-## Testing
-
-The `tests` directory contains a shell script you can invoke with `sh test.sh`.
-It should run just fine given the above setup, provided you've done a `cargo build` to build the compiler.
-This will compile the test binary using `sorelc` and your local `riscv64` assembler.
-It will run the binary using binfmt, which instructs the kernel to run your binary using `qemu-user`.
-
-### Debugging
-
-You can use `gdb`!
-Unfortunately, with `qemu-user` we can't just load the binary directly into `gdb`.
-Instead, we'll need to run `qemu-user` on a RISC-V 64 binary with a debugger port open:
-
-```
-qemu-riscv64 -g 4567 ./test1.out
-```
-
-Then, you can fire up `gdb`, and once inside it's prompt, connect to the "remote" port.
-
-```
-target remote :4567
-```
-
-Then you can use normal gdb commands!
-The `tui layout asm` is highly recommended.
-Note that if you'l linking `putstack.c`, that will use libc, which drastically inflates the binary and hijack's init.
-This can make things quite a pain in `gdb`, so try to build without it (i.e. uncomment the LD line and comment out the CC line in `test.sh`, and remove all references to `putstack` in code).
diff --git a/rel-lang/docs/language_overview.md b/rel-lang/docs/language_overview.md
deleted file mode 100644 (file)
index cb94446..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-# Language Overview
-
-**Sorel** is a concatenative stack language, similar in form to Forth.
-Unlike most Forth variants, it's strictly a compiled language, with no interpreter provided at run time.
-Today, it targets only 64-bit RISC-V architectures.
-It has a small standard library, that does not use libc.
-Instead it makes system calls directly, currently only supporting Linux.
-
-Like in Forth, code is written in Reverse Polish Notation.
-Every "word" corresponds to a function that manipulates the stack in some way.
-Unlike in Forth, strings count as a single word, and are written much like they are in C.
-
-## The Stack
-
-All Forths have a stack, and Sorel is no exception.
-Since Sorel's function calls follow the C ABI, there's no need for the traditional Forth return stack.
-There's only the data stack.
-Much like call stacks, it grows downward.
-Each stack cell is exactly 64 bits wide, regardless of type.
-For smaller types, the values are padded with 0s.
-Note that RISC-V systems are little-endian.
-
-## Type System
-
-All values are treated as 64-bit unsigned integers unles otherwise indicated.
-Number literals can provide type information after a colon.
-For example, `-37:i32` will add -37 as a signed 32-bit integer onto the stack.
-
-Values themselves don't have types.
-Operations have types.
-In other words, values are always just bits in memory, and only have a "type" when they are treated as such by a word manipulating the stack or heap memory.
-
-> This is, of course, very dangerous.
-> A planned improvement is to add static type-checking support via type comments.
-
-Complex types like arrays and structs don't exist (yet), but they can be maniplulated through direct memory operations.
-See `std:mem` for details.
-
-String literals, when pushed onto the stack, appear as pointers to null-terminated UTF-8 representations of them.
-Other than that, strings are just like any other complex type, in that there's no special support (yet).
-
-## Module System
-
-Sorel supports import statements of the form `import "./relative/path/to/file.sorel"`, with the exception of standard library imports (see below).
-Note that `import` is _not_ executed at run-time, and instead is evaluated as a pre-processing step.
-To make that clear, note that the import specifier comes _after_ the keyword.
-This indicates that `import` is not operating on a value in the stack.
-
-Similarly, words may be exported via `export word_name`.
-Only words deliberately exported will be made available to other files via `import`.
-
-Word invocations _outside_ word defintions will be executed as the program's entrypoint function (i.e. "main"), but _only_ if those invocations are in the entrypoint module.
-This means any module can have word invocations inside it that aren't executed when it's not the entrypoint.
-This is useful for writing tests.
-
-## Built-in Words
-
-<!-- TODO -->
-
-## Standard Library
-
-Built-in words are insufficient to create most programs.
-A standard library is provided with exported words to enable higher level operations.
-It's implemented primarily by wrapping system calls.
-
-The following standard library modules are provided, and can be imported by specificying their name as the import specifier:
-* **`std:mem`** : This module contains words to allocate and free memory.
-* **`std:out`** : This module contains words to print numbers and strings to stdout.
diff --git a/rel-lang/examples/alloc.sorel b/rel-lang/examples/alloc.sorel
deleted file mode 100644 (file)
index ab014eb..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-\ vim: filetype=forth
-
-: mmap 9 sys6 ;
-
-: PROT_READ 1 ;
-: PROT_WRITE 2 ;
-: MAP_PRIVATE 2 ;
-: MAP_ANONYMOUS 32 ;
-
-: ALLOC_PROT PROT_READ PROT_WRITE | ;
-: ALLOC_MAP MAP_PRIVATE MAP_ANONYMOUS | ;
-
-: alloc 0 swap ALLOC_PROT ALLOC_MAP -1:i16 0 mmap ;
-
-1024 alloc
-putn
-swap
-putn
diff --git a/rel-lang/examples/fib.sorel b/rel-lang/examples/fib.sorel
deleted file mode 100644 (file)
index ef22ee9..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-\ vim: filetype=forth
-
-import "./put2.rel"
-
-: fib 
-    dup 1 > if
-        dup 1 - fib
-        swap 2 - fib
-        +
-    endif
-;
-
-0 fib putn
-1 fib putn
-2 fib putn
-3 fib putn
-4 fib putn
-5 fib putn
-6 fib putn
-7 fib putn
-8 fib putn
-9 fib putn
-10 fib putn
-
-5 fib 6 fib put2
diff --git a/rel-lang/examples/full-fib/compile.sh b/rel-lang/examples/full-fib/compile.sh
deleted file mode 100644 (file)
index 6ef003e..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-../target/debug/sorelc fib.sorel
-riscv64-unknown-linux-gnu-as -o fib.o fib.asm
-riscv64-unknown-linux-gnu-cc -O1 -no-pie -o test.out fib.o putn.c -nostartfiles 
-./test.out
diff --git a/rel-lang/examples/full-fib/fib.rel b/rel-lang/examples/full-fib/fib.rel
deleted file mode 100644 (file)
index 2407f15..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-\ foo bar
-: fib
-    dup
-    1
-    >
-    if
-        dup
-        1 - fib
-        swap
-        2
-        -
-        fib
-        +
-    endif
-;
-
-0 fib putn drop
-1 fib putn drop
-2 fib putn drop
-3 fib putn drop
-4 fib putn drop
-5 fib putn drop
-6 fib putn drop
-7 fib putn drop
diff --git a/rel-lang/examples/full-fib/putn.c b/rel-lang/examples/full-fib/putn.c
deleted file mode 100644 (file)
index 201050c..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <stdio.h>
-
-extern unsigned long data_stack_end;
-register unsigned long * stack_pointer asm("s2");
-
-void putn() {
-  unsigned long * stack_index = &data_stack_end;
-  printf("stack: ");
-  while (stack_index != stack_pointer) {
-         printf("%ld ", *stack_index);
-    stack_index -= 1;
-  }
-       printf("%ld\n", *stack_pointer);
-}
-
-
diff --git a/rel-lang/examples/put2.sorel b/rel-lang/examples/put2.sorel
deleted file mode 100644 (file)
index 4f756c2..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-\ vim: filetype=forth
-
-: put2 putn putn ; 
-
-: foobar dup dup ;
-
-export put2
diff --git a/rel-lang/examples/syscalls.sorel b/rel-lang/examples/syscalls.sorel
deleted file mode 100644 (file)
index 9d7b88c..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-\ vim: filetype=forth
-
-: getpid
-  39 sys0
-  drop
-;
-
-getpid putn
diff --git a/rel-lang/flake.lock b/rel-lang/flake.lock
deleted file mode 100644 (file)
index 1384c7f..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-{
-  "nodes": {
-    "nixpkgs": {
-      "locked": {
-        "lastModified": 1765425892,
-        "narHash": "sha256-jlQpSkg2sK6IJVzTQBDyRxQZgKADC2HKMRfGCSgNMHo=",
-        "owner": "nixos",
-        "repo": "nixpkgs",
-        "rev": "5d6bdbddb4695a62f0d00a3620b37a15275a5093",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nixos",
-        "ref": "nixpkgs-unstable",
-        "repo": "nixpkgs",
-        "type": "github"
-      }
-    },
-    "root": {
-      "inputs": {
-        "nixpkgs": "nixpkgs"
-      }
-    }
-  },
-  "root": "root",
-  "version": 7
-}
diff --git a/rel-lang/flake.nix b/rel-lang/flake.nix
deleted file mode 100644 (file)
index 8aacadf..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  description = "uxn11";
-
-  inputs = {
-    nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
-  };
-
-  outputs = {nixpkgs, ...}: let
-    system = "x86_64-linux";
-    pkgs = import nixpkgs {
-      inherit system;
-    };
-  in {
-    devShells.${system}.default = pkgs.mkShell {
-      packages = [
-        pkgs.pkgsCross.riscv64.buildPackages.gcc
-        pkgs.pkgsCross.riscv64.buildPackages.gdb
-        pkgs.gdb
-        pkgs.qemu
-      ];
-    };
-  };
-}
diff --git a/rel-lang/qemu/run.sh b/rel-lang/qemu/run.sh
deleted file mode 100644 (file)
index a9d3a7e..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-
-qemu-system-riscv64 \
-    -machine virt \
-    -cpu rv64 \
-    -m 1G \
-    -device virtio-blk-device,drive=hd \
-    -drive file=machine/overlay.qcow2,if=none,id=hd \
-    -device virtio-net-device,netdev=net \
-    -netdev user,id=net,hostfwd=tcp::2222-:22 \
-    -kernel machine/u-boot-qemu/uboot.elf \
-    -object rng-random,filename=/dev/urandom,id=rng \
-    -device virtio-rng-device,rng=rng \
-    -append "root=LABEL=rootfs console=ttyS0" \
-    -nographic \
-
-echo "now do:"
-echo "  ssh debian@localhost -p 2222"
diff --git a/rel-lang/qemu/setup.sh b/rel-lang/qemu/setup.sh
deleted file mode 100644 (file)
index b206ac6..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-
-mkdir machine
-cd machine
-wget "https://gitlab.com/api/v4/projects/giomasce%2Fdqib/jobs/artifacts/master/download?job=convert_riscv64-virt" -O debian-rv64.zip
-
-unzip ./debian-rv64.zip
-
-# Grab the URL from https://packages.debian.org/sid/u-boot-qemu
-wget "http://ftp.us.debian.org/debian/pool/main/u/u-boot/u-boot-qemu_2025.01-3.1_all.deb" -O u-boot-qemu.deb
-mkdir u-boot-qemu
-cd u-boot-qemu
-ar -x ../u-boot-qemu.deb
-tar xvf data.tar.xz
-cp ./usr/lib/u-boot/qemu-riscv64_smode/uboot.elf ./uboot.elf
-
-cd ..
-qemu-img create -o backing_file=./dqib_riscv64-virt/image.qcow2,backing_fmt=qcow2 -f qcow2 overlay.qcow2
-
diff --git a/rel-lang/sorel-ir/Cargo.toml b/rel-lang/sorel-ir/Cargo.toml
deleted file mode 100644 (file)
index 5618a61..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-[package]
-name = "sorel-ir"
-version = "0.1.0"
-edition = "2024"
-
-[dependencies]
-serde = "1.0.228"
-serde_derive = "1.0.228"
-serde_yaml = "0.9.34"
diff --git a/rel-lang/sorel-ir/src/lib.rs b/rel-lang/sorel-ir/src/lib.rs
deleted file mode 100644 (file)
index 1a3ec27..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-use serde_yaml::{from_str, to_string, Error};
-use serde_derive::{Serialize, Deserialize};
-
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub enum IR {
-    Label(String),
-    Call(String),
-    Ret,
-    StackPush(u64),
-    StackPushString(String), // refers to string label, not the string itself
-    StringDef(String, String), // first is string label, second is string value
-
-    // These next ones should always be inlined, so they're in IR.
-    Load, // @ ( addr -- x ) -- Fetch memory contents at addr
-    Load8,
-    Load16,
-    Load32,
-    Store, // ! ( x addr -- ) -- Store x at addr
-    Store8,
-    Store16,
-    Store32,
-
-    // These ones might not be inlined, but should be built-in, so a compiler might
-    // turn this into `Call(String)` before translating to assembly/machine-code, but
-    // an IR interpreter may just execute them.
-    AddU64,
-    SubtractU64,
-    MultiplyU64,
-    DivideU64,
-    ModU64,
-    Equals,
-    GreaterThan,
-    LessThan,
-    BitwiseOr,
-    Dup,
-    Swap,
-    Drop,
-    Over,
-    Rot,
-    StackPointer,
-    If,
-    Else,
-    EndIf,
-    Loop,
-    EndLoop,
-
-    // System calls
-    Sys0,
-    Sys1,
-    Sys2,
-    Sys3,
-    Sys4,
-    Sys5,
-    Sys6,
-}
-
-// This is like an .o file.
-#[derive(Serialize, Deserialize, Debug)]
-pub struct IRObject {
-    pub text: Vec<IR>,
-    pub data: Vec<IR>,
-}
-
-impl IRObject {
-    pub fn to_s(&self) -> Result<String, Error> {
-        to_string(self)
-    }
-
-    pub fn from_s(source: &str) -> Result<Self, Error> {
-        from_str(source)
-    }
-}
diff --git a/rel-lang/sorelc/Cargo.toml b/rel-lang/sorelc/Cargo.toml
deleted file mode 100644 (file)
index 7859ecf..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[package]
-name = "sorelc"
-version = "0.1.0"
-edition = "2024"
-
-[dependencies]
-sorel-ir = { workspace = true }
-anyhow = "1.0.100"
diff --git a/rel-lang/sorelc/src/ir.rs b/rel-lang/sorelc/src/ir.rs
deleted file mode 100644 (file)
index 61e6115..0000000
+++ /dev/null
@@ -1,307 +0,0 @@
-use crate::parser::Module;
-use crate::tokenizer::{Token, tokenize};
-use sorel_ir::*;
-
-use std::collections::{HashSet, HashMap};
-use std::path::PathBuf;
-use std::rc::Rc;
-use std::include_str;
-
-use anyhow::{Result, bail};
-
-macro_rules! push_num {
-    ($num:ident) => { IR::StackPush(*$num as u64) };
-    ($num:ident, $num_typ:ty) => { IR::StackPush(*$num as $num_typ as u64) };
-}
-
-#[derive(Debug, Default)]
-struct IRModule {
-    data: Vec<IR>,
-    text: Vec<IR>,
-    imports: Vec<Rc<IRModule>>,
-    exports: Vec<String>,
-    externs: Vec<String>,
-    // TODO these next two should form an enum, not two options
-    source_file: Option<PathBuf>,
-    std_specifier: Option<String>,
-    number: usize,
-}
-
-impl IRModule {
-    fn get_label_for_call(&self, name: &String) -> String {
-        if self.externs.contains(name) {
-            return name.clone();
-        }
-        let mut found: Option<usize> = None;
-        for imported in &self.imports {
-            if imported.exports.contains(name) {
-                found = Some(imported.number);
-                // Don't break here, since the last one should win.
-            }            
-        }
-        if let Some(found) = found {
-            format!("_m{}_{}", found, name)
-        } else {
-            // TODO check if it's even a word locally. If not, bail.
-            format!("_m{}_{}", self.number, name)
-        }
-    }
-
-    fn get_label(&self, name: &String) -> String {
-        format!("_m{}_{}", self.number, name)
-    }
-}
-
-#[derive(Default)]
-struct ImportTree {
-    data: Vec<IR>,
-    text: Vec<IR>,
-    all_modules: HashMap<String, Rc<IRModule>>,
-    all_exports: HashSet<String>,
-    entrypoint: Rc<IRModule>,
-    module_count: usize,
-    collapse_seen: HashSet<String>,
-}
-
-fn std_import(specifier: &str) -> Result<&str> {
-    match specifier {
-        "std:mem" => Ok(include_str!("../../stdlib/mem.sorel")),
-        "std:out" => Ok(include_str!("../../stdlib/out.sorel")),
-        "std:string" => Ok(include_str!("../../stdlib/string.sorel")),
-        "std:process" => Ok(include_str!("../../stdlib/process.sorel")),
-        _ => bail!("{} is not a standard library module", specifier),
-    }
-}
-
-impl ImportTree {
-    fn import(&mut self, importer_dir: &PathBuf, specifier: &str, is_entrypoint: bool) -> Result<Rc<IRModule>> {
-        Ok(if specifier.starts_with("std:") {
-            if self.all_modules.contains_key(specifier) {
-                let module = self.all_modules.get(specifier).unwrap().clone();
-                return Ok(module);
-            }
-            let contents = std_import(specifier)?;
-            let parsed = &Module::parse(tokenize(&contents)?, is_entrypoint)?;
-            let module = self.generate_internal(None, Some(specifier.to_string()), parsed);
-            let module = Rc::new(module);
-            self.all_modules.insert(specifier.to_string(), module.clone());
-            module
-        } else {
-            let mut path = PathBuf::from(specifier);
-            if path.is_relative() {
-                let mut new_path = importer_dir.clone();
-                new_path.push(path);
-                path = new_path.canonicalize()?;
-            }
-            let path_key = path.to_string_lossy().to_string();
-            if self.all_modules.contains_key(&path_key) {
-                let module = self.all_modules.get(&path_key).unwrap().clone();
-                return Ok(module);
-            }
-
-            let contents = &std::fs::read_to_string(&path)?;
-            let tokens = tokenize(&contents)?;
-            let parsed = &Module::parse(tokens, is_entrypoint)?;
-
-            let module = self.generate_internal(Some(path), None, parsed);
-            let module = Rc::new(module);
-            self.all_modules.insert(path_key, module.clone());
-            if is_entrypoint {
-                self.entrypoint = module.clone();
-            }
-            module
-        })
-
-    }
-
-    fn generate_internal(&mut self, path: Option<PathBuf>, std_specifier: Option<String>, module: &Module) -> IRModule {
-        // Eventually these will end up being sections in assembly
-        let mut text = vec![];
-        let mut data = vec![];
-
-        let mut imports = vec![];
-        if let Some(ref path) = path {
-            module.imports.iter().for_each(|imported| {
-                if let Some(parent_path) = path.parent() {
-                    match self.import(&parent_path.to_path_buf(), imported, false) {
-                        Ok(module) => {
-                            imports.push(module);
-                        },
-                        Err(msg) => {
-                            eprintln!("{}", msg);
-                        }
-                    }
-                } else {
-                }
-            });
-        } else {
-            // We're in a stdlib module, which can only import other stdlib
-            // modules.
-            module.imports.iter().for_each(|imported| {
-                match self.import(&PathBuf::new(), imported, false) {
-                    Ok(module) => {
-                        imports.push(module);
-                    },
-                    Err(msg) => {
-                        eprintln!("{}", msg);
-                    }
-                }
-            });
-        }
-        
-        let exports: Vec<_> = module.exports.iter().map(|s| {
-            self.all_exports.insert(s.to_string());
-            s.to_string()
-        }).collect();
-
-        let externs = module.externs.iter().map(|s| s.to_string()).collect();
-
-        text.push(module.words.iter().map(|def| {
-            let mut body = def.instructions.iter().map(|inst| {
-                let mapped_ir = match inst {
-                    Token::Word(word) => {
-                        match *word {
-                            "@" => IR::Load,
-                            "@:8" => IR::Load8,
-                            "@:16" => IR::Load16,
-                            "@:32" => IR::Load32,
-                            "!" => IR::Store,
-                            "!:8" => IR::Store8,
-                            "!:16" => IR::Store16,
-                            "!:32" => IR::Store32,
-                            "dup" => IR::Dup,
-                            "swap" => IR::Swap,
-                            "drop" => IR::Drop,
-                            "over" => IR::Over,
-                            "rot" => IR::Rot,
-                            "sp" => IR::StackPointer,
-                            "if" => IR::If,
-                            "else" => IR::Else,
-                            "endif" => IR::EndIf,
-                            "loop" => IR::Loop,
-                            "endloop" => IR::EndLoop,
-                            "=" => IR::Equals,
-                            ">" => IR::GreaterThan,
-                            "<" => IR::LessThan,
-                            "+" => IR::AddU64,
-                            "-" => IR::SubtractU64,
-                            "*" => IR::MultiplyU64,
-                            "/" => IR::DivideU64,
-                            "%" => IR::ModU64,
-                            "|" => IR::BitwiseOr,
-                            "sys0" => IR::Sys0,
-                            "sys1" => IR::Sys1,
-                            "sys2" => IR::Sys2,
-                            "sys3" => IR::Sys3,
-                            "sys4" => IR::Sys4,
-                            "sys5" => IR::Sys5,
-                            "sys6" => IR::Sys6,
-                            // TODO num type specfic math like `+:i32`, etc.
-                            _ =>  IR::Call(String::from(*word))
-                        }
-                    },
-                    Token::String(text) => {
-                        let string_label = format!("string_{}", data.len());
-                        data.push(IR::StringDef(string_label.clone(), String::from(*text)));
-                        IR::StackPushString(string_label)
-                    },
-                    Token::NumU8(num) => push_num!(num),
-                    Token::NumI8(num) => push_num!(num, u8),
-                    Token::NumU16(num) => push_num!(num),
-                    Token::NumI16(num) => push_num!(num, u16),
-                    Token::NumU32(num) => push_num!(num),
-                    Token::NumI32(num) => push_num!(num, u32),
-                    Token::NumU64(num) => push_num!(num),
-                    Token::NumI64(num) => push_num!(num),
-                    Token::NumF32(num) => push_num!(num),
-                    Token::NumF64(num) => push_num!(num),
-                };
-                mapped_ir
-            }).collect::<Vec<_>>();
-
-            let mut result = vec![IR::Label(def.name.to_string())];
-            result.append(&mut body);
-            result.push(IR::Ret);
-            result
-        }).flatten().collect::<Vec<_>>());
-
-        let number = self.module_count;
-        self.module_count += 1;
-
-        IRModule {
-            text: text.into_iter().flatten().collect::<Vec<_>>(),
-            data,
-            imports,
-            exports,
-            externs,
-            source_file: path,
-            std_specifier, 
-            number,
-        }
-    }
-
-    fn collapse(&mut self, module: Rc<IRModule>) -> Result<()> {
-        let seen_key = if let Some(source_file) = &module.source_file {
-            source_file.to_string_lossy().to_string()
-        } else {
-            module.std_specifier.clone().unwrap()
-        };
-        if self.collapse_seen.contains(&seen_key) {
-            return Ok(())
-        }
-
-        for imported in module.imports.clone() {
-            self.collapse(imported)?;
-        }
-
-        let is_entrypoint = module.source_file == self.entrypoint.source_file;
-        
-        let module_number = module.number;
-
-        for string in &module.data {
-            if let IR::StringDef(name, val) = string {
-                let new_name = format!("{}_{}", name, module_number);
-                self.data.push(IR::StringDef(new_name, val.clone()));
-            } else {
-                bail!("non-string data");
-            }
-        }
-
-        for instruction in &module.text {
-            let new_instruction = match instruction {
-                IR::StackPushString(name) => {
-                    let new_name = format!("{}_{}", name, module_number);
-                    IR::StackPushString(new_name)
-                },
-                IR::Label(name) => {
-                    if is_entrypoint && name == "main" {
-                        instruction.clone()
-                    } else {
-                        IR::Label(module.get_label(name))
-                    }
-                },
-                IR::Call(name) => {
-                    IR::Call(module.get_label_for_call(name))
-                },
-                _ => instruction.clone()
-            };
-            self.text.push(new_instruction);
-        }
-
-        self.collapse_seen.insert(seen_key);
-
-        Ok(())
-    }
-}
-
-pub fn compile(path: &str) -> Result<IRObject> {
-    let dir = std::env::current_dir()?;
-    let mut tree: ImportTree = Default::default(); 
-    let module = tree.import(&dir, path, true)?;
-    tree.collapse(module)?;
-    // TODO remove unused words 
-    Ok(IRObject {
-        data: tree.data,
-        text: tree.text,
-    })
-}
diff --git a/rel-lang/sorelc/src/main.rs b/rel-lang/sorelc/src/main.rs
deleted file mode 100644 (file)
index 724df84..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-mod tokenizer;
-mod parser;
-mod ir;
-mod riscv_asm_codegen;
-
-use anyhow::Result;
-
-use std::fs::File;
-use std::io::Write;
-use std::path::PathBuf;
-
-fn main() -> Result<()> {
-    let filename = std::env::args().nth(1).expect("must provide a file to compile");
-    let module = ir::compile(&filename)?;
-    let mut generator = riscv_asm_codegen::CodeGen::new(&module, 4096);
-    let mut asm_path = PathBuf::from(filename);
-    asm_path.set_extension("asm");
-    let mut output = File::create(asm_path)?;
-    write!(output, "{}\n", generator.assembly()?)?;
-    Ok(())
-}
diff --git a/rel-lang/sorelc/src/parser.rs b/rel-lang/sorelc/src/parser.rs
deleted file mode 100644 (file)
index 94b53e4..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-use crate::tokenizer::Token;
-use anyhow::{Result, bail};
-
-#[derive(Debug)]
-pub struct WordDefinition<'a> {
-    pub name: &'a str,
-    pub instructions: Vec<Token<'a>>,
-}
-
-#[derive(Debug)]
-pub struct Module<'a> {
-    pub words: Vec<WordDefinition<'a>>,
-    pub imports: Vec<&'a str>,
-    pub exports: Vec<&'a str>,
-    pub externs: Vec<&'a str>,
-}
-
-impl<'a> Module<'a> {
-    pub fn parse(input: Vec<Token<'a>>, is_entrypoint: bool) -> Result<Self> {
-        let mut result = vec![];
-        let mut main = vec![];
-        let mut exports = vec![];
-        let mut imports = vec![];
-        let mut externs = vec![];
-        let mut current_word: Option<WordDefinition> = None;
-        let mut about_to_start_word_def = false;
-        let mut last_was_import = false;
-        let mut last_was_export = false;
-        let mut last_was_extern = false;
-
-        for token in input {
-            if about_to_start_word_def {
-                if let Token::Word(name) = token {
-                    current_word = Some(WordDefinition {
-                        name,
-                        instructions: vec![],
-                    });
-                    about_to_start_word_def = false;
-                    continue;
-                } else {
-                    bail!("{:?} is not a valid word name!", token);
-                }
-            } else if let Token::Word(word) = token {
-                if word == ":" {
-                    if current_word.is_some() {
-                        bail!("can't define words inside word definitions!");
-                    }
-                    about_to_start_word_def = true;
-                    continue;
-                }
-                if word == ";" {
-                    let word = current_word.take();
-                    if let Some(word) = word {
-                        result.push(word);
-                        continue;
-                    } else {
-                        bail!("`;` must be at the end of a word definition");
-                    }
-                }
-            }
-            if let Some(ref mut current_word) = current_word {
-                current_word.instructions.push(token);
-            } else {
-                match token {
-                    Token::Word(word) => {
-                        if word == "import" {
-                            last_was_import = true;
-                        } else if word == "export" {
-                            last_was_export = true;
-                        } else if word == "extern" {
-                            last_was_extern = true;
-                        } else {
-                            if last_was_export {
-                                exports.push(word);
-                                last_was_export = false;
-                            } else if last_was_extern {
-                                externs.push(word);
-                                last_was_extern = false;
-                            } else {
-                                main.push(token.clone());
-                            }
-                        }
-                    },
-                    Token::String(string) => {
-                        if last_was_import {
-                            imports.push(string);
-                            last_was_import = false;
-                        } else {
-                            main.push(token.clone());
-                        }
-                    },
-                    _ => {
-                        main.push(token.clone());
-                    }
-                };
-            }
-        }
-
-        if about_to_start_word_def || current_word.is_some() {
-            bail!("unfinished word definition!");
-        }
-
-        if is_entrypoint {
-            result.push(WordDefinition {
-                name: "main",
-                instructions: main,
-            });
-        }
-        
-        Ok(Module { words: result, imports, exports, externs })
-    }
-
-    #[cfg(test)]
-    pub fn debug_print(&self) {
-        for word in &self.words {
-            println!("{}", word.name);
-            for instruction in &word.instructions {
-                println!("    {:?}", instruction);
-            }
-        }
-    }
-}
-
-
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn try_some_parsing() {
-        let result = Module::parse(crate::tokenizer::tokenize("
-: hello world 16 \"planet\" ;
-: soup chicken 4.5 hello ;
-
-hello soup
-").unwrap(), true).unwrap();
-        result.debug_print();
-    }
-}
diff --git a/rel-lang/sorelc/src/riscv_asm_codegen.rs b/rel-lang/sorelc/src/riscv_asm_codegen.rs
deleted file mode 100644 (file)
index c382cdf..0000000
+++ /dev/null
@@ -1,361 +0,0 @@
-use sorel_ir::*;
-
-use anyhow::*;
-
-use std::collections::{HashMap, HashSet};
-use std::fmt::Display;
-
-pub struct CodeGen<'a> {
-    module: &'a IRObject,
-    data_stack_size: usize,
-    lines: Vec<String>,
-}
-
-
-// Some inspiration
-// ================
-//
-// * https://github.com/aw/fiveforths/blob/master/docs/REFERENCE.md#registers-list
-//     * Except using sp as a more C ABI style stack pointer, and s2 for the data stack
-// 
-
-// Implementation Choices
-// ======================
-//
-// Data Stack pointer: s2
-// No return stack pointer (using C ABI, so sp, sorta)
-// Use t0, t1, t2 for temporary values in words
-// Data stack grows down
-
-
-macro_rules! asm_macro {
-    ($name:ident, $src:expr) => {
-        fn $name(&mut self) {
-            self.line($src);
-        }
-    };
-    ($name:ident, $src:expr, $arg0:ty) => {
-        fn $name(&mut self, val0: $arg0) {
-            self.line(format!($src, val0));
-        }
-    };
-    ($name:ident, $src:expr, $arg0:ty, $arg1:ty) => {
-        fn $name(&mut self, val0: $arg0, val1: $arg1) {
-            self.line(format!($src, val0, val1));
-        }
-    };
-}
-
-fn mangle(input: &str) -> String {
-    input
-        .replace("<", "_LT_")
-        .replace(">", "_GT_")
-        .replace("-", "___")
-}
-
-impl<'a> CodeGen<'a> {
-    pub fn new(ir_mod: &'a IRObject, data_stack_size: usize) -> Self {
-        Self {
-            module: ir_mod,
-            data_stack_size,
-            lines: vec![],
-        }
-    }
-
-    fn line<S: Display>(&mut self, line: S) {
-        self.lines.push(format!("    {}", line));
-
-    }
-
-    fn label<S: Display>(&mut self, line: S) {
-        self.lines.push(line.to_string());
-    }
-
-    asm_macro!(copy_top_stack_value_to, "ld   {}, 0(s2)", &str);
-    asm_macro!(copy_offset_stack_value_to, "ld   {}, {}*8(s2)", &str, isize);
-    asm_macro!(copy_to_top_of_stack, "sd {}, 0(s2)", &str);
-    asm_macro!(move_stack_ptr_by_cells, "addi s2, s2, {}*8", isize);
-
-    fn pop_to(&mut self, reg: &str) {
-        self.copy_top_stack_value_to(reg);
-        self.move_stack_ptr_by_cells(1);
-    }
-
-    fn pop_some_to(&mut self, regs: &str) {
-        let mut regs = regs.trim().split(" ").collect::<Vec<_>>();
-        regs.reverse();
-        let count = regs.len();
-        let mut index = 0;
-        for reg in regs {
-            self.copy_offset_stack_value_to(reg, index);
-            index += 1;
-        }
-        self.move_stack_ptr_by_cells(count as isize);
-    }
-
-    fn push_from(&mut self, reg: &str) {
-        self.move_stack_ptr_by_cells(-1);
-        self.copy_to_top_of_stack(reg);
-    }
-
-    fn pop_call_push(&mut self, regs: &str, call: &str, reg: &str) {
-        self.pop_some_to(regs);
-        self.line(call);
-        self.push_from(reg);
-    }
-
-    pub fn assembly(&mut self) -> Result<String>{
-        let mut string_table = HashMap::new();
-
-        // Static strings
-        self.label(".section .rodata\n");
-        self.label(".align 3\n");
-        for ir in &self.module.data {
-            match ir {
-                IR::StringDef(string_label, some_string) => {
-                    string_table.insert(some_string.clone(), string_label);
-                    self.label(format!("{}:", string_label));
-                    self.line(format!(".asciz \"{}\"", some_string)); // should this be .asciz?
-                    self.label("");
-                },
-                _ => bail!("Currently only string definitions are supported in the data section.")
-            }
-        }
-
-        // Data stack
-        self.label(".data\n");
-        self.label(".align 3\n");
-        self.label("data_stack:");
-        self.line(format!(".space {}", self.data_stack_size));
-        self.label(".globl data_stack_end\ndata_stack_end:\n");
-
-        // Code
-        self.label(".text\n");
-        self.label(".align 3\n");
-
-        let mut if_block_count = 0;
-        let mut if_stack = vec![];
-        let mut loop_count = 0;
-        let mut loop_stack = vec![];
-        let mut seen_else = HashSet::new();
-        let mut last_label = "";
-
-        for ir in &self.module.text {
-            match ir {
-                IR::Label(name) => {
-                    last_label = name;
-                    if name == "main" {
-                        self.label(".globl _start"); // TODO is globl necessary?
-                        self.label("_start:");
-                        self.line("la s2, data_stack_end # set initial data stack pointer");
-                    } else {
-                        let mangled = mangle(name);
-                        self.label(format!(".globl {}", mangled));
-                        self.label(format!("{}:", mangled));
-                    }
-                    self.line("addi sp, sp, -16 # allocate 16 bytes on stack"); // allocate 16 bytes on stack
-                    self.line("sd ra, 8(sp) # store return address on stack");   // store return address on stack
-                },
-                IR::Call(name) => {
-                    let mangled = mangle(name);
-                    self.label(format!("# call {}", mangled));
-                    self.line(format!("call {}", mangled));    
-                },
-                IR::Ret => {
-                    if last_label == "main" {
-                        self.label("# exit 0 syscall");
-                        self.line("li a7, 93");
-                        self.line("mv a0, x0");
-                        self.line("ecall");
-                    } else {
-                        self.line("ld ra, 8(sp)");  // load return address from stack
-                        self.line("addi sp, sp, 16"); // restore stack pointer
-                        self.line("ret");
-                    }
-                },
-                IR::Load8 => {
-                    self.label("# load 8");
-                    self.copy_top_stack_value_to("t0");
-                    self.line("lbu   t0, 0(t0)"); // deref pointer in t0 to t0
-                    self.copy_to_top_of_stack("t0");
-                },
-                IR::Load16 => {
-                    self.label("# load 16");
-                    self.copy_top_stack_value_to("t0");
-                    self.line("lhu   t0, 0(t0)"); // deref pointer in t0 to t0
-                    self.copy_to_top_of_stack("t0");
-                },
-                IR::Load32 => {
-                    self.label("# load 32");
-                    self.copy_top_stack_value_to("t0");
-                    self.line("lwu   t0, 0(t0)"); // deref pointer in t0 to t0
-                    self.copy_to_top_of_stack("t0");
-                },
-                IR::Load => {
-                    self.label("# load 64");
-                    self.copy_top_stack_value_to("t0");
-                    self.line("ld   t0, 0(t0)"); // deref pointer in t0 to t0
-                    self.copy_to_top_of_stack("t0");
-                },
-                IR::Store8 => { // ( x addr -- )
-                    self.pop_some_to("t0 t1");
-                    self.line("sb t0, 0(t1)"); // store x at addr 
-                },
-                IR::Store16 => { // ( x addr -- )
-                    self.pop_some_to("t0 t1");
-                    self.line("sh t0, 0(t1)"); // store x at addr 
-                },
-                IR::Store32 => { // ( x addr -- )
-                    self.pop_some_to("t0 t1");
-                    self.line("sw t0, 0(t1)"); // store x at addr 
-                },
-                IR::Store => { // ( x addr -- )
-                    self.pop_some_to("t0 t1");
-                    self.line("sd t0, 0(t1)"); // store x at addr 
-                },
-                IR::StackPush(num) => {
-                    self.label(format!("# stackpush {}", num));
-                    self.line(format!("li t0, {}", num));
-                    self.push_from("t0");
-                },
-                IR::StackPushString(name) => {
-                    self.label(format!("# stackpushstring {}", name));
-                    self.line(format!("la t0, {}", name));
-                    self.push_from("t0");
-                },
-                IR::AddU64 => {
-                    self.label("# add");
-                    self.pop_call_push("t0 t1", "add t0, t0, t1", "t0"); 
-                },
-                IR::SubtractU64 => {
-                    self.label("# sub");
-                    self.pop_call_push("t0 t1", "sub t0, t0, t1", "t0"); 
-                },
-                IR::MultiplyU64 => {
-                    self.pop_call_push("t0 t1", "mul t0, t0, t1", "t0"); 
-                },
-                IR::DivideU64 => {
-                    self.pop_call_push("t0 t1", "div t0, t0, t1", "t0"); 
-                },
-                IR::ModU64 => {
-                    self.pop_call_push("t0 t1", "rem t0, t0, t1", "t0"); 
-                },
-                IR::Dup => {
-                    self.label("# dup");
-                    self.copy_top_stack_value_to("t0");
-                    self.push_from("t0");
-                },
-                IR::Swap => {
-                    self.label("# swap");
-                    self.pop_some_to("t1 t0");
-                    self.push_from("t0");
-                    self.push_from("t1");
-                },
-                IR::Rot => {
-                    self.label("# rot");
-                    self.pop_some_to("t0 t1 t2");
-                    self.push_from("t1");
-                    self.push_from("t2");
-                    self.push_from("t0");
-                },
-                IR::StackPointer => {
-                    self.label("# sp");
-                    self.line("addi t0, s2, 0");
-                    self.push_from("t0");
-                },
-                IR::Drop => {
-                    self.label("# drop");
-                    self.move_stack_ptr_by_cells(1);
-                },
-                IR::Equals => {
-                    // Yes, this is the same as subtract, since we're treating 0 as true, and
-                    // others as false.
-                    self.pop_call_push("t0 t1", "sub t0, t0, t1", "t0"); 
-                },
-                IR::GreaterThan => {
-                    self.label("# >");
-                    self.pop_some_to("t0 t1");
-                    self.line("sgt  t0, t0, t1");
-                    self.line("seqz t0, t0"); // remember, 0 is true, others are false
-                    self.push_from("t0");
-                },
-                IR::LessThan => {
-                    self.label("# <");
-                    self.pop_some_to("t0 t1");
-                    self.line("slt  t0, t0, t1");
-                    self.line("seqz t0, t0"); // remember, 0 is true, others are false
-                    self.push_from("t0");
-                },
-                IR::BitwiseOr => {
-                    self.pop_call_push("t0 t1", "or t0, t0, t1", "t0"); 
-                },
-                IR::Sys0 => {
-                    self.pop_call_push("a7", "ecall", "a0");
-                },
-                IR::Sys1 => {
-                    self.pop_call_push("a0 a7", "ecall", "a0");
-                },
-                IR::Sys2 => {
-                    self.pop_call_push("a0 a1 a7", "ecall", "a0");
-                },
-                IR::Sys3 => {
-                    self.pop_call_push("a0 a1 a2 a7", "ecall", "a0");
-                },
-                IR::Sys4 => {
-                    self.pop_call_push("a0 a1 a2 a3 a7", "ecall", "a0");
-                },
-                IR::Sys5 => {
-                    self.pop_call_push("a0 a1 a2 a3 a4 a7", "ecall", "a0");
-                },
-                IR::Sys6 => {
-                    self.pop_call_push("a0 a1 a2 a3 a4 a5 a7", "ecall", "a0");
-                },
-                // https://cmput229.github.io/229-labs-RISCV/RISC-V-Examples_Public/03-Conditionals/03b-If_Else.html
-                IR::If => {
-                    self.label("# if");
-                    self.pop_to("t0");
-                    self.line(format!("bnez t0, _else_{}", if_block_count));
-                    if_stack.push(if_block_count);
-                    if_block_count += 1;
-                },
-                IR::Else => {
-                    self.label("# else");
-                    let if_counter = if_stack.last().unwrap().clone();
-                    self.line(format!("j _endif_{}", if_counter));
-                    self.label(format!("_else_{}:", if_counter));
-                    seen_else.insert(if_counter);
-                },
-                IR::EndIf => {
-                    self.label("# endif");
-                    let stack = &mut if_stack;
-                    let if_counter = stack.last().unwrap().clone();
-                    if !seen_else.contains(&if_counter) {
-                        self.label(format!("_else_{}:", if_counter));
-                    } else {
-                        self.label(format!("_endif_{}:", if_counter));
-                        seen_else.remove(&if_counter);
-                    }
-                    stack.pop();
-                },
-                IR::Loop => { // keep looping until is true/0
-                    self.label(format!("_loop_{}:", loop_count));
-                    self.pop_to("t0");
-                    self.line(format!("beqz t0, _endloop_{}", loop_count));
-                    loop_stack.push(loop_count);
-                    loop_count += 1;
-                },
-                IR::EndLoop => {
-                    let stack = &mut loop_stack;
-                    let loop_counter = stack.last().unwrap().clone();
-                    self.line(format!("j _loop_{}", loop_counter));
-                    self.label(format!("_endloop_{}:", loop_counter));
-                    stack.pop();
-                },
-                _ => bail!("not implemented yet: {:?}", ir),
-            }
-        }
-
-        Ok(self.lines.join("\n"))
-    }
-}
-
diff --git a/rel-lang/sorelc/src/tokenizer.rs b/rel-lang/sorelc/src/tokenizer.rs
deleted file mode 100644 (file)
index d546c6b..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-use anyhow::{Result, anyhow};
-
-#[derive(Debug, Clone)]
-pub enum Token<'a> {
-    Word(&'a str),
-    String(&'a str),
-    NumU8(u8),
-    NumI8(i8),
-    NumU16(u16),
-    NumI16(i16),
-    NumU32(u32),
-    NumI32(i32),
-    NumU64(u64),
-    NumI64(i64),
-    NumF32(f32),
-    NumF64(f64),
-}
-
-impl<'a> Token<'a>{
-    fn parse_word_or_num(input: &'a str) -> Result<Token<'a>> {
-        if input == "-" {
-            return Ok(Token::Word(input))
-        }
-        
-        // we're assuming any token starting with `-` with length greater than one
-        // is a negative number
-        if input.starts_with('-') || input.chars().nth(0).map(|x| x.is_numeric()).unwrap_or(false) {
-            if input.contains(':') {
-                let mut splat = input.split(':');
-                let num = splat.next().ok_or(anyhow!("no number found"))?;
-                let typ = splat.next().ok_or(anyhow!("no number type found"))?;
-                match typ {
-                    "u8" => Ok(Token::NumU8(num.parse()?)),
-                    "i8" => Ok(Token::NumI8(num.parse()?)),
-                    "u16" => Ok(Token::NumU16(num.parse()?)),
-                    "i16" => Ok(Token::NumI16(num.parse()?)),
-                    "u32" => Ok(Token::NumU32(num.parse()?)),
-                    "i32" => Ok(Token::NumI32(num.parse()?)),
-                    "u64" => Ok(Token::NumU64(num.parse()?)),
-                    "i64" => Ok(Token::NumI64(num.parse()?)),
-                    "f32" => Ok(Token::NumF32(num.parse()?)),
-                    "f64" => Ok(Token::NumF64(num.parse()?)),
-                    _ => panic!("unknown number type")
-                }
-            } else {
-                if input.contains('.') {
-                    Ok(Token::NumF64(input.parse()?))
-                } else if input.starts_with('-') {
-                    Ok(Token::NumI64(input.parse()?))
-                } else {
-                    Ok(Token::NumU64(input.parse()?))
-                }
-            }
-        } else {
-            Ok(Token::Word(input))
-        }
-    }
-}
-
-// TODO really want an iterator, not a vector
-pub fn tokenize<'a>(input: &'a str) -> Result<Vec<Token<'a>>> {
-    let mut result = vec![];
-    let mut string_start: Option<usize> = None;
-    let mut word_or_num_start: Option<usize> = None;
-    let mut last_is_backslash = false;
-    let mut last_is_whitespace = true;
-    let mut in_doc_comment = false;
-    let mut in_line_comment = false;
-    let mut index = 0;
-    let mut first_char = true;
-
-
-    for char in input.chars() {
-        if first_char {
-            first_char = false;
-        } else {
-            index += 1;
-        }
-        
-        if in_doc_comment {
-            if char == ')' {
-                in_doc_comment = false;
-                last_is_whitespace = true; // not really true, but means don't need space after
-            }
-            continue;
-        }
-
-        if in_line_comment {
-            word_or_num_start = None;
-            if char == '\n' {
-                in_line_comment = false;
-                last_is_whitespace = true; // not really true, but means don't need space after
-            }
-            continue;
-        }
-
-        if char == '"' {
-            if let Some(start) = string_start {
-                if !last_is_backslash {
-                    result.push(Token::String(&input[start..index]));
-                    string_start = None;
-                }
-            } else {
-                string_start = Some(index + 1)                
-            }
-            last_is_backslash = false;
-            last_is_whitespace = false;
-            continue;
-        }
-
-
-        if string_start.is_some() {
-            last_is_backslash = char == '\\';
-            continue;
-        }
-
-        if char.is_whitespace() {
-            if last_is_backslash {
-                in_line_comment = true;                
-            } else if !last_is_whitespace && let Some(start) = word_or_num_start {
-                let token = &input[start..index];
-                if token == "(" {
-                    in_doc_comment = true;
-                } else {
-                    result.push(Token::parse_word_or_num(&input[start..index])?);
-                }
-                word_or_num_start = None;
-            }
-            last_is_whitespace = true;
-            last_is_backslash = false;
-            continue;
-        }
-
-        last_is_backslash = char == '\\';
-
-        if index == input.len() - 1 {
-            if !last_is_whitespace && let Some(start) = word_or_num_start {
-                result.push(Token::parse_word_or_num(&input[start..])?);
-            }
-            continue;
-        }
-
-        if last_is_whitespace { // start of word or num (we already handled strings)
-            word_or_num_start = Some(index);
-            last_is_whitespace = false;
-        }
-    }
-    Ok(result)
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn try_some_tokenizing() {
-        let result = tokenize("
-
-        \\ soup
-            2 3.4 - -88 bacon \"hello\" 43:f32 2345:u32 -57:i8 soup
-");
-        println!("result: {:?}", result);
-    }
-
-    #[test]
-    fn comments() {
-        let result = tokenize("
-            (
-                foo
-                bar
-            )
-            : baz ( x y -- z )
-              chicken
-              soup
-            ;
-        ");
-        println!("result: {:?}", result);
-    }
-
-    #[test]
-    fn strings() {
-        let result = tokenize("
-dup \\ ( stuff )
-\"hello!\"
-");
-        println!("result: {:?}", result);
-    }
-}
diff --git a/rel-lang/stdlib/mem.sorel b/rel-lang/stdlib/mem.sorel
deleted file mode 100644 (file)
index 5ed53c8..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-\ vim: filetype=forth
-
-: mmap 222 sys6 ;
-: munmap 215 sys2 ;
-
-: PROT_READ 1 ;
-: PROT_WRITE 2 ;
-: MAP_PRIVATE 2 ;
-: MAP_ANONYMOUS 32 ;
-
-: ALLOC_PROT PROT_READ PROT_WRITE | ;
-: ALLOC_MAP MAP_PRIVATE MAP_ANONYMOUS | ;
-
-\ This is a raw way to allocate memory. Callers to this will need to know
-\ how much memory was allocated, to call dealloc later.
-: alloc_raw ( byte-count -- addr )
-  0 swap ALLOC_PROT ALLOC_MAP -1:i32 0 mmap
-;
-
-: dealloc ( addr byte-count -- )
-  munmap
-;
-
-export alloc
-: alloc ( byte-count -- addr )
-  dup \ we'll need the length again later
-  8 + \ add 64 bits to the front where we'll store the length
-  alloc_raw \ ( byte-count addr )
-  dup \ ( byte-count addr addr)
-  rot \ ( addr addr byte-count )
-  rot \ ( addr byte-count addr )
-  ! \ write the length to the addr, now ( addr )
-  8 + \ here's the address we'll return, 8 bytes forward to account for the length
-;
-
-export free
-: free ( addr -- )
-  8 - \ the real memory start is 8 bytes before
-  dup \ ( addr addr )
-  @ \ ( addr byte-length )
-  8 + \ add 8 to the byte length to refer to the whole region from alloc.
-  dealloc
-; 
-
-import "std:out"
-
-: copy_byte ( addr_from addr_to -- addr_from addr_to )
-  swap \ ( addr_to addr_from )
-  dup \ ( addr_to addr_from addr_from )
-  @:8 \ ( addr_to addr_from byte )
-  rot \ ( addr_from byte addr_to )
-  dup \ ( addr_from byte addr_to addr_to)
-  rot \ ( addr_from addr_to addr_to byte )
-  swap \ ( addr_from addr_to byte addr_to )
-  !:8 \ ( addr_from addr_to )
-;
-
-export memcopy
-: memcopy ( addr_from addr_to count -- )
-  dup \ ( addr_from addr_to count count )
-  loop \ ( addr_from addr_to count )
-    rot \ ( addr_to count addr_from )
-    rot \ ( count addr_from addr_to )
-    copy_byte \ ( count addr_from addr_to )
-    1 + \ ( count addr_from addr_to+1 )
-    rot \ ( addr_from addr_to+1 count )
-    1 - \ ( addr_from addr_to+1 count-1 )
-    rot \ ( addr_to+1 count-1 addr_from )
-    1 + \ ( addr_to+1 count-1 addr_from+1 )
-    rot rot \ ( addr_from+1 addr_to+1 count-1 )
-    dup \ ( addr_from+1 addr_to+1 count-1 count-1)
-  endloop \ ( addr_from+count addr_to+count 0 )
-  drop drop drop \ ( )
-;
diff --git a/rel-lang/stdlib/out.sorel b/rel-lang/stdlib/out.sorel
deleted file mode 100644 (file)
index 5704f99..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-\ vim: filetype=forth
-
-: write ( fd ptr len -- bytes-written-or-err )
-  64
-  sys3
-;
-
-import "std:string"
-
-export puts
-
-: puts ( addr -- addr)
-  dup dup \ ( addr addr addr )
-  strlen \ ( addr addr len )
-  1 \ ( addr addr len 1 )
-  rot \ ( addr len 1 addr )
-  rot \ ( addr 1 addr len )
-  write \ ( addr bytes-written-or-err )
-  drop \ ( addr )
-;
-
-: ZERO_CHAR 48 ;
-: NEWLINE_CHAR 10 ;
-
-export putn
-
-: putn ( num -- num )
-  dup dup \ ( num num num )
-  10 rot rot \ ( num 10 num num ) // Mutltidigit stop point
-  10 < \ ( num 10 num is<10 )
-  loop \ ( num 10 num' )
-    dup \ ( num 10 num' num' )
-    10 % \ ( num 10 num' digit )
-    swap \ ( num 10 digit num' )
-    10 / \ ( num 10 digit num'/10 )
-    dup 10 < \ ( num 10 digit num'/10 is<10 )
-  endloop \ ( num 10 digitn ... digit1 )
-  dup 9 > \ ( num 10 digitn ... digit1 digit1>9 )
-  loop \ ( num 10 digitn ... digit1 )
-    ZERO_CHAR + \ \ ( num 10 digitn ... digit1 )
-    sp \ ( num 10 digitn ... digit1 ptr )
-    1 \ ( num 10 digitn ... digit1 ptr 1 )
-    swap \ ( num 10 digitn ... digit1 1 ptr )
-    1 \ ( num 10 digitn ... digit1 1 ptr 1 )
-    write \ ( num 10 digitn ... digit1 result )
-    drop drop \ ( num 10 digitn ... digit2 )
-    dup 9 > \ ( num 10 digitn ... digit2 digit2>9 )
-  endloop \ ( num 10 )
-  drop \ ( num )
-  NEWLINE_CHAR \ ( num 10 )
-  sp \ ( num 10 ptr )
-  1 \ ( num 10 ptr 1 )
-  swap \ (num 10 1 ptr )
-  1 \ ( num 10 1 ptr 1 )
-  write \ ( num 10 result )
-  drop \ ( num 10 )
-  drop \ ( num )
-;
-
diff --git a/rel-lang/stdlib/process.sorel b/rel-lang/stdlib/process.sorel
deleted file mode 100644 (file)
index 73a7008..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-\ vim: filetype=forth
-
-export exit
-
-: exit ( code -- )
-  93
-  sys1
-;
diff --git a/rel-lang/stdlib/string.sorel b/rel-lang/stdlib/string.sorel
deleted file mode 100644 (file)
index b0c76a8..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-\ vim: filetype=forth
-
-: strlen ( addr -- len )
-  dup dup \ ( addr addr addr )
-  @:8 \ ( addr addr byte )
-  loop \ ( addr addr )
-    1 + \ ( addr addr+1 )
-    dup \ ( addr addr+1 addr+1)
-    @:8 \ ( addr addr+1 byte )
-  endloop \ ( addr addr+len )
-  swap \ ( addr+len addr )
-  - \ ( len )
-;
-
-export strlen
diff --git a/rel-lang/tests/assert.sorel b/rel-lang/tests/assert.sorel
deleted file mode 100644 (file)
index 9d84758..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-\ vim: filetype=forth
-
-import "std:process"
-
-export assert
-
-: assert ( x -- )
-  if
-    drop
-  else
-    1 exit
-  endif
-;
diff --git a/rel-lang/tests/putstack.c b/rel-lang/tests/putstack.c
deleted file mode 100644 (file)
index a5200c4..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <stdio.h>
-
-extern unsigned long data_stack_end;
-register unsigned long * stack_pointer asm("s2");
-
-void putstack() {
-  unsigned long * stack_index = &data_stack_end;
-  printf("stack: ");
-  while (stack_index != stack_pointer) {
-         printf("%ld ", *stack_index);
-    stack_index -= 1;
-  }
-       printf("%ld\n", *stack_pointer);
-}
-
-
diff --git a/rel-lang/tests/test.sh b/rel-lang/tests/test.sh
deleted file mode 100644 (file)
index 279addc..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-UNAME=$(uname -m)
-CMD_PREFIX=$([ "$UNAME" = "riscv64" ] && echo "" || echo "riscv64-unknown-linux-gnu-")
-AS="${CMD_PREFIX}as"
-LD="${CMD_PREFIX}ld"
-CC="${CMD_PREFIX}cc"
-
-../target/debug/sorelc test1.sorel
-$AS -g -o test1.o test1.asm
-$CC -O1 -no-pie -o test1.out test1.o putstack.c -nostartfiles 
-# $LD -o test1.out test1.o
-./test1.out
diff --git a/rel-lang/tests/test1.sorel b/rel-lang/tests/test1.sorel
deleted file mode 100644 (file)
index 3256625..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-\ vim: filetype=forth
-
-import "./assert.sorel"
-import "std:out"
-
-"Hello, World! \n" puts
-drop
-
-42 putn
-drop
-
-import "std:mem"
-
-64 alloc
-free
-
-0 assert
-
-import "std:string"
-import "std:mem"
-import "std:out"
-
-extern putstack
-
-16 alloc \ ( ptr )
-dup \ ( ptr ptr )
-"To be copied!" \ ( ptr ptr strptr )
-dup \ ( ptr ptr strptr strptr )
-strlen 1 + \ ( ptr ptr strptr len )
-rot \ ( ptr strptr len ptr )
-swap \ ( ptr strptr ptr len )
-memcopy \ ( ptr )
-puts \ ( ptr )
-free \ ( )
-
-
diff --git a/sorel-ir/Cargo.toml b/sorel-ir/Cargo.toml
new file mode 100644 (file)
index 0000000..5618a61
--- /dev/null
@@ -0,0 +1,9 @@
+[package]
+name = "sorel-ir"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+serde = "1.0.228"
+serde_derive = "1.0.228"
+serde_yaml = "0.9.34"
diff --git a/sorel-ir/src/lib.rs b/sorel-ir/src/lib.rs
new file mode 100644 (file)
index 0000000..1a3ec27
--- /dev/null
@@ -0,0 +1,72 @@
+use serde_yaml::{from_str, to_string, Error};
+use serde_derive::{Serialize, Deserialize};
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub enum IR {
+    Label(String),
+    Call(String),
+    Ret,
+    StackPush(u64),
+    StackPushString(String), // refers to string label, not the string itself
+    StringDef(String, String), // first is string label, second is string value
+
+    // These next ones should always be inlined, so they're in IR.
+    Load, // @ ( addr -- x ) -- Fetch memory contents at addr
+    Load8,
+    Load16,
+    Load32,
+    Store, // ! ( x addr -- ) -- Store x at addr
+    Store8,
+    Store16,
+    Store32,
+
+    // These ones might not be inlined, but should be built-in, so a compiler might
+    // turn this into `Call(String)` before translating to assembly/machine-code, but
+    // an IR interpreter may just execute them.
+    AddU64,
+    SubtractU64,
+    MultiplyU64,
+    DivideU64,
+    ModU64,
+    Equals,
+    GreaterThan,
+    LessThan,
+    BitwiseOr,
+    Dup,
+    Swap,
+    Drop,
+    Over,
+    Rot,
+    StackPointer,
+    If,
+    Else,
+    EndIf,
+    Loop,
+    EndLoop,
+
+    // System calls
+    Sys0,
+    Sys1,
+    Sys2,
+    Sys3,
+    Sys4,
+    Sys5,
+    Sys6,
+}
+
+// This is like an .o file.
+#[derive(Serialize, Deserialize, Debug)]
+pub struct IRObject {
+    pub text: Vec<IR>,
+    pub data: Vec<IR>,
+}
+
+impl IRObject {
+    pub fn to_s(&self) -> Result<String, Error> {
+        to_string(self)
+    }
+
+    pub fn from_s(source: &str) -> Result<Self, Error> {
+        from_str(source)
+    }
+}
diff --git a/sorelc/Cargo.toml b/sorelc/Cargo.toml
new file mode 100644 (file)
index 0000000..7859ecf
--- /dev/null
@@ -0,0 +1,8 @@
+[package]
+name = "sorelc"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+sorel-ir = { workspace = true }
+anyhow = "1.0.100"
diff --git a/sorelc/src/ir.rs b/sorelc/src/ir.rs
new file mode 100644 (file)
index 0000000..61e6115
--- /dev/null
@@ -0,0 +1,307 @@
+use crate::parser::Module;
+use crate::tokenizer::{Token, tokenize};
+use sorel_ir::*;
+
+use std::collections::{HashSet, HashMap};
+use std::path::PathBuf;
+use std::rc::Rc;
+use std::include_str;
+
+use anyhow::{Result, bail};
+
+macro_rules! push_num {
+    ($num:ident) => { IR::StackPush(*$num as u64) };
+    ($num:ident, $num_typ:ty) => { IR::StackPush(*$num as $num_typ as u64) };
+}
+
+#[derive(Debug, Default)]
+struct IRModule {
+    data: Vec<IR>,
+    text: Vec<IR>,
+    imports: Vec<Rc<IRModule>>,
+    exports: Vec<String>,
+    externs: Vec<String>,
+    // TODO these next two should form an enum, not two options
+    source_file: Option<PathBuf>,
+    std_specifier: Option<String>,
+    number: usize,
+}
+
+impl IRModule {
+    fn get_label_for_call(&self, name: &String) -> String {
+        if self.externs.contains(name) {
+            return name.clone();
+        }
+        let mut found: Option<usize> = None;
+        for imported in &self.imports {
+            if imported.exports.contains(name) {
+                found = Some(imported.number);
+                // Don't break here, since the last one should win.
+            }            
+        }
+        if let Some(found) = found {
+            format!("_m{}_{}", found, name)
+        } else {
+            // TODO check if it's even a word locally. If not, bail.
+            format!("_m{}_{}", self.number, name)
+        }
+    }
+
+    fn get_label(&self, name: &String) -> String {
+        format!("_m{}_{}", self.number, name)
+    }
+}
+
+#[derive(Default)]
+struct ImportTree {
+    data: Vec<IR>,
+    text: Vec<IR>,
+    all_modules: HashMap<String, Rc<IRModule>>,
+    all_exports: HashSet<String>,
+    entrypoint: Rc<IRModule>,
+    module_count: usize,
+    collapse_seen: HashSet<String>,
+}
+
+fn std_import(specifier: &str) -> Result<&str> {
+    match specifier {
+        "std:mem" => Ok(include_str!("../../stdlib/mem.sorel")),
+        "std:out" => Ok(include_str!("../../stdlib/out.sorel")),
+        "std:string" => Ok(include_str!("../../stdlib/string.sorel")),
+        "std:process" => Ok(include_str!("../../stdlib/process.sorel")),
+        _ => bail!("{} is not a standard library module", specifier),
+    }
+}
+
+impl ImportTree {
+    fn import(&mut self, importer_dir: &PathBuf, specifier: &str, is_entrypoint: bool) -> Result<Rc<IRModule>> {
+        Ok(if specifier.starts_with("std:") {
+            if self.all_modules.contains_key(specifier) {
+                let module = self.all_modules.get(specifier).unwrap().clone();
+                return Ok(module);
+            }
+            let contents = std_import(specifier)?;
+            let parsed = &Module::parse(tokenize(&contents)?, is_entrypoint)?;
+            let module = self.generate_internal(None, Some(specifier.to_string()), parsed);
+            let module = Rc::new(module);
+            self.all_modules.insert(specifier.to_string(), module.clone());
+            module
+        } else {
+            let mut path = PathBuf::from(specifier);
+            if path.is_relative() {
+                let mut new_path = importer_dir.clone();
+                new_path.push(path);
+                path = new_path.canonicalize()?;
+            }
+            let path_key = path.to_string_lossy().to_string();
+            if self.all_modules.contains_key(&path_key) {
+                let module = self.all_modules.get(&path_key).unwrap().clone();
+                return Ok(module);
+            }
+
+            let contents = &std::fs::read_to_string(&path)?;
+            let tokens = tokenize(&contents)?;
+            let parsed = &Module::parse(tokens, is_entrypoint)?;
+
+            let module = self.generate_internal(Some(path), None, parsed);
+            let module = Rc::new(module);
+            self.all_modules.insert(path_key, module.clone());
+            if is_entrypoint {
+                self.entrypoint = module.clone();
+            }
+            module
+        })
+
+    }
+
+    fn generate_internal(&mut self, path: Option<PathBuf>, std_specifier: Option<String>, module: &Module) -> IRModule {
+        // Eventually these will end up being sections in assembly
+        let mut text = vec![];
+        let mut data = vec![];
+
+        let mut imports = vec![];
+        if let Some(ref path) = path {
+            module.imports.iter().for_each(|imported| {
+                if let Some(parent_path) = path.parent() {
+                    match self.import(&parent_path.to_path_buf(), imported, false) {
+                        Ok(module) => {
+                            imports.push(module);
+                        },
+                        Err(msg) => {
+                            eprintln!("{}", msg);
+                        }
+                    }
+                } else {
+                }
+            });
+        } else {
+            // We're in a stdlib module, which can only import other stdlib
+            // modules.
+            module.imports.iter().for_each(|imported| {
+                match self.import(&PathBuf::new(), imported, false) {
+                    Ok(module) => {
+                        imports.push(module);
+                    },
+                    Err(msg) => {
+                        eprintln!("{}", msg);
+                    }
+                }
+            });
+        }
+        
+        let exports: Vec<_> = module.exports.iter().map(|s| {
+            self.all_exports.insert(s.to_string());
+            s.to_string()
+        }).collect();
+
+        let externs = module.externs.iter().map(|s| s.to_string()).collect();
+
+        text.push(module.words.iter().map(|def| {
+            let mut body = def.instructions.iter().map(|inst| {
+                let mapped_ir = match inst {
+                    Token::Word(word) => {
+                        match *word {
+                            "@" => IR::Load,
+                            "@:8" => IR::Load8,
+                            "@:16" => IR::Load16,
+                            "@:32" => IR::Load32,
+                            "!" => IR::Store,
+                            "!:8" => IR::Store8,
+                            "!:16" => IR::Store16,
+                            "!:32" => IR::Store32,
+                            "dup" => IR::Dup,
+                            "swap" => IR::Swap,
+                            "drop" => IR::Drop,
+                            "over" => IR::Over,
+                            "rot" => IR::Rot,
+                            "sp" => IR::StackPointer,
+                            "if" => IR::If,
+                            "else" => IR::Else,
+                            "endif" => IR::EndIf,
+                            "loop" => IR::Loop,
+                            "endloop" => IR::EndLoop,
+                            "=" => IR::Equals,
+                            ">" => IR::GreaterThan,
+                            "<" => IR::LessThan,
+                            "+" => IR::AddU64,
+                            "-" => IR::SubtractU64,
+                            "*" => IR::MultiplyU64,
+                            "/" => IR::DivideU64,
+                            "%" => IR::ModU64,
+                            "|" => IR::BitwiseOr,
+                            "sys0" => IR::Sys0,
+                            "sys1" => IR::Sys1,
+                            "sys2" => IR::Sys2,
+                            "sys3" => IR::Sys3,
+                            "sys4" => IR::Sys4,
+                            "sys5" => IR::Sys5,
+                            "sys6" => IR::Sys6,
+                            // TODO num type specfic math like `+:i32`, etc.
+                            _ =>  IR::Call(String::from(*word))
+                        }
+                    },
+                    Token::String(text) => {
+                        let string_label = format!("string_{}", data.len());
+                        data.push(IR::StringDef(string_label.clone(), String::from(*text)));
+                        IR::StackPushString(string_label)
+                    },
+                    Token::NumU8(num) => push_num!(num),
+                    Token::NumI8(num) => push_num!(num, u8),
+                    Token::NumU16(num) => push_num!(num),
+                    Token::NumI16(num) => push_num!(num, u16),
+                    Token::NumU32(num) => push_num!(num),
+                    Token::NumI32(num) => push_num!(num, u32),
+                    Token::NumU64(num) => push_num!(num),
+                    Token::NumI64(num) => push_num!(num),
+                    Token::NumF32(num) => push_num!(num),
+                    Token::NumF64(num) => push_num!(num),
+                };
+                mapped_ir
+            }).collect::<Vec<_>>();
+
+            let mut result = vec![IR::Label(def.name.to_string())];
+            result.append(&mut body);
+            result.push(IR::Ret);
+            result
+        }).flatten().collect::<Vec<_>>());
+
+        let number = self.module_count;
+        self.module_count += 1;
+
+        IRModule {
+            text: text.into_iter().flatten().collect::<Vec<_>>(),
+            data,
+            imports,
+            exports,
+            externs,
+            source_file: path,
+            std_specifier, 
+            number,
+        }
+    }
+
+    fn collapse(&mut self, module: Rc<IRModule>) -> Result<()> {
+        let seen_key = if let Some(source_file) = &module.source_file {
+            source_file.to_string_lossy().to_string()
+        } else {
+            module.std_specifier.clone().unwrap()
+        };
+        if self.collapse_seen.contains(&seen_key) {
+            return Ok(())
+        }
+
+        for imported in module.imports.clone() {
+            self.collapse(imported)?;
+        }
+
+        let is_entrypoint = module.source_file == self.entrypoint.source_file;
+        
+        let module_number = module.number;
+
+        for string in &module.data {
+            if let IR::StringDef(name, val) = string {
+                let new_name = format!("{}_{}", name, module_number);
+                self.data.push(IR::StringDef(new_name, val.clone()));
+            } else {
+                bail!("non-string data");
+            }
+        }
+
+        for instruction in &module.text {
+            let new_instruction = match instruction {
+                IR::StackPushString(name) => {
+                    let new_name = format!("{}_{}", name, module_number);
+                    IR::StackPushString(new_name)
+                },
+                IR::Label(name) => {
+                    if is_entrypoint && name == "main" {
+                        instruction.clone()
+                    } else {
+                        IR::Label(module.get_label(name))
+                    }
+                },
+                IR::Call(name) => {
+                    IR::Call(module.get_label_for_call(name))
+                },
+                _ => instruction.clone()
+            };
+            self.text.push(new_instruction);
+        }
+
+        self.collapse_seen.insert(seen_key);
+
+        Ok(())
+    }
+}
+
+pub fn compile(path: &str) -> Result<IRObject> {
+    let dir = std::env::current_dir()?;
+    let mut tree: ImportTree = Default::default(); 
+    let module = tree.import(&dir, path, true)?;
+    tree.collapse(module)?;
+    // TODO remove unused words 
+    Ok(IRObject {
+        data: tree.data,
+        text: tree.text,
+    })
+}
diff --git a/sorelc/src/main.rs b/sorelc/src/main.rs
new file mode 100644 (file)
index 0000000..724df84
--- /dev/null
@@ -0,0 +1,21 @@
+mod tokenizer;
+mod parser;
+mod ir;
+mod riscv_asm_codegen;
+
+use anyhow::Result;
+
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() -> Result<()> {
+    let filename = std::env::args().nth(1).expect("must provide a file to compile");
+    let module = ir::compile(&filename)?;
+    let mut generator = riscv_asm_codegen::CodeGen::new(&module, 4096);
+    let mut asm_path = PathBuf::from(filename);
+    asm_path.set_extension("asm");
+    let mut output = File::create(asm_path)?;
+    write!(output, "{}\n", generator.assembly()?)?;
+    Ok(())
+}
diff --git a/sorelc/src/parser.rs b/sorelc/src/parser.rs
new file mode 100644 (file)
index 0000000..94b53e4
--- /dev/null
@@ -0,0 +1,140 @@
+use crate::tokenizer::Token;
+use anyhow::{Result, bail};
+
+#[derive(Debug)]
+pub struct WordDefinition<'a> {
+    pub name: &'a str,
+    pub instructions: Vec<Token<'a>>,
+}
+
+#[derive(Debug)]
+pub struct Module<'a> {
+    pub words: Vec<WordDefinition<'a>>,
+    pub imports: Vec<&'a str>,
+    pub exports: Vec<&'a str>,
+    pub externs: Vec<&'a str>,
+}
+
+impl<'a> Module<'a> {
+    pub fn parse(input: Vec<Token<'a>>, is_entrypoint: bool) -> Result<Self> {
+        let mut result = vec![];
+        let mut main = vec![];
+        let mut exports = vec![];
+        let mut imports = vec![];
+        let mut externs = vec![];
+        let mut current_word: Option<WordDefinition> = None;
+        let mut about_to_start_word_def = false;
+        let mut last_was_import = false;
+        let mut last_was_export = false;
+        let mut last_was_extern = false;
+
+        for token in input {
+            if about_to_start_word_def {
+                if let Token::Word(name) = token {
+                    current_word = Some(WordDefinition {
+                        name,
+                        instructions: vec![],
+                    });
+                    about_to_start_word_def = false;
+                    continue;
+                } else {
+                    bail!("{:?} is not a valid word name!", token);
+                }
+            } else if let Token::Word(word) = token {
+                if word == ":" {
+                    if current_word.is_some() {
+                        bail!("can't define words inside word definitions!");
+                    }
+                    about_to_start_word_def = true;
+                    continue;
+                }
+                if word == ";" {
+                    let word = current_word.take();
+                    if let Some(word) = word {
+                        result.push(word);
+                        continue;
+                    } else {
+                        bail!("`;` must be at the end of a word definition");
+                    }
+                }
+            }
+            if let Some(ref mut current_word) = current_word {
+                current_word.instructions.push(token);
+            } else {
+                match token {
+                    Token::Word(word) => {
+                        if word == "import" {
+                            last_was_import = true;
+                        } else if word == "export" {
+                            last_was_export = true;
+                        } else if word == "extern" {
+                            last_was_extern = true;
+                        } else {
+                            if last_was_export {
+                                exports.push(word);
+                                last_was_export = false;
+                            } else if last_was_extern {
+                                externs.push(word);
+                                last_was_extern = false;
+                            } else {
+                                main.push(token.clone());
+                            }
+                        }
+                    },
+                    Token::String(string) => {
+                        if last_was_import {
+                            imports.push(string);
+                            last_was_import = false;
+                        } else {
+                            main.push(token.clone());
+                        }
+                    },
+                    _ => {
+                        main.push(token.clone());
+                    }
+                };
+            }
+        }
+
+        if about_to_start_word_def || current_word.is_some() {
+            bail!("unfinished word definition!");
+        }
+
+        if is_entrypoint {
+            result.push(WordDefinition {
+                name: "main",
+                instructions: main,
+            });
+        }
+        
+        Ok(Module { words: result, imports, exports, externs })
+    }
+
+    #[cfg(test)]
+    pub fn debug_print(&self) {
+        for word in &self.words {
+            println!("{}", word.name);
+            for instruction in &word.instructions {
+                println!("    {:?}", instruction);
+            }
+        }
+    }
+}
+
+
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn try_some_parsing() {
+        let result = Module::parse(crate::tokenizer::tokenize("
+: hello world 16 \"planet\" ;
+: soup chicken 4.5 hello ;
+
+hello soup
+").unwrap(), true).unwrap();
+        result.debug_print();
+    }
+}
diff --git a/sorelc/src/riscv_asm_codegen.rs b/sorelc/src/riscv_asm_codegen.rs
new file mode 100644 (file)
index 0000000..c382cdf
--- /dev/null
@@ -0,0 +1,361 @@
+use sorel_ir::*;
+
+use anyhow::*;
+
+use std::collections::{HashMap, HashSet};
+use std::fmt::Display;
+
+pub struct CodeGen<'a> {
+    module: &'a IRObject,
+    data_stack_size: usize,
+    lines: Vec<String>,
+}
+
+
+// Some inspiration
+// ================
+//
+// * https://github.com/aw/fiveforths/blob/master/docs/REFERENCE.md#registers-list
+//     * Except using sp as a more C ABI style stack pointer, and s2 for the data stack
+// 
+
+// Implementation Choices
+// ======================
+//
+// Data Stack pointer: s2
+// No return stack pointer (using C ABI, so sp, sorta)
+// Use t0, t1, t2 for temporary values in words
+// Data stack grows down
+
+
+macro_rules! asm_macro {
+    ($name:ident, $src:expr) => {
+        fn $name(&mut self) {
+            self.line($src);
+        }
+    };
+    ($name:ident, $src:expr, $arg0:ty) => {
+        fn $name(&mut self, val0: $arg0) {
+            self.line(format!($src, val0));
+        }
+    };
+    ($name:ident, $src:expr, $arg0:ty, $arg1:ty) => {
+        fn $name(&mut self, val0: $arg0, val1: $arg1) {
+            self.line(format!($src, val0, val1));
+        }
+    };
+}
+
+fn mangle(input: &str) -> String {
+    input
+        .replace("<", "_LT_")
+        .replace(">", "_GT_")
+        .replace("-", "___")
+}
+
+impl<'a> CodeGen<'a> {
+    pub fn new(ir_mod: &'a IRObject, data_stack_size: usize) -> Self {
+        Self {
+            module: ir_mod,
+            data_stack_size,
+            lines: vec![],
+        }
+    }
+
+    fn line<S: Display>(&mut self, line: S) {
+        self.lines.push(format!("    {}", line));
+
+    }
+
+    fn label<S: Display>(&mut self, line: S) {
+        self.lines.push(line.to_string());
+    }
+
+    asm_macro!(copy_top_stack_value_to, "ld   {}, 0(s2)", &str);
+    asm_macro!(copy_offset_stack_value_to, "ld   {}, {}*8(s2)", &str, isize);
+    asm_macro!(copy_to_top_of_stack, "sd {}, 0(s2)", &str);
+    asm_macro!(move_stack_ptr_by_cells, "addi s2, s2, {}*8", isize);
+
+    fn pop_to(&mut self, reg: &str) {
+        self.copy_top_stack_value_to(reg);
+        self.move_stack_ptr_by_cells(1);
+    }
+
+    fn pop_some_to(&mut self, regs: &str) {
+        let mut regs = regs.trim().split(" ").collect::<Vec<_>>();
+        regs.reverse();
+        let count = regs.len();
+        let mut index = 0;
+        for reg in regs {
+            self.copy_offset_stack_value_to(reg, index);
+            index += 1;
+        }
+        self.move_stack_ptr_by_cells(count as isize);
+    }
+
+    fn push_from(&mut self, reg: &str) {
+        self.move_stack_ptr_by_cells(-1);
+        self.copy_to_top_of_stack(reg);
+    }
+
+    fn pop_call_push(&mut self, regs: &str, call: &str, reg: &str) {
+        self.pop_some_to(regs);
+        self.line(call);
+        self.push_from(reg);
+    }
+
+    pub fn assembly(&mut self) -> Result<String>{
+        let mut string_table = HashMap::new();
+
+        // Static strings
+        self.label(".section .rodata\n");
+        self.label(".align 3\n");
+        for ir in &self.module.data {
+            match ir {
+                IR::StringDef(string_label, some_string) => {
+                    string_table.insert(some_string.clone(), string_label);
+                    self.label(format!("{}:", string_label));
+                    self.line(format!(".asciz \"{}\"", some_string)); // should this be .asciz?
+                    self.label("");
+                },
+                _ => bail!("Currently only string definitions are supported in the data section.")
+            }
+        }
+
+        // Data stack
+        self.label(".data\n");
+        self.label(".align 3\n");
+        self.label("data_stack:");
+        self.line(format!(".space {}", self.data_stack_size));
+        self.label(".globl data_stack_end\ndata_stack_end:\n");
+
+        // Code
+        self.label(".text\n");
+        self.label(".align 3\n");
+
+        let mut if_block_count = 0;
+        let mut if_stack = vec![];
+        let mut loop_count = 0;
+        let mut loop_stack = vec![];
+        let mut seen_else = HashSet::new();
+        let mut last_label = "";
+
+        for ir in &self.module.text {
+            match ir {
+                IR::Label(name) => {
+                    last_label = name;
+                    if name == "main" {
+                        self.label(".globl _start"); // TODO is globl necessary?
+                        self.label("_start:");
+                        self.line("la s2, data_stack_end # set initial data stack pointer");
+                    } else {
+                        let mangled = mangle(name);
+                        self.label(format!(".globl {}", mangled));
+                        self.label(format!("{}:", mangled));
+                    }
+                    self.line("addi sp, sp, -16 # allocate 16 bytes on stack"); // allocate 16 bytes on stack
+                    self.line("sd ra, 8(sp) # store return address on stack");   // store return address on stack
+                },
+                IR::Call(name) => {
+                    let mangled = mangle(name);
+                    self.label(format!("# call {}", mangled));
+                    self.line(format!("call {}", mangled));    
+                },
+                IR::Ret => {
+                    if last_label == "main" {
+                        self.label("# exit 0 syscall");
+                        self.line("li a7, 93");
+                        self.line("mv a0, x0");
+                        self.line("ecall");
+                    } else {
+                        self.line("ld ra, 8(sp)");  // load return address from stack
+                        self.line("addi sp, sp, 16"); // restore stack pointer
+                        self.line("ret");
+                    }
+                },
+                IR::Load8 => {
+                    self.label("# load 8");
+                    self.copy_top_stack_value_to("t0");
+                    self.line("lbu   t0, 0(t0)"); // deref pointer in t0 to t0
+                    self.copy_to_top_of_stack("t0");
+                },
+                IR::Load16 => {
+                    self.label("# load 16");
+                    self.copy_top_stack_value_to("t0");
+                    self.line("lhu   t0, 0(t0)"); // deref pointer in t0 to t0
+                    self.copy_to_top_of_stack("t0");
+                },
+                IR::Load32 => {
+                    self.label("# load 32");
+                    self.copy_top_stack_value_to("t0");
+                    self.line("lwu   t0, 0(t0)"); // deref pointer in t0 to t0
+                    self.copy_to_top_of_stack("t0");
+                },
+                IR::Load => {
+                    self.label("# load 64");
+                    self.copy_top_stack_value_to("t0");
+                    self.line("ld   t0, 0(t0)"); // deref pointer in t0 to t0
+                    self.copy_to_top_of_stack("t0");
+                },
+                IR::Store8 => { // ( x addr -- )
+                    self.pop_some_to("t0 t1");
+                    self.line("sb t0, 0(t1)"); // store x at addr 
+                },
+                IR::Store16 => { // ( x addr -- )
+                    self.pop_some_to("t0 t1");
+                    self.line("sh t0, 0(t1)"); // store x at addr 
+                },
+                IR::Store32 => { // ( x addr -- )
+                    self.pop_some_to("t0 t1");
+                    self.line("sw t0, 0(t1)"); // store x at addr 
+                },
+                IR::Store => { // ( x addr -- )
+                    self.pop_some_to("t0 t1");
+                    self.line("sd t0, 0(t1)"); // store x at addr 
+                },
+                IR::StackPush(num) => {
+                    self.label(format!("# stackpush {}", num));
+                    self.line(format!("li t0, {}", num));
+                    self.push_from("t0");
+                },
+                IR::StackPushString(name) => {
+                    self.label(format!("# stackpushstring {}", name));
+                    self.line(format!("la t0, {}", name));
+                    self.push_from("t0");
+                },
+                IR::AddU64 => {
+                    self.label("# add");
+                    self.pop_call_push("t0 t1", "add t0, t0, t1", "t0"); 
+                },
+                IR::SubtractU64 => {
+                    self.label("# sub");
+                    self.pop_call_push("t0 t1", "sub t0, t0, t1", "t0"); 
+                },
+                IR::MultiplyU64 => {
+                    self.pop_call_push("t0 t1", "mul t0, t0, t1", "t0"); 
+                },
+                IR::DivideU64 => {
+                    self.pop_call_push("t0 t1", "div t0, t0, t1", "t0"); 
+                },
+                IR::ModU64 => {
+                    self.pop_call_push("t0 t1", "rem t0, t0, t1", "t0"); 
+                },
+                IR::Dup => {
+                    self.label("# dup");
+                    self.copy_top_stack_value_to("t0");
+                    self.push_from("t0");
+                },
+                IR::Swap => {
+                    self.label("# swap");
+                    self.pop_some_to("t1 t0");
+                    self.push_from("t0");
+                    self.push_from("t1");
+                },
+                IR::Rot => {
+                    self.label("# rot");
+                    self.pop_some_to("t0 t1 t2");
+                    self.push_from("t1");
+                    self.push_from("t2");
+                    self.push_from("t0");
+                },
+                IR::StackPointer => {
+                    self.label("# sp");
+                    self.line("addi t0, s2, 0");
+                    self.push_from("t0");
+                },
+                IR::Drop => {
+                    self.label("# drop");
+                    self.move_stack_ptr_by_cells(1);
+                },
+                IR::Equals => {
+                    // Yes, this is the same as subtract, since we're treating 0 as true, and
+                    // others as false.
+                    self.pop_call_push("t0 t1", "sub t0, t0, t1", "t0"); 
+                },
+                IR::GreaterThan => {
+                    self.label("# >");
+                    self.pop_some_to("t0 t1");
+                    self.line("sgt  t0, t0, t1");
+                    self.line("seqz t0, t0"); // remember, 0 is true, others are false
+                    self.push_from("t0");
+                },
+                IR::LessThan => {
+                    self.label("# <");
+                    self.pop_some_to("t0 t1");
+                    self.line("slt  t0, t0, t1");
+                    self.line("seqz t0, t0"); // remember, 0 is true, others are false
+                    self.push_from("t0");
+                },
+                IR::BitwiseOr => {
+                    self.pop_call_push("t0 t1", "or t0, t0, t1", "t0"); 
+                },
+                IR::Sys0 => {
+                    self.pop_call_push("a7", "ecall", "a0");
+                },
+                IR::Sys1 => {
+                    self.pop_call_push("a0 a7", "ecall", "a0");
+                },
+                IR::Sys2 => {
+                    self.pop_call_push("a0 a1 a7", "ecall", "a0");
+                },
+                IR::Sys3 => {
+                    self.pop_call_push("a0 a1 a2 a7", "ecall", "a0");
+                },
+                IR::Sys4 => {
+                    self.pop_call_push("a0 a1 a2 a3 a7", "ecall", "a0");
+                },
+                IR::Sys5 => {
+                    self.pop_call_push("a0 a1 a2 a3 a4 a7", "ecall", "a0");
+                },
+                IR::Sys6 => {
+                    self.pop_call_push("a0 a1 a2 a3 a4 a5 a7", "ecall", "a0");
+                },
+                // https://cmput229.github.io/229-labs-RISCV/RISC-V-Examples_Public/03-Conditionals/03b-If_Else.html
+                IR::If => {
+                    self.label("# if");
+                    self.pop_to("t0");
+                    self.line(format!("bnez t0, _else_{}", if_block_count));
+                    if_stack.push(if_block_count);
+                    if_block_count += 1;
+                },
+                IR::Else => {
+                    self.label("# else");
+                    let if_counter = if_stack.last().unwrap().clone();
+                    self.line(format!("j _endif_{}", if_counter));
+                    self.label(format!("_else_{}:", if_counter));
+                    seen_else.insert(if_counter);
+                },
+                IR::EndIf => {
+                    self.label("# endif");
+                    let stack = &mut if_stack;
+                    let if_counter = stack.last().unwrap().clone();
+                    if !seen_else.contains(&if_counter) {
+                        self.label(format!("_else_{}:", if_counter));
+                    } else {
+                        self.label(format!("_endif_{}:", if_counter));
+                        seen_else.remove(&if_counter);
+                    }
+                    stack.pop();
+                },
+                IR::Loop => { // keep looping until is true/0
+                    self.label(format!("_loop_{}:", loop_count));
+                    self.pop_to("t0");
+                    self.line(format!("beqz t0, _endloop_{}", loop_count));
+                    loop_stack.push(loop_count);
+                    loop_count += 1;
+                },
+                IR::EndLoop => {
+                    let stack = &mut loop_stack;
+                    let loop_counter = stack.last().unwrap().clone();
+                    self.line(format!("j _loop_{}", loop_counter));
+                    self.label(format!("_endloop_{}:", loop_counter));
+                    stack.pop();
+                },
+                _ => bail!("not implemented yet: {:?}", ir),
+            }
+        }
+
+        Ok(self.lines.join("\n"))
+    }
+}
+
diff --git a/sorelc/src/tokenizer.rs b/sorelc/src/tokenizer.rs
new file mode 100644 (file)
index 0000000..d546c6b
--- /dev/null
@@ -0,0 +1,188 @@
+use anyhow::{Result, anyhow};
+
+#[derive(Debug, Clone)]
+pub enum Token<'a> {
+    Word(&'a str),
+    String(&'a str),
+    NumU8(u8),
+    NumI8(i8),
+    NumU16(u16),
+    NumI16(i16),
+    NumU32(u32),
+    NumI32(i32),
+    NumU64(u64),
+    NumI64(i64),
+    NumF32(f32),
+    NumF64(f64),
+}
+
+impl<'a> Token<'a>{
+    fn parse_word_or_num(input: &'a str) -> Result<Token<'a>> {
+        if input == "-" {
+            return Ok(Token::Word(input))
+        }
+        
+        // we're assuming any token starting with `-` with length greater than one
+        // is a negative number
+        if input.starts_with('-') || input.chars().nth(0).map(|x| x.is_numeric()).unwrap_or(false) {
+            if input.contains(':') {
+                let mut splat = input.split(':');
+                let num = splat.next().ok_or(anyhow!("no number found"))?;
+                let typ = splat.next().ok_or(anyhow!("no number type found"))?;
+                match typ {
+                    "u8" => Ok(Token::NumU8(num.parse()?)),
+                    "i8" => Ok(Token::NumI8(num.parse()?)),
+                    "u16" => Ok(Token::NumU16(num.parse()?)),
+                    "i16" => Ok(Token::NumI16(num.parse()?)),
+                    "u32" => Ok(Token::NumU32(num.parse()?)),
+                    "i32" => Ok(Token::NumI32(num.parse()?)),
+                    "u64" => Ok(Token::NumU64(num.parse()?)),
+                    "i64" => Ok(Token::NumI64(num.parse()?)),
+                    "f32" => Ok(Token::NumF32(num.parse()?)),
+                    "f64" => Ok(Token::NumF64(num.parse()?)),
+                    _ => panic!("unknown number type")
+                }
+            } else {
+                if input.contains('.') {
+                    Ok(Token::NumF64(input.parse()?))
+                } else if input.starts_with('-') {
+                    Ok(Token::NumI64(input.parse()?))
+                } else {
+                    Ok(Token::NumU64(input.parse()?))
+                }
+            }
+        } else {
+            Ok(Token::Word(input))
+        }
+    }
+}
+
+// TODO really want an iterator, not a vector
+pub fn tokenize<'a>(input: &'a str) -> Result<Vec<Token<'a>>> {
+    let mut result = vec![];
+    let mut string_start: Option<usize> = None;
+    let mut word_or_num_start: Option<usize> = None;
+    let mut last_is_backslash = false;
+    let mut last_is_whitespace = true;
+    let mut in_doc_comment = false;
+    let mut in_line_comment = false;
+    let mut index = 0;
+    let mut first_char = true;
+
+
+    for char in input.chars() {
+        if first_char {
+            first_char = false;
+        } else {
+            index += 1;
+        }
+        
+        if in_doc_comment {
+            if char == ')' {
+                in_doc_comment = false;
+                last_is_whitespace = true; // not really true, but means don't need space after
+            }
+            continue;
+        }
+
+        if in_line_comment {
+            word_or_num_start = None;
+            if char == '\n' {
+                in_line_comment = false;
+                last_is_whitespace = true; // not really true, but means don't need space after
+            }
+            continue;
+        }
+
+        if char == '"' {
+            if let Some(start) = string_start {
+                if !last_is_backslash {
+                    result.push(Token::String(&input[start..index]));
+                    string_start = None;
+                }
+            } else {
+                string_start = Some(index + 1)                
+            }
+            last_is_backslash = false;
+            last_is_whitespace = false;
+            continue;
+        }
+
+
+        if string_start.is_some() {
+            last_is_backslash = char == '\\';
+            continue;
+        }
+
+        if char.is_whitespace() {
+            if last_is_backslash {
+                in_line_comment = true;                
+            } else if !last_is_whitespace && let Some(start) = word_or_num_start {
+                let token = &input[start..index];
+                if token == "(" {
+                    in_doc_comment = true;
+                } else {
+                    result.push(Token::parse_word_or_num(&input[start..index])?);
+                }
+                word_or_num_start = None;
+            }
+            last_is_whitespace = true;
+            last_is_backslash = false;
+            continue;
+        }
+
+        last_is_backslash = char == '\\';
+
+        if index == input.len() - 1 {
+            if !last_is_whitespace && let Some(start) = word_or_num_start {
+                result.push(Token::parse_word_or_num(&input[start..])?);
+            }
+            continue;
+        }
+
+        if last_is_whitespace { // start of word or num (we already handled strings)
+            word_or_num_start = Some(index);
+            last_is_whitespace = false;
+        }
+    }
+    Ok(result)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn try_some_tokenizing() {
+        let result = tokenize("
+
+        \\ soup
+            2 3.4 - -88 bacon \"hello\" 43:f32 2345:u32 -57:i8 soup
+");
+        println!("result: {:?}", result);
+    }
+
+    #[test]
+    fn comments() {
+        let result = tokenize("
+            (
+                foo
+                bar
+            )
+            : baz ( x y -- z )
+              chicken
+              soup
+            ;
+        ");
+        println!("result: {:?}", result);
+    }
+
+    #[test]
+    fn strings() {
+        let result = tokenize("
+dup \\ ( stuff )
+\"hello!\"
+");
+        println!("result: {:?}", result);
+    }
+}
diff --git a/stdlib/mem.sorel b/stdlib/mem.sorel
new file mode 100644 (file)
index 0000000..5ed53c8
--- /dev/null
@@ -0,0 +1,74 @@
+\ vim: filetype=forth
+
+: mmap 222 sys6 ;
+: munmap 215 sys2 ;
+
+: PROT_READ 1 ;
+: PROT_WRITE 2 ;
+: MAP_PRIVATE 2 ;
+: MAP_ANONYMOUS 32 ;
+
+: ALLOC_PROT PROT_READ PROT_WRITE | ;
+: ALLOC_MAP MAP_PRIVATE MAP_ANONYMOUS | ;
+
+\ This is a raw way to allocate memory. Callers to this will need to know
+\ how much memory was allocated, to call dealloc later.
+: alloc_raw ( byte-count -- addr )
+  0 swap ALLOC_PROT ALLOC_MAP -1:i32 0 mmap
+;
+
+: dealloc ( addr byte-count -- )
+  munmap
+;
+
+export alloc
+: alloc ( byte-count -- addr )
+  dup \ we'll need the length again later
+  8 + \ add 64 bits to the front where we'll store the length
+  alloc_raw \ ( byte-count addr )
+  dup \ ( byte-count addr addr)
+  rot \ ( addr addr byte-count )
+  rot \ ( addr byte-count addr )
+  ! \ write the length to the addr, now ( addr )
+  8 + \ here's the address we'll return, 8 bytes forward to account for the length
+;
+
+export free
+: free ( addr -- )
+  8 - \ the real memory start is 8 bytes before
+  dup \ ( addr addr )
+  @ \ ( addr byte-length )
+  8 + \ add 8 to the byte length to refer to the whole region from alloc.
+  dealloc
+; 
+
+import "std:out"
+
+: copy_byte ( addr_from addr_to -- addr_from addr_to )
+  swap \ ( addr_to addr_from )
+  dup \ ( addr_to addr_from addr_from )
+  @:8 \ ( addr_to addr_from byte )
+  rot \ ( addr_from byte addr_to )
+  dup \ ( addr_from byte addr_to addr_to)
+  rot \ ( addr_from addr_to addr_to byte )
+  swap \ ( addr_from addr_to byte addr_to )
+  !:8 \ ( addr_from addr_to )
+;
+
+export memcopy
+: memcopy ( addr_from addr_to count -- )
+  dup \ ( addr_from addr_to count count )
+  loop \ ( addr_from addr_to count )
+    rot \ ( addr_to count addr_from )
+    rot \ ( count addr_from addr_to )
+    copy_byte \ ( count addr_from addr_to )
+    1 + \ ( count addr_from addr_to+1 )
+    rot \ ( addr_from addr_to+1 count )
+    1 - \ ( addr_from addr_to+1 count-1 )
+    rot \ ( addr_to+1 count-1 addr_from )
+    1 + \ ( addr_to+1 count-1 addr_from+1 )
+    rot rot \ ( addr_from+1 addr_to+1 count-1 )
+    dup \ ( addr_from+1 addr_to+1 count-1 count-1)
+  endloop \ ( addr_from+count addr_to+count 0 )
+  drop drop drop \ ( )
+;
diff --git a/stdlib/out.sorel b/stdlib/out.sorel
new file mode 100644 (file)
index 0000000..5704f99
--- /dev/null
@@ -0,0 +1,59 @@
+\ vim: filetype=forth
+
+: write ( fd ptr len -- bytes-written-or-err )
+  64
+  sys3
+;
+
+import "std:string"
+
+export puts
+
+: puts ( addr -- addr)
+  dup dup \ ( addr addr addr )
+  strlen \ ( addr addr len )
+  1 \ ( addr addr len 1 )
+  rot \ ( addr len 1 addr )
+  rot \ ( addr 1 addr len )
+  write \ ( addr bytes-written-or-err )
+  drop \ ( addr )
+;
+
+: ZERO_CHAR 48 ;
+: NEWLINE_CHAR 10 ;
+
+export putn
+
+: putn ( num -- num )
+  dup dup \ ( num num num )
+  10 rot rot \ ( num 10 num num ) // Mutltidigit stop point
+  10 < \ ( num 10 num is<10 )
+  loop \ ( num 10 num' )
+    dup \ ( num 10 num' num' )
+    10 % \ ( num 10 num' digit )
+    swap \ ( num 10 digit num' )
+    10 / \ ( num 10 digit num'/10 )
+    dup 10 < \ ( num 10 digit num'/10 is<10 )
+  endloop \ ( num 10 digitn ... digit1 )
+  dup 9 > \ ( num 10 digitn ... digit1 digit1>9 )
+  loop \ ( num 10 digitn ... digit1 )
+    ZERO_CHAR + \ \ ( num 10 digitn ... digit1 )
+    sp \ ( num 10 digitn ... digit1 ptr )
+    1 \ ( num 10 digitn ... digit1 ptr 1 )
+    swap \ ( num 10 digitn ... digit1 1 ptr )
+    1 \ ( num 10 digitn ... digit1 1 ptr 1 )
+    write \ ( num 10 digitn ... digit1 result )
+    drop drop \ ( num 10 digitn ... digit2 )
+    dup 9 > \ ( num 10 digitn ... digit2 digit2>9 )
+  endloop \ ( num 10 )
+  drop \ ( num )
+  NEWLINE_CHAR \ ( num 10 )
+  sp \ ( num 10 ptr )
+  1 \ ( num 10 ptr 1 )
+  swap \ (num 10 1 ptr )
+  1 \ ( num 10 1 ptr 1 )
+  write \ ( num 10 result )
+  drop \ ( num 10 )
+  drop \ ( num )
+;
+
diff --git a/stdlib/process.sorel b/stdlib/process.sorel
new file mode 100644 (file)
index 0000000..73a7008
--- /dev/null
@@ -0,0 +1,8 @@
+\ vim: filetype=forth
+
+export exit
+
+: exit ( code -- )
+  93
+  sys1
+;
diff --git a/stdlib/string.sorel b/stdlib/string.sorel
new file mode 100644 (file)
index 0000000..b0c76a8
--- /dev/null
@@ -0,0 +1,15 @@
+\ vim: filetype=forth
+
+: strlen ( addr -- len )
+  dup dup \ ( addr addr addr )
+  @:8 \ ( addr addr byte )
+  loop \ ( addr addr )
+    1 + \ ( addr addr+1 )
+    dup \ ( addr addr+1 addr+1)
+    @:8 \ ( addr addr+1 byte )
+  endloop \ ( addr addr+len )
+  swap \ ( addr+len addr )
+  - \ ( len )
+;
+
+export strlen
diff --git a/target/rust-analyzer/flycheck0/stderr b/target/rust-analyzer/flycheck0/stderr
deleted file mode 100644 (file)
index f244f75..0000000
+++ /dev/null
@@ -1 +0,0 @@
-    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.03s
diff --git a/target/rust-analyzer/flycheck0/stdout b/target/rust-analyzer/flycheck0/stdout
deleted file mode 100644 (file)
index 8e625da..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.103","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.103/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.103/build.rs","edition":"2021","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":0,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","proc-macro"],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/build/proc-macro2-bdf9ed00dad30299/build-script-build"],"executable":null,"fresh":true}
-{"reason":"build-script-executed","package_id":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.103","linked_libs":[],"linked_paths":[],"cfgs":["wrap_proc_macro","proc_macro_span_location","proc_macro_span_file"],"env":[],"out_dir":"/home/bengl/rethought/hylo-lang/target/debug/build/proc-macro2-01b6d2c5fdefd348/out"}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.22","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-ident-1.0.22/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"unicode_ident","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unicode-ident-1.0.22/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":0,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libunicode_ident-4fec18d14b15363e.rlib","/home/bengl/rethought/hylo-lang/target/debug/deps/libunicode_ident-4fec18d14b15363e.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.42","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/quote-1.0.42/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/quote-1.0.42/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":0,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","proc-macro"],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/build/quote-03d3df1af8178ab4/build-script-build"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.103","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.103/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"proc_macro2","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/proc-macro2-1.0.103/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":0,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","proc-macro"],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libproc_macro2-696ff2d61f065b42.rlib","/home/bengl/rethought/hylo-lang/target/debug/deps/libproc_macro2-696ff2d61f065b42.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_core-1.0.228/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_core-1.0.228/build.rs","edition":"2021","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":0,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["result","std"],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/build/serde_core-31b971cf163f0f6c/build-script-build"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde-1.0.228/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde-1.0.228/build.rs","edition":"2021","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":0,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","derive","serde_derive","std"],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/build/serde-49a10a9683562367/build-script-build"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.16.1","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.16.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"hashbrown","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.16.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libhashbrown-3d3c587f4038bc13.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#equivalent@1.0.2","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/equivalent-1.0.2/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"equivalent","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/equivalent-1.0.2/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libequivalent-9b88d636aa69c8ed.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#unsafe-libyaml@0.2.11","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unsafe-libyaml-0.2.11/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"unsafe_libyaml","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/unsafe-libyaml-0.2.11/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libunsafe_libyaml-5d3cabcbf60870d4.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.15","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/itoa-1.0.15/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"itoa","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/itoa-1.0.15/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libitoa-bc1ca429a31fb99c.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#ryu@1.0.20","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.20/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"ryu","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.20/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libryu-e3abc3dffcbd16d9.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#syscalls@0.7.0","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syscalls-0.7.0/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syscalls-0.7.0/build.rs","edition":"2021","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":0,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","serde","serde_repr","std"],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/build/syscalls-fd98ea5acf3ec59b/build-script-build"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#anyhow@1.0.100","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/anyhow-1.0.100/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/anyhow-1.0.100/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":0,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/build/anyhow-f0f8ac34947eb6de/build-script-build"],"executable":null,"fresh":true}
-{"reason":"build-script-executed","package_id":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.42","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/bengl/rethought/hylo-lang/target/debug/build/quote-4fccd938ab7a0173/out"}
-{"reason":"build-script-executed","package_id":"registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/bengl/rethought/hylo-lang/target/debug/build/serde_core-7b7cb0cfdf46fc20/out"}
-{"reason":"build-script-executed","package_id":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","linked_libs":[],"linked_paths":[],"cfgs":["if_docsrs_then_no_serde_core"],"env":[],"out_dir":"/home/bengl/rethought/hylo-lang/target/debug/build/serde-0c79bc1bb5bf9eba/out"}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#indexmap@2.12.1","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.12.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"indexmap","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/indexmap-2.12.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libindexmap-274ebdc57a13914e.rmeta"],"executable":null,"fresh":true}
-{"reason":"build-script-executed","package_id":"registry+https://github.com/rust-lang/crates.io-index#anyhow@1.0.100","linked_libs":[],"linked_paths":[],"cfgs":["std_backtrace"],"env":[],"out_dir":"/home/bengl/rethought/hylo-lang/target/debug/build/anyhow-7dffd08ca73f1c01/out"}
-{"reason":"build-script-executed","package_id":"registry+https://github.com/rust-lang/crates.io-index#syscalls@0.7.0","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/bengl/rethought/hylo-lang/target/debug/build/syscalls-45c42af557e79235/out"}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.42","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/quote-1.0.42/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"quote","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/quote-1.0.42/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":0,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","proc-macro"],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libquote-49a93d0cff3d7298.rlib","/home/bengl/rethought/hylo-lang/target/debug/deps/libquote-49a93d0cff3d7298.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_core-1.0.228/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"serde_core","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_core-1.0.228/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["result","std"],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libserde_core-a2045ddd2bbb2331.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#anyhow@1.0.100","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/anyhow-1.0.100/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"anyhow","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/anyhow-1.0.100/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libanyhow-a8e29080dfa88f6a.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.111","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.111/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"syn","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.111/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":0,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["clone-impls","default","derive","parsing","printing","proc-macro"],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libsyn-d395eb58cbb9ffa6.rlib","/home/bengl/rethought/hylo-lang/target/debug/deps/libsyn-d395eb58cbb9ffa6.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_derive-1.0.228/Cargo.toml","target":{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"serde_derive","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_derive-1.0.228/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":0,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libserde_derive-dca8d717b71cd876.so"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#serde_repr@0.1.20","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_repr-0.1.20/Cargo.toml","target":{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"serde_repr","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_repr-0.1.20/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":0,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libserde_repr-1a41abf411086f19.so"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde-1.0.228/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"serde","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde-1.0.228/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","derive","serde_derive","std"],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libserde-a90341a4e2031dae.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#serde_yaml@0.9.34+deprecated","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_yaml-0.9.34+deprecated/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"serde_yaml","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_yaml-0.9.34+deprecated/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libserde_yaml-22ed54ad12da1e59.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"registry+https://github.com/rust-lang/crates.io-index#syscalls@0.7.0","manifest_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syscalls-0.7.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"syscalls","src_path":"/home/bengl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syscalls-0.7.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","serde","serde_repr","std"],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libsyscalls-7a60ab2d0d4883d8.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"path+file:///home/bengl/rethought/hylo-lang/hylo-ir#0.1.0","manifest_path":"/home/bengl/rethought/hylo-lang/hylo-ir/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"hylo_ir","src_path":"/home/bengl/rethought/hylo-lang/hylo-ir/src/lib.rs","edition":"2024","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libhylo_ir-4b38e4623b5ebbc5.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"path+file:///home/bengl/rethought/hylo-lang/hylo-ir#0.1.0","manifest_path":"/home/bengl/rethought/hylo-lang/hylo-ir/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"hylo_ir","src_path":"/home/bengl/rethought/hylo-lang/hylo-ir/src/lib.rs","edition":"2024","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":true},"features":[],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libhylo_ir-6637db13cb880a28.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-message","package_id":"path+file:///home/bengl/rethought/hylo-lang/hylo-interpret#0.1.0","manifest_path":"/home/bengl/rethought/hylo-lang/hylo-interpret/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"hylo_interpret","src_path":"/home/bengl/rethought/hylo-lang/hylo-interpret/src/lib.rs","edition":"2024","doc":true,"doctest":true,"test":true},"message":{"rendered":"warning: field `strings` is never read\n  --> hylo-interpret/src/lib.rs:14:5\n   |\n 8 | pub struct Interpreter<'a> {\n   |            ----------- field in this struct\n...\n14 |     strings: Vec<String>,\n   |     ^^^^^^^\n   |\n   = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default\n\n","$message_type":"diagnostic","children":[{"children":[],"code":null,"level":"note","message":"`#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default","rendered":null,"spans":[]}],"level":"warning","message":"field `strings` is never read","spans":[{"byte_end":119,"byte_start":108,"column_end":23,"column_start":12,"expansion":null,"file_name":"hylo-interpret/src/lib.rs","is_primary":false,"label":"field in this struct","line_end":8,"line_start":8,"suggested_replacement":null,"suggestion_applicability":null,"text":[{"highlight_end":23,"highlight_start":12,"text":"pub struct Interpreter<'a> {"}]},{"byte_end":287,"byte_start":280,"column_end":12,"column_start":5,"expansion":null,"file_name":"hylo-interpret/src/lib.rs","is_primary":true,"label":null,"line_end":14,"line_start":14,"suggested_replacement":null,"suggestion_applicability":null,"text":[{"highlight_end":12,"highlight_start":5,"text":"    strings: Vec<String>,"}]}],"code":{"code":"dead_code","explanation":null}}}
-{"reason":"compiler-artifact","package_id":"path+file:///home/bengl/rethought/hylo-lang/hylo-interpret#0.1.0","manifest_path":"/home/bengl/rethought/hylo-lang/hylo-interpret/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"hylo_interpret","src_path":"/home/bengl/rethought/hylo-lang/hylo-interpret/src/lib.rs","edition":"2024","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libhylo_interpret-3e4441b288f50835.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-message","package_id":"path+file:///home/bengl/rethought/hylo-lang/hylo-interpret#0.1.0","manifest_path":"/home/bengl/rethought/hylo-lang/hylo-interpret/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"hylo_interpret","src_path":"/home/bengl/rethought/hylo-lang/hylo-interpret/src/lib.rs","edition":"2024","doc":true,"doctest":true,"test":true},"message":{"rendered":"warning: field `strings` is never read\n  --> hylo-interpret/src/lib.rs:14:5\n   |\n 8 | pub struct Interpreter<'a> {\n   |            ----------- field in this struct\n...\n14 |     strings: Vec<String>,\n   |     ^^^^^^^\n   |\n   = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default\n\n","$message_type":"diagnostic","children":[{"children":[],"code":null,"level":"note","message":"`#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default","rendered":null,"spans":[]}],"level":"warning","message":"field `strings` is never read","spans":[{"byte_end":119,"byte_start":108,"column_end":23,"column_start":12,"expansion":null,"file_name":"hylo-interpret/src/lib.rs","is_primary":false,"label":"field in this struct","line_end":8,"line_start":8,"suggested_replacement":null,"suggestion_applicability":null,"text":[{"highlight_end":23,"highlight_start":12,"text":"pub struct Interpreter<'a> {"}]},{"byte_end":287,"byte_start":280,"column_end":12,"column_start":5,"expansion":null,"file_name":"hylo-interpret/src/lib.rs","is_primary":true,"label":null,"line_end":14,"line_start":14,"suggested_replacement":null,"suggestion_applicability":null,"text":[{"highlight_end":12,"highlight_start":5,"text":"    strings: Vec<String>,"}]}],"code":{"code":"dead_code","explanation":null}}}
-{"reason":"compiler-artifact","package_id":"path+file:///home/bengl/rethought/hylo-lang/hylo-interpret#0.1.0","manifest_path":"/home/bengl/rethought/hylo-lang/hylo-interpret/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"hylo_interpret","src_path":"/home/bengl/rethought/hylo-lang/hylo-interpret/src/lib.rs","edition":"2024","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":true},"features":[],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libhylo_interpret-8c8a8fc209c55986.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"path+file:///home/bengl/rethought/hylo-lang/hyloc#0.1.0","manifest_path":"/home/bengl/rethought/hylo-lang/hyloc/Cargo.toml","target":{"kind":["bin"],"crate_types":["bin"],"name":"hyloc","src_path":"/home/bengl/rethought/hylo-lang/hyloc/src/main.rs","edition":"2024","doc":true,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libhyloc-58f075f82bafc9dc.rmeta"],"executable":null,"fresh":true}
-{"reason":"compiler-artifact","package_id":"path+file:///home/bengl/rethought/hylo-lang/hyloc#0.1.0","manifest_path":"/home/bengl/rethought/hylo-lang/hyloc/Cargo.toml","target":{"kind":["bin"],"crate_types":["bin"],"name":"hyloc","src_path":"/home/bengl/rethought/hylo-lang/hyloc/src/main.rs","edition":"2024","doc":true,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":true},"features":[],"filenames":["/home/bengl/rethought/hylo-lang/target/debug/deps/libhyloc-4df3a47409d99128.rmeta"],"executable":null,"fresh":true}
-{"reason":"build-finished","success":true}
diff --git a/tests/assert.sorel b/tests/assert.sorel
new file mode 100644 (file)
index 0000000..9d84758
--- /dev/null
@@ -0,0 +1,13 @@
+\ vim: filetype=forth
+
+import "std:process"
+
+export assert
+
+: assert ( x -- )
+  if
+    drop
+  else
+    1 exit
+  endif
+;
diff --git a/tests/putstack.c b/tests/putstack.c
new file mode 100644 (file)
index 0000000..a5200c4
--- /dev/null
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+extern unsigned long data_stack_end;
+register unsigned long * stack_pointer asm("s2");
+
+void putstack() {
+  unsigned long * stack_index = &data_stack_end;
+  printf("stack: ");
+  while (stack_index != stack_pointer) {
+         printf("%ld ", *stack_index);
+    stack_index -= 1;
+  }
+       printf("%ld\n", *stack_pointer);
+}
+
+
diff --git a/tests/test.sh b/tests/test.sh
new file mode 100644 (file)
index 0000000..279addc
--- /dev/null
@@ -0,0 +1,11 @@
+UNAME=$(uname -m)
+CMD_PREFIX=$([ "$UNAME" = "riscv64" ] && echo "" || echo "riscv64-unknown-linux-gnu-")
+AS="${CMD_PREFIX}as"
+LD="${CMD_PREFIX}ld"
+CC="${CMD_PREFIX}cc"
+
+../target/debug/sorelc test1.sorel
+$AS -g -o test1.o test1.asm
+$CC -O1 -no-pie -o test1.out test1.o putstack.c -nostartfiles 
+# $LD -o test1.out test1.o
+./test1.out
diff --git a/tests/test1.sorel b/tests/test1.sorel
new file mode 100644 (file)
index 0000000..3256625
--- /dev/null
@@ -0,0 +1,36 @@
+\ vim: filetype=forth
+
+import "./assert.sorel"
+import "std:out"
+
+"Hello, World! \n" puts
+drop
+
+42 putn
+drop
+
+import "std:mem"
+
+64 alloc
+free
+
+0 assert
+
+import "std:string"
+import "std:mem"
+import "std:out"
+
+extern putstack
+
+16 alloc \ ( ptr )
+dup \ ( ptr ptr )
+"To be copied!" \ ( ptr ptr strptr )
+dup \ ( ptr ptr strptr strptr )
+strlen 1 + \ ( ptr ptr strptr len )
+rot \ ( ptr strptr len ptr )
+swap \ ( ptr strptr ptr len )
+memcopy \ ( ptr )
+puts \ ( ptr )
+free \ ( )
+
+