diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..480fc042
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "core-lib"]
+ path = core-lib
+ url = https://github.com/SOM-st/SOM.git
diff --git a/Cargo.lock b/Cargo.lock
index c0ff8bcd..fecbb7c7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -10,7 +10,7 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.28"
+version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -18,8 +18,8 @@ name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "hermit-abi 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -35,7 +35,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "clap"
-version = "2.33.0"
+version = "2.33.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -44,7 +44,7 @@ dependencies = [
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -57,10 +57,10 @@ dependencies = [
[[package]]
name = "hermit-abi"
-version = "0.1.12"
+version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -70,7 +70,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
-version = "0.2.69"
+version = "0.2.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -106,10 +106,10 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro-error-attr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
- "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -117,16 +117,16 @@ name = "proc-macro-error-attr"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
"syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro2"
-version = "1.0.12"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -134,10 +134,10 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.4"
+version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -148,7 +148,7 @@ version = "0.1.0"
name = "som-interpreter"
version = "0.1.0"
dependencies = [
- "anyhow 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
+ "anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)",
"num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"som-core 0.1.0",
"som-lexer 0.1.0",
@@ -185,7 +185,7 @@ name = "structopt"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt-derive 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -197,18 +197,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro-error 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
-version = "1.0.18"
+version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -217,9 +217,9 @@ name = "syn-mid"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -247,12 +247,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vec_map"
-version = "0.8.1"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "version_check"
-version = "0.9.1"
+version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -276,33 +276,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
-"checksum anyhow 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff"
+"checksum anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
-"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
+"checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
-"checksum hermit-abi 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4"
+"checksum hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-"checksum libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005"
+"checksum libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
"checksum num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
"checksum proc-macro-error 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678"
"checksum proc-macro-error-attr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53"
-"checksum proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319"
-"checksum quote 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7"
+"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
+"checksum quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum structopt 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "863246aaf5ddd0d6928dfeb1a9ca65f505599e4e1b399935ef7e75107516b4ef"
"checksum structopt-derive 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d239ca4b13aee7a2142e6795cbd69e457665ff8037aed33b3effdc430d2f927a"
-"checksum syn 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213"
+"checksum syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2"
"checksum syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
-"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
-"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
+"checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+"checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/README.md b/README.md
index 8c1cbc0f..ae7a0e89 100644
--- a/README.md
+++ b/README.md
@@ -17,3 +17,63 @@ Here is a rundown of these different crates (as of now, layout may change in the
| **`som-parser-symbols`** | A SOM parser that works with **`som-lexer`**'s output. |
[**Cargo workspace**]: https://doc.rust-lang.org/cargo/reference/workspaces.html
+
+How to build and run
+--------------------
+
+The interpreter is already usable, you can use it to start evaluating from a file or to have a Read-Eval-Print Loop to play around with the language.
+
+To compile the program, you'll need to have Rust installed on your machine.
+You can find the instructions on how to install on the [**official Rust language website**].
+We recommend using whatever is the latest stable toolchain (which was 1.44 at the time of this writing).
+
+[**official Rust language website**]: https://www.rust-lang.org/tools/install
+
+Once you have Rust installed, simply run:
+
+```bash
+# the '--release' flag indicates to build with optimizations enabled.
+# you can remove this flag if you wish to have more debug information in the emitted binary.
+cargo build --release
+```
+
+This will compile the project and take care of fetching and building dependencies for you.
+Once the build is finished, you should have a `target/` folder created in the current directory.
+You'll find the interpreter's binary at `target/release/som-interpreter`.
+
+To start the REPL, you can run:
+
+```bash
+# the '-c' flag instructs the interpreter where to find the SOM standard library.
+./target/release/som-interpreter -c core-lib/Smalltalk
+```
+
+You'll get a prompt in which you can type SOM expressions and see what they get evaluated to.
+The REPL makes the latest value successfully evaluated available via the `it` variable, so you can keep poking at the result of a previous expression, like this:
+
+```plain
+(0) SOM Shell | #(4 5 6)
+returned: #(4 5 6) (Array([Integer(4), Integer(5), Integer(6)]))
+(1) SOM Shell | it at: 2
+returned: 5 (Integer(5))
+(2) SOM Shell | it timesRepeat: [ 'hello from SOM !' println ]
+hello from SOM !
+hello from SOM !
+hello from SOM !
+hello from SOM !
+hello from SOM !
+returned: 5 (Integer(5))
+```
+
+To evaluate from a file, simply pass the file as another argument to the interpreter.
+But, since the '-c' accepts multiple files, you might need to add the '--' argument before that file, like so:
+
+```bash
+./target/release/som-interpreter -c core-lib/Smalltalk -- core-lib/Examples/Hello.som
+```
+
+For other purposes, you can use '-h' (or '--help') to print the complete help message:
+
+```bash
+./target/release/som-interpreter -h
+```
diff --git a/core-lib b/core-lib
new file mode 160000
index 00000000..67a3f58f
--- /dev/null
+++ b/core-lib
@@ -0,0 +1 @@
+Subproject commit 67a3f58f21ff3b458b1651ac7e99c0a555c92c31
diff --git a/som-interpreter/Cargo.toml b/som-interpreter/Cargo.toml
index 72997d6b..896dc386 100644
--- a/som-interpreter/Cargo.toml
+++ b/som-interpreter/Cargo.toml
@@ -17,12 +17,14 @@ path = "src/main.rs"
# internal
som-core = { path = "../som-core", version = "0.1.0" }
som-lexer = { path = "../som-lexer", version = "0.1.0" }
-som-parser = { package = "som-parser-symbols", path = "../som-parser-symbols" }
-# som-parser = { package = "som-parser-text", path = "../som-parser-text" }
+som-parser = { package = "som-parser-symbols", path = "../som-parser-symbols", version = "0.1.0" }
+# som-parser = { package = "som-parser-text", path = "../som-parser-text", version = "0.1.0" }
# CLI interface
structopt = "0.3.14"
# error handling
-anyhow = "1.0.28"
+anyhow = "1.0.31"
+
+# big integers
num-bigint = "0.2.6"
diff --git a/som-interpreter/src/block.rs b/som-interpreter/src/block.rs
index d1f781ae..5abd2e52 100644
--- a/som-interpreter/src/block.rs
+++ b/som-interpreter/src/block.rs
@@ -1,15 +1,17 @@
+use std::fmt;
+
use som_core::ast;
use crate::class::Class;
use crate::frame::Frame;
use crate::universe::Universe;
-use crate::{SOMRef, SOMWeakRef};
+use crate::SOMRef;
/// Represents an executable block.
-#[derive(Debug, Clone)]
+#[derive(Clone)]
pub struct Block {
/// Reference to the captured stack frame.
- pub frame: SOMWeakRef,
+ pub frame: SOMRef,
/// Block definition from the AST.
pub block: ast::Block,
}
@@ -30,3 +32,10 @@ impl Block {
self.block.parameters.len()
}
}
+
+impl fmt::Debug for Block {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct(&format!("Block{}", self.nb_parameters() + 1))
+ .finish()
+ }
+}
diff --git a/som-interpreter/src/class.rs b/som-interpreter/src/class.rs
index ec278543..5b454a68 100644
--- a/som-interpreter/src/class.rs
+++ b/som-interpreter/src/class.rs
@@ -13,7 +13,7 @@ use crate::{SOMRef, SOMWeakRef};
#[derive(Debug, Clone)]
pub enum MaybeWeak {
/// An owned reference.
- Owned(SOMRef),
+ Strong(SOMRef),
/// A weak reference.
Weak(SOMWeakRef),
}
@@ -26,16 +26,17 @@ pub struct Class {
/// The class of this class.
pub class: MaybeWeak,
/// The superclass of this class.
+ // TODO: Should probably be `Option>`.
pub super_class: SOMWeakRef,
/// The class' locals.
pub locals: HashMap,
/// The class' methods/invokables.
- pub methods: HashMap,
+ pub methods: HashMap>,
}
impl Class {
/// Load up a class from its class definition from the AST.
- pub fn from_class_def(defn: ClassDef) -> Self {
+ pub fn from_class_def(defn: ClassDef) -> SOMRef {
let static_locals = defn
.static_locals
.iter()
@@ -55,7 +56,7 @@ impl Class {
),
MethodBody::Body { .. } => Invokable::MethodDef(method.clone()),
};
- (signature, method)
+ (signature, Rc::new(method))
})
.collect();
@@ -78,25 +79,25 @@ impl Class {
),
MethodBody::Body { .. } => Invokable::MethodDef(method.clone()),
};
- (signature, method)
+ (signature, Rc::new(method))
})
.collect();
- let static_class = Self {
+ let static_class = Rc::new(RefCell::new(Self {
name: format!("{} class", defn.name),
class: MaybeWeak::Weak(Weak::new()),
super_class: Weak::new(),
locals: static_locals,
methods: static_methods,
- };
+ }));
- Self {
+ Rc::new(RefCell::new(Self {
name: defn.name,
- class: MaybeWeak::Owned(Rc::new(RefCell::new(static_class))),
+ class: MaybeWeak::Strong(static_class),
super_class: Weak::new(),
locals: instance_locals,
methods: instance_methods,
- }
+ }))
}
/// Get the class' name.
@@ -110,7 +111,7 @@ impl Class {
MaybeWeak::Weak(ref weak) => weak.upgrade().unwrap_or_else(|| {
panic!("superclass dropped, cannot upgrade ref ({})", self.name())
}),
- MaybeWeak::Owned(ref owned) => owned.clone(),
+ MaybeWeak::Strong(ref owned) => owned.clone(),
}
}
@@ -119,11 +120,14 @@ impl Class {
self.class = MaybeWeak::Weak(Rc::downgrade(class));
}
+ /// Set the class of this class (as a strong reference).
+ pub fn set_class_owned(&mut self, class: &SOMRef) {
+ self.class = MaybeWeak::Strong(class.clone());
+ }
+
/// Get the superclass of this class.
- pub fn super_class(&self) -> SOMRef {
- self.super_class
- .upgrade()
- .unwrap_or_else(|| panic!("superclass dropped, cannot upgrade ref ({})", self.name()))
+ pub fn super_class(&self) -> Option> {
+ self.super_class.upgrade()
}
/// Set the superclass of this class (as a weak reference).
@@ -132,9 +136,7 @@ impl Class {
}
/// Search for a given method within this class.
- pub fn lookup_method(&self, signature: impl AsRef) -> Option {
- // dbg!(self.signature.as_str());
- // let signature = dbg!(signature.as_ref());
+ pub fn lookup_method(&self, signature: impl AsRef) -> Option> {
let signature = signature.as_ref();
self.methods.get(signature).cloned().or_else(|| {
self.super_class
@@ -160,9 +162,9 @@ impl fmt::Debug for Class {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Class")
.field("name", &self.name)
- .field("locals", &self.locals)
- .field("class", &self.class)
- .field("super_class", &self.super_class)
+ .field("locals", &self.locals.keys())
+ // .field("class", &self.class)
+ // .field("super_class", &self.super_class)
.finish()
}
}
diff --git a/som-interpreter/src/evaluate.rs b/som-interpreter/src/evaluate.rs
index dc0d7581..3d06b7c2 100644
--- a/som-interpreter/src/evaluate.rs
+++ b/som-interpreter/src/evaluate.rs
@@ -30,8 +30,10 @@ impl Evaluate for ast::Expression {
let value = propagate!(expr.evaluate(universe));
universe
.assign_local(name, value.clone())
- .unwrap_or_else(|| panic!("variable '{}' not found", name));
- Return::Local(value)
+ .map(|_| Return::Local(value))
+ .unwrap_or_else(|| {
+ Return::Exception(format!("variable '{}' not found to assign to", name))
+ })
}
Self::BinaryOp(bin_op) => bin_op.evaluate(universe),
Self::Block(blk) => blk.evaluate(universe),
@@ -41,27 +43,11 @@ impl Evaluate for ast::Expression {
Return::NonLocal(value, frame)
}
Self::Literal(literal) => literal.evaluate(universe),
- Self::Reference(name) => Return::Local(
- (universe.lookup(name))
- .or_else(|| universe.lookup_global(name))
- .or_else(|| {
- let sym = dbg!(universe.intern_symbol(name));
- Value::System
- .lookup_method(universe, "unknownGlobal:")
- .map(|method| {
- match method
- .invoke(universe, vec![Value::System, Value::Symbol(sym)])
- {
- Return::Local(value) | Return::NonLocal(value, _) => value,
- Return::Exception(err) => panic!(
- "got exception from 'System>>#unknownGlobal:': {}",
- err
- ),
- }
- })
- })
- .unwrap_or_else(|| panic!("variable '{}' not found", name)),
- ),
+ Self::Reference(name) => (universe.lookup_local(name))
+ .or_else(|| universe.lookup_global(name))
+ .map(Return::Local)
+ .or_else(|| universe.unknown_global(name.as_str()))
+ .unwrap_or_else(|| Return::Exception(format!("variable '{}' not found", name))),
Self::Term(term) => term.evaluate(universe),
Self::Message(msg) => msg.evaluate(universe),
}
@@ -73,10 +59,21 @@ impl Evaluate for ast::BinaryOp {
let lhs = propagate!(self.lhs.evaluate(universe));
let rhs = propagate!(self.rhs.evaluate(universe));
+ // println!(
+ // "invoking {}>>#{}",
+ // lhs.class(universe).borrow().name(),
+ // self.op
+ // );
+
if let Some(invokable) = lhs.lookup_method(universe, &self.op) {
invokable.invoke(universe, vec![lhs, rhs])
} else {
- Return::Local(Value::Nil)
+ Return::Exception(format!(
+ "could not find method '{}>>#{}'",
+ lhs.class(universe).borrow().name(),
+ self.op
+ ))
+ // Return::Local(Value::Nil)
}
}
}
@@ -109,16 +106,36 @@ impl Evaluate for ast::Term {
impl Evaluate for ast::Block {
fn evaluate(&self, universe: &mut Universe) -> Return {
let frame = universe.current_frame();
- Return::Local(Value::Block(Block {
+ // TODO: avoid cloning the whole block's AST.
+ Return::Local(Value::Block(Rc::new(Block {
block: self.clone(),
- frame: Rc::downgrade(&frame),
- }))
+ frame: frame.clone(),
+ })))
}
}
impl Evaluate for ast::Message {
fn evaluate(&self, universe: &mut Universe) -> Return {
- let receiver = propagate!(self.receiver.evaluate(universe));
+ let (receiver, invokable) = match self.receiver.as_ref() {
+ ast::Expression::Reference(ident) if ident == "super" => {
+ let receiver = universe.current_frame().borrow().get_self();
+ let super_class = match receiver.class(universe).borrow().super_class() {
+ Some(class) => class,
+ None => {
+ return Return::Exception(
+ "`super` used without any superclass available".to_string(),
+ )
+ }
+ };
+ let invokable = super_class.borrow().lookup_method(&self.signature);
+ (receiver, invokable)
+ }
+ expr => {
+ let receiver = propagate!(expr.evaluate(universe));
+ let invokable = receiver.lookup_method(universe, &self.signature);
+ (receiver, invokable)
+ }
+ };
let args = {
let mut output = Vec::with_capacity(self.values.len() + 1);
output.push(receiver.clone());
@@ -129,11 +146,27 @@ impl Evaluate for ast::Message {
output
};
- if let Some(invokable) = receiver.lookup_method(universe, &self.signature) {
- invokable.invoke(universe, args)
- } else {
- Return::Local(Value::Nil)
- }
+ // println!(
+ // "invoking {}>>#{} with ({:?})",
+ // receiver.class(universe).borrow().name(),
+ // self.signature,
+ // self.values,
+ // );
+
+ let value = match invokable {
+ Some(invokable) => invokable.invoke(universe, args),
+ None => {
+ Return::Exception(format!(
+ "could not find method '{}>>#{}'",
+ receiver.class(universe).borrow().name(),
+ self.signature
+ ))
+ // Return::Local(Value::Nil)
+ }
+ };
+
+
+ value
}
}
@@ -141,7 +174,6 @@ impl Evaluate for ast::Body {
fn evaluate(&self, universe: &mut Universe) -> Return {
let mut last_value = Value::Nil;
for expr in &self.exprs {
- // last_value = propagate!(dbg!(expr).evaluate(universe));
last_value = propagate!(expr.evaluate(universe));
}
Return::Local(last_value)
diff --git a/som-interpreter/src/frame.rs b/som-interpreter/src/frame.rs
index 0815db7f..ba793546 100644
--- a/som-interpreter/src/frame.rs
+++ b/som-interpreter/src/frame.rs
@@ -1,7 +1,7 @@
use std::collections::HashMap;
use crate::value::Value;
-use crate::SOMWeakRef;
+use crate::SOMRef;
/// The kind of a given frame.
#[derive(Debug, Clone)]
@@ -9,7 +9,7 @@ pub enum FrameKind {
/// A frame created from a block evaluation.
Block(
/// Weak reference to its parent frame.
- SOMWeakRef,
+ SOMRef,
),
/// A frame created from a method invocation.
Method(
@@ -45,10 +45,7 @@ impl Frame {
pub fn get_self(&self) -> Value {
match &self.kind {
FrameKind::Method(value) => value.clone(),
- FrameKind::Block(frame_ref) => frame_ref
- .upgrade()
- .map(|frame| frame.borrow().get_self())
- .unwrap_or(Value::Nil),
+ FrameKind::Block(frame) => frame.borrow().get_self(),
}
}
@@ -60,9 +57,7 @@ impl Frame {
}
match &self.kind {
FrameKind::Method(value) => value.lookup_local(name),
- FrameKind::Block(frame_ref) => frame_ref
- .upgrade()
- .and_then(|frame| frame.borrow().lookup_local(name)),
+ FrameKind::Block(frame) => frame.borrow().lookup_local(name),
}
}
@@ -75,9 +70,15 @@ impl Frame {
}
match &mut self.kind {
FrameKind::Method(self_value) => self_value.assign_local(name, value),
- FrameKind::Block(frame_ref) => frame_ref
- .upgrade()
- .and_then(|frame| frame.borrow_mut().assign_local(name, value)),
+ FrameKind::Block(frame) => frame.borrow_mut().assign_local(name, value),
+ }
+ }
+
+ /// Get the method invocation frame for that frame.
+ pub fn method_frame(frame: &SOMRef) -> SOMRef {
+ match frame.borrow().kind() {
+ FrameKind::Block(frame) => Frame::method_frame(frame),
+ FrameKind::Method(_) => frame.clone(),
}
}
}
diff --git a/som-interpreter/src/instance.rs b/som-interpreter/src/instance.rs
index b38fc4cf..aa4d3bc2 100644
--- a/som-interpreter/src/instance.rs
+++ b/som-interpreter/src/instance.rs
@@ -1,11 +1,12 @@
use std::collections::HashMap;
+use std::fmt;
use crate::class::Class;
use crate::value::Value;
use crate::SOMRef;
/// Represents a generic (non-primitive) class instance.
-#[derive(Debug, Clone)]
+#[derive(Clone)]
pub struct Instance {
/// The class of which this is an instance from.
pub class: SOMRef,
@@ -16,13 +17,23 @@ pub struct Instance {
impl Instance {
/// Construct an instance for a given class.
pub fn from_class(class: SOMRef) -> Self {
- let locals = class
- .borrow()
- .locals
- .keys()
- .cloned()
- .zip(std::iter::repeat(Value::Nil))
- .collect();
+ let mut locals = HashMap::new();
+
+ fn collect_locals(class: &SOMRef, locals: &mut HashMap) {
+ if let Some(class) = class.borrow().super_class() {
+ collect_locals(&class, locals);
+ }
+ locals.extend(
+ class
+ .borrow()
+ .locals
+ .keys()
+ .cloned()
+ .zip(std::iter::repeat(Value::Nil)),
+ );
+ }
+
+ collect_locals(&class, &mut locals);
Self { class, locals }
}
@@ -33,7 +44,7 @@ impl Instance {
}
/// Get the superclass of this instance's class.
- pub fn super_class(&self) -> SOMRef {
+ pub fn super_class(&self) -> Option> {
self.class.borrow().super_class()
}
@@ -48,3 +59,12 @@ impl Instance {
Some(())
}
}
+
+impl fmt::Debug for Instance {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Instance")
+ .field("name", &self.class.borrow().name())
+ .field("locals", &self.locals.keys())
+ .finish()
+ }
+}
diff --git a/som-interpreter/src/invokable.rs b/som-interpreter/src/invokable.rs
index ce2807b9..46e6ef8f 100644
--- a/som-interpreter/src/invokable.rs
+++ b/som-interpreter/src/invokable.rs
@@ -3,6 +3,7 @@ use std::rc::Rc;
use som_core::ast;
use crate::block::Block;
+use crate::class::Class;
use crate::evaluate::Evaluate;
use crate::frame::Frame;
use crate::frame::FrameKind;
@@ -21,6 +22,8 @@ pub enum Return {
NonLocal(Value, SOMRef),
/// An exception, expected to bubble all the way up.
Exception(String),
+ /// A request to restart execution from the top of the closest body.
+ Restart,
}
/// The trait for invoking methods and primitives.
@@ -57,6 +60,8 @@ impl Invokable {
"String" => primitives::string::get_primitive(signature),
"Symbol" => primitives::symbol::get_primitive(signature),
"System" => primitives::system::get_primitive(signature),
+ "Method" => primitives::method::get_primitive(signature),
+ "Primitive" => primitives::method::get_primitive(signature),
"Block" => primitives::block1::get_primitive(signature),
"Block1" => primitives::block1::get_primitive(signature),
"Block2" => primitives::block2::get_primitive(signature),
@@ -74,6 +79,14 @@ impl Invokable {
.unwrap_or_else(|| Self::NotImplemented(format!("{}>>#{}", class_name, signature)))
}
+ pub fn class(&self, universe: &Universe) -> SOMRef {
+ if self.is_primitive() {
+ universe.primitive_class()
+ } else {
+ universe.method_class()
+ }
+ }
+
/// Whether this invocable is a primitive.
pub fn is_primitive(&self) -> bool {
matches!(self, Self::Primitive(_))
@@ -85,7 +98,9 @@ impl Invoke for Invokable {
match self {
Self::MethodDef(method) => method.invoke(universe, args),
Self::Primitive(func) => func(universe, args),
- Self::NotImplemented(name) => unimplemented!("unimplemented primitive: {}", name),
+ Self::NotImplemented(name) => {
+ Return::Exception(format!("unimplemented primitive: {}", name))
+ }
}
}
}
@@ -99,7 +114,7 @@ impl Invoke for ast::MethodDef {
};
universe.with_frame(FrameKind::Method(self_value), |universe| {
- let current_frame = universe.current_frame();
+ let current_frame = universe.current_frame().clone();
match &self.kind {
ast::MethodKind::Unary => {}
ast::MethodKind::Positional { parameters } => current_frame
@@ -119,19 +134,24 @@ impl Invoke for ast::MethodDef {
.borrow_mut()
.bindings
.extend(locals.iter().cloned().zip(std::iter::repeat(Value::Nil)));
- match body.evaluate(universe) {
- Return::NonLocal(value, frame) => {
- if Rc::ptr_eq(¤t_frame, &frame) {
- Return::Local(value)
- } else {
- Return::NonLocal(value, frame)
+ loop {
+ match body.evaluate(universe) {
+ Return::NonLocal(value, frame) => {
+ if Rc::ptr_eq(¤t_frame, &frame) {
+ break Return::Local(value);
+ } else {
+ break Return::NonLocal(value, frame);
+ }
+ }
+ Return::Local(_) => {
+ break Return::Local(current_frame.borrow().get_self())
}
+ Return::Exception(msg) => break Return::Exception(msg),
+ Return::Restart => continue,
}
- Return::Local(_) => Return::Local(current_frame.borrow().get_self()),
- Return::Exception(msg) => Return::Exception(msg),
}
}
- ast::MethodBody::Primitive => unimplemented!(
+ ast::MethodBody::Primitive => Return::Exception(format!(
"unimplemented primitive: {}>>#{}",
current_frame
.borrow()
@@ -140,7 +160,7 @@ impl Invoke for ast::MethodDef {
.borrow()
.name(),
self.signature,
- ),
+ )),
}
})
}
diff --git a/som-interpreter/src/main.rs b/som-interpreter/src/main.rs
index 9907c9b3..5ebe1d00 100644
--- a/som-interpreter/src/main.rs
+++ b/som-interpreter/src/main.rs
@@ -3,7 +3,6 @@
//!
// #![allow(dead_code, unused_variables, unused_imports)]
-use std::cell::RefCell;
use std::path::PathBuf;
use std::rc::Rc;
@@ -11,7 +10,7 @@ use structopt::StructOpt;
mod shell;
-use som_interpreter::invokable::Invoke;
+use som_interpreter::invokable::Return;
use som_interpreter::universe::Universe;
use som_interpreter::value::Value;
@@ -38,44 +37,33 @@ struct Options {
}
fn main() -> anyhow::Result<()> {
- dbg!(std::mem::size_of::());
- dbg!(std::mem::size_of::());
- dbg!(std::mem::size_of::());
- dbg!(std::mem::size_of::());
- dbg!(std::mem::size_of::());
- dbg!(std::mem::size_of::());
- dbg!(std::mem::size_of::());
-
- // return Ok(());
-
let opts: Options = Options::from_args();
- let mut universe = Universe::from_classpath(opts.classpath)?;
+ let mut universe = Universe::with_classpath(opts.classpath)?;
match opts.file {
None => shell::interactive(&mut universe, opts.verbose)?,
Some(file) => {
- let initialize = Value::System
- .lookup_method(&universe, "initialize:")
- .expect("could not find 'System>>#initialize:'");
- initialize.invoke(
- &mut universe,
- vec![
- Value::System,
- Value::Array(Rc::new(RefCell::new(
- std::iter::once(
- file.file_stem()
- .and_then(|stem| stem.to_str())
- .map(String::from)
- .expect("no file stem ?"),
- )
- .chain(opts.args.iter().cloned())
- .map(Rc::new)
- .map(Value::String)
- .collect(),
- ))),
- ],
- );
+ let args = std::iter::once(
+ file.file_stem()
+ .and_then(|stem| stem.to_str())
+ .map(String::from)
+ .expect("no file stem ?"),
+ )
+ .chain(opts.args.iter().cloned())
+ .map(Rc::new)
+ .map(Value::String)
+ .collect();
+
+ let output = universe.initialize(args).unwrap_or_else(|| {
+ Return::Exception(format!("could not find 'System>>#initialize:'"))
+ });
+
+ match output {
+ Return::Exception(message) => println!("ERROR: {}", message),
+ Return::Restart => println!("ERROR: asked for a restart to the top-level"),
+ _ => {}
+ }
}
}
diff --git a/som-interpreter/src/primitives/array.rs b/som-interpreter/src/primitives/array.rs
index 372b6a20..0fabbcd3 100644
--- a/som-interpreter/src/primitives/array.rs
+++ b/som-interpreter/src/primitives/array.rs
@@ -2,6 +2,7 @@ use std::cell::RefCell;
use std::convert::TryFrom;
use std::rc::Rc;
+use crate::expect_args;
use crate::invokable::Return;
use crate::primitives::PrimitiveFn;
use crate::universe::Universe;
@@ -10,70 +11,66 @@ use crate::value::Value;
fn at(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Array>>#at:";
- let index = match args[1] {
- Value::Integer(value) => match usize::try_from(value - 1) {
- Ok(index) => index,
- Err(err) => return Return::Exception(format!("'{}': {}", SIGNATURE, err)),
- },
- _ => return Return::Exception(format!("'{}': invalid index type", SIGNATURE)),
+ expect_args!(SIGNATURE, args, [
+ Value::Array(values) => values,
+ Value::Integer(index) => index,
+ ]);
+
+ let index = match usize::try_from(index - 1) {
+ Ok(index) => index,
+ Err(err) => return Return::Exception(format!("'{}': {}", SIGNATURE, err)),
};
- match args[0] {
- Value::Array(ref values) => {
- Return::Local(values.borrow().get(index).cloned().unwrap_or(Value::Nil))
- }
- _ => Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
- }
+ let value = values.borrow().get(index).cloned().unwrap_or(Value::Nil);
+ Return::Local(value)
}
fn at_put(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Array>>#at:put:";
- let mut iter = args.into_iter();
- let mut receiver = iter.next().unwrap();
- let index = iter.next().unwrap();
- let value = iter.next().unwrap();
- let index = match index {
- Value::Integer(value) => match usize::try_from(value - 1) {
- Ok(index) => index,
- Err(err) => return Return::Exception(format!("'{}': {}", SIGNATURE, err)),
- },
- _ => return Return::Exception(format!("'{}': invalid index type", SIGNATURE)),
+ expect_args!(SIGNATURE, args, [
+ Value::Array(values) => values,
+ Value::Integer(index) => index,
+ value => value,
+ ]);
+
+ let index = match usize::try_from(index - 1) {
+ Ok(index) => index,
+ Err(err) => return Return::Exception(format!("'{}': {}", SIGNATURE, err)),
};
- match receiver {
- Value::Array(ref mut values) => {
- if let Some(location) = values.borrow_mut().get_mut(index as usize) {
- *location = value;
- }
- Return::Local(receiver)
- }
- _ => Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
+ if let Some(location) = values.borrow_mut().get_mut(index) {
+ *location = value;
}
+ Return::Local(Value::Array(values))
}
fn length(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Array>>#length";
- match args[0] {
- Value::Array(ref values) => match i64::try_from(values.borrow().len()) {
- Ok(length) => Return::Local(Value::Integer(length)),
- Err(err) => Return::Exception(format!("'{}': {}", SIGNATURE, err)),
- },
- _ => Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
+ expect_args!(SIGNATURE, args, [
+ Value::Array(values) => values,
+ ]);
+
+ let length = values.borrow().len();
+ match i64::try_from(length) {
+ Ok(length) => Return::Local(Value::Integer(length)),
+ Err(err) => Return::Exception(format!("'{}': {}", SIGNATURE, err)),
}
}
fn new(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Array>>#new:";
- match args[1] {
- Value::Integer(value) => match usize::try_from(value) {
- Ok(length) => Return::Local(Value::Array(Rc::new(RefCell::new(vec![
- Value::Nil;
- length
- ])))),
- Err(err) => Return::Exception(format!("'{}': {}", SIGNATURE, err)),
- },
- _ => Return::Exception(format!("'{}': invalid length type", SIGNATURE)),
+ expect_args!(SIGNATURE, args, [
+ _,
+ Value::Integer(count) => count,
+ ]);
+
+ match usize::try_from(count) {
+ Ok(length) => Return::Local(Value::Array(Rc::new(RefCell::new(vec![
+ Value::Nil;
+ length
+ ])))),
+ Err(err) => Return::Exception(format!("'{}': {}", SIGNATURE, err)),
}
}
diff --git a/som-interpreter/src/primitives/blocks.rs b/som-interpreter/src/primitives/blocks.rs
index fbe01f85..3460eadf 100644
--- a/som-interpreter/src/primitives/blocks.rs
+++ b/som-interpreter/src/primitives/blocks.rs
@@ -1,3 +1,4 @@
+use crate::expect_args;
use crate::invokable::Invoke;
use crate::invokable::Return;
use crate::primitives::PrimitiveFn;
@@ -11,34 +12,27 @@ pub mod block1 {
fn value(universe: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Block1>>#value";
- match args[0] {
- Value::Block(ref block) => block.invoke(universe, args.clone()),
- _ => Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
- }
+ let block_args = args.clone();
+ expect_args!(SIGNATURE, args, [
+ Value::Block(block) => block,
+ ]);
+
+ block.invoke(universe, block_args)
}
- fn while_true(universe: &mut Universe, args: Vec) -> Return {
- const SIGNATURE: &str = "Block>>#whileTrue:";
-
- match (&args[0], &args[1]) {
- (Value::Block(cond), Value::Block(action)) => loop {
- let value = cond.invoke(universe, vec![args[0].clone()]);
- if let Return::Local(Value::Boolean(true)) = value {
- action.invoke(universe, vec![args[1].clone()]);
- } else {
- break value;
- }
- },
- _ => Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
- }
+ fn restart(_: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &str = "Block>>#restart";
+
+ expect_args!(SIGNATURE, args, [Value::Block(_)]);
+
+ Return::Restart
}
/// Search for a primitive matching the given signature.
pub fn get_primitive(signature: impl AsRef) -> Option {
match signature.as_ref() {
"value" => Some(self::value),
- "whileTrue:" => Some(self::while_true),
- // "restart" => Some(self::value),
+ "restart" => Some(self::restart),
_ => None,
}
}
@@ -51,10 +45,13 @@ pub mod block2 {
fn value(universe: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Block2>>#value:";
- match args[0] {
- Value::Block(ref block) => block.invoke(universe, args.clone()),
- _ => Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
- }
+ let block_args = args.clone();
+ expect_args!(SIGNATURE, args, [
+ Value::Block(block) => block,
+ _,
+ ]);
+
+ block.invoke(universe, block_args)
}
/// Search for a primitive matching the given signature.
@@ -73,10 +70,14 @@ pub mod block3 {
fn value_with(universe: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Block3>>#value:with:";
- match args[0] {
- Value::Block(ref block) => block.invoke(universe, args.clone()),
- _ => Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
- }
+ let block_args = args.clone();
+ expect_args!(SIGNATURE, args, [
+ Value::Block(block) => block,
+ _,
+ _,
+ ]);
+
+ block.invoke(universe, block_args)
}
/// Search for a primitive matching the given signature.
diff --git a/som-interpreter/src/primitives/class.rs b/som-interpreter/src/primitives/class.rs
index b0b36c9d..4afaa6f5 100644
--- a/som-interpreter/src/primitives/class.rs
+++ b/som-interpreter/src/primitives/class.rs
@@ -1,6 +1,7 @@
use std::cell::RefCell;
use std::rc::Rc;
+use crate::expect_args;
use crate::instance::Instance;
use crate::invokable::Return;
use crate::primitives::PrimitiveFn;
@@ -10,48 +11,69 @@ use crate::value::Value;
fn superclass(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Class>>#superclass";
- match args[0] {
- Value::Class(ref class) => Return::Local(Value::Class(class.borrow().super_class())),
- _ => Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Class(class) => class,
+ ]);
+
+ let super_class = class.borrow().super_class();
+ Return::Local(super_class.map(Value::Class).unwrap_or(Value::Nil))
}
fn new(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Class>>#new";
- match args[0] {
- Value::Class(ref class) => Return::Local(Value::Instance(Rc::new(RefCell::new(
- Instance::from_class(class.clone()),
- )))),
- _ => Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Class(class) => class,
+ ]);
+
+ let instance = Instance::from_class(class);
+ let instance = Rc::new(RefCell::new(instance));
+ Return::Local(Value::Instance(instance))
}
fn name(universe: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Class>>#name";
- match args[0] {
- Value::Class(ref class) => {
- Return::Local(Value::Symbol(universe.intern_symbol(class.borrow().name())))
- }
- _ => Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Class(class) => class,
+ ]);
+
+ let sym = universe.intern_symbol(class.borrow().name());
+ Return::Local(Value::Symbol(sym))
+}
+
+fn methods(_: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &str = "Class>>#methods";
+
+ expect_args!(SIGNATURE, args, [
+ Value::Class(class) => class,
+ ]);
+
+ let methods = class
+ .borrow()
+ .methods
+ .values()
+ .map(|invokable| Value::Invokable(class.clone(), invokable.clone()))
+ .collect();
+
+ Return::Local(Value::Array(Rc::new(RefCell::new(methods))))
}
fn fields(universe: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Class>>#fields";
- match args[0] {
- Value::Class(ref class) => Return::Local(Value::Array(Rc::new(RefCell::new(
- class
- .borrow()
- .locals
- .keys()
- .map(|field| Value::Symbol(universe.intern_symbol(field)))
- .collect(),
- )))),
- _ => Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Class(class) => class,
+ ]);
+
+ let fields = class
+ .borrow()
+ .locals
+ .keys()
+ .map(|field| Value::Symbol(universe.intern_symbol(field)))
+ .collect();
+
+ Return::Local(Value::Array(Rc::new(RefCell::new(fields))))
}
/// Search for a primitive matching the given signature.
@@ -60,7 +82,7 @@ pub fn get_primitive(signature: impl AsRef) -> Option {
"new" => Some(self::new),
"name" => Some(self::name),
"fields" => Some(self::fields),
- // "methods" => Some(self::methods),
+ "methods" => Some(self::methods),
"superclass" => Some(self::superclass),
_ => None,
}
diff --git a/som-interpreter/src/primitives/double.rs b/som-interpreter/src/primitives/double.rs
index 0361d7e0..f2fe007c 100644
--- a/som-interpreter/src/primitives/double.rs
+++ b/som-interpreter/src/primitives/double.rs
@@ -1,5 +1,6 @@
use std::rc::Rc;
+use crate::expect_args;
use crate::invokable::Return;
use crate::primitives::PrimitiveFn;
use crate::universe::Universe;
@@ -8,130 +9,160 @@ use crate::value::Value;
fn from_string(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Double>>#fromString:";
- match args[1] {
- Value::String(ref string) => match string.parse() {
- Ok(parsed) => Return::Local(Value::Double(parsed)),
- Err(err) => Return::Exception(err.to_string()),
- },
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
+ expect_args!(SIGNATURE, args, [
+ _,
+ Value::String(string) => string,
+ ]);
+
+ match string.parse() {
+ Ok(parsed) => Return::Local(Value::Double(parsed)),
+ Err(err) => Return::Exception(format!("'{}': {}", SIGNATURE, err)),
}
}
fn as_string(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Double>>#asString";
- match args[0] {
- Value::Double(double) => Return::Local(Value::String(Rc::new(double.to_string()))),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Double(value) => value,
+ ]);
+
+ Return::Local(Value::String(Rc::new(value.to_string())))
}
fn as_integer(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Double>>#asInteger";
- match args[0] {
- Value::Double(double) => Return::Local(Value::Integer(double.trunc() as i64)),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Double(value) => value,
+ ]);
+
+ Return::Local(Value::Integer(value.trunc() as i64))
}
fn sqrt(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Double>>#sqrt";
- match args[0] {
- Value::Double(double) => Return::Local(Value::Double(double.sqrt())),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Double(value) => value,
+ ]);
+
+ Return::Local(Value::Double(value.sqrt()))
}
fn round(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Double>>#round";
- match args[0] {
- Value::Double(double) => Return::Local(Value::Double(double.round())),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Double(value) => value,
+ ]);
+
+ Return::Local(Value::Double(value.round()))
}
fn cos(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Double>>#cos";
- match args[0] {
- Value::Double(double) => Return::Local(Value::Double(double.cos())),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Double(value) => value,
+ ]);
+
+ Return::Local(Value::Double(value.cos()))
}
fn sin(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Double>>#sin";
- match args[0] {
- Value::Double(double) => Return::Local(Value::Double(double.sin())),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Double(value) => value,
+ ]);
+
+ Return::Local(Value::Double(value.sin()))
}
fn eq(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Double>>#=";
- match (&args[0], &args[1]) {
- (Value::Double(a), Value::Double(b)) => Return::Local(Value::Boolean(a == b)),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ // Value::Double(a) => a,
+ // Value::Double(b) => b,
+ a => a,
+ b => b,
+ ]);
+
+ Return::Local(Value::Boolean(a == b))
}
fn lt(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Double>>#<";
- match (&args[0], &args[1]) {
- (Value::Double(a), Value::Double(b)) => Return::Local(Value::Boolean(a < b)),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Double(a) => a,
+ Value::Double(b) => b,
+ ]);
+
+ Return::Local(Value::Boolean(a < b))
}
fn plus(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Double>>#+";
- match (&args[0], &args[1]) {
- (Value::Double(a), Value::Double(b)) => Return::Local(Value::Double(a + b)),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Double(a) => a,
+ Value::Double(b) => b,
+ ]);
+
+ Return::Local(Value::Double(a + b))
}
fn minus(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Double>>#-";
- match (&args[0], &args[1]) {
- (Value::Double(a), Value::Double(b)) => Return::Local(Value::Double(a - b)),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Double(a) => a,
+ Value::Double(b) => b,
+ ]);
+
+ Return::Local(Value::Double(a - b))
}
fn times(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Double>>#*";
- match (&args[0], &args[1]) {
- (Value::Double(a), Value::Double(b)) => Return::Local(Value::Double(a * b)),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Double(a) => a,
+ Value::Double(b) => b,
+ ]);
+
+ Return::Local(Value::Double(a * b))
}
fn divide(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Double>>#//";
- match (&args[0], &args[1]) {
- (Value::Double(a), Value::Double(b)) => Return::Local(Value::Double(a / b)),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Double(a) => a,
+ Value::Double(b) => b,
+ ]);
+
+ Return::Local(Value::Double(a / b))
}
fn modulo(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Double>>#%";
- match (&args[0], &args[1]) {
- (Value::Double(a), Value::Double(b)) => Return::Local(Value::Double(a % b)),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Double(a) => a,
+ Value::Double(b) => b,
+ ]);
+
+ Return::Local(Value::Double(a % b))
+}
+
+fn positive_infinity(_: &mut Universe, _: Vec) -> Return {
+ const _: &str = "Double>>#positiveInfinity";
+
+ Return::Local(Value::Double(f64::INFINITY))
}
/// Search for a primitive matching the given signature.
@@ -151,6 +182,7 @@ pub fn get_primitive(signature: impl AsRef) -> Option {
"fromString:" => Some(self::from_string),
"asString" => Some(self::as_string),
"asInteger" => Some(self::as_integer),
+ "PositiveInfinity" => Some(self::positive_infinity),
_ => None,
}
}
diff --git a/som-interpreter/src/primitives/integer.rs b/som-interpreter/src/primitives/integer.rs
index 0af2ac75..4de78efb 100644
--- a/som-interpreter/src/primitives/integer.rs
+++ b/som-interpreter/src/primitives/integer.rs
@@ -1,5 +1,6 @@
use std::rc::Rc;
+use crate::expect_args;
use crate::invokable::Return;
use crate::primitives::PrimitiveFn;
use crate::universe::Universe;
@@ -8,105 +9,126 @@ use crate::value::Value;
fn from_string(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Integer>>#fromString:";
- match args[1] {
- Value::String(ref string) => match string.parse() {
- Ok(parsed) => Return::Local(Value::Integer(parsed)),
- Err(err) => Return::Exception(err.to_string()),
- },
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
+ expect_args!(SIGNATURE, args, [
+ _,
+ Value::String(string) => string,
+ ]);
+
+ match string.parse() {
+ Ok(parsed) => Return::Local(Value::Integer(parsed)),
+ Err(err) => Return::Exception(err.to_string()),
}
}
fn as_string(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Integer>>#asString";
- match args[0] {
- Value::Integer(integer) => Return::Local(Value::String(Rc::new(integer.to_string()))),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Integer(value) => value,
+ ]);
+
+ Return::Local(Value::String(Rc::new(value.to_string())))
}
fn plus(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Integer>>#+";
- match (&args[0], &args[1]) {
- (Value::Integer(a), Value::Integer(b)) => Return::Local(Value::Integer(a + b)),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Integer(a) => a,
+ Value::Integer(b) => b,
+ ]);
+
+ Return::Local(Value::Integer(a + b))
}
fn minus(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Integer>>#-";
- match (&args[0], &args[1]) {
- (Value::Integer(a), Value::Integer(b)) => Return::Local(Value::Integer(a - b)),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Integer(a) => a,
+ Value::Integer(b) => b,
+ ]);
+
+ Return::Local(Value::Integer(a - b))
}
fn times(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Integer>>#*";
- match (&args[0], &args[1]) {
- (Value::Integer(a), Value::Integer(b)) => Return::Local(Value::Integer(a * b)),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Integer(a) => a,
+ Value::Integer(b) => b,
+ ]);
+
+ Return::Local(Value::Integer(a * b))
}
fn divide(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Integer>>#/";
- match (&args[0], &args[1]) {
- (Value::Integer(a), Value::Integer(b)) => Return::Local(Value::Integer(a.div_euclid(*b))),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Integer(a) => a,
+ Value::Integer(b) => b,
+ ]);
+
+ Return::Local(Value::Integer(a.div_euclid(b)))
}
fn divide_float(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Integer>>#//";
- match (&args[0], &args[1]) {
- (Value::Integer(a), Value::Integer(b)) => {
- Return::Local(Value::Double((*a as f64) / (*b as f64)))
- }
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Integer(a) => a,
+ Value::Integer(b) => b,
+ ]);
+
+ Return::Local(Value::Double((a as f64) / (b as f64)))
}
fn modulo(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Integer>>#%";
- match (&args[0], &args[1]) {
- (Value::Integer(a), Value::Integer(b)) => Return::Local(Value::Integer(a.rem_euclid(*b))),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Integer(a) => a,
+ Value::Integer(b) => b,
+ ]);
+
+ Return::Local(Value::Integer(a.rem_euclid(b)))
}
fn bitand(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Integer>>#&";
- match (&args[0], &args[1]) {
- (Value::Integer(a), Value::Integer(b)) => Return::Local(Value::Integer(a & b)),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Integer(a) => a,
+ Value::Integer(b) => b,
+ ]);
+
+ Return::Local(Value::Integer(a & b))
}
fn lt(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Integer>>#<";
- match (&args[0], &args[1]) {
- (Value::Integer(a), Value::Integer(b)) => Return::Local(Value::Boolean(a < b)),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Integer(a) => a,
+ Value::Integer(b) => b,
+ ]);
+
+ Return::Local(Value::Boolean(a < b))
}
fn eq(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Integer>>#=";
- match (&args[0], &args[1]) {
- (Value::Integer(a), Value::Integer(b)) => Return::Local(Value::Boolean(a == b)),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ // Value::Integer(a) => a,
+ // Value::Integer(b) => b,
+ a => a,
+ b => b,
+ ]);
+
+ Return::Local(Value::Boolean(a == b))
}
/// Search for a primitive matching the given signature.
diff --git a/som-interpreter/src/primitives/method.rs b/som-interpreter/src/primitives/method.rs
new file mode 100644
index 00000000..fdfbe4e4
--- /dev/null
+++ b/som-interpreter/src/primitives/method.rs
@@ -0,0 +1,54 @@
+use crate::expect_args;
+use crate::invokable::{Invokable, Invoke, Return};
+use crate::primitives::PrimitiveFn;
+use crate::universe::Universe;
+use crate::value::Value;
+
+fn holder(_: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &str = "Method>>#holder";
+
+ expect_args!(SIGNATURE, args, [
+ Value::Invokable(holder, _) => holder,
+ ]);
+
+ Return::Local(Value::Class(holder))
+}
+
+fn signature(universe: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &str = "Method>>#signature";
+
+ expect_args!(SIGNATURE, args, [
+ Value::Invokable(_, invokable) => invokable,
+ ]);
+
+ let sym = match invokable.as_ref() {
+ Invokable::MethodDef(defn) => universe.intern_symbol(defn.signature.as_str()),
+ _ => universe.intern_symbol("instance of Primitive"),
+ };
+ Return::Local(Value::Symbol(sym))
+}
+
+fn invoke_on_with(universe: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &str = "Method>>#invokeOn:with:";
+
+ expect_args!(SIGNATURE, args, [
+ Value::Invokable(_, invokable) => invokable,
+ receiver => receiver,
+ Value::Array(args) => args,
+ ]);
+
+ let args = std::iter::once(receiver.clone())
+ .chain(args.borrow().iter().cloned())
+ .collect();
+ invokable.invoke(universe, args)
+}
+
+/// Search for a primitive matching the given signature.
+pub fn get_primitive(signature: impl AsRef) -> Option {
+ match signature.as_ref() {
+ "holder" => Some(self::holder),
+ "signature" => Some(self::signature),
+ "invokeOn:with:" => Some(self::invoke_on_with),
+ _ => None,
+ }
+}
diff --git a/som-interpreter/src/primitives/mod.rs b/som-interpreter/src/primitives/mod.rs
index e310ef8c..f45b8620 100644
--- a/som-interpreter/src/primitives/mod.rs
+++ b/som-interpreter/src/primitives/mod.rs
@@ -8,6 +8,8 @@ pub mod class;
pub mod double;
/// Primitives for the **Integer** class.
pub mod integer;
+/// Primitives for the **Method** class and the **Primitive** class.
+pub mod method;
/// Primitives for the **Object** class.
pub mod object;
/// Primitives for the **String** class.
@@ -25,3 +27,31 @@ use crate::value::Value;
/// A interpreter primitive (just a bare function pointer).
pub type PrimitiveFn = fn(universe: &mut Universe, args: Vec) -> Return;
+
+#[macro_export]
+macro_rules! missing_arg {
+ ($signature:expr, $expr:expr) => {
+ match $expr {
+ Some(arg) => arg,
+ None => return Return::Exception(format!("'{}': missing argument", $signature)),
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! expect_args {
+ ($signature:expr, $args:expr, [ $( $ptrn:pat $( => $name:ident )? ),* $(,)? ]) => {
+ #[allow(unused_mut)]
+ let ($($(mut $name,)?)*) = {
+ #[allow(unused_variables, unused_mut)]
+ let mut iter = $args.into_iter();
+ $(#[allow(unreachable_patterns)]
+ $(let $name =)? match iter.next() {
+ Some($ptrn) => {$($name)?},
+ Some(_) => return Return::Exception(format!("'{}': wrong type", $signature)),
+ None => return Return::Exception(format!("'{}': missing argument", $signature)),
+ };)*
+ ($($($name,)?)*)
+ };
+ };
+}
diff --git a/som-interpreter/src/primitives/object.rs b/som-interpreter/src/primitives/object.rs
index 4c77b964..12281668 100644
--- a/som-interpreter/src/primitives/object.rs
+++ b/som-interpreter/src/primitives/object.rs
@@ -1,12 +1,17 @@
-use crate::invokable::Return;
+use crate::expect_args;
+use crate::invokable::{Invoke, Return};
use crate::primitives::PrimitiveFn;
use crate::universe::Universe;
use crate::value::Value;
fn class(universe: &mut Universe, args: Vec) -> Return {
- const _: &'static str = "Object>>#class";
+ const SIGNATURE: &'static str = "Object>>#class";
- Return::Local(Value::Class(args[0].class(universe)))
+ expect_args!(SIGNATURE, args, [
+ object => object,
+ ]);
+
+ Return::Local(Value::Class(object.class(universe)))
}
fn object_size(_: &mut Universe, _: Vec) -> Return {
@@ -16,9 +21,147 @@ fn object_size(_: &mut Universe, _: Vec) -> Return {
}
fn eq(_: &mut Universe, args: Vec) -> Return {
- const _: &'static str = "Object>>#==";
+ const SIGNATURE: &'static str = "Object>>#==";
+
+ expect_args!(SIGNATURE, args, [
+ a => a,
+ b => b,
+ ]);
+
+ Return::Local(Value::Boolean(a == b))
+}
+
+fn perform(universe: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &'static str = "Object>>#perform:";
+
+ expect_args!(SIGNATURE, args, [
+ object => object,
+ Value::Symbol(sym) => sym,
+ ]);
+
+ let signature = universe.lookup_symbol(sym);
+ let method = object.lookup_method(universe, signature);
+
+ let exception = Return::Exception(format!(
+ "'{}': method '{}' not found for '{}'",
+ SIGNATURE,
+ signature,
+ object.to_string(universe)
+ ));
+
+ method
+ .map(|invokable| invokable.invoke(universe, vec![object]))
+ .unwrap_or(exception)
+}
+
+fn perform_with_arguments(universe: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &'static str = "Object>>#perform:withArguments:";
+
+ expect_args!(SIGNATURE, args, [
+ object => object,
+ Value::Symbol(sym) => sym,
+ Value::Array(arr) => arr,
+ ]);
+
+ let signature = universe.lookup_symbol(sym);
+ let method = object.lookup_method(universe, signature);
+
+ let exception = Return::Exception(format!(
+ "'{}': method '{}' not found for '{}'",
+ SIGNATURE,
+ signature,
+ object.to_string(universe)
+ ));
+
+ let args = std::iter::once(object)
+ .chain(arr.replace(Vec::default()).into_iter())
+ .collect();
+
+ method
+ .map(|invokable| invokable.invoke(universe, args))
+ .unwrap_or(exception)
+}
+
+fn perform_in_super_class(universe: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &'static str = "Object>>#perform:inSuperclass:";
+
+ expect_args!(SIGNATURE, args, [
+ object => object,
+ Value::Symbol(sym) => sym,
+ Value::Class(class) => class,
+ ]);
+
+ let signature = universe.lookup_symbol(sym);
+ let method = class.borrow().lookup_method(signature);
+
+ let exception = Return::Exception(format!(
+ "'{}': method '{}' not found for '{}'",
+ SIGNATURE,
+ signature,
+ object.to_string(universe)
+ ));
+
+ method
+ .map(|invokable| invokable.invoke(universe, vec![object.clone()]))
+ .unwrap_or(exception)
+}
+
+fn perform_with_arguments_in_super_class(universe: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &'static str = "Object>>#perform:withArguments:inSuperclass:";
+
+ expect_args!(SIGNATURE, args, [
+ object => object,
+ Value::Symbol(sym) => sym,
+ Value::Array(arr) => arr,
+ Value::Class(class) => class,
+ ]);
+
+ let signature = universe.lookup_symbol(sym);
+ let method = class.borrow().lookup_method(signature);
+
+ let exception = Return::Exception(format!(
+ "'{}': method '{}' not found for '{}'",
+ SIGNATURE,
+ signature,
+ object.to_string(universe)
+ ));
+
+ let args = std::iter::once(object)
+ .chain(arr.replace(Vec::default()).into_iter())
+ .collect();
+
+ method
+ .map(|invokable| invokable.invoke(universe, args))
+ .unwrap_or(exception)
+}
+
+fn inst_var_at(universe: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &'static str = "Object>>#instVarAt:";
+
+ expect_args!(SIGNATURE, args, [
+ object => object,
+ Value::Symbol(sym) => sym,
+ ]);
+
+ let signature = universe.lookup_symbol(sym);
+ let local = object.lookup_local(signature);
+
+ Return::Local(local.unwrap_or(Value::Nil))
+}
+
+fn inst_var_at_put(universe: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &'static str = "Object>>#instVarAt:put:";
+
+ expect_args!(SIGNATURE, args, [
+ object => object,
+ Value::Symbol(sym) => sym,
+ value => value,
+ ]);
+
+ let signature = universe.lookup_symbol(sym);
+ let outcome = object.assign_local(signature, value.clone());
- Return::Local(Value::Boolean(args[0] == args[1]))
+ Return::Local(outcome.map(|_| value).unwrap_or(Value::Nil))
}
/// Search for a primitive matching the given signature.
@@ -26,6 +169,12 @@ pub fn get_primitive(signature: impl AsRef) -> Option {
match signature.as_ref() {
"class" => Some(self::class),
"objectSize" => Some(self::object_size),
+ "perform:" => Some(self::perform),
+ "perform:withArguments:" => Some(self::perform_with_arguments),
+ "perform:inSuperclass:" => Some(self::perform_in_super_class),
+ "perform:withArguments:inSuperclass:" => Some(self::perform_with_arguments_in_super_class),
+ "instVarAt:" => Some(self::inst_var_at),
+ "instVarAt:put:" => Some(self::inst_var_at_put),
"==" => Some(self::eq),
_ => None,
}
diff --git a/som-interpreter/src/primitives/string.rs b/som-interpreter/src/primitives/string.rs
index 0b556fef..c1bc1bce 100644
--- a/som-interpreter/src/primitives/string.rs
+++ b/som-interpreter/src/primitives/string.rs
@@ -1,74 +1,187 @@
use std::convert::TryFrom;
use std::rc::Rc;
+use crate::expect_args;
use crate::invokable::Return;
use crate::primitives::PrimitiveFn;
use crate::universe::Universe;
use crate::value::Value;
-fn at(_: &mut Universe, args: Vec) -> Return {
+fn at(universe: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "String>>#at:";
- let index = match args[1] {
- Value::Integer(value) => match usize::try_from(value) {
- Ok(idx) => idx,
- Err(err) => return Return::Exception(format!("'{}': {}", SIGNATURE, err)),
- },
- _ => return Return::Exception(format!("'{}': invalid index type", SIGNATURE)),
+ expect_args!(SIGNATURE, args, [
+ value => value,
+ Value::Integer(index) => index,
+ ]);
+
+ let index = match usize::try_from(index) {
+ Ok(index) => index,
+ Err(err) => return Return::Exception(format!("'{}': {}", SIGNATURE, err)),
};
- match args[0] {
- Value::String(ref value) => Return::Local(
- value
- .chars()
- .nth(index as usize)
- .map(|ch| Value::String(Rc::new(ch.to_string())))
- .unwrap_or(Value::Nil),
- ),
- _ => Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
- }
+ let value = match value {
+ Value::String(ref value) => value.as_str(),
+ Value::Symbol(sym) => universe.lookup_symbol(sym),
+ _ => return Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
+ };
+
+ Return::Local(
+ value
+ .chars()
+ .nth(index as usize)
+ .map(|ch| Value::String(Rc::new(ch.to_string())))
+ .unwrap_or(Value::Nil),
+ )
}
-fn length(_: &mut Universe, args: Vec) -> Return {
+fn length(universe: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "String>>#length";
- match args[0] {
- Value::String(ref value) => match i64::try_from(value.len()) {
- Ok(idx) => Return::Local(Value::Integer(idx)),
- Err(err) => Return::Exception(format!("'{}': {}", SIGNATURE, err)),
- },
- _ => Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
+ expect_args!(SIGNATURE, args, [
+ value => value,
+ ]);
+
+ let value = match value {
+ Value::String(ref value) => value.as_str(),
+ Value::Symbol(sym) => universe.lookup_symbol(sym),
+ _ => return Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
+ };
+
+ match i64::try_from(value.len()) {
+ Ok(idx) => Return::Local(Value::Integer(idx)),
+ Err(err) => Return::Exception(format!("'{}': {}", SIGNATURE, err)),
}
}
-fn concatenate(_: &mut Universe, args: Vec) -> Return {
+fn is_letters(universe: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &str = "String>>#isLetters";
+
+ expect_args!(SIGNATURE, args, [
+ value => value,
+ ]);
+
+ let value = match value {
+ Value::String(ref value) => value.as_str(),
+ Value::Symbol(sym) => universe.lookup_symbol(sym),
+ _ => return Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
+ };
+
+ Return::Local(Value::Boolean(value.chars().all(char::is_alphabetic)))
+}
+
+fn is_digits(universe: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &str = "String>>#isDigits";
+
+ expect_args!(SIGNATURE, args, [
+ value => value,
+ ]);
+
+ let value = match value {
+ Value::String(ref value) => value.as_str(),
+ Value::Symbol(sym) => universe.lookup_symbol(sym),
+ _ => return Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
+ };
+
+ Return::Local(Value::Boolean(value.chars().all(char::is_numeric)))
+}
+
+fn is_whitespace(universe: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &str = "String>>#isWhitespace";
+
+ expect_args!(SIGNATURE, args, [
+ value => value,
+ ]);
+
+ let value = match value {
+ Value::String(ref value) => value.as_str(),
+ Value::Symbol(sym) => universe.lookup_symbol(sym),
+ _ => return Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
+ };
+
+ Return::Local(Value::Boolean(value.chars().all(char::is_whitespace)))
+}
+
+fn concatenate(universe: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "String>>#concatenate:";
- match (&args[0], &args[1]) {
- (Value::String(s1), Value::String(s2)) => {
- Return::Local(Value::String(Rc::new(format!("{}{}", s1, s2))))
- }
- _ => Return::Exception(format!("'{}': wrong types", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ s1 => s1,
+ s2 => s2,
+ ]);
+
+ let s1 = match s1 {
+ Value::String(ref value) => value.as_str(),
+ Value::Symbol(sym) => universe.lookup_symbol(sym),
+ _ => return Return::Exception(format!("'{}': wrong types", SIGNATURE)),
+ };
+ let s2 = match s2 {
+ Value::String(ref value) => value.as_str(),
+ Value::Symbol(sym) => universe.lookup_symbol(sym),
+ _ => return Return::Exception(format!("'{}': wrong types", SIGNATURE)),
+ };
+
+ Return::Local(Value::String(Rc::new(format!("{}{}", s1, s2))))
}
fn as_symbol(universe: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "String>>#asSymbol";
- match args[0] {
+ expect_args!(SIGNATURE, args, [
+ value => value,
+ ]);
+
+ match value {
Value::String(ref value) => {
Return::Local(Value::Symbol(universe.intern_symbol(value.as_str())))
}
+ Value::Symbol(sym) => Return::Local(Value::Symbol(sym)),
_ => Return::Exception(format!("'{}': invalid self type", SIGNATURE)),
}
}
+fn eq(_: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &str = "String>>#=";
+
+ expect_args!(SIGNATURE, args, [
+ s1 => s1,
+ s2 => s2,
+ ]);
+
+ Return::Local(Value::Boolean(s1 == s2))
+}
+
+fn prim_substring_from_to(universe: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &str = "String>>#primSubstringFrom:to:";
+
+ expect_args!(SIGNATURE, args, [
+ value => value,
+ Value::Integer(from) => from,
+ Value::Integer(to) => to,
+ ]);
+
+ let (value, from, to) = match (&value, usize::try_from(from - 1), usize::try_from(to)) {
+ (Value::String(ref value), Ok(from), Ok(to)) => (value.as_str(), from, to),
+ (Value::Symbol(sym), Ok(from), Ok(to)) => (universe.lookup_symbol(*sym), from, to),
+ (_, _, _) => return Return::Exception(format!("'{}': wrong types", SIGNATURE)),
+ };
+
+ let string = Rc::new(value.chars().skip(from).take(to - from).collect());
+
+ Return::Local(Value::String(string))
+}
+
/// Search for a primitive matching the given signature.
pub fn get_primitive(signature: impl AsRef) -> Option {
match signature.as_ref() {
"at:" => Some(self::at),
"length" => Some(self::length),
+ "isLetters" => Some(self::is_letters),
+ "isDigits" => Some(self::is_digits),
+ "isWhitespace" => Some(self::is_whitespace),
"asSymbol" => Some(self::as_symbol),
"concatenate:" => Some(self::concatenate),
+ "primSubstringFrom:to:" => Some(self::prim_substring_from_to),
+ "=" => Some(self::eq),
_ => None,
}
}
diff --git a/som-interpreter/src/primitives/symbol.rs b/som-interpreter/src/primitives/symbol.rs
index e51c4eeb..fdaa6b8c 100644
--- a/som-interpreter/src/primitives/symbol.rs
+++ b/som-interpreter/src/primitives/symbol.rs
@@ -1,5 +1,6 @@
use std::rc::Rc;
+use crate::expect_args;
use crate::invokable::Return;
use crate::primitives::PrimitiveFn;
use crate::universe::Universe;
@@ -8,12 +9,13 @@ use crate::value::Value;
fn as_string(universe: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "Symbol>>#asString";
- match args[0] {
- Value::Symbol(sym) => Return::Local(Value::String(Rc::new(
- universe.lookup_symbol(sym).to_string(),
- ))),
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::Symbol(sym) => sym,
+ ]);
+
+ Return::Local(Value::String(Rc::new(
+ universe.lookup_symbol(sym).to_string(),
+ )))
}
/// Search for a primitive matching the given signature.
diff --git a/som-interpreter/src/primitives/system.rs b/som-interpreter/src/primitives/system.rs
index b0435d49..29e38aef 100644
--- a/som-interpreter/src/primitives/system.rs
+++ b/som-interpreter/src/primitives/system.rs
@@ -1,24 +1,41 @@
use std::convert::TryFrom;
+// use std::io::BufRead;
+// use std::rc::Rc;
+use crate::expect_args;
use crate::invokable::Return;
use crate::primitives::PrimitiveFn;
use crate::universe::Universe;
use crate::value::Value;
+// fn read_line(_: &mut Universe, args: Vec) -> Return {
+// const SIGNATURE: &str = "System>>#readLine";
+
+// expect_args!(SIGNATURE, args, [Value::System]);
+
+// match std::io::stdin().lock().lines().next() {
+// Some(Ok(line)) => Return::Local(Value::String(Rc::new(line))),
+// Some(Err(err)) => Return::Exception(format!("'{}': {}", SIGNATURE, err)),
+// None => Return::Exception(format!("'{}': {}", SIGNATURE, "error")),
+// }
+// }
+
fn print_string(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "System>>#printString:";
- match args.into_iter().nth(1) {
- Some(Value::String(string)) => {
- print!("{}", string);
- Return::Local(Value::String(string))
- }
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::System,
+ Value::String(string) => string,
+ ]);
+
+ print!("{}", string);
+ Return::Local(Value::String(string))
}
-fn print_newline(_: &mut Universe, _: Vec) -> Return {
- const _: &'static str = "System>>#printNewline";
+fn print_newline(_: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &'static str = "System>>#printNewline";
+
+ expect_args!(SIGNATURE, args, [Value::System]);
println!();
Return::Local(Value::Nil)
@@ -27,62 +44,98 @@ fn print_newline(_: &mut Universe, _: Vec) -> Return {
fn load(universe: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "System>>#load:";
- match args[1] {
- Value::Symbol(sym) => {
- let name = universe.lookup_symbol(sym).to_string();
- match universe.load_class(name.as_str()) {
- Ok(class) => Return::Local(Value::Class(class)),
- Err(err) => Return::Exception(format!("'{}': {}", SIGNATURE, err)),
- }
- }
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
+ expect_args!(SIGNATURE, args, [
+ Value::System,
+ Value::Symbol(sym) => sym,
+ ]);
+
+ let name = universe.lookup_symbol(sym).to_string();
+ match universe.load_class(name) {
+ Ok(class) => Return::Local(Value::Class(class)),
+ Err(err) => Return::Exception(format!("'{}': {}", SIGNATURE, err)),
}
}
fn global(universe: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "System>>#global:";
- dbg!(universe.current_method_frame());
- match &dbg!(&args)[1] {
- Value::Symbol(sym) => {
- let symbol = universe.lookup_symbol(*sym);
- Return::Local(universe.lookup_global(symbol).unwrap_or(Value::Nil))
- }
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::System,
+ Value::Symbol(sym) => sym,
+ ]);
+
+ let symbol = universe.lookup_symbol(sym);
+ Return::Local(universe.lookup_global(symbol).unwrap_or(Value::Nil))
}
fn global_put(universe: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "System>>#global:put:";
- match args[1] {
- Value::Symbol(sym) => {
- let symbol = universe.lookup_symbol(sym).to_string();
- universe.assign_local(symbol, args[2].clone());
- Return::Local(args[2].clone())
- }
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
- }
+ expect_args!(SIGNATURE, args, [
+ Value::System,
+ Value::Symbol(sym) => sym,
+ value => value,
+ ]);
+
+ let symbol = universe.lookup_symbol(sym).to_string();
+ universe.assign_global(symbol, value.clone());
+ Return::Local(value)
}
fn exit(_: &mut Universe, args: Vec) -> Return {
const SIGNATURE: &str = "System>>#exit:";
- match args[1] {
- Value::Integer(code) => match i32::try_from(code) {
- Ok(code) => std::process::exit(code),
- Err(err) => Return::Exception(format!("'{}': {}", SIGNATURE, err)),
- },
- _ => Return::Exception(format!("'{}': wrong type", SIGNATURE)),
+ expect_args!(SIGNATURE, args, [
+ Value::System,
+ Value::Integer(code) => code,
+ ]);
+
+ match i32::try_from(code) {
+ Ok(code) => std::process::exit(code),
+ Err(err) => Return::Exception(format!("'{}': {}", SIGNATURE, err)),
+ }
+}
+
+fn ticks(universe: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &str = "System>>#ticks";
+
+ expect_args!(SIGNATURE, args, [Value::System]);
+
+ match i64::try_from(universe.start_time.elapsed().as_micros()) {
+ Ok(micros) => Return::Local(Value::Integer(micros)),
+ Err(err) => Return::Exception(format!("'{}': {}", SIGNATURE, err)),
}
}
+fn time(universe: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &str = "System>>#time";
+
+ expect_args!(SIGNATURE, args, [Value::System]);
+
+ match i64::try_from(universe.start_time.elapsed().as_millis()) {
+ Ok(micros) => Return::Local(Value::Integer(micros)),
+ Err(err) => Return::Exception(format!("'{}': {}", SIGNATURE, err)),
+ }
+}
+
+fn full_gc(_: &mut Universe, args: Vec) -> Return {
+ const SIGNATURE: &str = "System>>#fullGC";
+
+ expect_args!(SIGNATURE, args, [Value::System]);
+
+ Return::Local(Value::Boolean(true))
+}
+
/// Search for a primitive matching the given signature.
pub fn get_primitive(signature: impl AsRef) -> Option {
match signature.as_ref() {
+ // "readLine" => Some(self::read_line),
"printString:" => Some(self::print_string),
"printNewline" => Some(self::print_newline),
"load:" => Some(self::load),
+ "ticks" => Some(self::ticks),
+ "time" => Some(self::time),
+ "fullGC" => Some(self::full_gc),
"exit:" => Some(self::exit),
"global:" => Some(self::global),
"global:put:" => Some(self::global_put),
diff --git a/som-interpreter/src/shell.rs b/som-interpreter/src/shell.rs
index 7041abb4..17077ece 100644
--- a/som-interpreter/src/shell.rs
+++ b/som-interpreter/src/shell.rs
@@ -42,7 +42,7 @@ pub fn interactive(universe: &mut Universe, verbose: bool) -> Result<(), Error>
}
let start = Instant::now();
- let symbols: Vec = Lexer::new(line)
+ let tokens: Vec = Lexer::new(line)
.skip_comments(true)
.skip_whitespace(true)
.collect();
@@ -57,7 +57,7 @@ pub fn interactive(universe: &mut Universe, verbose: bool) -> Result<(), Error>
}
let start = Instant::now();
- let (expr, _) = lang::expression().parse(symbols.as_slice()).unwrap();
+ let (expr, _) = lang::expression().parse(tokens.as_slice()).unwrap();
let elapsed = start.elapsed();
if verbose {
writeln!(
@@ -95,6 +95,7 @@ pub fn interactive(universe: &mut Universe, verbose: bool) -> Result<(), Error>
last_value = value;
}
Return::Exception(message) => println!("ERROR: {}", message),
+ Return::Restart => println!("ERROR: asked for a restart to the top-level"),
}
counter += 1;
}
diff --git a/som-interpreter/src/universe.rs b/som-interpreter/src/universe.rs
index 0cd93a98..3bccce27 100644
--- a/som-interpreter/src/universe.rs
+++ b/som-interpreter/src/universe.rs
@@ -4,28 +4,23 @@ use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::rc::Rc;
+use std::time::Instant;
use anyhow::{anyhow, Error};
use crate::class::Class;
use crate::frame::{Frame, FrameKind};
use crate::interner::{Interned, Interner};
+use crate::invokable::{Invoke, Return};
use crate::value::Value;
use crate::SOMRef;
-/// The central data structure for the interpreter.
+/// The core classes of the SOM interpreter.
///
-/// It represents the complete state of the interpreter, like the known class definitions,
-/// the string interner and the stack frames.
+/// This struct allows to always keep a reference to important classes,
+/// even in case of modifications to global bindings by user-defined code.
#[derive(Debug)]
-pub struct Universe {
- /// The string interner for symbols.
- pub interner: Interner,
- /// The known global bindings.
- pub globals: HashMap,
- /// The path to search in for new classes.
- pub classpath: Vec,
-
+pub struct CoreClasses {
/// The **Object** class.
pub object_class: SOMRef,
/// The **Class** class.
@@ -43,10 +38,10 @@ pub struct Universe {
pub array_class: SOMRef,
/// The **Method** class.
pub method_class: SOMRef,
- /// The **Symbol** class.
- pub symbol_class: SOMRef,
/// The **Primitive** class.
pub primitive_class: SOMRef,
+ /// The **Symbol** class.
+ pub symbol_class: SOMRef,
/// The **String** class.
pub string_class: SOMRef,
/// The **System** class.
@@ -67,15 +62,31 @@ pub struct Universe {
pub true_class: SOMRef,
/// The **False** class.
pub false_class: SOMRef,
+}
+/// The central data structure for the interpreter.
+///
+/// It represents the complete state of the interpreter, like the known class definitions,
+/// the string interner and the stack frames.
+pub struct Universe {
+ /// The string interner for symbols.
+ pub interner: Interner,
+ /// The known global bindings.
+ pub globals: HashMap,
+ /// The path to search in for new classes.
+ pub classpath: Vec,
+ /// The interpreter's core classes.
+ pub core: CoreClasses,
+ /// The time record of the universe's creation.
+ pub start_time: Instant,
/// The interpreter's stack frames.
pub frames: Vec>,
}
impl Universe {
/// Initialize the universe from the given classpath.
- pub fn from_classpath(classpath: Vec) -> Result {
- let symbol_table = Interner::with_capacity(100);
+ pub fn with_classpath(classpath: Vec) -> Result {
+ let interner = Interner::with_capacity(100);
let mut globals = HashMap::new();
let object_class = Self::load_system_class(classpath.as_slice(), "Object")?;
@@ -102,7 +113,12 @@ impl Universe {
let false_class = Self::load_system_class(classpath.as_slice(), "False")?;
// initializeSystemClass(objectClass, null, "Object");
- // set_super_class(&object_class, &nil_class);
+ // set_super_class(&object_class, &nil_class, &metaclass_class);
+ object_class
+ .borrow()
+ .class()
+ .borrow_mut()
+ .set_class(&metaclass_class);
object_class
.borrow()
.class()
@@ -140,37 +156,25 @@ impl Universe {
set_super_class(&true_class, &boolean_class, &metaclass_class);
set_super_class(&false_class, &boolean_class, &metaclass_class);
- globals.insert("Object".into(), Value::Class(object_class.borrow().class()));
- globals.insert("Class".into(), Value::Class(class_class.borrow().class()));
- globals.insert(
- "Metaclass".into(),
- Value::Class(metaclass_class.borrow().class()),
- );
- globals.insert("Nil".into(), Value::Class(nil_class.borrow().class()));
- globals.insert(
- "Integer".into(),
- Value::Class(integer_class.borrow().class()),
- );
- globals.insert("Array".into(), Value::Class(array_class.borrow().class()));
- globals.insert("Method".into(), Value::Class(method_class.borrow().class()));
- globals.insert("Symbol".into(), Value::Class(symbol_class.borrow().class()));
- globals.insert(
- "Primitive".into(),
- Value::Class(primitive_class.borrow().class()),
- );
- globals.insert("String".into(), Value::Class(string_class.borrow().class()));
- globals.insert("System".into(), Value::Class(system_class.borrow().class()));
- globals.insert("Double".into(), Value::Class(double_class.borrow().class()));
- globals.insert(
- "Boolean".into(),
- Value::Class(boolean_class.borrow().class()),
- );
- globals.insert("True".into(), Value::Class(true_class.borrow().class()));
- globals.insert("False".into(), Value::Class(false_class.borrow().class()));
- globals.insert("Block".into(), Value::Class(block_class.borrow().class()));
- globals.insert("Block1".into(), Value::Class(block1_class.borrow().class()));
- globals.insert("Block2".into(), Value::Class(block2_class.borrow().class()));
- globals.insert("Block3".into(), Value::Class(block3_class.borrow().class()));
+ globals.insert("Object".into(), Value::Class(object_class.clone()));
+ globals.insert("Class".into(), Value::Class(class_class.clone()));
+ globals.insert("Metaclass".into(), Value::Class(metaclass_class.clone()));
+ globals.insert("Nil".into(), Value::Class(nil_class.clone()));
+ globals.insert("Integer".into(), Value::Class(integer_class.clone()));
+ globals.insert("Array".into(), Value::Class(array_class.clone()));
+ globals.insert("Method".into(), Value::Class(method_class.clone()));
+ globals.insert("Symbol".into(), Value::Class(symbol_class.clone()));
+ globals.insert("Primitive".into(), Value::Class(primitive_class.clone()));
+ globals.insert("String".into(), Value::Class(string_class.clone()));
+ globals.insert("System".into(), Value::Class(system_class.clone()));
+ globals.insert("Double".into(), Value::Class(double_class.clone()));
+ globals.insert("Boolean".into(), Value::Class(boolean_class.clone()));
+ globals.insert("True".into(), Value::Class(true_class.clone()));
+ globals.insert("False".into(), Value::Class(false_class.clone()));
+ globals.insert("Block".into(), Value::Class(block_class.clone()));
+ globals.insert("Block1".into(), Value::Class(block1_class.clone()));
+ globals.insert("Block2".into(), Value::Class(block2_class.clone()));
+ globals.insert("Block3".into(), Value::Class(block3_class.clone()));
globals.insert("true".into(), Value::Boolean(true));
globals.insert("false".into(), Value::Boolean(false));
@@ -178,29 +182,32 @@ impl Universe {
globals.insert("system".into(), Value::System);
Ok(Self {
- interner: symbol_table,
globals,
+ interner,
classpath,
- object_class,
- class_class,
- metaclass_class,
- nil_class,
- integer_class,
- array_class,
- method_class,
- symbol_class,
- primitive_class,
- string_class,
- system_class,
- double_class,
- block_class,
- block1_class,
- block2_class,
- block3_class,
- boolean_class,
- true_class,
- false_class,
frames: Vec::new(),
+ start_time: Instant::now(),
+ core: CoreClasses {
+ object_class,
+ class_class,
+ metaclass_class,
+ nil_class,
+ integer_class,
+ array_class,
+ method_class,
+ symbol_class,
+ primitive_class,
+ string_class,
+ system_class,
+ double_class,
+ block_class,
+ block1_class,
+ block2_class,
+ block3_class,
+ boolean_class,
+ true_class,
+ false_class,
+ },
})
}
@@ -222,13 +229,13 @@ impl Universe {
};
// Collect all tokens from the file.
- let symbols: Vec<_> = som_lexer::Lexer::new(contents.as_str())
+ let tokens: Vec<_> = som_lexer::Lexer::new(contents.as_str())
.skip_comments(true)
.skip_whitespace(true)
.collect();
// Parse class definition from the tokens.
- let defn = match som_parser::parse_file(symbols.as_slice()) {
+ let defn = match som_parser::parse_file(tokens.as_slice()) {
Some(defn) => defn,
None => return Err(anyhow!("could not parse the '{}' system class", class_name)),
};
@@ -240,7 +247,7 @@ impl Universe {
));
}
- return Ok(Rc::new(RefCell::new(Class::from_class_def(defn))));
+ return Ok(Class::from_class_def(defn));
}
Err(anyhow!("could not find the '{}' system class", class_name))
@@ -260,13 +267,13 @@ impl Universe {
};
// Collect all tokens from the file.
- let symbols: Vec<_> = som_lexer::Lexer::new(contents.as_str())
+ let tokens: Vec<_> = som_lexer::Lexer::new(contents.as_str())
.skip_comments(true)
.skip_whitespace(true)
.collect();
// Parse class definition from the tokens.
- let defn = match som_parser::parse_file(symbols.as_slice()) {
+ let defn = match som_parser::parse_file(tokens.as_slice()) {
Some(defn) => defn,
None => continue,
};
@@ -280,19 +287,19 @@ impl Universe {
let super_class = if let Some(ref super_class) = defn.super_class {
match self.lookup_global(super_class) {
- Some(Value::Class(class)) => class,
- _ => return Err(anyhow!("{}: is not a known class")),
+ Some(Value::Class(super_class)) => super_class,
+ _ => self.load_class(super_class)?,
}
} else {
- self.object_class.clone()
+ self.core.object_class.clone()
};
- let class = Rc::new(RefCell::new(Class::from_class_def(defn)));
- set_super_class(&class, &super_class, &self.metaclass_class);
+ let class = Class::from_class_def(defn);
+ set_super_class(&class, &super_class, &self.core.metaclass_class);
self.globals.insert(
class.borrow().name().to_string(),
- Value::Class(class.borrow().class()),
+ Value::Class(class.clone()),
);
return Ok(class);
@@ -301,66 +308,119 @@ impl Universe {
Err(anyhow!("could not find the '{}' class", class_name))
}
+ /// Load a class from its path into this universe.
+ pub fn load_class_from_path(&mut self, path: impl AsRef) -> Result, Error> {
+ let path = path.as_ref();
+
+ // Read file contents.
+ let contents = match fs::read_to_string(path) {
+ Ok(contents) => contents,
+ Err(err) => return Err(Error::from(err)),
+ };
+
+ // Collect all tokens from the file.
+ let tokens: Vec<_> = som_lexer::Lexer::new(contents.as_str())
+ .skip_comments(true)
+ .skip_whitespace(true)
+ .collect();
+
+ // Parse class definition from the tokens.
+ let defn = match som_parser::parse_file(tokens.as_slice()) {
+ Some(defn) => defn,
+ None => return Err(Error::msg("could not parse file")),
+ };
+
+ if defn.name.as_str() != path.file_stem().unwrap() {
+ return Err(anyhow!(
+ "{}: class name is different from file name.",
+ path.display(),
+ ));
+ }
+
+ let super_class = if let Some(ref super_class) = defn.super_class {
+ match self.lookup_global(super_class) {
+ Some(Value::Class(class)) => class,
+ _ => self.load_class(super_class)?,
+ }
+ } else {
+ self.core.object_class.clone()
+ };
+
+ let class = Class::from_class_def(defn);
+ set_super_class(&class, &super_class, &self.core.metaclass_class);
+
+ Ok(class)
+ }
+
/// Get the **Nil** class.
pub fn nil_class(&self) -> SOMRef {
- self.nil_class.clone()
+ self.core.nil_class.clone()
}
/// Get the **System** class.
pub fn system_class(&self) -> SOMRef {
- self.system_class.clone()
+ self.core.system_class.clone()
}
/// Get the **Symbol** class.
pub fn symbol_class(&self) -> SOMRef {
- self.symbol_class.clone()
+ self.core.symbol_class.clone()
}
/// Get the **String** class.
pub fn string_class(&self) -> SOMRef {
- self.string_class.clone()
+ self.core.string_class.clone()
}
/// Get the **Array** class.
pub fn array_class(&self) -> SOMRef {
- self.array_class.clone()
+ self.core.array_class.clone()
}
/// Get the **Integer** class.
pub fn integer_class(&self) -> SOMRef {
- self.integer_class.clone()
+ self.core.integer_class.clone()
}
/// Get the **Double** class.
pub fn double_class(&self) -> SOMRef {
- self.double_class.clone()
+ self.core.double_class.clone()
}
/// Get the **Block** class.
pub fn block_class(&self) -> SOMRef {
- self.block_class.clone()
+ self.core.block_class.clone()
}
/// Get the **Block1** class.
pub fn block1_class(&self) -> SOMRef {
- self.block1_class.clone()
+ self.core.block1_class.clone()
}
/// Get the **Block2** class.
pub fn block2_class(&self) -> SOMRef {
- self.block2_class.clone()
+ self.core.block2_class.clone()
}
/// Get the **Block3** class.
pub fn block3_class(&self) -> SOMRef {
- self.block3_class.clone()
+ self.core.block3_class.clone()
}
/// Get the **True** class.
pub fn true_class(&self) -> SOMRef {
- self.true_class.clone()
+ self.core.true_class.clone()
}
/// Get the **False** class.
pub fn false_class(&self) -> SOMRef {
- self.false_class.clone()
+ self.core.false_class.clone()
}
/// Get the **Metaclass** class.
pub fn metaclass_class(&self) -> SOMRef {
- self.metaclass_class.clone()
+ self.core.metaclass_class.clone()
+ }
+
+ /// Get the **Method** class.
+ pub fn method_class(&self) -> SOMRef {
+ self.core.method_class.clone()
+ }
+ /// Get the **Primitive** class.
+ pub fn primitive_class(&self) -> SOMRef {
+ self.core.primitive_class.clone()
}
}
@@ -375,22 +435,13 @@ impl Universe {
}
/// Get the current frame.
- pub fn current_frame(&self) -> SOMRef {
- self.frames.last().expect("no frames left").clone()
+ pub fn current_frame(&self) -> &SOMRef {
+ self.frames.last().expect("no frames left")
}
/// Get the method invocation frame for the current frame.
pub fn current_method_frame(&self) -> SOMRef {
- let mut frame = self.current_frame();
- loop {
- let kind = frame.borrow().kind().clone();
- frame = match kind {
- FrameKind::Block(frame_ref) => frame_ref
- .upgrade()
- .expect("block frame has escaped method frame"),
- FrameKind::Method(_) => return frame,
- }
- }
+ Frame::method_frame(self.current_frame())
}
/// Intern a symbol.
@@ -404,7 +455,7 @@ impl Universe {
}
/// Search for a local binding.
- pub fn lookup(&self, name: impl AsRef) -> Option {
+ pub fn lookup_local(&self, name: impl AsRef) -> Option {
let name = name.as_ref();
match name {
"self" => {
@@ -416,7 +467,7 @@ impl Universe {
let frame = self.current_frame();
let class = frame.borrow().get_self().class(self);
let super_class = class.borrow().super_class();
- Some(Value::Class(super_class))
+ Some(super_class.map(Value::Class).unwrap_or(Value::Nil))
}
name => self.current_frame().borrow().lookup_local(name),
}
@@ -432,6 +483,40 @@ impl Universe {
pub fn assign_local(&mut self, name: impl AsRef, value: Value) -> Option<()> {
self.current_frame().borrow_mut().assign_local(name, value)
}
+
+ /// Assign a value to a global binding.
+ pub fn assign_global(&mut self, name: impl AsRef, value: Value) -> Option<()> {
+ self.globals
+ .insert(name.as_ref().to_string(), value)
+ .map(|_| ())
+ }
+
+ /// Call `System>>#initialize:` with the given name, if it is defined.
+ pub fn initialize(&mut self, args: Vec) -> Option {
+ let initialize = Value::System.lookup_method(self, "initialize:")?;
+ let args = Value::Array(Rc::new(RefCell::new(args)));
+
+ Some(initialize.invoke(self, vec![Value::System, args]))
+ }
+}
+
+impl Universe {
+ /// Call `System>>#unknownGlobal:` with the given name, if it is defined.
+ pub fn unknown_global(&mut self, name: impl AsRef) -> Option {
+ let sym = self.intern_symbol(name.as_ref());
+ let method = Value::System.lookup_method(self, "unknownGlobal:")?;
+
+ match method.invoke(self, vec![Value::System, Value::Symbol(sym)]) {
+ Return::Local(value) | Return::NonLocal(value, _) => Some(Return::Local(value)),
+ Return::Exception(err) => Some(Return::Exception(format!(
+ "(from 'System>>#unknownGlobal:') {}",
+ err,
+ ))),
+ Return::Restart => Some(Return::Exception(
+ "(from 'System>>#unknownGlobal:') incorrectly asked for a restart".to_string(),
+ )),
+ }
+ }
}
fn set_super_class(
diff --git a/som-interpreter/src/value.rs b/som-interpreter/src/value.rs
index ef166b6d..370fcbe8 100644
--- a/som-interpreter/src/value.rs
+++ b/som-interpreter/src/value.rs
@@ -1,3 +1,4 @@
+use std::fmt;
use std::rc::Rc;
use crate::block::Block;
@@ -9,7 +10,7 @@ use crate::universe::Universe;
use crate::SOMRef;
/// Represents an SOM value.
-#[derive(Debug, Clone)]
+#[derive(Clone)]
pub enum Value {
/// The **nil** value.
Nil,
@@ -28,11 +29,13 @@ pub enum Value {
/// An array of values.
Array(SOMRef>),
/// A block value, ready to be evaluated.
- Block(Block),
+ Block(Rc),
/// A generic (non-primitive) class instance.
Instance(SOMRef),
/// A bare class object.
Class(SOMRef),
+ /// A bare invokable.
+ Invokable(SOMRef, Rc),
}
impl Value {
@@ -51,6 +54,7 @@ impl Value {
Self::Block(block) => block.class(universe),
Self::Instance(instance) => instance.borrow().class(),
Self::Class(class) => class.borrow().class(),
+ Self::Invokable(_, invokable) => invokable.class(universe),
}
}
@@ -59,17 +63,8 @@ impl Value {
&self,
universe: &Universe,
signature: impl AsRef,
- ) -> Option {
- let signature = signature.as_ref();
- if let Self::Class(ref class) = self {
- // dbg!(class.borrow().name());
- // dbg!(signature);
- class.borrow().lookup_method(signature)
- } else {
- // dbg!(self);
- // dbg!(signature);
- self.class(universe).borrow().lookup_method(signature)
- }
+ ) -> Option> {
+ self.class(universe).borrow().lookup_method(signature)
}
/// Search for a local binding within this value.
@@ -122,6 +117,12 @@ impl Value {
instance.borrow().class().borrow().name(),
),
Self::Class(class) => class.borrow().name().to_string(),
+ Self::Invokable(holder, invokable) => match invokable.as_ref() {
+ Invokable::MethodDef(defn) => {
+ format!("{}>>#{}", holder.borrow().name(), defn.signature)
+ }
+ _ => format!("instance of Primitive"),
+ },
}
}
}
@@ -137,8 +138,35 @@ impl PartialEq for Value {
(Self::Symbol(a), Self::Symbol(b)) => a.eq(b),
(Self::Array(a), Self::Array(b)) => a.eq(b),
(Self::Instance(a), Self::Instance(b)) => Rc::ptr_eq(a, b),
- (Self::Class(a), Self::Class(b)) => Rc::ptr_eq(a, b),
+ (Self::Class(a), Self::Class(b)) => a.as_ptr() == b.as_ptr(),
_ => false,
}
}
}
+
+impl fmt::Debug for Value {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Nil => f.debug_tuple("Nil").finish(),
+ Self::System => f.debug_tuple("System").finish(),
+ Self::Boolean(val) => f.debug_tuple("Boolean").field(val).finish(),
+ Self::Integer(val) => f.debug_tuple("Integer").field(val).finish(),
+ Self::Double(val) => f.debug_tuple("Double").field(val).finish(),
+ Self::Symbol(val) => f.debug_tuple("Symbol").field(val).finish(),
+ Self::String(val) => f.debug_tuple("String").field(val).finish(),
+ Self::Array(val) => f.debug_tuple("Array").field(&val.borrow()).finish(),
+ Self::Block(val) => f.debug_tuple("Block").field(val).finish(),
+ Self::Instance(val) => f.debug_tuple("Instance").field(&val.borrow()).finish(),
+ Self::Class(val) => f.debug_tuple("Class").field(&val.borrow()).finish(),
+ Self::Invokable(holder, val) => {
+ let signature = match val.as_ref() {
+ Invokable::MethodDef(defn) => {
+ format!("{}>>#{}", holder.borrow().name(), defn.signature)
+ }
+ _ => format!("instance of Primitive"),
+ };
+ f.debug_tuple("Invokable").field(&signature).finish()
+ }
+ }
+ }
+}
diff --git a/som-parser-symbols/tests/tests.rs b/som-parser-symbols/tests/tests.rs
index ca97d34a..cc2416bb 100644
--- a/som-parser-symbols/tests/tests.rs
+++ b/som-parser-symbols/tests/tests.rs
@@ -6,12 +6,12 @@ use som_parser_symbols::Parser;
#[test]
fn literal_tests() {
- let syms: Vec = Lexer::new("1.2 5 #foo 'test'")
+ let tokens: Vec = Lexer::new("1.2 5 #foo 'test'")
.skip_whitespace(true)
.collect();
let parser = many(literal());
- let result = parser.parse(syms.as_slice());
+ let result = parser.parse(tokens.as_slice());
assert!(result.is_some(), "input did not parse successfully");
let (literals, rest) = result.unwrap();
@@ -27,12 +27,12 @@ fn literal_tests() {
#[test]
fn expression_test_1() {
- let syms: Vec = Lexer::new("3 + counter get")
+ let tokens: Vec = Lexer::new("3 + counter get")
.skip_whitespace(true)
.collect();
let parser = expression();
- let result = parser.parse(syms.as_slice());
+ let result = parser.parse(tokens.as_slice());
assert!(result.is_some(), "input did not parse successfully");
let (expression, rest) = result.unwrap();
@@ -54,13 +54,13 @@ fn expression_test_1() {
#[test]
fn block_test() {
- let syms: Vec =
+ let tokens: Vec =
Lexer::new("[ :test | |local| local := 'this is correct'. local println. ]")
.skip_whitespace(true)
.collect();
let parser = block();
- let result = parser.parse(syms.as_slice());
+ let result = parser.parse(tokens.as_slice());
assert!(result.is_some(), "input did not parse successfully");
let (block, rest) = result.unwrap();
@@ -93,14 +93,14 @@ fn block_test() {
#[test]
fn expression_test_2() {
- let syms: Vec = Lexer::new(
+ let tokens: Vec = Lexer::new(
"( 3 == 3 ) ifTrue: [ 'this is correct' println. ] ifFalse: [ 'oh no' println ]",
)
.skip_whitespace(true)
.collect();
let parser = expression();
- let result = parser.parse(syms.as_slice());
+ let result = parser.parse(tokens.as_slice());
assert!(result.is_some(), "input did not parse successfully");
let (expression, rest) = result.unwrap();
@@ -156,11 +156,11 @@ fn expression_test_2() {
#[test]
fn primary_test() {
- let syms: Vec = Lexer::new("[ self fib: (n - 1) + (self fib: (n - 2)) ]")
+ let tokens: Vec = Lexer::new("[ self fib: (n - 1) + (self fib: (n - 2)) ]")
.skip_whitespace(true)
.collect();
let parser = primary();
- let result = parser.parse(syms.as_slice());
+ let result = parser.parse(tokens.as_slice());
assert!(result.is_some(), "input did not parse successfully");
let (primary, rest) = result.unwrap();
diff --git a/som-parser-text/tests/tests.rs b/som-parser-text/tests/tests.rs
index a011026a..fcb91f68 100644
--- a/som-parser-text/tests/tests.rs
+++ b/som-parser-text/tests/tests.rs
@@ -5,10 +5,10 @@ use som_parser_text::Parser;
#[test]
fn literal_tests() {
- let syms: Vec = "1.2 5 #foo 'test'".chars().collect();
+ let tokens: Vec = "1.2 5 #foo 'test'".chars().collect();
let parser = sep_by(spacing(), literal());
- let result = parser.parse(syms.as_slice());
+ let result = parser.parse(tokens.as_slice());
assert!(result.is_some(), "input did not parse successfully");
let (literals, rest) = result.unwrap();
@@ -24,10 +24,10 @@ fn literal_tests() {
#[test]
fn expression_test_1() {
- let syms: Vec = "3 + counter get".chars().collect();
+ let tokens: Vec = "3 + counter get".chars().collect();
let parser = expression();
- let result = parser.parse(syms.as_slice());
+ let result = parser.parse(tokens.as_slice());
assert!(result.is_some(), "input did not parse successfully");
let (expression, rest) = result.unwrap();
@@ -49,12 +49,12 @@ fn expression_test_1() {
#[test]
fn block_test() {
- let syms: Vec = "[ :test | |local| local := 'this is correct'. local println. ]"
+ let tokens: Vec = "[ :test | |local| local := 'this is correct'. local println. ]"
.chars()
.collect();
let parser = block();
- let result = parser.parse(syms.as_slice());
+ let result = parser.parse(tokens.as_slice());
assert!(result.is_some(), "input did not parse successfully");
let (block, rest) = result.unwrap();
@@ -87,13 +87,13 @@ fn block_test() {
#[test]
fn expression_test_2() {
- let syms: Vec =
+ let tokens: Vec =
"( 3 == 3 ) ifTrue: [ 'this is correct' println. ] ifFalse: [ 'oh no' println ]"
.chars()
.collect();
let parser = expression();
- let result = parser.parse(syms.as_slice());
+ let result = parser.parse(tokens.as_slice());
assert!(result.is_some(), "input did not parse successfully");
let (expression, rest) = result.unwrap();
@@ -149,11 +149,11 @@ fn expression_test_2() {
#[test]
fn primary_test() {
- let syms: Vec = "[ self fib: (n - 1) + (self fib: (n - 2)) ]"
+ let tokens: Vec = "[ (self fib: (n - 1)) + (self fib: (n - 2)) ]"
.chars()
.collect();
let parser = primary();
- let result = parser.parse(syms.as_slice());
+ let result = parser.parse(tokens.as_slice());
assert!(result.is_some(), "input did not parse successfully");
let (primary, rest) = result.unwrap();
@@ -165,45 +165,52 @@ fn primary_test() {
parameters: vec![],
locals: vec![],
body: Body {
- exprs: vec![Expression::Message(Message {
- receiver: Box::new(Expression::Reference(String::from("self"))),
- signature: String::from("fib:"),
- values: vec![Expression::BinaryOp(BinaryOp {
- op: String::from("+"),
- lhs: Box::new(Expression::Term(Term {
- body: Body {
- exprs: vec![Expression::BinaryOp(BinaryOp {
- op: String::from("-"),
- lhs: Box::new(Expression::Reference(String::from("n"))),
- rhs: Box::new(Expression::Literal(Literal::Integer(1))),
- })],
- full_stopped: false,
- }
- })),
- rhs: Box::new(Expression::Term(Term {
- body: Body {
- exprs: vec![Expression::Message(Message {
- receiver: Box::new(Expression::Reference(String::from("self"))),
- signature: String::from("fib:"),
- values: vec![Expression::Term(Term {
- body: Body {
- exprs: vec![Expression::BinaryOp(BinaryOp {
- op: String::from("-"),
- lhs: Box::new(Expression::Reference(String::from(
- "n"
- ))),
- rhs: Box::new(Expression::Literal(
- Literal::Integer(2)
- )),
+ exprs: vec![Expression::Term(Term {
+ body: Body {
+ exprs: vec![Expression::Message(Message {
+ receiver: Box::new(Expression::Reference(String::from("self"))),
+ signature: String::from("fib:"),
+ values: vec![Expression::BinaryOp(BinaryOp {
+ op: String::from("+"),
+ lhs: Box::new(Expression::Term(Term {
+ body: Body {
+ exprs: vec![Expression::BinaryOp(BinaryOp {
+ op: String::from("-"),
+ lhs: Box::new(Expression::Reference(String::from("n"))),
+ rhs: Box::new(Expression::Literal(Literal::Integer(1))),
+ })],
+ full_stopped: false,
+ }
+ })),
+ rhs: Box::new(Expression::Term(Term {
+ body: Body {
+ exprs: vec![Expression::Message(Message {
+ receiver: Box::new(Expression::Reference(
+ String::from("self")
+ )),
+ signature: String::from("fib:"),
+ values: vec![Expression::Term(Term {
+ body: Body {
+ exprs: vec![Expression::BinaryOp(BinaryOp {
+ op: String::from("-"),
+ lhs: Box::new(Expression::Reference(
+ String::from("n")
+ )),
+ rhs: Box::new(Expression::Literal(
+ Literal::Integer(2)
+ )),
+ })],
+ full_stopped: false,
+ }
})],
- full_stopped: false,
- }
- })],
- })],
- full_stopped: false,
- }
- }))
- })],
+ })],
+ full_stopped: false,
+ }
+ }))
+ })],
+ })],
+ full_stopped: false
+ }
})],
full_stopped: false,
}