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