From 8229c4a5d629f17b63c114ba0871b498b72cc1c7 Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Fri, 5 Jun 2020 11:28:41 +0200 Subject: [PATCH] Lots of improvements to `som-interpreter` Added `Method` and `Primitive` class support Removed `Block>>#whileTrue` as a primitive Implemented `Block>>#restart` Implemented more primitives Improved how arguments are checked for correctness in primitives Fixed class hierarchies and lookups Blocks can now be evaluated outside of their original frame Replaced most panics by in-interpreter exceptions Improved string representations of values Renamed `som_lexer::Symbol` to `som_lexer::Token` Added more documentation Completed README.md Added `core-lib` as a submodule from `SOM-st/SOM` --- .gitmodules | 3 + Cargo.lock | 82 +++--- README.md | 60 +++++ core-lib | 1 + som-interpreter/Cargo.toml | 8 +- som-interpreter/src/block.rs | 15 +- som-interpreter/src/class.rs | 44 ++-- som-interpreter/src/evaluate.rs | 100 +++++--- som-interpreter/src/frame.rs | 25 +- som-interpreter/src/instance.rs | 38 ++- som-interpreter/src/invokable.rs | 44 +++- som-interpreter/src/main.rs | 56 ++-- som-interpreter/src/primitives/array.rs | 87 +++---- som-interpreter/src/primitives/blocks.rs | 57 +++-- som-interpreter/src/primitives/class.rs | 78 ++++-- som-interpreter/src/primitives/double.rs | 148 ++++++----- som-interpreter/src/primitives/integer.rs | 118 +++++---- som-interpreter/src/primitives/method.rs | 54 ++++ som-interpreter/src/primitives/mod.rs | 30 +++ som-interpreter/src/primitives/object.rs | 159 +++++++++++- som-interpreter/src/primitives/string.rs | 177 ++++++++++--- som-interpreter/src/primitives/symbol.rs | 14 +- som-interpreter/src/primitives/system.rs | 133 +++++++--- som-interpreter/src/shell.rs | 5 +- som-interpreter/src/universe.rs | 297 ++++++++++++++-------- som-interpreter/src/value.rs | 56 +++- som-parser-symbols/tests/tests.rs | 20 +- som-parser-text/tests/tests.rs | 103 ++++---- 28 files changed, 1373 insertions(+), 639 deletions(-) create mode 100644 .gitmodules create mode 160000 core-lib create mode 100644 som-interpreter/src/primitives/method.rs 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, }