From 697375d33b352f1d5b7d4e46b7bff29580789565 Mon Sep 17 00:00:00 2001 From: Nicolas Polomack Date: Tue, 4 Aug 2020 14:59:55 +0200 Subject: [PATCH] Completed the SOM bytecode interpreter --- .github/workflows/bench.yaml | 7 +- .github/workflows/tests.yaml | 9 +- Cargo.lock | 18 +- Cargo.toml | 4 +- rebench.conf | 10 +- som-compiler/Cargo.toml | 18 - som-compiler/src/lib.rs | 3 - som-core/src/bytecode.rs | 164 ++--- .../Cargo.toml | 9 +- .../README.md | 0 .../src/block.rs | 0 .../src/class.rs | 0 .../src/evaluate.rs | 0 .../src/frame.rs | 0 .../src/hashcode.rs | 0 .../src/instance.rs | 0 .../src/interner.rs | 0 .../src/invokable.rs | 0 .../src/lib.rs | 0 .../src/main.rs | 6 +- .../src/method.rs | 0 .../src/primitives/array.rs | 0 .../src/primitives/blocks.rs | 0 .../src/primitives/class.rs | 0 .../src/primitives/double.rs | 0 .../src/primitives/integer.rs | 0 .../src/primitives/method.rs | 0 .../src/primitives/mod.rs | 0 .../src/primitives/object.rs | 0 .../src/primitives/string.rs | 0 .../src/primitives/symbol.rs | 0 .../src/primitives/system.rs | 0 .../src/shell.rs | 10 +- .../src/universe.rs | 0 .../src/value.rs | 0 .../tests/basic_interpreter_tests.rs | 10 +- som-interpreter-bc/Cargo.toml | 31 + .../README.md | 0 som-interpreter-bc/src/block.rs | 45 ++ som-interpreter-bc/src/class.rs | 114 ++++ som-interpreter-bc/src/compiler.rs | 591 +++++++++++++++++ som-interpreter-bc/src/frame.rs | 192 ++++++ som-interpreter-bc/src/hashcode.rs | 111 ++++ som-interpreter-bc/src/instance.rs | 64 ++ som-interpreter-bc/src/interner.rs | 81 +++ som-interpreter-bc/src/interpreter.rs | 406 ++++++++++++ som-interpreter-bc/src/lib.rs | 36 + som-interpreter-bc/src/main.rs | 103 +++ som-interpreter-bc/src/method.rs | 216 ++++++ som-interpreter-bc/src/primitives/array.rs | 97 +++ som-interpreter-bc/src/primitives/blocks.rs | 104 +++ som-interpreter-bc/src/primitives/class.rs | 109 ++++ som-interpreter-bc/src/primitives/double.rs | 269 ++++++++ som-interpreter-bc/src/primitives/integer.rs | 613 ++++++++++++++++++ som-interpreter-bc/src/primitives/method.rs | 58 ++ som-interpreter-bc/src/primitives/mod.rs | 58 ++ som-interpreter-bc/src/primitives/object.rs | 272 ++++++++ som-interpreter-bc/src/primitives/string.rs | 211 ++++++ som-interpreter-bc/src/primitives/symbol.rs | 29 + som-interpreter-bc/src/primitives/system.rs | 169 +++++ som-interpreter-bc/src/shell.rs | 168 +++++ som-interpreter-bc/src/universe.rs | 612 +++++++++++++++++ som-interpreter-bc/src/value.rs | 186 ++++++ .../tests/basic_interpreter_tests.rs | 183 ++++++ 64 files changed, 5243 insertions(+), 153 deletions(-) delete mode 100644 som-compiler/Cargo.toml delete mode 100644 som-compiler/src/lib.rs rename {som-interpreter => som-interpreter-ast}/Cargo.toml (87%) rename {som-compiler => som-interpreter-ast}/README.md (100%) rename {som-interpreter => som-interpreter-ast}/src/block.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/class.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/evaluate.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/frame.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/hashcode.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/instance.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/interner.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/invokable.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/lib.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/main.rs (95%) rename {som-interpreter => som-interpreter-ast}/src/method.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/primitives/array.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/primitives/blocks.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/primitives/class.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/primitives/double.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/primitives/integer.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/primitives/method.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/primitives/mod.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/primitives/object.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/primitives/string.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/primitives/symbol.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/primitives/system.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/shell.rs (94%) rename {som-interpreter => som-interpreter-ast}/src/universe.rs (100%) rename {som-interpreter => som-interpreter-ast}/src/value.rs (100%) rename {som-interpreter => som-interpreter-ast}/tests/basic_interpreter_tests.rs (96%) create mode 100644 som-interpreter-bc/Cargo.toml rename {som-interpreter => som-interpreter-bc}/README.md (100%) create mode 100644 som-interpreter-bc/src/block.rs create mode 100644 som-interpreter-bc/src/class.rs create mode 100644 som-interpreter-bc/src/compiler.rs create mode 100644 som-interpreter-bc/src/frame.rs create mode 100644 som-interpreter-bc/src/hashcode.rs create mode 100644 som-interpreter-bc/src/instance.rs create mode 100644 som-interpreter-bc/src/interner.rs create mode 100644 som-interpreter-bc/src/interpreter.rs create mode 100644 som-interpreter-bc/src/lib.rs create mode 100644 som-interpreter-bc/src/main.rs create mode 100644 som-interpreter-bc/src/method.rs create mode 100644 som-interpreter-bc/src/primitives/array.rs create mode 100644 som-interpreter-bc/src/primitives/blocks.rs create mode 100644 som-interpreter-bc/src/primitives/class.rs create mode 100644 som-interpreter-bc/src/primitives/double.rs create mode 100644 som-interpreter-bc/src/primitives/integer.rs create mode 100644 som-interpreter-bc/src/primitives/method.rs create mode 100644 som-interpreter-bc/src/primitives/mod.rs create mode 100644 som-interpreter-bc/src/primitives/object.rs create mode 100644 som-interpreter-bc/src/primitives/string.rs create mode 100644 som-interpreter-bc/src/primitives/symbol.rs create mode 100644 som-interpreter-bc/src/primitives/system.rs create mode 100644 som-interpreter-bc/src/shell.rs create mode 100644 som-interpreter-bc/src/universe.rs create mode 100644 som-interpreter-bc/src/value.rs create mode 100644 som-interpreter-bc/tests/basic_interpreter_tests.rs diff --git a/.github/workflows/bench.yaml b/.github/workflows/bench.yaml index 13b64d71..89b7229e 100644 --- a/.github/workflows/bench.yaml +++ b/.github/workflows/bench.yaml @@ -10,6 +10,11 @@ jobs: rebench: name: Run and report benchmarks runs-on: ubuntu-latest + strategy: + matrix: + interpreter: + - som-interpreter-ast + - som-interpreter-bc steps: - name: Checkout master branch uses: actions/checkout@v2 @@ -24,7 +29,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: build - args: --release -p som-interpreter + args: --release -p som-interpreter-ast -p som-interpreter-bc - name: Install ReBench run: | pip install setuptools diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index bd173a21..e4cecac5 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -10,6 +10,11 @@ jobs: run_test_suite: name: Run SOM Test Suite runs-on: ubuntu-latest + strategy: + matrix: + interpreter: + - som-interpreter-ast + - som-interpreter-bc steps: - name: Checkout master branch uses: actions/checkout@v2 @@ -24,10 +29,10 @@ jobs: uses: actions-rs/cargo@v1 with: command: build - args: --release -p som-interpreter + args: --release -p ${{ matrix.interpreter }} - name: Run test suite run: | - ./target/release/som-interpreter -c core-lib/Smalltalk core-lib/TestSuite -- TestHarness + ./target/release/${{ matrix.interpreter }} -c core-lib/Smalltalk core-lib/TestSuite -- TestHarness run_own_tests: name: Run own tests diff --git a/Cargo.lock b/Cargo.lock index 6ce9cb4a..d8db751a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,20 +206,26 @@ dependencies = [ ] [[package]] -name = "som-compiler" +name = "som-core" +version = "0.1.0" + +[[package]] +name = "som-interpreter-ast" version = "0.1.0" dependencies = [ "anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "som-core 0.1.0", + "som-lexer 0.1.0", + "som-parser-symbols 0.1.0", + "structopt 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "som-core" -version = "0.1.0" - -[[package]] -name = "som-interpreter" +name = "som-interpreter-bc" version = "0.1.0" dependencies = [ "anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 44944819..dc7c85ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [workspace] members = [ "som-core", - "som-compiler", - "som-interpreter", + "som-interpreter-bc", + "som-interpreter-ast", "som-lexer", "som-parser-core", "som-parser-symbols", diff --git a/rebench.conf b/rebench.conf index 49933eba..24394210 100644 --- a/rebench.conf +++ b/rebench.conf @@ -55,9 +55,12 @@ benchmark_suites: - Mandelbrot: {extra_args: 30} executors: - som-rs: + som-rs-ast: + path: . + executable: ./target/release/som-interpreter-ast + som-rs-bc: path: . - executable: ./target/release/som-interpreter + executable: ./target/release/som-interpreter-bc # define the benchmarks to be executed for a re-executable benchmark run experiments: @@ -67,4 +70,5 @@ experiments: - micro - macro executions: - - som-rs + - som-rs-ast + - som-rs-bc diff --git a/som-compiler/Cargo.toml b/som-compiler/Cargo.toml deleted file mode 100644 index 7060f05d..00000000 --- a/som-compiler/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "som-compiler" -version = "0.1.0" -description = "A bytecode compiler for the Simple Object Machine" -authors = ["Nicolas Polomack "] -edition = "2018" -publish = false -license = "MIT OR Apache-2.0" - -[dependencies] -# internal -som-core = { path = "../som-core", version = "0.1.0" } - -# error handling -anyhow = "1.0.31" - -# big integers -num-bigint = "0.2.6" diff --git a/som-compiler/src/lib.rs b/som-compiler/src/lib.rs deleted file mode 100644 index 3936a4a0..00000000 --- a/som-compiler/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! -//! This is the bytecode compiler for the Simple Object Machine. -//! diff --git a/som-core/src/bytecode.rs b/som-core/src/bytecode.rs index f32d038d..1d5a111f 100644 --- a/som-core/src/bytecode.rs +++ b/som-core/src/bytecode.rs @@ -2,24 +2,23 @@ use std::fmt; #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[rustfmt::skip] pub enum Bytecode { - Halt = 0, - Dup = 1, - PushLocal = 2, - PushArgument = 3, - PushField = 4, - PushBlock = 5, - PushConstant = 6, - PushGlobal = 7, - Pop = 8, - PopLocal = 9, - PopArgument = 10, - PopField = 11, - Send = 12, - SuperSend = 13, - ReturnLocal = 14, - ReturnNonLocal = 15, + Halt, + Dup, + PushLocal(u8, u8), + PushArgument(u8, u8), + PushField(u8), + PushBlock(u8), + PushConstant(u8), + PushGlobal(u8), + Pop, + PopLocal(u8, u8), + PopArgument(u8, u8), + PopField(u8), + Send(u8), + SuperSend(u8), + ReturnLocal, + ReturnNonLocal, } impl Bytecode { @@ -28,22 +27,22 @@ impl Bytecode { pub fn name(self) -> &'static str { // NAMES[self as usize] match self { - Self::Halt => "HALT", - Self::Dup => "DUP", - Self::PushLocal => "PUSH_LOCAL", - Self::PushArgument => "PUSH_ARGUMENT", - Self::PushField => "PUSH_FIELD", - Self::PushBlock => "PUSH_BLOCK", - Self::PushConstant => "PUSH_CONSTANT", - Self::PushGlobal => "PUSH_GLOBAL", - Self::Pop => "POP", - Self::PopLocal => "POP_LOCAL", - Self::PopArgument => "POP_ARGUMENT", - Self::PopField => "POP_FIELD", - Self::Send => "SEND", - Self::SuperSend => "SUPER_SEND", - Self::ReturnLocal => "RETURN_LOCAL", - Self::ReturnNonLocal => "RETURN_NON_LOCAL", + Self::Halt => "HALT", + Self::Dup => "DUP", + Self::PushLocal(_, _) => "PUSH_LOCAL", + Self::PushArgument(_, _) => "PUSH_ARGUMENT", + Self::PushField(_) => "PUSH_FIELD", + Self::PushBlock(_) => "PUSH_BLOCK", + Self::PushConstant(_) => "PUSH_CONSTANT", + Self::PushGlobal(_) => "PUSH_GLOBAL", + Self::Pop => "POP", + Self::PopLocal(_, _) => "POP_LOCAL", + Self::PopArgument(_, _) => "POP_ARGUMENT", + Self::PopField(_) => "POP_FIELD", + Self::Send(_) => "SEND", + Self::SuperSend(_) => "SUPER_SEND", + Self::ReturnLocal => "RETURN_LOCAL", + Self::ReturnNonLocal => "RETURN_NON_LOCAL", } } @@ -52,69 +51,22 @@ impl Bytecode { pub fn padded_name(self) -> &'static str { // PADDED_NAMES[self as usize] match self { - Self::Halt => "HALT ", - Self::Dup => "DUP ", - Self::PushLocal => "PUSH_LOCAL ", - Self::PushArgument => "PUSH_ARGUMENT ", - Self::PushField => "PUSH_FIELD ", - Self::PushBlock => "PUSH_BLOCK ", - Self::PushConstant => "PUSH_CONSTANT ", - Self::PushGlobal => "PUSH_GLOBAL ", - Self::Pop => "POP ", - Self::PopLocal => "POP_LOCAL ", - Self::PopArgument => "POP_ARGUMENT ", - Self::PopField => "POP_FIELD ", - Self::Send => "SEND ", - Self::SuperSend => "SUPER_SEND ", - Self::ReturnLocal => "RETURN_LOCAL ", - Self::ReturnNonLocal => "RETURN_NON_LOCAL", - } - } - - /// Get the number of bytes to read to process the instruction. - #[rustfmt::skip] - pub fn bytecode_len(self) -> usize { - match self { - Self::Halt => 1, - Self::Dup => 1, - Self::PushLocal => 3, - Self::PushArgument => 3, - Self::PushField => 2, - Self::PushBlock => 2, - Self::PushConstant => 2, - Self::PushGlobal => 2, - Self::Pop => 1, - Self::PopLocal => 3, - Self::PopArgument => 3, - Self::PopField => 2, - Self::Send => 2, - Self::SuperSend => 2, - Self::ReturnLocal => 1, - Self::ReturnNonLocal => 1, - } - } - - /// Attempt to convert a raw byte to an instruction. - #[rustfmt::skip] - pub fn from_byte(byte: u8) -> Option { - match byte { - 0 => Some(Self::Halt), - 1 => Some(Self::Dup), - 2 => Some(Self::PushLocal), - 3 => Some(Self::PushArgument), - 4 => Some(Self::PushField), - 5 => Some(Self::PushBlock), - 6 => Some(Self::PushConstant), - 7 => Some(Self::PushGlobal), - 8 => Some(Self::Pop), - 9 => Some(Self::PopLocal), - 10 => Some(Self::PopArgument), - 11 => Some(Self::PopField), - 12 => Some(Self::Send), - 13 => Some(Self::SuperSend), - 14 => Some(Self::ReturnLocal), - 15 => Some(Self::ReturnNonLocal), - _ => None, + Self::Halt => "HALT ", + Self::Dup => "DUP ", + Self::PushLocal(_, _) => "PUSH_LOCAL ", + Self::PushArgument(_, _) => "PUSH_ARGUMENT ", + Self::PushField(_) => "PUSH_FIELD ", + Self::PushBlock(_) => "PUSH_BLOCK ", + Self::PushConstant(_) => "PUSH_CONSTANT ", + Self::PushGlobal(_) => "PUSH_GLOBAL ", + Self::Pop => "POP ", + Self::PopLocal(_, _) => "POP_LOCAL ", + Self::PopArgument(_, _) => "POP_ARGUMENT ", + Self::PopField(_) => "POP_FIELD ", + Self::Send(_) => "SEND ", + Self::SuperSend(_) => "SUPER_SEND ", + Self::ReturnLocal => "RETURN_LOCAL ", + Self::ReturnNonLocal => "RETURN_NON_LOCAL", } } } @@ -158,7 +110,25 @@ pub static PADDED_NAMES: [&str; 16] = [ ]; impl fmt::Display for Bytecode { + #[rustfmt::skip] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.name()) + match self { + Self::Halt => write!(f, "HALT"), + Self::Dup => write!(f, "DUP"), + Self::PushLocal(up_idx, idx) => write!(f, "PUSH_LOCAL {}, {}", up_idx, idx), + Self::PushArgument(up_idx, idx) => write!(f, "PUSH_ARGUMENT {}, {}", up_idx, idx), + Self::PushField(idx) => write!(f, "PUSH_FIELD {}", idx), + Self::PushBlock(idx) => write!(f, "PUSH_BLOCK {}", idx), + Self::PushConstant(idx) => write!(f, "PUSH_CONSTANT {}", idx), + Self::PushGlobal(idx) => write!(f, "PUSH_GLOBAL {}", idx), + Self::Pop => write!(f, "POP"), + Self::PopLocal(up_idx, idx) => write!(f, "POP_LOCAL {}, {}", up_idx, idx), + Self::PopArgument(up_idx, idx) => write!(f, "POP_ARGUMENT {}, {}", up_idx, idx), + Self::PopField(idx) => write!(f, "POP_FIELD {}", idx), + Self::Send(idx) => write!(f, "SEND {}", idx), + Self::SuperSend(idx) => write!(f, "SUPER_SEND {}", idx), + Self::ReturnLocal => write!(f, "RETURN_LOCAL", ), + Self::ReturnNonLocal => write!(f, "RETURN_NON_LOCAL", ), + } } } diff --git a/som-interpreter/Cargo.toml b/som-interpreter-ast/Cargo.toml similarity index 87% rename from som-interpreter/Cargo.toml rename to som-interpreter-ast/Cargo.toml index 582760e3..d01fd063 100644 --- a/som-interpreter/Cargo.toml +++ b/som-interpreter-ast/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "som-interpreter" +name = "som-interpreter-ast" version = "0.1.0" description = "An interpreter for the Simple Object Machine" authors = ["Nicolas Polomack "] @@ -7,13 +7,6 @@ edition = "2018" publish = false license = "MIT OR Apache-2.0" -[lib] -path = "src/lib.rs" - -[[bin]] -name = "som-interpreter" -path = "src/main.rs" - [dependencies] # internal som-core = { path = "../som-core", version = "0.1.0" } diff --git a/som-compiler/README.md b/som-interpreter-ast/README.md similarity index 100% rename from som-compiler/README.md rename to som-interpreter-ast/README.md diff --git a/som-interpreter/src/block.rs b/som-interpreter-ast/src/block.rs similarity index 100% rename from som-interpreter/src/block.rs rename to som-interpreter-ast/src/block.rs diff --git a/som-interpreter/src/class.rs b/som-interpreter-ast/src/class.rs similarity index 100% rename from som-interpreter/src/class.rs rename to som-interpreter-ast/src/class.rs diff --git a/som-interpreter/src/evaluate.rs b/som-interpreter-ast/src/evaluate.rs similarity index 100% rename from som-interpreter/src/evaluate.rs rename to som-interpreter-ast/src/evaluate.rs diff --git a/som-interpreter/src/frame.rs b/som-interpreter-ast/src/frame.rs similarity index 100% rename from som-interpreter/src/frame.rs rename to som-interpreter-ast/src/frame.rs diff --git a/som-interpreter/src/hashcode.rs b/som-interpreter-ast/src/hashcode.rs similarity index 100% rename from som-interpreter/src/hashcode.rs rename to som-interpreter-ast/src/hashcode.rs diff --git a/som-interpreter/src/instance.rs b/som-interpreter-ast/src/instance.rs similarity index 100% rename from som-interpreter/src/instance.rs rename to som-interpreter-ast/src/instance.rs diff --git a/som-interpreter/src/interner.rs b/som-interpreter-ast/src/interner.rs similarity index 100% rename from som-interpreter/src/interner.rs rename to som-interpreter-ast/src/interner.rs diff --git a/som-interpreter/src/invokable.rs b/som-interpreter-ast/src/invokable.rs similarity index 100% rename from som-interpreter/src/invokable.rs rename to som-interpreter-ast/src/invokable.rs diff --git a/som-interpreter/src/lib.rs b/som-interpreter-ast/src/lib.rs similarity index 100% rename from som-interpreter/src/lib.rs rename to som-interpreter-ast/src/lib.rs diff --git a/som-interpreter/src/main.rs b/som-interpreter-ast/src/main.rs similarity index 95% rename from som-interpreter/src/main.rs rename to som-interpreter-ast/src/main.rs index e47801d2..3a49d246 100644 --- a/som-interpreter/src/main.rs +++ b/som-interpreter-ast/src/main.rs @@ -11,9 +11,9 @@ use structopt::StructOpt; mod shell; -use som_interpreter::invokable::Return; -use som_interpreter::universe::Universe; -use som_interpreter::value::Value; +use som_interpreter_ast::invokable::Return; +use som_interpreter_ast::universe::Universe; +use som_interpreter_ast::value::Value; #[derive(Debug, Clone, PartialEq, StructOpt)] #[structopt(about, author)] diff --git a/som-interpreter/src/method.rs b/som-interpreter-ast/src/method.rs similarity index 100% rename from som-interpreter/src/method.rs rename to som-interpreter-ast/src/method.rs diff --git a/som-interpreter/src/primitives/array.rs b/som-interpreter-ast/src/primitives/array.rs similarity index 100% rename from som-interpreter/src/primitives/array.rs rename to som-interpreter-ast/src/primitives/array.rs diff --git a/som-interpreter/src/primitives/blocks.rs b/som-interpreter-ast/src/primitives/blocks.rs similarity index 100% rename from som-interpreter/src/primitives/blocks.rs rename to som-interpreter-ast/src/primitives/blocks.rs diff --git a/som-interpreter/src/primitives/class.rs b/som-interpreter-ast/src/primitives/class.rs similarity index 100% rename from som-interpreter/src/primitives/class.rs rename to som-interpreter-ast/src/primitives/class.rs diff --git a/som-interpreter/src/primitives/double.rs b/som-interpreter-ast/src/primitives/double.rs similarity index 100% rename from som-interpreter/src/primitives/double.rs rename to som-interpreter-ast/src/primitives/double.rs diff --git a/som-interpreter/src/primitives/integer.rs b/som-interpreter-ast/src/primitives/integer.rs similarity index 100% rename from som-interpreter/src/primitives/integer.rs rename to som-interpreter-ast/src/primitives/integer.rs diff --git a/som-interpreter/src/primitives/method.rs b/som-interpreter-ast/src/primitives/method.rs similarity index 100% rename from som-interpreter/src/primitives/method.rs rename to som-interpreter-ast/src/primitives/method.rs diff --git a/som-interpreter/src/primitives/mod.rs b/som-interpreter-ast/src/primitives/mod.rs similarity index 100% rename from som-interpreter/src/primitives/mod.rs rename to som-interpreter-ast/src/primitives/mod.rs diff --git a/som-interpreter/src/primitives/object.rs b/som-interpreter-ast/src/primitives/object.rs similarity index 100% rename from som-interpreter/src/primitives/object.rs rename to som-interpreter-ast/src/primitives/object.rs diff --git a/som-interpreter/src/primitives/string.rs b/som-interpreter-ast/src/primitives/string.rs similarity index 100% rename from som-interpreter/src/primitives/string.rs rename to som-interpreter-ast/src/primitives/string.rs diff --git a/som-interpreter/src/primitives/symbol.rs b/som-interpreter-ast/src/primitives/symbol.rs similarity index 100% rename from som-interpreter/src/primitives/symbol.rs rename to som-interpreter-ast/src/primitives/symbol.rs diff --git a/som-interpreter/src/primitives/system.rs b/som-interpreter-ast/src/primitives/system.rs similarity index 100% rename from som-interpreter/src/primitives/system.rs rename to som-interpreter-ast/src/primitives/system.rs diff --git a/som-interpreter/src/shell.rs b/som-interpreter-ast/src/shell.rs similarity index 94% rename from som-interpreter/src/shell.rs rename to som-interpreter-ast/src/shell.rs index c5af0691..f2e705d9 100644 --- a/som-interpreter/src/shell.rs +++ b/som-interpreter-ast/src/shell.rs @@ -7,11 +7,11 @@ use anyhow::Error; use som_lexer::{Lexer, Token}; use som_parser::lang; -use som_interpreter::evaluate::Evaluate; -use som_interpreter::frame::FrameKind; -use som_interpreter::invokable::Return; -use som_interpreter::universe::Universe; -use som_interpreter::value::Value; +use som_interpreter_ast::evaluate::Evaluate; +use som_interpreter_ast::frame::FrameKind; +use som_interpreter_ast::invokable::Return; +use som_interpreter_ast::universe::Universe; +use som_interpreter_ast::value::Value; /// Launches an interactive Read-Eval-Print-Loop within the given universe. pub fn interactive(universe: &mut Universe, verbose: bool) -> Result<(), Error> { diff --git a/som-interpreter/src/universe.rs b/som-interpreter-ast/src/universe.rs similarity index 100% rename from som-interpreter/src/universe.rs rename to som-interpreter-ast/src/universe.rs diff --git a/som-interpreter/src/value.rs b/som-interpreter-ast/src/value.rs similarity index 100% rename from som-interpreter/src/value.rs rename to som-interpreter-ast/src/value.rs diff --git a/som-interpreter/tests/basic_interpreter_tests.rs b/som-interpreter-ast/tests/basic_interpreter_tests.rs similarity index 96% rename from som-interpreter/tests/basic_interpreter_tests.rs rename to som-interpreter-ast/tests/basic_interpreter_tests.rs index b161f331..8e835bd6 100644 --- a/som-interpreter/tests/basic_interpreter_tests.rs +++ b/som-interpreter-ast/tests/basic_interpreter_tests.rs @@ -1,10 +1,10 @@ use std::path::PathBuf; -use som_interpreter::evaluate::Evaluate; -use som_interpreter::frame::FrameKind; -use som_interpreter::invokable::Return; -use som_interpreter::universe::Universe; -use som_interpreter::value::Value; +use som_interpreter_ast::evaluate::Evaluate; +use som_interpreter_ast::frame::FrameKind; +use som_interpreter_ast::invokable::Return; +use som_interpreter_ast::universe::Universe; +use som_interpreter_ast::value::Value; use som_lexer::{Lexer, Token}; use som_parser::lang; diff --git a/som-interpreter-bc/Cargo.toml b/som-interpreter-bc/Cargo.toml new file mode 100644 index 00000000..b725b4c7 --- /dev/null +++ b/som-interpreter-bc/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "som-interpreter-bc" +version = "0.1.0" +description = "A bytecode compiler for the Simple Object Machine" +authors = ["Nicolas Polomack "] +edition = "2018" +publish = false +license = "MIT OR Apache-2.0" + +[dependencies] +# 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", 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.31" + +# consistently-ordered hashmaps +indexmap = "1.4.0" + +# big integers +num-bigint = "0.2.6" +num-traits = "0.2.11" + +# random numbers +rand = "0.7.3" diff --git a/som-interpreter/README.md b/som-interpreter-bc/README.md similarity index 100% rename from som-interpreter/README.md rename to som-interpreter-bc/README.md diff --git a/som-interpreter-bc/src/block.rs b/som-interpreter-bc/src/block.rs new file mode 100644 index 00000000..81b144bb --- /dev/null +++ b/som-interpreter-bc/src/block.rs @@ -0,0 +1,45 @@ +use std::fmt; + +use som_core::bytecode::Bytecode; + +use crate::class::Class; +use crate::compiler::Literal; +use crate::frame::Frame; +use crate::universe::Universe; +use crate::value::Value; +use crate::SOMRef; + +/// Represents an executable block. +#[derive(Clone)] +pub struct Block { + /// Reference to the captured stack frame. + pub frame: Option>, + pub locals: Vec, + pub literals: Vec, + pub body: Vec, + pub nb_params: usize, +} + +impl Block { + /// Get the block's class. + pub fn class(&self, universe: &Universe) -> SOMRef { + match self.nb_parameters() { + 0 => universe.block1_class(), + 1 => universe.block2_class(), + 2 => universe.block3_class(), + _ => panic!("no support for blocks with more than 2 paramters"), + } + } + + /// Retrieve the number of parameters this block accepts. + pub fn nb_parameters(&self) -> usize { + self.nb_params + } +} + +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-bc/src/class.rs b/som-interpreter-bc/src/class.rs new file mode 100644 index 00000000..2f41b74c --- /dev/null +++ b/som-interpreter-bc/src/class.rs @@ -0,0 +1,114 @@ +use std::fmt; +use std::rc::Rc; + +use indexmap::IndexMap; + +use crate::interner::Interned; +use crate::method::Method; +use crate::value::Value; +use crate::{SOMRef, SOMWeakRef}; + +/// A reference that may be either weak or owned/strong. +#[derive(Debug, Clone)] +pub enum MaybeWeak { + /// An owned reference. + Strong(SOMRef), + /// A weak reference. + Weak(SOMWeakRef), +} + +/// Represents a loaded class. +#[derive(Clone)] +pub struct Class { + /// The class' name. + pub name: String, + /// 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: IndexMap, + /// The class' methods/invokables. + pub methods: IndexMap>, + /// Is this class a static one ? + pub is_static: bool, +} + +impl Class { + /// Get the class' name. + pub fn name(&self) -> &str { + self.name.as_str() + } + + /// Get the class of this class. + pub fn class(&self) -> SOMRef { + match self.class { + MaybeWeak::Weak(ref weak) => weak.upgrade().unwrap_or_else(|| { + panic!("superclass dropped, cannot upgrade ref ({})", self.name()) + }), + MaybeWeak::Strong(ref owned) => owned.clone(), + } + } + + /// Set the class of this class (as a weak reference). + pub fn set_class(&mut self, class: &SOMRef) { + 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) -> Option> { + self.super_class.upgrade() + } + + /// Set the superclass of this class (as a weak reference). + pub fn set_super_class(&mut self, class: &SOMRef) { + self.super_class = Rc::downgrade(class); + } + + /// Search for a given method within this class. + pub fn lookup_method(&self, signature: Interned) -> Option> { + self.methods.get(&signature).cloned().or_else(|| { + self.super_class + .upgrade()? + .borrow() + .lookup_method(signature) + }) + } + + /// Search for a local binding. + pub fn lookup_local(&self, idx: usize) -> Option { + self.locals.values().nth(idx).cloned().or_else(|| { + let super_class = self.super_class()?; + let local = super_class.borrow_mut().lookup_local(idx)?; + Some(local) + }) + } + + /// Assign a value to a local binding. + pub fn assign_local(&mut self, idx: usize, value: Value) -> Option<()> { + if let Some(local) = self.locals.values_mut().nth(idx) { + *local = value; + return Some(()); + } + let super_class = self.super_class()?; + super_class.borrow_mut().assign_local(idx, value)?; + Some(()) + } +} + +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.keys()) + // .field("class", &self.class) + // .field("super_class", &self.super_class) + .finish() + } +} diff --git a/som-interpreter-bc/src/compiler.rs b/som-interpreter-bc/src/compiler.rs new file mode 100644 index 00000000..be653d9c --- /dev/null +++ b/som-interpreter-bc/src/compiler.rs @@ -0,0 +1,591 @@ +//! +//! This is the bytecode compiler for the Simple Object Machine. +//! +use std::cell::RefCell; +use std::hash::{Hash, Hasher}; +use std::rc::{Rc, Weak}; + +use indexmap::{IndexMap, IndexSet}; +use num_bigint::BigInt; + +use som_core::ast; +use som_core::bytecode::Bytecode; + +use crate::block::Block; +use crate::class::{Class, MaybeWeak}; +use crate::interner::{Interned, Interner}; +use crate::method::{Method, MethodEnv, MethodKind}; +use crate::value::Value; +use crate::SOMRef; + +#[derive(Debug, Clone)] +pub enum Literal { + Symbol(Interned), + String(Rc), + Double(f64), + Integer(i64), + BigInteger(BigInt), + Array(Vec), + Block(Rc), +} + +impl PartialEq for Literal { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Literal::Symbol(val1), Literal::Symbol(val2)) => val1.eq(val2), + (Literal::String(val1), Literal::String(val2)) => val1.eq(val2), + (Literal::Double(val1), Literal::Double(val2)) => val1.eq(val2), + (Literal::Integer(val1), Literal::Integer(val2)) => val1.eq(val2), + (Literal::BigInteger(val1), Literal::BigInteger(val2)) => val1.eq(val2), + (Literal::Array(val1), Literal::Array(val2)) => val1.eq(val2), + (Literal::Block(val1), Literal::Block(val2)) => Rc::ptr_eq(val1, val2), + _ => false, + } + } +} + +impl Eq for Literal {} + +impl Hash for Literal { + fn hash(&self, state: &mut H) { + match self { + Literal::Symbol(val) => { + state.write(b"sym#"); + val.hash(state); + } + Literal::String(val) => { + state.write(b"string#"); + val.hash(state); + } + Literal::Double(val) => { + state.write(b"dbl#"); + val.to_bits().hash(state); + } + Literal::Integer(val) => { + state.write(b"int#"); + val.hash(state); + } + Literal::BigInteger(val) => { + state.write(b"bigint#"); + val.hash(state); + } + Literal::Array(val) => { + state.write(b"array#"); + val.hash(state); + } + Literal::Block(val) => { + state.write(b"blk"); + val.hash(state); + } + } + } +} + +enum FoundVar { + Local(u8, u8), + Argument(u8, u8), + Field(u8), +} + +trait GenCtxt { + fn find_var(&mut self, name: &str) -> Option; + fn intern_symbol(&mut self, name: &str) -> Interned; + fn class_name(&self) -> &str; +} + +trait InnerGenCtxt: GenCtxt { + fn as_gen_ctxt(&mut self) -> &mut dyn GenCtxt; + fn push_instr(&mut self, instr: Bytecode); + fn push_arg(&mut self, name: String) -> usize; + fn push_local(&mut self, name: String) -> usize; + fn push_literal(&mut self, literal: Literal) -> usize; +} + +struct BlockGenCtxt<'a> { + pub outer: &'a mut dyn GenCtxt, + pub args: IndexSet, + pub locals: IndexSet, + pub literals: IndexSet, + pub body: Option>, +} + +impl GenCtxt for BlockGenCtxt<'_> { + fn find_var(&mut self, name: &str) -> Option { + let name = match name { + "super" => "self", + name => name, + }; + (self.locals.get_index_of(name)) + .map(|idx| FoundVar::Local(0, idx as u8)) + .or_else(|| (self.args.get_index_of(name)).map(|idx| FoundVar::Argument(0, idx as u8))) + .or_else(|| { + self.outer.find_var(name).map(|found| match found { + FoundVar::Local(up_idx, idx) => FoundVar::Local(up_idx + 1, idx), + FoundVar::Argument(up_idx, idx) => FoundVar::Argument(up_idx + 1, idx), + FoundVar::Field(idx) => FoundVar::Field(idx), + }) + }) + } + + fn intern_symbol(&mut self, name: &str) -> Interned { + self.outer.intern_symbol(name) + } + + fn class_name(&self) -> &str { + self.outer.class_name() + } +} + +impl InnerGenCtxt for BlockGenCtxt<'_> { + fn as_gen_ctxt(&mut self) -> &mut dyn GenCtxt { + self + } + + fn push_instr(&mut self, instr: Bytecode) { + let body = self.body.get_or_insert_with(|| vec![]); + body.push(instr); + } + + fn push_arg(&mut self, name: String) -> usize { + let (idx, _) = self.args.insert_full(name); + idx + } + + fn push_local(&mut self, name: String) -> usize { + let (idx, _) = self.locals.insert_full(name); + idx + } + + fn push_literal(&mut self, literal: Literal) -> usize { + let (idx, _) = self.literals.insert_full(literal); + idx + } +} + +struct MethodGenCtxt<'a> { + pub signature: String, + pub inner: BlockGenCtxt<'a>, +} + +impl MethodGenCtxt<'_> {} + +impl GenCtxt for MethodGenCtxt<'_> { + fn find_var(&mut self, name: &str) -> Option { + self.inner.find_var(name) + } + + fn intern_symbol(&mut self, name: &str) -> Interned { + self.inner.intern_symbol(name) + } + + fn class_name(&self) -> &str { + self.inner.class_name() + } +} + +impl InnerGenCtxt for MethodGenCtxt<'_> { + fn as_gen_ctxt(&mut self) -> &mut dyn GenCtxt { + self + } + + fn push_instr(&mut self, instr: Bytecode) { + self.inner.push_instr(instr) + } + + fn push_arg(&mut self, name: String) -> usize { + self.inner.push_arg(name) + } + + fn push_local(&mut self, name: String) -> usize { + self.inner.push_local(name) + } + + fn push_literal(&mut self, literal: Literal) -> usize { + self.inner.push_literal(literal) + } +} + +trait MethodCodegen { + fn codegen(&self, ctxt: &mut dyn InnerGenCtxt) -> Option<()>; +} + +impl MethodCodegen for ast::Body { + fn codegen(&self, ctxt: &mut dyn InnerGenCtxt) -> Option<()> { + for expr in &self.exprs { + expr.codegen(ctxt)?; + } + Some(()) + } +} + +impl MethodCodegen for ast::Expression { + fn codegen(&self, ctxt: &mut dyn InnerGenCtxt) -> Option<()> { + match self { + ast::Expression::Reference(name) => { + match ctxt.find_var(name.as_str()) { + Some(FoundVar::Local(up_idx, idx)) => { + ctxt.push_instr(Bytecode::PushLocal(up_idx, idx)) + } + Some(FoundVar::Argument(up_idx, idx)) => { + ctxt.push_instr(Bytecode::PushArgument(up_idx, idx)) + } + Some(FoundVar::Field(idx)) => ctxt.push_instr(Bytecode::PushField(idx)), + None => { + let name = ctxt.intern_symbol(name); + let idx = ctxt.push_literal(Literal::Symbol(name)); + ctxt.push_instr(Bytecode::PushGlobal(idx as u8)); + } + } + Some(()) + } + ast::Expression::Assignment(name, expr) => { + expr.codegen(ctxt)?; + ctxt.push_instr(Bytecode::Dup); + match ctxt.find_var(name.as_str())? { + FoundVar::Local(up_idx, idx) => { + ctxt.push_instr(Bytecode::PopLocal(up_idx, idx)) + } + FoundVar::Argument(up_idx, idx) => { + ctxt.push_instr(Bytecode::PopArgument(up_idx, idx)) + } + FoundVar::Field(idx) => ctxt.push_instr(Bytecode::PopField(idx)), + } + Some(()) + } + ast::Expression::Message(message) => { + let super_send = match message.receiver.as_ref() { + ast::Expression::Reference(value) if value == "super" => true, + _ => false, + }; + message.receiver.codegen(ctxt)?; + message + .values + .iter() + .try_for_each(|value| value.codegen(ctxt))?; + let sym = ctxt.intern_symbol(message.signature.as_str()); + let idx = ctxt.push_literal(Literal::Symbol(sym)); + if super_send { + ctxt.push_instr(Bytecode::SuperSend(idx as u8)); + } else { + ctxt.push_instr(Bytecode::Send(idx as u8)); + } + Some(()) + } + ast::Expression::BinaryOp(message) => { + message.lhs.codegen(ctxt)?; + message.rhs.codegen(ctxt)?; + let sym = ctxt.intern_symbol(message.op.as_str()); + let idx = ctxt.push_literal(Literal::Symbol(sym)); + ctxt.push_instr(Bytecode::Send(idx as u8)); + Some(()) + } + ast::Expression::Exit(expr) => { + expr.codegen(ctxt)?; + ctxt.push_instr(Bytecode::ReturnNonLocal); + Some(()) + } + ast::Expression::Literal(literal) => { + fn convert_literal(ctxt: &mut dyn InnerGenCtxt, literal: &ast::Literal) -> Literal { + match literal { + ast::Literal::Symbol(val) => { + Literal::Symbol(ctxt.intern_symbol(val.as_str())) + } + ast::Literal::String(val) => Literal::String(Rc::new(val.clone())), + ast::Literal::Double(val) => Literal::Double(*val), + ast::Literal::Integer(val) => Literal::Integer(*val), + ast::Literal::BigInteger(val) => Literal::BigInteger(val.parse().unwrap()), + ast::Literal::Array(val) => { + let literals = val + .iter() + .map(|val| { + let literal = convert_literal(ctxt, val); + ctxt.push_literal(literal) as u8 + }) + .collect(); + Literal::Array(literals) + } + } + } + + let literal = convert_literal(ctxt, literal); + let idx = ctxt.push_literal(literal); + ctxt.push_instr(Bytecode::PushConstant(idx as u8)); + Some(()) + } + ast::Expression::Block(val) => { + let block = compile_block(ctxt.as_gen_ctxt(), val)?; + let block = Rc::new(block); + let block = Literal::Block(block); + let idx = ctxt.push_literal(block); + ctxt.push_instr(Bytecode::PushBlock(idx as u8)); + Some(()) + } + ast::Expression::Term(term) => term + .body + .exprs + .iter() + .try_for_each(|expr| expr.codegen(ctxt)), + } + } +} + +struct ClassGenCtxt<'a> { + pub name: String, + pub fields: IndexSet, + pub methods: IndexMap>, + pub interner: &'a mut Interner, +} + +impl GenCtxt for ClassGenCtxt<'_> { + fn find_var(&mut self, name: &str) -> Option { + let sym = self.interner.intern(name); + self.fields + .get_index_of(&sym) + .map(|idx| FoundVar::Field(idx as u8)) + } + + fn intern_symbol(&mut self, name: &str) -> Interned { + self.interner.intern(name) + } + + fn class_name(&self) -> &str { + self.name.as_str() + } +} + +fn compile_method(outer: &mut dyn GenCtxt, defn: &ast::MethodDef) -> Option { + // println!("(method) compiling '{}' ...", defn.signature); + + let mut ctxt = MethodGenCtxt { + signature: defn.signature.clone(), + inner: BlockGenCtxt { + outer, + args: { + let mut args = IndexSet::new(); + args.insert(String::from("self")); + args + }, + locals: match &defn.body { + ast::MethodBody::Primitive => IndexSet::new(), + ast::MethodBody::Body { locals, .. } => locals.iter().cloned().collect(), + }, + literals: IndexSet::new(), + body: None, + }, + }; + + match &defn.kind { + ast::MethodKind::Unary => {} + ast::MethodKind::Positional { parameters } => { + for param in parameters { + ctxt.push_arg(param.clone()); + } + } + ast::MethodKind::Operator { rhs } => { + ctxt.push_arg(rhs.clone()); + } + } + + match &defn.body { + ast::MethodBody::Primitive => {} + ast::MethodBody::Body { body, .. } => { + for expr in &body.exprs { + expr.codegen(&mut ctxt)?; + ctxt.push_instr(Bytecode::Pop); + } + ctxt.push_instr(Bytecode::PushArgument(0, 0)); + ctxt.push_instr(Bytecode::ReturnLocal); + } + } + + let method = Method { + kind: match &defn.body { + ast::MethodBody::Primitive => MethodKind::primitive_from_signature( + ctxt.class_name().split_whitespace().next().unwrap(), + defn.signature.as_str(), + ), + // ast::MethodBody::Primitive => MethodKind::NotImplemented(defn.signature.clone()), + ast::MethodBody::Body { .. } => { + let env = MethodEnv { + locals: ctxt.inner.locals.iter().map(|_| Value::Nil).collect(), + literals: ctxt.inner.literals.into_iter().collect(), + body: ctxt.inner.body.unwrap_or_default(), + }; + MethodKind::Defined(env) + } + }, + holder: Weak::new(), + signature: ctxt.signature, + }; + + // println!("(method) compiled '{}' !", defn.signature); + + Some(method) +} + +fn compile_block(outer: &mut dyn GenCtxt, defn: &ast::Block) -> Option { + // println!("(system) compiling block ..."); + + let mut ctxt = BlockGenCtxt { + outer, + args: defn.parameters.iter().cloned().collect(), + locals: defn.locals.iter().cloned().collect(), + literals: IndexSet::new(), + body: None, + }; + + let splitted = defn.body.exprs.split_last(); + if let Some((last, rest)) = splitted { + for expr in rest { + expr.codegen(&mut ctxt)?; + ctxt.push_instr(Bytecode::Pop); + } + last.codegen(&mut ctxt)?; + ctxt.push_instr(Bytecode::ReturnLocal); + } + + let block = Block { + frame: None, + locals: ctxt.locals.into_iter().map(|_| Value::Nil).collect(), + literals: ctxt.literals.into_iter().collect(), + body: ctxt.body.unwrap_or_default(), + nb_params: ctxt.args.len(), + }; + + // println!("(system) compiled block !"); + + Some(block) +} + +// println!("compiling '{}' ...", defn.name); +pub fn compile_class( + interner: &mut Interner, + defn: &ast::ClassDef, + super_class: Option<&SOMRef>, +) -> Option> { + let mut locals = IndexSet::new(); + + fn collect_static_locals( + interner: &mut Interner, + class: &SOMRef, + locals: &mut IndexSet, + ) { + if let Some(class) = class.borrow().super_class() { + collect_static_locals(interner, &class, locals); + } + locals.extend(class.borrow().locals.keys().copied()); + } + + if let Some(super_class) = super_class { + collect_static_locals(interner, &super_class.borrow().class(), &mut locals); + } + + locals.extend( + defn.static_locals + .iter() + .map(|name| interner.intern(name.as_str())), + ); + + let mut static_class_ctxt = ClassGenCtxt { + name: format!("{} class", defn.name), + fields: locals, + methods: IndexMap::new(), + interner, + }; + + let static_class = Rc::new(RefCell::new(Class { + name: static_class_ctxt.name.clone(), + class: MaybeWeak::Weak(Weak::new()), + super_class: Weak::new(), + locals: IndexMap::new(), + methods: IndexMap::new(), + is_static: true, + })); + + for method in &defn.static_methods { + let mut method = compile_method(&mut static_class_ctxt, method)?; + let signature = static_class_ctxt.interner.intern(method.signature.as_str()); + method.holder = Rc::downgrade(&static_class); + static_class_ctxt.methods.insert(signature, Rc::new(method)); + } + + let mut static_class_mut = static_class.borrow_mut(); + static_class_mut.locals = static_class_ctxt + .fields + .into_iter() + .map(|name| (name, Value::Nil)) + .collect(); + static_class_mut.methods = static_class_ctxt.methods; + drop(static_class_mut); + + // for method in static_class.borrow().methods.values() { + // println!("{}", method); + // } + + let mut locals = IndexSet::new(); + + fn collect_instance_locals( + interner: &mut Interner, + class: &SOMRef, + locals: &mut IndexSet, + ) { + if let Some(class) = class.borrow().super_class() { + collect_instance_locals(interner, &class, locals); + } + locals.extend(class.borrow().locals.keys()); + } + + if let Some(super_class) = super_class { + collect_instance_locals(interner, super_class, &mut locals); + } + + locals.extend( + defn.instance_locals + .iter() + .map(|name| interner.intern(name.as_str())), + ); + + let mut instance_class_ctxt = ClassGenCtxt { + name: defn.name.clone(), + fields: locals, + methods: IndexMap::new(), + interner, + }; + + let instance_class = Rc::new(RefCell::new(Class { + name: instance_class_ctxt.name.clone(), + class: MaybeWeak::Strong(static_class.clone()), + super_class: Weak::new(), + locals: IndexMap::new(), + methods: IndexMap::new(), + is_static: false, + })); + + for method in &defn.instance_methods { + let mut method = compile_method(&mut instance_class_ctxt, method)?; + let signature = instance_class_ctxt + .interner + .intern(method.signature.as_str()); + method.holder = Rc::downgrade(&instance_class); + instance_class_ctxt + .methods + .insert(signature, Rc::new(method)); + } + + let mut instance_class_mut = instance_class.borrow_mut(); + instance_class_mut.locals = instance_class_ctxt + .fields + .into_iter() + .map(|name| (name, Value::Nil)) + .collect(); + instance_class_mut.methods = instance_class_ctxt.methods; + drop(instance_class_mut); + + // for method in instance_class.borrow().methods.values() { + // println!("{}", method); + // } + + // println!("compiled '{}' !", defn.name); + + Some(instance_class) +} diff --git a/som-interpreter-bc/src/frame.rs b/som-interpreter-bc/src/frame.rs new file mode 100644 index 00000000..79910843 --- /dev/null +++ b/som-interpreter-bc/src/frame.rs @@ -0,0 +1,192 @@ +use std::rc::Rc; + +use som_core::bytecode::Bytecode; + +use crate::block::Block; +use crate::class::Class; +use crate::compiler::Literal; +use crate::method::{Method, MethodKind}; +use crate::value::Value; +use crate::SOMRef; + +/// The kind of a given frame. +#[derive(Clone)] +pub enum FrameKind { + /// A frame created from a block evaluation. + Block { + /// The block instance for the current frame. + block: Rc, + }, + /// A frame created from a method invocation. + Method { + /// The holder of the current method (used for lexical self/super). + holder: SOMRef, + /// The current method. + method: Rc, + /// The self value. + self_value: Value, + }, +} + +/// Represents a stack frame. +pub struct Frame { + /// This frame's kind. + pub kind: FrameKind, + /// The arguments within this frame. + pub args: Vec, + /// The bindings within this frame. + pub locals: Vec, + /// Execution stack. + pub stack: Vec, + /// Bytecode index. + pub bytecode_idx: usize, +} + +impl Frame { + /// Construct a new empty frame from its kind. + pub fn from_kind(kind: FrameKind) -> Self { + match &kind { + FrameKind::Block { block } => { + let locals = block.locals.iter().map(|_| Value::Nil).collect(); + Self { + kind, + locals, + args: vec![], + stack: vec![], + bytecode_idx: 0, + } + } + FrameKind::Method { method, .. } => { + if let MethodKind::Defined(env) = method.kind() { + let locals = env.locals.iter().map(|_| Value::Nil).collect(); + Self { + kind, + locals, + args: vec![], + stack: vec![], + bytecode_idx: 0, + } + } else { + Self { + kind, + locals: vec![], + args: vec![], + stack: vec![], + bytecode_idx: 0, + } + } + } + } + } + + /// Get the frame's kind. + pub fn kind(&self) -> &FrameKind { + &self.kind + } + + /// Get the self value for this frame. + pub fn get_self(&self) -> Value { + match &self.kind { + FrameKind::Method { self_value, .. } => self_value.clone(), + FrameKind::Block { block, .. } => block.frame.as_ref().unwrap().borrow().get_self(), + } + } + + /// Get the holder for this current method. + pub fn get_method_holder(&self) -> SOMRef { + match &self.kind { + FrameKind::Method { holder, .. } => holder.clone(), + FrameKind::Block { block, .. } => { + block.frame.as_ref().unwrap().borrow().get_method_holder() + } + } + } + + /// Get the bytecode at the specified index for the current method. + pub fn get_bytecode(&self, idx: usize) -> Option { + match &self.kind { + FrameKind::Method { method, .. } => match method.kind() { + MethodKind::Defined(env) => env.body.get(idx).copied(), + MethodKind::Primitive(_) => None, + MethodKind::NotImplemented(_) => None, + }, + FrameKind::Block { block, .. } => block.body.get(idx).copied(), + } + } + + /// Get the current bytecode for the current method. + pub fn get_current_bytecode(&self) -> Option { + self.get_bytecode(self.bytecode_idx) + } + + pub fn lookup_constant(&self, idx: usize) -> Option { + match self.kind() { + FrameKind::Block { block } => block.literals.get(idx).cloned(), + FrameKind::Method { method, .. } => match method.kind() { + MethodKind::Defined(env) => env.literals.get(idx).cloned(), + MethodKind::Primitive(_) => None, + MethodKind::NotImplemented(_) => None, + }, + } + } + + pub fn lookup_argument(&self, idx: usize) -> Option { + self.args.get(idx).cloned() + } + + /// Search for a local binding. + pub fn lookup_local(&self, idx: usize) -> Option { + self.locals.get(idx).cloned() + // if let Some(value) = self.locals.get(idx).cloned() { + // return Some(value); + // } + // match &self.kind { + // FrameKind::Method { + // self_value, holder, .. + // } => { + // if holder.borrow().is_static { + // holder.borrow().lookup_local(idx) + // } else { + // self_value.lookup_local(idx) + // } + // } + // FrameKind::Block { block, .. } => { + // block.frame.as_ref().unwrap().borrow().lookup_local(idx) + // } + // } + } + + /// Assign to a local binding. + pub fn assign_local(&mut self, idx: usize, value: Value) -> Option<()> { + // if let Some(local) = self.locals.get_mut(idx) { + // *local = value; + // return Some(()); + // } + // match &mut self.kind { + // FrameKind::Method { + // self_value, holder, .. + // } => { + // if holder.borrow().is_static { + // holder.borrow_mut().assign_local(idx, value) + // } else { + // self_value.assign_local(idx, value) + // } + // } + // FrameKind::Block { block, .. } => block + // .frame + // .as_ref() + // .unwrap() + // .borrow_mut() + // .assign_local(idx, value), + // } + self.locals.get_mut(idx).map(|local| *local = value) + } + + /// Get the method invocation frame for that frame. + pub fn method_frame(frame: &SOMRef) -> SOMRef { + match frame.borrow().kind() { + FrameKind::Block { block, .. } => Frame::method_frame(block.frame.as_ref().unwrap()), + FrameKind::Method { .. } => frame.clone(), + } + } +} diff --git a/som-interpreter-bc/src/hashcode.rs b/som-interpreter-bc/src/hashcode.rs new file mode 100644 index 00000000..6b375808 --- /dev/null +++ b/som-interpreter-bc/src/hashcode.rs @@ -0,0 +1,111 @@ +use std::hash::{Hash, Hasher}; + +use crate::block::Block; +use crate::class::Class; +use crate::instance::Instance; +use crate::method::Method; +use crate::value::Value; + +impl Hash for Value { + fn hash(&self, hasher: &mut H) { + match self { + Value::Nil => { + hasher.write(b"#nil#"); + } + Value::System => { + hasher.write(b"#system#"); + } + Value::Boolean(value) => { + hasher.write(b"#bool#"); + value.hash(hasher); + } + Value::Integer(value) => { + hasher.write(b"#int#"); + value.hash(hasher); + } + Value::BigInteger(value) => { + hasher.write(b"#bigint#"); + value.hash(hasher); + } + Value::Double(value) => { + hasher.write(b"#double#"); + let raw_bytes: &[u8] = unsafe { + std::slice::from_raw_parts( + (value as *const f64) as *const u8, + std::mem::size_of::(), + ) + }; + hasher.write(raw_bytes); + } + Value::Symbol(value) => { + hasher.write(b"#sym#"); + value.hash(hasher); + } + Value::String(value) => { + hasher.write(b"#string#"); + value.hash(hasher); + } + Value::Array(value) => { + hasher.write(b"#arr#"); + for value in value.borrow().iter() { + value.hash(hasher); + } + } + Value::Block(value) => { + hasher.write(b"#blk#"); + value.hash(hasher); + } + Value::Class(value) => { + hasher.write(b"#cls#"); + value.borrow().hash(hasher); + } + Value::Instance(value) => { + hasher.write(b"#inst#"); + value.borrow().hash(hasher); + } + Value::Invokable(value) => { + hasher.write(b"#mthd#"); + value.hash(hasher); + } + } + } +} + +impl Hash for Class { + fn hash(&self, hasher: &mut H) { + self.name.hash(hasher); + self.locals.iter().for_each(|value| { + value.hash(hasher); + }); + } +} + +impl Hash for Instance { + fn hash(&self, hasher: &mut H) { + self.class.borrow().hash(hasher); + self.locals.iter().for_each(|value| { + value.hash(hasher); + }); + } +} + +impl Hash for Block { + fn hash(&self, hasher: &mut H) { + self.literals.iter().for_each(|it| it.hash(hasher)); + self.locals.iter().for_each(|it| it.hash(hasher)); + self.nb_params.hash(hasher); + self.body.iter().for_each(|it| it.hash(hasher)); + } +} + +impl Hash for Method { + fn hash(&self, hasher: &mut H) { + if let Some(holder) = self.holder().upgrade() { + holder.borrow().hash(hasher); + } else { + hasher.write(b"??"); + } + hasher.write(b">>"); + self.signature.hash(hasher); + } +} diff --git a/som-interpreter-bc/src/instance.rs b/som-interpreter-bc/src/instance.rs new file mode 100644 index 00000000..a8f61ee0 --- /dev/null +++ b/som-interpreter-bc/src/instance.rs @@ -0,0 +1,64 @@ +use std::fmt; + +use crate::class::Class; +use crate::value::Value; +use crate::SOMRef; + +/// Represents a generic (non-primitive) class instance. +#[derive(Clone)] +pub struct Instance { + /// The class of which this is an instance from. + pub class: SOMRef, + /// This instance's locals. + pub locals: Vec, +} + +impl Instance { + /// Construct an instance for a given class. + pub fn from_class(class: SOMRef) -> Self { + let mut locals = Vec::new(); + + fn collect_locals(class: &SOMRef, locals: &mut Vec) { + if let Some(class) = class.borrow().super_class() { + collect_locals(&class, locals); + } + locals.extend(class.borrow().locals.iter().map(|_| Value::Nil)); + } + + collect_locals(&class, &mut locals); + + // let locals = class.borrow().locals.iter().map(|_| Value::Nil).collect(); + + Self { class, locals } + } + + /// Get the class of which this is an instance from. + pub fn class(&self) -> SOMRef { + self.class.clone() + } + + /// Get the superclass of this instance's class. + pub fn super_class(&self) -> Option> { + self.class.borrow().super_class() + } + + /// Search for a local binding. + pub fn lookup_local(&self, idx: usize) -> Option { + self.locals.get(idx).cloned() + } + + /// Assign a value to a local binding. + pub fn assign_local(&mut self, idx: usize, value: Value) -> Option<()> { + *self.locals.get_mut(idx)? = value; + 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-bc/src/interner.rs b/som-interpreter-bc/src/interner.rs new file mode 100644 index 00000000..2c6023db --- /dev/null +++ b/som-interpreter-bc/src/interner.rs @@ -0,0 +1,81 @@ +//! +//! This is an implementation of a string interner. +//! +//! It allows to bring down the memory usage from strings and allows for fast comparisons by replacing the strings by essentially an ID. +//! +//! This particular implementation comes from [matklad's "Fast and Simple Rust Interner" blog post](https://matklad.github.io/2020/03/22/fast-simple-rust-interner.html). +//! + +use std::collections::HashMap; +use std::mem; + +/// An interned string. +/// +/// This is fast to move, clone and compare. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub struct Interned(u32); + +/// A string interner. +/// +/// This particular implementation comes from [matklad's "Fast and Simple Rust Interner" blog post](https://matklad.github.io/2020/03/22/fast-simple-rust-interner.html). +#[derive(Debug)] +pub struct Interner { + map: HashMap<&'static str, u32>, + vec: Vec<&'static str>, + buf: String, + full: Vec, +} + +impl Interner { + /// Initialize the interner with an initial capacity. + pub fn with_capacity(cap: usize) -> Self { + let cap = cap.next_power_of_two(); + Self { + map: HashMap::default(), + vec: Vec::new(), + buf: String::with_capacity(cap), + full: Vec::new(), + } + } + + /// Intern a given string. + pub fn intern(&mut self, name: &str) -> Interned { + if let Some(&id) = self.map.get(name) { + return Interned(id); + } + let name = unsafe { self.alloc(name) }; + let id = self.map.len() as u32; + self.map.insert(name, id); + self.vec.push(name); + + let id = Interned(id); + + debug_assert!(self.lookup(id) == name); + debug_assert!(self.intern(name) == id); + + id + } + + /// Get the string associated to a given interning ID. + pub fn lookup(&self, id: Interned) -> &str { + self.vec[id.0 as usize] + } + + unsafe fn alloc(&mut self, name: &str) -> &'static str { + let cap = self.buf.capacity(); + if cap < self.buf.len() + name.len() { + let new_cap = (cap.max(name.len()) + 1).next_power_of_two(); + let new_buf = String::with_capacity(new_cap); + let old_buf = mem::replace(&mut self.buf, new_buf); + self.full.push(old_buf); + } + + let interned = { + let start = self.buf.len(); + self.buf.push_str(name); + &self.buf[start..] + }; + + &*(interned as *const str) + } +} diff --git a/som-interpreter-bc/src/interpreter.rs b/som-interpreter-bc/src/interpreter.rs new file mode 100644 index 00000000..355ec168 --- /dev/null +++ b/som-interpreter-bc/src/interpreter.rs @@ -0,0 +1,406 @@ +use std::cell::RefCell; +use std::rc::Rc; +use std::time::Instant; + +use som_core::bytecode::Bytecode; + +use crate::block::Block; +use crate::compiler::Literal; +use crate::frame::{Frame, FrameKind}; +use crate::method::MethodKind; +use crate::universe::Universe; +use crate::value::Value; +use crate::SOMRef; + +pub struct Interpreter { + /// The interpreter's stack frames. + pub frames: Vec>, + /// The time record of the interpreter's creation. + pub start_time: Instant, +} + +impl Interpreter { + pub fn new() -> Self { + Self { + frames: vec![], + start_time: Instant::now(), + } + } + + pub fn push_frame(&mut self, kind: FrameKind) -> SOMRef { + let frame = Rc::new(RefCell::new(Frame::from_kind(kind))); + self.frames.push(frame.clone()); + frame + } + + pub fn pop_frame(&mut self) { + self.frames.pop(); + } + + pub fn current_frame(&self) -> Option<&SOMRef> { + self.frames.last() + } + + pub fn run(&mut self, universe: &mut Universe) -> Option { + loop { + let frame = match self.current_frame() { + Some(frame) => frame, + None => return dbg!(Some(Value::Nil)), + }; + + let opt_bytecode = frame.borrow().get_current_bytecode(); + let bytecode = match opt_bytecode { + Some(bytecode) => bytecode, + None => { + self.pop_frame(); + self.current_frame() + .map(|frame| frame.borrow_mut().stack.push(Value::Nil)); + continue; + } + }; + + frame.borrow_mut().bytecode_idx += 1; + + match bytecode { + Bytecode::Halt => { + return Some(Value::Nil); + } + Bytecode::Dup => { + let value = frame.borrow().stack.last().cloned().unwrap(); + frame.borrow_mut().stack.push(value); + } + Bytecode::PushLocal(up_idx, idx) => { + let mut from = frame.clone(); + for _ in 0..up_idx { + let temp = match from.borrow().kind() { + FrameKind::Block { block } => block.frame.clone().unwrap(), + FrameKind::Method { .. } => { + panic!("requested local from non-existing frame") + } + }; + from = temp; + } + let value = from.borrow().lookup_local(idx as usize).unwrap(); + frame.borrow_mut().stack.push(value); + } + Bytecode::PushArgument(up_idx, idx) => { + let mut from = frame.clone(); + for _ in 0..up_idx { + let temp = match from.borrow().kind() { + FrameKind::Block { block } => block.frame.clone().unwrap(), + FrameKind::Method { .. } => { + panic!("requested local from non-existing frame") + } + }; + from = temp; + } + let value = from.borrow().lookup_argument(idx as usize).unwrap(); + frame.borrow_mut().stack.push(value); + } + Bytecode::PushField(idx) => { + let holder = frame.borrow().get_method_holder(); + let value = if holder.borrow().is_static { + holder.borrow_mut().lookup_local(idx as usize).unwrap() + } else { + let self_value = frame.borrow().get_self(); + self_value.lookup_local(idx as usize).unwrap() + }; + frame.borrow_mut().stack.push(value); + } + Bytecode::PushBlock(idx) => { + let literal = frame.borrow().lookup_constant(idx as usize).unwrap(); + let mut block = match literal { + Literal::Block(blk) => Block::clone(&blk), + _ => return None, + }; + block.frame.replace(Rc::clone(frame)); + frame.borrow_mut().stack.push(Value::Block(Rc::new(block))); + } + Bytecode::PushConstant(idx) => { + let literal = frame.borrow().lookup_constant(idx as usize).unwrap(); + let value = convert_literal(frame, literal).unwrap(); + frame.borrow_mut().stack.push(value); + } + Bytecode::PushGlobal(idx) => { + let literal = frame.borrow().lookup_constant(idx as usize).unwrap(); + let symbol = match literal { + Literal::Symbol(sym) => sym, + _ => return None, + }; + if let Some(value) = universe.lookup_global(symbol) { + frame.borrow_mut().stack.push(value); + } else { + let self_value = frame.borrow().get_self(); + universe.unknown_global(self, self_value, symbol).unwrap(); + } + } + Bytecode::Pop => { + frame.borrow_mut().stack.pop(); + } + Bytecode::PopLocal(up_idx, idx) => { + let value = frame.borrow_mut().stack.pop().unwrap(); + let mut from = frame.clone(); + for _ in 0..up_idx { + let temp = match from.borrow().kind() { + FrameKind::Block { block } => block.frame.clone().unwrap(), + FrameKind::Method { .. } => { + panic!("requested local from non-existing frame") + } + }; + from = temp; + } + from.borrow_mut().assign_local(idx as usize, value).unwrap(); + } + Bytecode::PopArgument(up_idx, idx) => { + let value = frame.borrow_mut().stack.pop().unwrap(); + let mut from = frame.clone(); + for _ in 0..up_idx { + let temp = match from.borrow().kind() { + FrameKind::Block { block } => block.frame.clone().unwrap(), + FrameKind::Method { .. } => { + panic!("requested local from non-existing frame") + } + }; + from = temp; + } + from.borrow_mut() + .args + .get_mut(idx as usize) + .map(|loc| *loc = value) + .unwrap(); + } + Bytecode::PopField(idx) => { + let value = frame.borrow_mut().stack.pop().unwrap(); + let holder = frame.borrow().get_method_holder(); + if holder.borrow().is_static { + holder + .borrow_mut() + .assign_local(idx as usize, value) + .unwrap(); + } else { + let mut self_value = frame.borrow().get_self(); + self_value.assign_local(idx as usize, value).unwrap(); + } + } + Bytecode::Send(idx) => { + let literal = frame.borrow().lookup_constant(idx as usize).unwrap(); + let symbol = match literal { + Literal::Symbol(sym) => sym, + _ => { + return None; + } + }; + let signature = universe.lookup_symbol(symbol); + let nb_params = nb_params(signature); + let method = frame + .borrow() + .stack + .iter() + .nth_back(nb_params) + .unwrap() + .lookup_method(universe, symbol); + + if let Some(method) = method { + match method.kind() { + MethodKind::Defined(_) => { + let mut args = Vec::with_capacity(nb_params + 1); + + for _ in 0..nb_params { + let arg = frame.borrow_mut().stack.pop().unwrap(); + args.push(arg); + } + let self_value = frame.borrow_mut().stack.pop().unwrap(); + args.push(self_value.clone()); + + args.reverse(); + + let holder = method.holder.upgrade().unwrap(); + self.push_frame(FrameKind::Method { + self_value, + method, + holder, + }); + + let frame = self.current_frame().unwrap(); + frame.borrow_mut().args = args; + } + MethodKind::Primitive(func) => { + func(self, universe); + } + MethodKind::NotImplemented(err) => { + panic!("Primitive `#{}` not implemented", err) + } + } + } else { + let mut args = Vec::with_capacity(nb_params + 1); + + for _ in 0..nb_params { + let arg = frame.borrow_mut().stack.pop().unwrap(); + args.push(arg); + } + let self_value = frame.borrow_mut().stack.pop().unwrap(); + + args.reverse(); + + universe.does_not_understand(self, self_value, symbol, args) + .expect( + "A message cannot be handled and `doesNotUnderstand:arguments:` is not defined on receiver" + ); + } + } + Bytecode::SuperSend(idx) => { + let literal = frame.borrow().lookup_constant(idx as usize).unwrap(); + let symbol = match literal { + Literal::Symbol(sym) => sym, + _ => { + return None; + } + }; + let signature = universe.lookup_symbol(symbol); + let nb_params = nb_params(signature); + let self_value = frame.borrow().get_self(); + + let method = self_value + .class(universe) + .borrow() + .super_class() + .unwrap() + .borrow() + .lookup_method(symbol); + + if let Some(method) = method { + match method.kind() { + MethodKind::Defined(_) => { + let mut args = Vec::with_capacity(nb_params + 1); + + for _ in 0..nb_params { + let arg = frame.borrow_mut().stack.pop().unwrap(); + args.push(arg); + } + let self_value = frame.borrow_mut().stack.pop().unwrap(); + args.push(self_value.clone()); + + args.reverse(); + + let holder = method.holder.upgrade().unwrap(); + self.push_frame(FrameKind::Method { + self_value, + method, + holder, + }); + + let frame = self.current_frame().unwrap(); + frame.borrow_mut().args = args; + } + MethodKind::Primitive(func) => { + func(self, universe); + } + MethodKind::NotImplemented(err) => { + panic!("Primitive `#{}` not implemented", err) + } + } + } else { + let mut args = Vec::with_capacity(nb_params + 1); + + for _ in 0..nb_params { + let arg = frame.borrow_mut().stack.pop().unwrap(); + args.push(arg); + } + let self_value = frame.borrow_mut().stack.pop().unwrap(); + + args.reverse(); + + universe.does_not_understand(self, self_value, symbol, args) + .expect( + "A message cannot be handled and `doesNotUnderstand:arguments:` is not defined on receiver" + ); + } + } + Bytecode::ReturnLocal => { + let value = frame.borrow_mut().stack.pop().unwrap(); + self.pop_frame(); + if let Some(frame) = self.current_frame() { + frame.borrow_mut().stack.push(value); + } else { + return Some(value); + } + } + Bytecode::ReturnNonLocal => { + let value = frame.borrow_mut().stack.pop().unwrap(); + let method_frame = Frame::method_frame(&frame); + let escaped_frames = self + .frames + .iter() + .rev() + .position(|live_frame| Rc::ptr_eq(&live_frame, &method_frame)); + + if let Some(count) = escaped_frames { + dbg!(&value); + match frame.borrow().kind() { + FrameKind::Block { .. } => eprintln!("from: blk"), + FrameKind::Method { method, .. } => eprintln!("from: '{}'", method.signature()), + }; + (0..count).for_each(|_| self.pop_frame()); + self.pop_frame(); + if let Some(frame) = self.current_frame() { + match frame.borrow().kind() { + FrameKind::Block { .. } => eprintln!("from: blk"), + FrameKind::Method { method, .. } => eprintln!("to: '{}'", method.signature()), + }; + frame.borrow_mut().stack.push(value); + } else { + return dbg!(Some(value)); + } + } else { + // Block has escaped its method frame. + let instance = frame.borrow().get_self(); + let block = match frame.borrow().kind() { + FrameKind::Block { block, .. } => block.clone(), + _ => { + // Should never happen, because `universe.current_frame()` would + // have been equal to `universe.current_method_frame()`. + panic!("A method frame has escaped itself ??"); + } + }; + // TODO: should we call `doesNotUnderstand:` here ? + universe.escaped_block(self, instance, block).expect( + "A block has escaped and `escapedBlock:` is not defined on receiver", + ); + } + } + } + } + + fn convert_literal(frame: &SOMRef, literal: Literal) -> Option { + let value = match literal { + Literal::Symbol(sym) => Value::Symbol(sym), + Literal::String(val) => Value::String(val), + Literal::Double(val) => Value::Double(val), + Literal::Integer(val) => Value::Integer(val), + Literal::BigInteger(val) => Value::BigInteger(val), + Literal::Array(val) => { + let arr = val + .into_iter() + .map(|idx| { + frame + .borrow() + .lookup_constant(idx as usize) + .and_then(|lit| convert_literal(frame, lit)) + }) + .collect::>>() + .unwrap(); + Value::Array(Rc::new(RefCell::new(arr))) + } + Literal::Block(val) => Value::Block(val), + }; + Some(value) + } + + fn nb_params(signature: &str) -> usize { + match signature.chars().nth(0) { + Some(ch) if !ch.is_alphabetic() => 1, + _ => signature.chars().filter(|ch| *ch == ':').count(), + } + } + } +} diff --git a/som-interpreter-bc/src/lib.rs b/som-interpreter-bc/src/lib.rs new file mode 100644 index 00000000..1256f14c --- /dev/null +++ b/som-interpreter-bc/src/lib.rs @@ -0,0 +1,36 @@ +//! +//! This is the interpreter for the Simple Object Machine. +//! + +use std::cell::RefCell; +use std::rc::{Rc, Weak}; + +/// Facilities for manipulating blocks. +pub mod block; +/// Facilities for manipulating classes. +pub mod class; +/// Facilities for compiling code into bytecode. +pub mod compiler; +/// Facilities for manipulating stack frames. +pub mod frame; +/// Facilities for manipulating values. +pub mod hashcode; +/// Facilities for manipulating class instances. +pub mod instance; +/// Facilities for string interning. +pub mod interner; +/// Facilities for manipulating class methods. +pub mod method; +/// Definitions for all supported primitives. +pub mod primitives; +/// The collection of all known SOM objects during execution. +pub mod universe; +/// The interpreter's main data structure. +pub mod interpreter; +/// Facilities for manipulating values. +pub mod value; + +/// A strong and owning reference to an object. +pub type SOMRef = Rc>; +/// A weak reference to an object. +pub type SOMWeakRef = Weak>; diff --git a/som-interpreter-bc/src/main.rs b/som-interpreter-bc/src/main.rs new file mode 100644 index 00000000..ce98572c --- /dev/null +++ b/som-interpreter-bc/src/main.rs @@ -0,0 +1,103 @@ +//! +//! This is the interpreter for the Simple Object Machine. +//! +#![warn(missing_docs)] + +use std::path::PathBuf; +use std::rc::Rc; + +use anyhow::anyhow; +use structopt::StructOpt; + +mod shell; + +use som_interpreter_bc::universe::Universe; +use som_interpreter_bc::interpreter::Interpreter; +use som_interpreter_bc::value::Value; + +#[derive(Debug, Clone, PartialEq, StructOpt)] +#[structopt(about, author)] +struct Options { + /// Files to evaluate. + #[structopt(name = "FILE")] + file: Option, + + #[structopt(name = "ARGS")] + args: Vec, + + /// Set search path for application classes. + #[structopt(short, long)] + classpath: Vec, + + // /// enable disassembling + // #[structopt(short = "d")] + // disassembling: bool, + /// Enable verbose output (with timing information). + #[structopt(short = "v")] + verbose: bool, +} + +fn main() -> anyhow::Result<()> { + let opts: Options = Options::from_args(); + + let mut interpreter = Interpreter::new(); + + match dbg!(opts.file) { + None => { + let mut universe = Universe::with_classpath(opts.classpath)?; + shell::interactive(&mut interpreter, &mut universe, opts.verbose)? + } + Some(file) => { + let file_stem = file + .file_stem() + .ok_or_else(|| anyhow!("the given path has no file stem"))?; + let file_stem = file_stem + .to_str() + .ok_or_else(|| anyhow!("the given path contains invalid UTF-8 in its file stem"))?; + + let mut classpath = opts.classpath; + if let Some(directory) = file.parent() { + classpath.push(directory.to_path_buf()); + } + + let mut universe = Universe::with_classpath(classpath)?; + + // let class = universe.load_class("System"); + // if let Ok(class) = class { + // for method in class.borrow().methods.values() { + // println!("System>>#{}", method.signature); + // if let som_interpreter_bc::method::MethodKind::Defined(env) = &method.kind { + // for bytecode in &env.body { + // println!(" {}", bytecode); + // } + // } + // } + // } + + let args = std::iter::once(String::from(file_stem)) + .chain(opts.args.iter().cloned()) + .map(Rc::new) + .map(Value::String) + .collect(); + + universe.initialize(&mut interpreter, args).expect("issue running program"); + + interpreter.run(&mut universe); + + // let class = universe.load_class_from_path(file)?; + // let instance = som_interpreter::instance::Instance::from_class(class); + // let instance = Value::Instance(Rc::new(std::cell::RefCell::new(instance))); + + // let invokable = instance.lookup_method(&universe, "run").unwrap(); + // let output = som_interpreter::invokable::Invoke::invoke(invokable.as_ref(), &mut universe, vec![instance]); + + // match output { + // Return::Exception(message) => println!("ERROR: {}", message), + // Return::Restart => println!("ERROR: asked for a restart to the top-level"), + // _ => {} + // } + } + } + + Ok(()) +} diff --git a/som-interpreter-bc/src/method.rs b/som-interpreter-bc/src/method.rs new file mode 100644 index 00000000..3091e11c --- /dev/null +++ b/som-interpreter-bc/src/method.rs @@ -0,0 +1,216 @@ +use std::fmt; +use std::rc::Rc; + +use som_core::bytecode::Bytecode; + +use crate::class::Class; +use crate::compiler::Literal; +use crate::frame::FrameKind; +use crate::interpreter::Interpreter; +use crate::primitives; +use crate::primitives::PrimitiveFn; +use crate::universe::Universe; +use crate::value::Value; +use crate::{SOMRef, SOMWeakRef}; + +#[derive(Clone)] +pub struct MethodEnv { + pub locals: Vec, + pub literals: Vec, + pub body: Vec, +} + +/// The kind of a class method. +#[derive(Clone)] +pub enum MethodKind { + /// A user-defined method from the AST. + Defined(MethodEnv), + /// An interpreter primitive. + Primitive(PrimitiveFn), + /// A non-implemented primitive. + NotImplemented(String), +} + +impl MethodKind { + /// Return the interpreter primitive matching a given class name and signature. + pub fn primitive_from_signature( + class_name: impl AsRef, + signature: impl AsRef, + ) -> Self { + let class_name = class_name.as_ref(); + let signature = signature.as_ref(); + let primitive = match class_name { + "Object" => primitives::object::get_primitive(signature), + "Class" => primitives::class::get_primitive(signature), + "Integer" => primitives::integer::get_primitive(signature), + "Double" => primitives::double::get_primitive(signature), + "Array" => primitives::array::get_primitive(signature), + "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), + "Block3" => primitives::block3::get_primitive(signature), + _ => None, + }; + // println!( + // "loading primitive of '{}>>#{}': {}", + // class_name, + // signature, + // primitive.is_some() + // ); + primitive.map(MethodKind::Primitive).unwrap_or_else(|| { + MethodKind::NotImplemented(format!("{}>>#{}", class_name, signature)) + }) + // .unwrap_or_else(|| panic!("unimplemented primitive: '{}>>#{}'", class_name, signature)) + } + + /// Whether this invocable is a primitive. + pub fn is_primitive(&self) -> bool { + matches!(self, Self::Primitive(_)) + } +} + +/// Represents a class method. +#[derive(Clone)] +pub struct Method { + pub kind: MethodKind, + pub holder: SOMWeakRef, + pub signature: String, +} + +impl Method { + pub fn class(&self, universe: &Universe) -> SOMRef { + if self.is_primitive() { + universe.primitive_class() + } else { + universe.method_class() + } + } + + pub fn kind(&self) -> &MethodKind { + &self.kind + } + + pub fn holder(&self) -> &SOMWeakRef { + &self.holder + } + + pub fn signature(&self) -> &str { + self.signature.as_str() + } + + /// Whether this invocable is a primitive. + pub fn is_primitive(&self) -> bool { + self.kind.is_primitive() + } + + pub fn invoke( + self: Rc, + interpreter: &mut Interpreter, + universe: &mut Universe, + receiver: Value, + mut args: Vec, + ) { + match self.kind() { + MethodKind::Defined(_) => { + let holder = self.holder().upgrade().unwrap(); + let kind = FrameKind::Method { + method: self, + holder, + self_value: receiver.clone(), + }; + + let frame = interpreter.push_frame(kind); + frame.borrow_mut().args.push(receiver); + frame.borrow_mut().args.append(&mut args); + } + MethodKind::Primitive(func) => { + let frame = interpreter.current_frame().unwrap(); + frame.borrow_mut().stack.push(receiver); + frame.borrow_mut().stack.append(&mut args); + func(interpreter, universe) + } + MethodKind::NotImplemented(_) => todo!(), + } + } +} + +impl fmt::Display for Method { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "#{}>>#{} = ", + self.holder.upgrade().unwrap().borrow().name(), + self.signature + )?; + match &self.kind { + MethodKind::Defined(env) => { + writeln!(f, "(")?; + write!(f, " <{} locals>", env.locals.len())?; + for bytecode in &env.body { + writeln!(f)?; + write!(f, " {} ", bytecode.padded_name())?; + match bytecode { + Bytecode::Halt => {} + Bytecode::Dup => {} + Bytecode::PushLocal(up_idx, idx) => { + write!(f, "local: {}, context: {}", idx, up_idx)?; + } + Bytecode::PushArgument(up_idx, idx) => { + write!(f, "argument: {}, context: {}", idx, up_idx)?; + } + Bytecode::PushField(idx) => { + write!(f, "index: {}", idx)?; + } + Bytecode::PushBlock(idx) => { + write!(f, "index: {}", idx)?; + } + Bytecode::PushConstant(idx) => { + write!(f, "index: {}, ", idx)?; + let constant = &env.literals[*idx as usize]; + match constant { + Literal::Symbol(_) => write!(f, "value: (#Symbol)"), + Literal::String(value) => write!(f, "value: (#String) {:?}", value), + Literal::Double(value) => write!(f, "value: (#Double) {}", value), + Literal::Integer(value) => write!(f, "value: (#Integer) {}", value), + Literal::BigInteger(value) => { + write!(f, "value: (#Integer) {}", value) + } + Literal::Array(_) => write!(f, "value: (#Array)"), + Literal::Block(_) => write!(f, "value: (#Block)"), + }?; + } + Bytecode::PushGlobal(idx) => { + write!(f, "index: {}", idx)?; + } + Bytecode::Pop => {} + Bytecode::PopLocal(up_idx, idx) => { + write!(f, "local: {}, context: {}", idx, up_idx)?; + } + Bytecode::PopArgument(up_idx, idx) => { + write!(f, "argument: {}, context: {}", idx, up_idx)?; + } + Bytecode::PopField(idx) => { + write!(f, "index: {}", idx)?; + } + Bytecode::Send(idx) => { + write!(f, "index: {}", idx)?; + } + Bytecode::SuperSend(idx) => { + write!(f, "index: {}", idx)?; + } + Bytecode::ReturnLocal => {} + Bytecode::ReturnNonLocal => {} + } + } + Ok(()) + } + MethodKind::Primitive(_) => write!(f, ""), + MethodKind::NotImplemented(_) => write!(f, ""), + } + } +} diff --git a/som-interpreter-bc/src/primitives/array.rs b/som-interpreter-bc/src/primitives/array.rs new file mode 100644 index 00000000..1740995d --- /dev/null +++ b/som-interpreter-bc/src/primitives/array.rs @@ -0,0 +1,97 @@ +use std::cell::RefCell; +use std::convert::TryFrom; +use std::rc::Rc; + +use crate::interpreter::Interpreter; +use crate::primitives::PrimitiveFn; +use crate::universe::Universe; +use crate::value::Value; +use crate::{expect_args, reverse}; + +fn at(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Array>>#at:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Array(values) => values, + Value::Integer(index) => index, + ]); + + let index = match usize::try_from(index - 1) { + Ok(index) => index, + Err(err) => panic!("'{}': {}", SIGNATURE, err), + }; + let value = values.borrow().get(index).cloned().unwrap_or(Value::Nil); + frame.borrow_mut().stack.push(value) +} + +fn at_put(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Array>>#at:put:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Array(values) => values, + Value::Integer(index) => index, + value => value, + ]); + + let index = match usize::try_from(index - 1) { + Ok(index) => index, + Err(err) => panic!("'{}': {}", SIGNATURE, err), + }; + if let Some(location) = values.borrow_mut().get_mut(index) { + *location = value; + } + frame.borrow_mut().stack.push(Value::Array(values)) +} + +fn length(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Array>>#length"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Array(values) => values, + ]); + + let length = values.borrow().len(); + match i64::try_from(length) { + Ok(length) => frame.borrow_mut().stack.push(Value::Integer(length)), + Err(err) => panic!("'{}': {}", SIGNATURE, err), + } +} + +fn new(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Array>>#new:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + _, + Value::Integer(count) => count, + ]); + + match usize::try_from(count) { + Ok(length) => frame + .borrow_mut() + .stack + .push(Value::Array(Rc::new(RefCell::new(vec![ + Value::Nil; + length + ])))), + Err(err) => panic!("'{}': {}", SIGNATURE, err), + } +} + +/// Search for a primitive matching the given signature. +pub fn get_primitive(signature: impl AsRef) -> Option { + match signature.as_ref() { + "at:" => Some(self::at), + "at:put:" => Some(self::at_put), + "length" => Some(self::length), + "new:" => Some(self::new), + _ => None, + } +} diff --git a/som-interpreter-bc/src/primitives/blocks.rs b/som-interpreter-bc/src/primitives/blocks.rs new file mode 100644 index 00000000..52df4b89 --- /dev/null +++ b/som-interpreter-bc/src/primitives/blocks.rs @@ -0,0 +1,104 @@ +use crate::frame::FrameKind; +use crate::interpreter::Interpreter; +use crate::primitives::PrimitiveFn; +use crate::universe::Universe; +use crate::value::Value; +use crate::{expect_args, reverse}; + +/// Primitives for the **Block** and **Block1** class. +pub mod block1 { + use super::*; + + fn value(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Block1>>#value"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Block(block) => block, + ]); + + let kind = FrameKind::Block { block: block.clone() }; + + interpreter.push_frame(kind); + } + + fn restart(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Block>>#restart"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [Value::Block(_)]); + + frame.borrow_mut().bytecode_idx = 0; + } + + /// Search for a primitive matching the given signature. + pub fn get_primitive(signature: impl AsRef) -> Option { + match signature.as_ref() { + "value" => Some(self::value), + "restart" => Some(self::restart), + _ => None, + } + } +} + +/// Primitives for the **Block2** class. +pub mod block2 { + use super::*; + + fn value(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Block2>>#value:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Block(block) => block, + argument => argument, + ]); + + let kind = FrameKind::Block { block: block.clone() }; + + let frame = interpreter.push_frame(kind); + frame.borrow_mut().args.push(argument); + } + + /// Search for a primitive matching the given signature. + pub fn get_primitive(signature: impl AsRef) -> Option { + match signature.as_ref() { + "value:" => Some(self::value), + _ => None, + } + } +} + +/// Primitives for the **Block3** class. +pub mod block3 { + use super::*; + + fn value_with(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Block3>>#value:with:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Block(block) => block, + argument1 => argument1, + argument2 => argument2, + ]); + + let kind = FrameKind::Block { block: block.clone() }; + + let frame = interpreter.push_frame(kind); + frame.borrow_mut().args.push(argument1); + frame.borrow_mut().args.push(argument2); + } + + /// Search for a primitive matching the given signature. + pub fn get_primitive(signature: impl AsRef) -> Option { + match signature.as_ref() { + "value:with:" => Some(self::value_with), + _ => None, + } + } +} diff --git a/som-interpreter-bc/src/primitives/class.rs b/som-interpreter-bc/src/primitives/class.rs new file mode 100644 index 00000000..d7d4863a --- /dev/null +++ b/som-interpreter-bc/src/primitives/class.rs @@ -0,0 +1,109 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use crate::instance::Instance; +use crate::interpreter::Interpreter; +use crate::primitives::PrimitiveFn; +use crate::universe::Universe; +use crate::value::Value; +use crate::{expect_args, reverse}; + +fn superclass(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Class>>#superclass"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Class(class) => class, + ]); + + let super_class = class.borrow().super_class(); + frame + .borrow_mut() + .stack + .push(super_class.map(Value::Class).unwrap_or(Value::Nil)); +} + +fn new(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Class>>#new"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Class(class) => class, + ]); + + let instance = Instance::from_class(class); + let instance = Rc::new(RefCell::new(instance)); + frame.borrow_mut().stack.push(Value::Instance(instance)); +} + +fn name(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "Class>>#name"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Class(class) => class, + ]); + + let sym = universe.intern_symbol(class.borrow().name()); + frame.borrow_mut().stack.push(Value::Symbol(sym)); +} + +fn methods(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Class>>#methods"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Class(class) => class, + ]); + + let methods = class + .borrow() + .methods + .values() + .map(|invokable| Value::Invokable(invokable.clone())) + .collect(); + + frame + .borrow_mut() + .stack + .push(Value::Array(Rc::new(RefCell::new(methods)))); +} + +fn fields(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Class>>#fields"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Class(class) => class, + ]); + + frame + .borrow_mut() + .stack + .push(Value::Array(Rc::new(RefCell::new( + class + .borrow() + .locals + .keys() + .copied() + .map(Value::Symbol) + .collect(), + )))); +} + +/// Search for a primitive matching the given signature. +pub fn get_primitive(signature: impl AsRef) -> Option { + match signature.as_ref() { + "new" => Some(self::new), + "name" => Some(self::name), + "fields" => Some(self::fields), + "methods" => Some(self::methods), + "superclass" => Some(self::superclass), + _ => None, + } +} diff --git a/som-interpreter-bc/src/primitives/double.rs b/som-interpreter-bc/src/primitives/double.rs new file mode 100644 index 00000000..2368ae66 --- /dev/null +++ b/som-interpreter-bc/src/primitives/double.rs @@ -0,0 +1,269 @@ +use std::rc::Rc; + +use crate::interpreter::Interpreter; +use crate::primitives::PrimitiveFn; +use crate::universe::Universe; +use crate::value::Value; +use crate::{expect_args, reverse}; + +macro_rules! promote { + ($signature:expr, $value:expr) => { + match $value { + Value::Integer(value) => value as f64, + Value::Double(value) => value, + _ => panic!( + "'{}': wrong type (expected `integer` or `double`)", + $signature + ), + } + }; +} + +fn from_string(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Double>>#fromString:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + _, + Value::String(string) => string, + ]); + + match string.parse() { + Ok(parsed) => frame.borrow_mut().stack.push(Value::Double(parsed)), + Err(err) => panic!("'{}': {}", SIGNATURE, err), + } +} + +fn as_string(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Double>>#asString"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + let value = promote!(SIGNATURE, value); + + frame + .borrow_mut() + .stack + .push(Value::String(Rc::new(value.to_string()))); +} + +fn as_integer(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Double>>#asInteger"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Double(value) => value, + ]); + + frame + .borrow_mut() + .stack + .push(Value::Integer(value.trunc() as i64)); +} + +fn sqrt(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Double>>#sqrt"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + let value = promote!(SIGNATURE, value); + + frame.borrow_mut().stack.push(Value::Double(value.sqrt())); +} + +fn round(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Double>>#round"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + let value = promote!(SIGNATURE, value); + + frame.borrow_mut().stack.push(Value::Double(value.round())); +} + +fn cos(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Double>>#cos"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + let value = promote!(SIGNATURE, value); + + frame.borrow_mut().stack.push(Value::Double(value.cos())); +} + +fn sin(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Double>>#sin"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + let value = promote!(SIGNATURE, value); + + frame.borrow_mut().stack.push(Value::Double(value.sin())); +} + +fn eq(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Double>>#="; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + // Value::Double(a) => a, + // Value::Double(b) => b, + a => a, + b => b, + ]); + + frame.borrow_mut().stack.push(Value::Boolean(a == b)); +} + +fn lt(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Double>>#<"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + let a = promote!(SIGNATURE, a); + let b = promote!(SIGNATURE, b); + + frame.borrow_mut().stack.push(Value::Boolean(a < b)); +} + +fn plus(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Double>>#+"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + let a = promote!(SIGNATURE, a); + let b = promote!(SIGNATURE, b); + + frame.borrow_mut().stack.push(Value::Double(a + b)); +} + +fn minus(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Double>>#-"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + let a = promote!(SIGNATURE, a); + let b = promote!(SIGNATURE, b); + + frame.borrow_mut().stack.push(Value::Double(a - b)); +} + +fn times(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Double>>#*"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + let a = promote!(SIGNATURE, a); + let b = promote!(SIGNATURE, b); + + frame.borrow_mut().stack.push(Value::Double(a * b)); +} + +fn divide(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Double>>#//"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + let a = promote!(SIGNATURE, a); + let b = promote!(SIGNATURE, b); + + frame.borrow_mut().stack.push(Value::Double(a / b)); +} + +fn modulo(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Double>>#%"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + let a = promote!(SIGNATURE, a); + let b = promote!(SIGNATURE, b); + + frame.borrow_mut().stack.push(Value::Double(a % b)); +} + +fn positive_infinity(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Double>>#positiveInfinity"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [_]); + + let frame = interpreter.current_frame().expect("no current frame"); + + frame.borrow_mut().stack.push(Value::Double(f64::INFINITY)); +} + +/// Search for a primitive matching the given signature. +pub fn get_primitive(signature: impl AsRef) -> Option { + match signature.as_ref() { + "+" => Some(self::plus), + "-" => Some(self::minus), + "*" => Some(self::times), + "//" => Some(self::divide), + "%" => Some(self::modulo), + "=" => Some(self::eq), + "<" => Some(self::lt), + "sqrt" => Some(self::sqrt), + "round" => Some(self::round), + "cos" => Some(self::cos), + "sin" => Some(self::sin), + "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-bc/src/primitives/integer.rs b/som-interpreter-bc/src/primitives/integer.rs new file mode 100644 index 00000000..1d854c6f --- /dev/null +++ b/som-interpreter-bc/src/primitives/integer.rs @@ -0,0 +1,613 @@ +use std::rc::Rc; + +use num_bigint::{BigInt, Sign}; +use num_traits::ToPrimitive; +use rand::distributions::Uniform; +use rand::Rng; + +use crate::{expect_args, reverse}; +use crate::interpreter::Interpreter; +use crate::primitives::PrimitiveFn; +use crate::universe::Universe; +use crate::value::Value; + +macro_rules! demote { + ($frame:expr, $expr:expr) => {{ + let value = $expr; + match value.to_i64() { + Some(value) => { + $frame.borrow_mut().stack.push(Value::Integer(value)); + return; + } + None => { + $frame.borrow_mut().stack.push(Value::BigInteger(value)); + return; + } + } + }}; +} + +fn from_string(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "Integer>>#fromString:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + _, + value => value, + ]); + + let value = match value { + Value::String(ref value) => value.as_str(), + Value::Symbol(sym) => universe.lookup_symbol(sym), + _ => panic!("'{}': wrong types", SIGNATURE), + }; + + let parsed = + (value.parse().map(Value::Integer)).or_else(|_| value.parse().map(Value::BigInteger)); + + match parsed { + Ok(parsed) => { + frame.borrow_mut().stack.push(parsed); + return; + } + Err(err) => panic!("{}", err), + } +} + +fn as_string(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#asString"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + let value = match value { + Value::Integer(value) => value.to_string(), + Value::BigInteger(value) => value.to_string(), + _ => panic!("'{}': wrong types", SIGNATURE), + }; + + { + frame.borrow_mut().stack.push(Value::String(Rc::new(value))); + return; + } +} + +fn at_random(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#atRandom"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + let chosen = match value { + Value::Integer(value) => { + let distribution = Uniform::new(0, value); + let mut rng = rand::thread_rng(); + rng.sample(distribution) + } + Value::BigInteger(_) => panic!( + "'{}': the range is too big to pick a random value from", + SIGNATURE, + ), + _ => panic!("'{}': wrong types", SIGNATURE), + }; + + { + frame.borrow_mut().stack.push(Value::Integer(chosen)); + return; + } +} + +fn as_32bit_signed_value(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#as32BitSignedValue"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + let value = match value { + Value::Integer(value) => value as i32 as i64, + Value::BigInteger(value) => match value.to_u32_digits() { + (Sign::Minus, values) => -(values[0] as i64), + (Sign::Plus, values) | (Sign::NoSign, values) => values[0] as i64, + }, + _ => panic!("'{}': wrong types", SIGNATURE), + }; + + { + frame.borrow_mut().stack.push(Value::Integer(value)); + return; + } +} + +fn as_32bit_unsigned_value(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#as32BitUnsignedValue"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + let value = match value { + Value::Integer(value) => value as u32 as i64, + Value::BigInteger(value) => { + let (_, values) = value.to_u32_digits(); + values[0] as i64 + } + _ => panic!("'{}': wrong types", SIGNATURE), + }; + + { + frame.borrow_mut().stack.push(Value::Integer(value)); + return; + } +} + +fn plus(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#+"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + match (a, b) { + (Value::Integer(a), Value::Integer(b)) => match a.checked_add(b) { + Some(value) => { + frame.borrow_mut().stack.push(Value::Integer(value)); + return; + } + None => demote!(frame, BigInt::from(a) + BigInt::from(b)), + }, + (Value::BigInteger(a), Value::BigInteger(b)) => demote!(frame, a + b), + (Value::BigInteger(a), Value::Integer(b)) | (Value::Integer(b), Value::BigInteger(a)) => { + demote!(frame, a + BigInt::from(b)) + } + (Value::Double(a), Value::Double(b)) => { + frame.borrow_mut().stack.push(Value::Double(a + b)); + return; + } + (Value::Integer(a), Value::Double(b)) | (Value::Double(b), Value::Integer(a)) => { + frame.borrow_mut().stack.push(Value::Double((a as f64) + b)); + return; + } + _ => panic!("'{}': wrong types", SIGNATURE), + } +} + +fn minus(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#-"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + match (a, b) { + (Value::Integer(a), Value::Integer(b)) => match a.checked_sub(b) { + Some(value) => { + frame.borrow_mut().stack.push(Value::Integer(value)); + return; + } + None => demote!(frame, BigInt::from(a) - BigInt::from(b)), + }, + (Value::BigInteger(a), Value::BigInteger(b)) => demote!(frame, a - b), + (Value::BigInteger(a), Value::Integer(b)) | (Value::Integer(b), Value::BigInteger(a)) => { + demote!(frame, a - BigInt::from(b)) + } + (Value::Double(a), Value::Double(b)) => { + frame.borrow_mut().stack.push(Value::Double(a - b)); + return; + } + (Value::Integer(a), Value::Double(b)) | (Value::Double(b), Value::Integer(a)) => { + frame.borrow_mut().stack.push(Value::Double((a as f64) - b)); + return; + } + _ => panic!("'{}': wrong types", SIGNATURE), + } +} + +fn times(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#*"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + match (a, b) { + (Value::Integer(a), Value::Integer(b)) => match a.checked_mul(b) { + Some(value) => { + frame.borrow_mut().stack.push(Value::Integer(value)); + return; + } + None => demote!(frame, BigInt::from(a) * BigInt::from(b)), + }, + (Value::BigInteger(a), Value::BigInteger(b)) => demote!(frame, a * b), + (Value::BigInteger(a), Value::Integer(b)) | (Value::Integer(b), Value::BigInteger(a)) => { + demote!(frame, a * BigInt::from(b)) + } + (Value::Double(a), Value::Double(b)) => { + frame.borrow_mut().stack.push(Value::Double(a * b)); + return; + } + (Value::Integer(a), Value::Double(b)) | (Value::Double(b), Value::Integer(a)) => { + frame.borrow_mut().stack.push(Value::Double((a as f64) * b)); + return; + } + _ => panic!("'{}': wrong types", SIGNATURE), + } +} + +fn divide(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#/"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + match (a, b) { + (Value::Integer(a), Value::Integer(b)) => match a.checked_div(b) { + Some(value) => { + frame.borrow_mut().stack.push(Value::Integer(value)); + return; + } + None => demote!(frame, BigInt::from(a) / BigInt::from(b)), + }, + (Value::BigInteger(a), Value::BigInteger(b)) => demote!(frame, a / b), + (Value::BigInteger(a), Value::Integer(b)) | (Value::Integer(b), Value::BigInteger(a)) => { + demote!(frame, a / BigInt::from(b)) + } + (Value::Double(a), Value::Double(b)) => { + frame.borrow_mut().stack.push(Value::Double(a / b)); + return; + } + (Value::Integer(a), Value::Double(b)) | (Value::Double(b), Value::Integer(a)) => { + frame.borrow_mut().stack.push(Value::Double((a as f64) / b)); + return; + } + _ => panic!("'{}': wrong types", SIGNATURE), + } +} + +fn divide_float(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#//"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + match (a, b) { + (Value::Integer(a), Value::Integer(b)) => { + frame + .borrow_mut() + .stack + .push(Value::Double((a as f64) / (b as f64))); + return; + } + (Value::Integer(a), Value::Double(b)) | (Value::Double(b), Value::Integer(a)) => { + frame.borrow_mut().stack.push(Value::Double((a as f64) / b)); + return; + } + (Value::Double(a), Value::Double(b)) => { + frame.borrow_mut().stack.push(Value::Double(a / b)); + return; + } + _ => panic!("'{}': wrong types", SIGNATURE), + } +} + +fn modulo(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#%"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Integer(a) => a, + Value::Integer(b) => b, + ]); + + let result = a % b; + if result.signum() != b.signum() { + { + frame + .borrow_mut() + .stack + .push(Value::Integer((result + b) % b)); + return; + } + } else { + { + frame.borrow_mut().stack.push(Value::Integer(result)); + return; + } + } +} + +fn remainder(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#rem:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Integer(a) => a, + Value::Integer(b) => b, + ]); + + let result = a % b; + if result.signum() != a.signum() { + { + frame + .borrow_mut() + .stack + .push(Value::Integer((result + a) % a)); + return; + } + } else { + { + frame.borrow_mut().stack.push(Value::Integer(result)); + return; + } + } +} + +fn sqrt(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#sqrt"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + ]); + + match a { + Value::Integer(a) => { + let sqrt = (a as f64).sqrt(); + let trucated = sqrt.trunc(); + if sqrt == trucated { + { + frame + .borrow_mut() + .stack + .push(Value::Integer(trucated as i64)); + return; + } + } else { + { + frame.borrow_mut().stack.push(Value::Double(sqrt)); + return; + } + } + } + Value::BigInteger(a) => demote!(frame, a.sqrt()), + Value::Double(a) => { + frame.borrow_mut().stack.push(Value::Double(a.sqrt())); + return; + } + _ => panic!("'{}': wrong types", SIGNATURE), + } +} + +fn bitand(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#&"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + match (a, b) { + (Value::Integer(a), Value::Integer(b)) => { + frame.borrow_mut().stack.push(Value::Integer(a & b)); + return; + } + (Value::BigInteger(a), Value::BigInteger(b)) => demote!(frame, a & b), + (Value::BigInteger(a), Value::Integer(b)) | (Value::Integer(b), Value::BigInteger(a)) => { + demote!(frame, a & BigInt::from(b)) + } + _ => panic!("'{}': wrong types", SIGNATURE), + } +} + +fn bitxor(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#bitXor:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + match (a, b) { + (Value::Integer(a), Value::Integer(b)) => { + frame.borrow_mut().stack.push(Value::Integer(a ^ b)); + return; + } + (Value::BigInteger(a), Value::BigInteger(b)) => demote!(frame, a ^ b), + (Value::BigInteger(a), Value::Integer(b)) | (Value::Integer(b), Value::BigInteger(a)) => { + demote!(frame, a ^ BigInt::from(b)) + } + _ => panic!("'{}': wrong types", SIGNATURE), + } +} + +fn lt(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#<"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + match (a, b) { + (Value::Integer(a), Value::Integer(b)) => { + frame.borrow_mut().stack.push(Value::Boolean(a < b)); + return; + } + (Value::BigInteger(a), Value::BigInteger(b)) => { + frame.borrow_mut().stack.push(Value::Boolean(a < b)); + return; + } + (Value::Double(a), Value::Double(b)) => { + frame.borrow_mut().stack.push(Value::Boolean(a < b)); + return; + } + (Value::Integer(a), Value::Double(b)) | (Value::Double(b), Value::Integer(a)) => { + frame + .borrow_mut() + .stack + .push(Value::Boolean((a as f64) < b)); + return; + } + (Value::BigInteger(a), Value::Integer(b)) => { + frame + .borrow_mut() + .stack + .push(Value::Boolean(a < BigInt::from(b))); + return; + } + (Value::Integer(a), Value::BigInteger(b)) => { + frame + .borrow_mut() + .stack + .push(Value::Boolean(b < BigInt::from(a))); + return; + } + (a, b) => panic!("'{}': wrong types ({:?} | {:?})", SIGNATURE, a, b), + } +} + +fn eq(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#="; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + match (a, b) { + (Value::Integer(a), Value::Integer(b)) => { + frame.borrow_mut().stack.push(Value::Boolean(a == b)); + return; + } + (Value::BigInteger(a), Value::BigInteger(b)) => { + frame.borrow_mut().stack.push(Value::Boolean(a == b)); + return; + } + (Value::Double(a), Value::Double(b)) => { + frame.borrow_mut().stack.push(Value::Boolean(a == b)); + return; + } + (Value::Integer(a), Value::Double(b)) | (Value::Double(b), Value::Integer(a)) => { + frame + .borrow_mut() + .stack + .push(Value::Boolean((a as f64) == b)); + return; + } + _ => { + frame.borrow_mut().stack.push(Value::Boolean(false)); + return; + } + } +} + +fn shift_left(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#<<"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + Value::Integer(b) => b, + ]); + + match a { + Value::Integer(a) => match a.checked_shl(b as u32) { + Some(value) => { + frame.borrow_mut().stack.push(Value::Integer(value)); + return; + } + None => demote!(frame, BigInt::from(a) << (b as usize)), + }, + Value::BigInteger(a) => demote!(frame, a << (b as usize)), + _ => panic!("'{}': wrong types", SIGNATURE), + } +} + +fn shift_right(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Integer>>#>>"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + Value::Integer(b) => b, + ]); + + match a { + Value::Integer(a) => match a.checked_shr(b as u32) { + Some(value) => { + frame.borrow_mut().stack.push(Value::Integer(value)); + return; + } + None => demote!(frame, BigInt::from(a) >> (b as usize)), + }, + Value::BigInteger(a) => demote!(frame, a >> (b as usize)), + _ => panic!("'{}': wrong types", SIGNATURE), + } +} + +/// Search for a primitive matching the given signature. +pub fn get_primitive(signature: impl AsRef) -> Option { + match signature.as_ref() { + "fromString:" => Some(self::from_string), + "asString" => Some(self::as_string), + "atRandom" => Some(self::at_random), + "as32BitSignedValue" => Some(self::as_32bit_signed_value), + "as32BitUnsignedValue" => Some(self::as_32bit_unsigned_value), + "<" => Some(self::lt), + "=" => Some(self::eq), + "+" => Some(self::plus), + "-" => Some(self::minus), + "*" => Some(self::times), + "/" => Some(self::divide), + "//" => Some(self::divide_float), + "%" => Some(self::modulo), + "rem:" => Some(self::remainder), + "&" => Some(self::bitand), + "<<" => Some(self::shift_left), + ">>>" => Some(self::shift_right), + "bitXor:" => Some(self::bitxor), + "sqrt" => Some(self::sqrt), + _ => None, + } +} diff --git a/som-interpreter-bc/src/primitives/method.rs b/som-interpreter-bc/src/primitives/method.rs new file mode 100644 index 00000000..125f0d84 --- /dev/null +++ b/som-interpreter-bc/src/primitives/method.rs @@ -0,0 +1,58 @@ +use crate::interpreter::Interpreter; +use crate::primitives::PrimitiveFn; +use crate::universe::Universe; +use crate::value::Value; +use crate::{expect_args, reverse}; + +fn holder(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "Method>>#holder"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Invokable(invokable) => invokable, + ]); + + match invokable.holder().upgrade() { + Some(holder) => frame.borrow_mut().stack.push(Value::Class(holder)), + None => panic!("'{}': method sholder has been collected", SIGNATURE), + } +} + +fn signature(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "Method>>#signature"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Invokable(invokable) => invokable, + ]); + + let sym = universe.intern_symbol(invokable.signature()); + frame.borrow_mut().stack.push(Value::Symbol(sym)) +} + +fn invoke_on_with(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "Method>>#invokeOn:with:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Invokable(invokable) => invokable, + receiver => receiver, + Value::Array(args) => args, + ]); + + let args = args.borrow().iter().cloned().collect(); + invokable.invoke(interpreter, universe, receiver, 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-bc/src/primitives/mod.rs b/som-interpreter-bc/src/primitives/mod.rs new file mode 100644 index 00000000..fd6fd5f5 --- /dev/null +++ b/som-interpreter-bc/src/primitives/mod.rs @@ -0,0 +1,58 @@ +mod blocks; + +/// Primitives for the **Array** class. +pub mod array; +/// Primitives for the **Class** class. +pub mod class; +/// Primitives for the **Double** 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. +pub mod string; +/// Primitives for the **Symbol** class. +pub mod symbol; +/// Primitives for the **System** class. +pub mod system; + +pub use self::blocks::{block1, block2, block3}; + +use crate::interpreter::Interpreter; +use crate::universe::Universe; + +/// A interpreter primitive (just a bare function pointer). +pub type PrimitiveFn = fn(interpreter: &mut Interpreter, universe: &mut Universe); + +#[macro_export] +macro_rules! reverse { + ($signature:expr, $frame:expr, [], [ $( $ptrn:pat $( => $name:ident )? ),* $(,)? ]) => { + #[allow(unused_mut)] + let ($($(mut $name,)?)*) = { + $(#[allow(unreachable_patterns)] + $(let $name =)? match $frame.borrow_mut().stack.pop() { + Some($ptrn) => {$($name)?}, + Some(_) => panic!("'{}': wrong type", $signature), + None => panic!("'{}': missing argument", $signature), + };)* + ($($($name,)?)*) + }; + }; + ($signature:expr, $frame:expr, [ $( $first_ptrn:pat $( => $first_name:ident )? )? $(,)? ], [ $( $ptrn2:pat $( => $name2:ident )? ),* $(,)? ]) => { + reverse!($signature, $frame, [], [ $( $first_ptrn $( => $first_name )? )? , $( $ptrn2 $( => $name2 )? ,)* ]) + }; + ($signature:expr, $frame:expr, [ $( $first_ptrn:pat $( => $first_name:ident )? )? , $( $ptrn1:pat $( => $name1:ident )? ),* $(,)? ], [ $( $ptrn2:pat $( => $name2:ident )? ),* $(,)? ]) => { + reverse!($signature, $frame, [ $( $ptrn1 $( => $name1 )? ,)* ], [ $( $first_ptrn $( => $first_name )? ,)? $( $ptrn2 $( => $name2 )? ,)* ]) + }; +} + +/// Macro for checking and destructuring arguments passed to primitives. +#[macro_export] +macro_rules! expect_args { + ($signature:expr, $frame:expr, [ $( $ptrn:pat $( => $name:ident )? ),* $(,)? ]) => { + reverse!($signature, $frame, [ $( $ptrn $( => $name )? ,)* ], []) + }; +} diff --git a/som-interpreter-bc/src/primitives/object.rs b/som-interpreter-bc/src/primitives/object.rs new file mode 100644 index 00000000..c08cbf7e --- /dev/null +++ b/som-interpreter-bc/src/primitives/object.rs @@ -0,0 +1,272 @@ +use std::collections::hash_map::DefaultHasher; +use std::convert::TryFrom; +use std::hash::{Hash, Hasher}; + +use crate::interpreter::Interpreter; +use crate::primitives::PrimitiveFn; +use crate::universe::Universe; +use crate::value::Value; +use crate::{expect_args, reverse}; + +fn class(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &'static str = "Object>>#class"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + object => object, + ]); + + frame + .borrow_mut() + .stack + .push(Value::Class(object.class(universe))); +} + +fn object_size(interpreter: &mut Interpreter, _: &mut Universe) { + const _: &'static str = "Object>>#objectSize"; + + let frame = interpreter.current_frame().expect("no current frame"); + + frame + .borrow_mut() + .stack + .push(Value::Integer(std::mem::size_of::() as i64)); +} + +fn hashcode(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &'static str = "Object>>#hashcode"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + let mut hasher = DefaultHasher::new(); + value.hash(&mut hasher); + let hash = (hasher.finish() as i64).abs(); + + frame.borrow_mut().stack.push(Value::Integer(hash)); +} + +fn eq(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &'static str = "Object>>#=="; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + a => a, + b => b, + ]); + + frame.borrow_mut().stack.push(Value::Boolean(a == b)); +} + +fn perform(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &'static str = "Object>>#perform:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + object => object, + Value::Symbol(sym) => sym, + ]); + + let object: Value = object; + + let signature = universe.lookup_symbol(sym); + let method = object.lookup_method(universe, sym); + + match method { + Some(invokable) => invokable.invoke(interpreter, universe, object, vec![]), + None => { + let signature = signature.to_string(); + universe + .does_not_understand(interpreter, object.clone(), sym, vec![object.clone()]) + .unwrap_or_else(|| { + panic!( + "'{}': method '{}' not found for '{}'", + SIGNATURE, + signature, + object.to_string(universe), + ) + // Return::Local(Value::Nil) + }) + } + } +} + +fn perform_with_arguments(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &'static str = "Object>>#perform:withArguments:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + object => object, + Value::Symbol(sym) => sym, + Value::Array(arr) => arr, + ]); + + let signature = universe.lookup_symbol(sym); + let method = object.lookup_method(universe, sym); + + match method { + Some(invokable) => { + let args = arr.borrow().iter().cloned().collect(); + invokable.invoke(interpreter, universe, object, args) + } + None => { + let signature = signature.to_string(); + let args = std::iter::once(object.clone()) + .chain(arr.borrow().iter().cloned()) + .collect(); + universe + .does_not_understand(interpreter, object.clone(), sym, args) + .unwrap_or_else(|| { + panic!( + "'{}': method '{}' not found for '{}'", + SIGNATURE, + signature, + object.to_string(universe) + ) + // Return::Local(Value::Nil) + }) + } + } +} + +fn perform_in_super_class(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &'static str = "Object>>#perform:inSuperclass:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + object => object, + Value::Symbol(sym) => sym, + Value::Class(class) => class, + ]); + + let signature = universe.lookup_symbol(sym); + let method = class.borrow().lookup_method(sym); + + match method { + Some(invokable) => invokable.invoke(interpreter, universe, object, vec![]), + None => { + let signature = signature.to_string(); + let args = vec![object.clone()]; + universe + .does_not_understand(interpreter, Value::Class(class), sym, args) + .unwrap_or_else(|| { + panic!( + "'{}': method '{}' not found for '{}'", + SIGNATURE, + signature, + object.to_string(universe) + ) + // Return::Local(Value::Nil) + }) + } + } +} + +fn perform_with_arguments_in_super_class(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &'static str = "Object>>#perform:withArguments:inSuperclass:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + 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(sym); + + match method { + Some(invokable) => { + let args = arr.borrow().iter().cloned().collect(); + invokable.invoke(interpreter, universe, object, args) + } + None => { + let args = std::iter::once(object.clone()) + .chain(arr.replace(Vec::default()).into_iter()) + .collect(); + let signature = signature.to_string(); + universe + .does_not_understand(interpreter, Value::Class(class), sym, args) + .unwrap_or_else(|| { + panic!( + "'{}': method '{}' not found for '{}'", + SIGNATURE, + signature, + object.to_string(universe) + ) + // Return::Local(Value::Nil) + }) + } + } +} + +fn inst_var_at(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &'static str = "Object>>#instVarAt:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + object => object, + Value::Integer(index) => index, + ]); + + let index = match usize::try_from(index - 1) { + Ok(index) => index, + Err(err) => panic!("'{}': {}", SIGNATURE, err), + }; + + let local = object.lookup_local(index).unwrap_or(Value::Nil); + + frame.borrow_mut().stack.push(local); +} + +fn inst_var_at_put(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &'static str = "Object>>#instVarAt:put:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + object => object, + Value::Integer(index) => index, + value => value, + ]); + + let index = match usize::try_from(index - 1) { + Ok(index) => index, + Err(err) => panic!("'{}': {}", SIGNATURE, err), + }; + + let local = object + .assign_local(index, value.clone()) + .map(|_| value) + .unwrap_or(Value::Nil); + + frame.borrow_mut().stack.push(local); +} + +/// Search for a primitive matching the given signature. +pub fn get_primitive(signature: impl AsRef) -> Option { + match signature.as_ref() { + "class" => Some(self::class), + "objectSize" => Some(self::object_size), + "hashcode" => Some(self::hashcode), + "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-bc/src/primitives/string.rs b/som-interpreter-bc/src/primitives/string.rs new file mode 100644 index 00000000..33dff8fa --- /dev/null +++ b/som-interpreter-bc/src/primitives/string.rs @@ -0,0 +1,211 @@ +use std::collections::hash_map::DefaultHasher; +use std::convert::TryFrom; +use std::hash::Hasher; +use std::rc::Rc; + +use crate::{expect_args, reverse}; +use crate::interpreter::Interpreter; +use crate::primitives::PrimitiveFn; +use crate::universe::Universe; +use crate::value::Value; + +fn length(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "String>>#length"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + let value = match value { + Value::String(ref value) => value.as_str(), + Value::Symbol(sym) => universe.lookup_symbol(sym), + _ => panic!("'{}': invalid self type", SIGNATURE), + }; + + match i64::try_from(value.chars().count()) { + Ok(idx) => frame.borrow_mut().stack.push(Value::Integer(idx)), + Err(err) => panic!("'{}': {}", SIGNATURE, err), + } +} + +fn hashcode(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "String>>#hashcode"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + let value = match value { + Value::String(ref value) => value.as_str(), + Value::Symbol(sym) => universe.lookup_symbol(sym), + _ => panic!("'{}': invalid self type", SIGNATURE), + }; + + let mut hasher = DefaultHasher::new(); + + hasher.write(value.as_bytes()); + + // match i64::try_from(hasher.finish()) { + // Ok(hash) => frame.borrow_mut().stack.push(Value::Integer(hash)), + // Err(err) => panic!("'{}': {}", SIGNATURE, err), + // } + + frame.borrow_mut().stack.push(Value::Integer((hasher.finish() as i64).abs())) +} + +fn is_letters(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "String>>#isLetters"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + let value = match value { + Value::String(ref value) => value.as_str(), + Value::Symbol(sym) => universe.lookup_symbol(sym), + _ => panic!("'{}': invalid self type", SIGNATURE), + }; + + frame.borrow_mut().stack.push(Value::Boolean( + !value.is_empty() && !value.is_empty() && value.chars().all(char::is_alphabetic), + )) +} + +fn is_digits(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "String>>#isDigits"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + let value = match value { + Value::String(ref value) => value.as_str(), + Value::Symbol(sym) => universe.lookup_symbol(sym), + _ => panic!("'{}': invalid self type", SIGNATURE), + }; + + frame.borrow_mut().stack.push(Value::Boolean( + !value.is_empty() && value.chars().all(char::is_numeric), + )) +} + +fn is_whitespace(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "String>>#isWhiteSpace"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + let value = match value { + Value::String(ref value) => value.as_str(), + Value::Symbol(sym) => universe.lookup_symbol(sym), + _ => panic!("'{}': invalid self type", SIGNATURE), + }; + + frame.borrow_mut().stack.push(Value::Boolean( + !value.is_empty() && value.chars().all(char::is_whitespace), + )) +} + +fn concatenate(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "String>>#concatenate:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + s1 => s1, + s2 => s2, + ]); + + let s1 = match s1 { + Value::String(ref value) => value.as_str(), + Value::Symbol(sym) => universe.lookup_symbol(sym), + _ => panic!("'{}': wrong types", SIGNATURE), + }; + let s2 = match s2 { + Value::String(ref value) => value.as_str(), + Value::Symbol(sym) => universe.lookup_symbol(sym), + _ => panic!("'{}': wrong types", SIGNATURE), + }; + + frame.borrow_mut().stack.push(Value::String(Rc::new(format!("{}{}", s1, s2)))) +} + +fn as_symbol(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "String>>#asSymbol"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + value => value, + ]); + + match value { + Value::String(ref value) => { + frame.borrow_mut().stack.push(Value::Symbol(universe.intern_symbol(value.as_str()))) + } + Value::Symbol(sym) => frame.borrow_mut().stack.push(Value::Symbol(sym)), + _ => panic!("'{}': invalid self type", SIGNATURE), + } +} + +fn eq(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "String>>#="; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + s1 => s1, + s2 => s2, + ]); + + frame.borrow_mut().stack.push(Value::Boolean(s1 == s2)) +} + +fn prim_substring_from_to(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "String>>#primSubstringFrom:to:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + 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), + (_, _, _) => panic!("'{}': wrong types", SIGNATURE), + }; + + let string = Rc::new(value.chars().skip(from).take(to - from).collect()); + + frame.borrow_mut().stack.push(Value::String(string)) +} + +/// Search for a primitive matching the given signature. +pub fn get_primitive(signature: impl AsRef) -> Option { + match signature.as_ref() { + "length" => Some(self::length), + "hashcode" => Some(self::hashcode), + "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-bc/src/primitives/symbol.rs b/som-interpreter-bc/src/primitives/symbol.rs new file mode 100644 index 00000000..d7d2932c --- /dev/null +++ b/som-interpreter-bc/src/primitives/symbol.rs @@ -0,0 +1,29 @@ +use std::rc::Rc; + +use crate::interpreter::Interpreter; +use crate::primitives::PrimitiveFn; +use crate::universe::Universe; +use crate::value::Value; +use crate::{expect_args, reverse}; + +fn as_string(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "Symbol>>#asString"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::Symbol(sym) => sym, + ]); + + frame.borrow_mut().stack.push(Value::String(Rc::new( + universe.lookup_symbol(sym).to_string(), + ))); +} + +/// Search for a primitive matching the given signature. +pub fn get_primitive(signature: impl AsRef) -> Option { + match signature.as_ref() { + "asString" => Some(self::as_string), + _ => None, + } +} diff --git a/som-interpreter-bc/src/primitives/system.rs b/som-interpreter-bc/src/primitives/system.rs new file mode 100644 index 00000000..cdfcef80 --- /dev/null +++ b/som-interpreter-bc/src/primitives/system.rs @@ -0,0 +1,169 @@ +use std::convert::TryFrom; +// use std::io::BufRead; +// use std::rc::Rc; + +use crate::interpreter::Interpreter; +use crate::primitives::PrimitiveFn; +use crate::universe::Universe; +use crate::value::Value; +use crate::{expect_args, reverse}; + +// fn read_line(interpreter: &mut Interpreter, _: &mut Universe) { +// const SIGNATURE: &str = "System>>#readLine"; + +// let frame = interpreter.current_frame().expect("no current frame"); + +// expect_args!(SIGNATURE, frame, [Value::System]); + +// match std::io::stdin().lock().lines().next() { +// Some(Ok(line)) => frame.borrow_mut().stack.push(Value::String(Rc::new(line))), +// Some(Err(err)) => panic!("'{}': {}", SIGNATURE, err), +// None => panic!("'{}': {}", SIGNATURE, "error"), +// } +// } + +fn print_string(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "System>>#printString:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::System, + value => value, + ]); + + let string = match value { + Value::String(ref string) => string, + Value::Symbol(sym) => universe.lookup_symbol(sym), + _ => panic!("'{}': wrong type", SIGNATURE), + }; + + print!("{}", string); + frame.borrow_mut().stack.push(Value::System) +} + +fn print_newline(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &'static str = "System>>#printNewline"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [Value::System]); + + println!(); + frame.borrow_mut().stack.push(Value::Nil) +} + +fn load(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "System>>#load:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::System, + Value::Symbol(sym) => sym, + ]); + + let name = universe.lookup_symbol(sym).to_string(); + match universe.load_class(name) { + Ok(class) => frame.borrow_mut().stack.push(Value::Class(class)), + Err(err) => panic!("'{}': {}", SIGNATURE, err), + } +} + +fn global(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "System>>#global:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::System, + Value::Symbol(sym) => sym, + ]); + + frame.borrow_mut().stack.push(universe.lookup_global(sym).unwrap_or(Value::Nil)) +} + +fn global_put(interpreter: &mut Interpreter, universe: &mut Universe) { + const SIGNATURE: &str = "System>>#global:put:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::System, + Value::Symbol(sym) => sym, + value => value, + ]); + + universe.assign_global(sym, value.clone()); + frame.borrow_mut().stack.push(value) +} + +fn exit(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "System>>#exit:"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [ + Value::System, + Value::Integer(code) => code, + ]); + + match i32::try_from(code) { + Ok(code) => std::process::exit(code), + Err(err) => panic!("'{}': {}", SIGNATURE, err), + } +} + +fn ticks(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "System>>#ticks"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [Value::System]); + + match i64::try_from(interpreter.start_time.elapsed().as_micros()) { + Ok(micros) => frame.borrow_mut().stack.push(Value::Integer(micros)), + Err(err) => panic!("'{}': {}", SIGNATURE, err), + } +} + +fn time(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "System>>#time"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [Value::System]); + + match i64::try_from(interpreter.start_time.elapsed().as_millis()) { + Ok(micros) => frame.borrow_mut().stack.push(Value::Integer(micros)), + Err(err) => panic!("'{}': {}", SIGNATURE, err), + } +} + +fn full_gc(interpreter: &mut Interpreter, _: &mut Universe) { + const SIGNATURE: &str = "System>>#fullGC"; + + let frame = interpreter.current_frame().expect("no current frame"); + + expect_args!(SIGNATURE, frame, [Value::System]); + + // We don't do any garbage collection at all, so we return false. + frame.borrow_mut().stack.push(Value::Boolean(false)) +} + +/// 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), + _ => None, + } +} diff --git a/som-interpreter-bc/src/shell.rs b/som-interpreter-bc/src/shell.rs new file mode 100644 index 00000000..a6fdb7f7 --- /dev/null +++ b/som-interpreter-bc/src/shell.rs @@ -0,0 +1,168 @@ +use std::io; +use std::io::{BufRead, Write}; +use std::time::Instant; + +use anyhow::Error; + +use som_lexer::{Lexer, Token}; +use som_parser::lang; +use som_parser::Parser; + +use som_interpreter_bc::compiler; +use som_interpreter_bc::frame::FrameKind; +use som_interpreter_bc::interpreter::Interpreter; +use som_interpreter_bc::universe::Universe; +use som_interpreter_bc::value::Value; + +/// Launches an interactive Read-Eval-Print-Loop within the given universe. +pub fn interactive( + interpreter: &mut Interpreter, + universe: &mut Universe, + verbose: bool, +) -> Result<(), Error> { + let stdin = io::stdin(); + let mut stdin = stdin.lock(); + let stdout = io::stdout(); + let mut stdout = stdout.lock(); + + let mut counter = 0; + let method_name = universe.intern_symbol("run:"); + let mut line = String::new(); + let mut last_value = Value::Nil; + loop { + write!(&mut stdout, "({}) SOM Shell | ", counter)?; + stdout.flush()?; + line.clear(); + stdin.read_line(&mut line)?; + if line.is_empty() { + writeln!(&mut stdout, "exit")?; + break; + } + let line = line.trim(); + if line.is_empty() { + continue; + } + if line == "exit" { + break; + } + + let line = format!("ShellExpr{} = ( run: it = ( ^ ( {} ) ) )", counter, line); + + let start = Instant::now(); + let tokens: Vec = Lexer::new(line.as_str()) + .skip_comments(true) + .skip_whitespace(true) + .collect(); + let elapsed = start.elapsed(); + if verbose { + writeln!( + &mut stdout, + "Lexing time: {} ms ({} µs)", + elapsed.as_millis(), + elapsed.as_micros(), + )?; + } + + let start = Instant::now(); + let class_def = match lang::class_def().parse(tokens.as_slice()) { + Some((expr, rest)) if rest.is_empty() => expr, + Some(_) | None => { + println!("ERROR: could not fully parse the given expression"); + continue; + } + }; + let elapsed = start.elapsed(); + if verbose { + writeln!( + &mut stdout, + "Parsing time: {} ms ({} µs)", + elapsed.as_millis(), + elapsed.as_micros(), + )?; + } + + let object_class = universe.object_class(); + let class = match compiler::compile_class(&mut universe.interner, &class_def, Some(&object_class)) { + Some(class) => class, + None => { + writeln!(&mut stdout, "could not compile expression")?; + continue; + } + }; + let metaclass_class = universe.metaclass_class(); + class.borrow_mut().set_super_class(&object_class); + class + .borrow() + .class() + .borrow_mut() + .set_super_class(&object_class.borrow().class()); + class + .borrow() + .class() + .borrow_mut() + .set_class(&metaclass_class); + + let method = class + .borrow() + .lookup_method(method_name) + .expect("method not found ??"); + let start = Instant::now(); + let kind = FrameKind::Method { + method, + holder: class.clone(), + self_value: Value::Class(class), + }; + let frame = interpreter.push_frame(kind); + frame.borrow_mut().args.push(Value::System); + frame.borrow_mut().args.push(last_value.clone()); + if let Some(value) = interpreter.run(universe) { + writeln!( + &mut stdout, + "returned: {} ({:?})", + value.to_string(&universe), + value + )?; + last_value = value; + } + // , |universe| { + // universe + // .current_frame() + // .borrow_mut() + // .bindings + // .insert("it".into(), last_value.clone()); + + // expr.evaluate(universe) + // }); + let elapsed = start.elapsed(); + if verbose { + writeln!( + &mut stdout, + "Execution time: {} ms ({} µs)", + elapsed.as_millis(), + elapsed.as_micros(), + )?; + writeln!(&mut stdout)?; + } + + // match output { + // Return::Local(value) => { + // writeln!(&mut stdout, "returned: {} ({:?})", value.to_string(&universe), value)?; + // last_value = value; + // } + // Return::NonLocal(value, frame) => { + // writeln!(&mut stdout, + // "returned (non-local, escaped): {} ({:?})", + // value.to_string(&universe), + // value + // )?; + // writeln!(&mut stdout, "intended for frame: {:?}", frame)?; + // last_value = value; + // } + // Return::Exception(message) => println!("ERROR: {}", message), + // Return::Restart => println!("ERROR: asked for a restart to the top-level"), + // } + counter += 1; + } + + Ok(()) +} diff --git a/som-interpreter-bc/src/universe.rs b/som-interpreter-bc/src/universe.rs new file mode 100644 index 00000000..ef931027 --- /dev/null +++ b/som-interpreter-bc/src/universe.rs @@ -0,0 +1,612 @@ +use std::cell::RefCell; +use std::collections::HashMap; +use std::fs; +use std::io; +use std::path::{Path, PathBuf}; +use std::rc::Rc; + +use anyhow::{anyhow, Error}; + +use crate::block::Block; +use crate::class::Class; +use crate::compiler; +use crate::frame::FrameKind; +use crate::interner::{Interned, Interner}; +use crate::interpreter::Interpreter; +use crate::value::Value; +use crate::SOMRef; + +/// The core classes of the SOM interpreter. +/// +/// 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 CoreClasses { + /// The **Object** class. + pub object_class: SOMRef, + /// The **Class** class. + pub class_class: SOMRef, + /// The **Class** class. + pub metaclass_class: SOMRef, + + /// The **Nil** class. + pub nil_class: SOMRef, + /// The **Integer** class. + pub integer_class: SOMRef, + /// The **Double** class. + pub double_class: SOMRef, + /// The **Array** class. + pub array_class: SOMRef, + /// The **Method** class. + pub method_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. + pub system_class: SOMRef, + + /// The **Block** class. + pub block_class: SOMRef, + /// The **Block1** class. + pub block1_class: SOMRef, + /// The **Block2** class. + pub block2_class: SOMRef, + /// The **Block3** class. + pub block3_class: SOMRef, + + /// The **Boolean** class. + pub boolean_class: SOMRef, + /// The **True** class. + 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, +} + +impl Universe { + /// Initialize the universe from the given classpath. + pub fn with_classpath(classpath: Vec) -> Result { + let mut interner = Interner::with_capacity(100); + let mut globals = HashMap::new(); + + let object_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Object")?; + let class_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Class")?; + let metaclass_class = + Self::load_system_class(&mut interner, classpath.as_slice(), "Metaclass")?; + + let nil_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Nil")?; + let integer_class = + Self::load_system_class(&mut interner, classpath.as_slice(), "Integer")?; + let array_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Array")?; + let method_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Method")?; + let symbol_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Symbol")?; + let primitive_class = + Self::load_system_class(&mut interner, classpath.as_slice(), "Primitive")?; + let string_class = Self::load_system_class(&mut interner, classpath.as_slice(), "String")?; + let system_class = Self::load_system_class(&mut interner, classpath.as_slice(), "System")?; + let double_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Double")?; + + let block_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Block")?; + let block1_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Block1")?; + let block2_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Block2")?; + let block3_class = Self::load_system_class(&mut interner, classpath.as_slice(), "Block3")?; + + let boolean_class = + Self::load_system_class(&mut interner, classpath.as_slice(), "Boolean")?; + let true_class = Self::load_system_class(&mut interner, classpath.as_slice(), "True")?; + let false_class = Self::load_system_class(&mut interner, classpath.as_slice(), "False")?; + + // initializeSystemClass(objectClass, null, "Object"); + // set_super_class(&object_class, &nil_class, &metaclass_class); + object_class + .borrow() + .class() + .borrow_mut() + .set_class(&metaclass_class); + object_class + .borrow() + .class() + .borrow_mut() + .set_super_class(&class_class); + // initializeSystemClass(classClass, objectClass, "Class"); + set_super_class(&class_class, &object_class, &metaclass_class); + // initializeSystemClass(metaclassClass, classClass, "Metaclass"); + set_super_class(&metaclass_class, &class_class, &metaclass_class); + // initializeSystemClass(nilClass, objectClass, "Nil"); + set_super_class(&nil_class, &object_class, &metaclass_class); + // initializeSystemClass(arrayClass, objectClass, "Array"); + set_super_class(&array_class, &object_class, &metaclass_class); + // initializeSystemClass(methodClass, arrayClass, "Method"); + set_super_class(&method_class, &array_class, &metaclass_class); + // initializeSystemClass(stringClass, objectClass, "String"); + set_super_class(&string_class, &object_class, &metaclass_class); + // initializeSystemClass(symbolClass, stringClass, "Symbol"); + set_super_class(&symbol_class, &string_class, &metaclass_class); + // initializeSystemClass(integerClass, objectClass, "Integer"); + set_super_class(&integer_class, &object_class, &metaclass_class); + // initializeSystemClass(primitiveClass, objectClass, "Primitive"); + set_super_class(&primitive_class, &object_class, &metaclass_class); + // initializeSystemClass(doubleClass, objectClass, "Double"); + set_super_class(&double_class, &object_class, &metaclass_class); + + set_super_class(&system_class, &object_class, &metaclass_class); + + set_super_class(&block_class, &object_class, &metaclass_class); + set_super_class(&block1_class, &block_class, &metaclass_class); + set_super_class(&block2_class, &block_class, &metaclass_class); + set_super_class(&block3_class, &block_class, &metaclass_class); + + set_super_class(&boolean_class, &object_class, &metaclass_class); + set_super_class(&true_class, &boolean_class, &metaclass_class); + set_super_class(&false_class, &boolean_class, &metaclass_class); + + #[rustfmt::skip] { + globals.insert(interner.intern("Object"), Value::Class(object_class.clone())); + globals.insert(interner.intern("Class"), Value::Class(class_class.clone())); + globals.insert(interner.intern("Metaclass"), Value::Class(metaclass_class.clone())); + globals.insert(interner.intern("Nil"), Value::Class(nil_class.clone())); + globals.insert(interner.intern("Integer"), Value::Class(integer_class.clone())); + globals.insert(interner.intern("Array"), Value::Class(array_class.clone())); + globals.insert(interner.intern("Method"), Value::Class(method_class.clone())); + globals.insert(interner.intern("Symbol"), Value::Class(symbol_class.clone())); + globals.insert(interner.intern("Primitive"), Value::Class(primitive_class.clone())); + globals.insert(interner.intern("String"), Value::Class(string_class.clone())); + globals.insert(interner.intern("System"), Value::Class(system_class.clone())); + globals.insert(interner.intern("Double"), Value::Class(double_class.clone())); + globals.insert(interner.intern("Boolean"), Value::Class(boolean_class.clone())); + globals.insert(interner.intern("True"), Value::Class(true_class.clone())); + globals.insert(interner.intern("False"), Value::Class(false_class.clone())); + globals.insert(interner.intern("Block"), Value::Class(block_class.clone())); + globals.insert(interner.intern("Block1"), Value::Class(block1_class.clone())); + globals.insert(interner.intern("Block2"), Value::Class(block2_class.clone())); + globals.insert(interner.intern("Block3"), Value::Class(block3_class.clone())); + + globals.insert(interner.intern("true"), Value::Boolean(true)); + globals.insert(interner.intern("false"), Value::Boolean(false)); + globals.insert(interner.intern("nil"), Value::Nil); + globals.insert(interner.intern("system"), Value::System); + }; + + Ok(Self { + globals, + interner, + classpath, + 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, + }, + }) + } + + /// Load a system class (with an incomplete hierarchy). + pub fn load_system_class( + interner: &mut Interner, + classpath: &[impl AsRef], + class_name: impl Into, + ) -> Result, Error> { + let class_name = class_name.into(); + for path in classpath { + let mut path = path.as_ref().join(class_name.as_str()); + path.set_extension("som"); + + // Read file contents. + let contents = match fs::read_to_string(path.as_path()) { + Ok(contents) => contents, + Err(err) if err.kind() == io::ErrorKind::NotFound => continue, + 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(anyhow!("could not parse the '{}' system class", class_name)), + }; + + if defn.name != class_name { + return Err(anyhow!( + "{}: class name is different from file name.", + path.display(), + )); + } + let class = compiler::compile_class(interner, &defn, None) + .ok_or_else(|| Error::msg(format!("")))?; + + return Ok(class); + } + + Err(anyhow!("could not find the '{}' system class", class_name)) + } + + /// Load a class from its name into this universe. + pub fn load_class(&mut self, class_name: impl Into) -> Result, Error> { + let class_name = class_name.into(); + for path in self.classpath.iter() { + let mut path = path.join(class_name.as_str()); + path.set_extension("som"); + + // Read file contents. + let contents = match fs::read_to_string(path.as_path()) { + Ok(contents) => contents, + Err(_) => continue, + }; + + // 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 => continue, + }; + + if defn.name != class_name { + return Err(anyhow!( + "{}: class name is different from file name.", + path.display(), + )); + } + + let super_class = if let Some(ref super_class) = defn.super_class { + let symbol = self.intern_symbol(super_class.as_str()); + match self.lookup_global(symbol) { + Some(Value::Class(super_class)) => super_class, + _ => self.load_class(super_class)?, + } + } else { + self.core.object_class.clone() + }; + + let class = compiler::compile_class(&mut self.interner, &defn, Some(&super_class)) + .ok_or_else(|| Error::msg(format!("")))?; + set_super_class(&class, &super_class, &self.core.metaclass_class); + + // fn has_duplicated_field(class: &SOMRef) -> Option<(String, (String, String))> { + // let super_class_iterator = std::iter::successors(Some(class.clone()), |class| { + // class.borrow().super_class() + // }); + // let mut map = HashMap::::new(); + // for class in super_class_iterator { + // let class_name = class.borrow().name().to_string(); + // for (field, _) in class.borrow().locals.iter() { + // let field_name = field.clone(); + // match map.entry(field_name.clone()) { + // Entry::Occupied(entry) => { + // return Some((field_name, (class_name, entry.get().clone()))) + // } + // Entry::Vacant(v) => { + // v.insert(class_name.clone()); + // } + // } + // } + // } + // return None; + // } + + // if let Some((field, (c1, c2))) = has_duplicated_field(&class) { + // return Err(anyhow!( + // "the field named '{}' is defined more than once (by '{}' and '{}', where the latter inherits from the former)", + // field, c1, c2, + // )); + // } + + // if let Some((field, (c1, c2))) = has_duplicated_field(&class.borrow().class()) { + // return Err(anyhow!( + // "the field named '{}' is defined more than once (by '{}' and '{}', where the latter inherits from the former)", + // field, c1, c2, + // )); + // } + + let symbol = self.intern_symbol(class.borrow().name()); + self.globals.insert(symbol, Value::Class(class.clone())); + + return Ok(class); + } + + 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(); + let file_stem = path + .file_stem() + .ok_or_else(|| anyhow!("The given path has no file stem"))?; + + // 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() != file_stem { + return Err(anyhow!( + "{}: class name is different from file name.", + path.display(), + )); + } + + let super_class = if let Some(ref super_class) = defn.super_class { + let symbol = self.intern_symbol(super_class); + match self.lookup_global(symbol) { + Some(Value::Class(class)) => class, + _ => self.load_class(super_class)?, + } + } else { + self.core.object_class.clone() + }; + + let class = compiler::compile_class(&mut self.interner, &defn, Some(&super_class)) + .ok_or_else(|| Error::msg(format!("")))?; + set_super_class(&class, &super_class, &self.core.metaclass_class); + + Ok(class) + } + + /// Get the **Nil** class. + pub fn nil_class(&self) -> SOMRef { + self.core.nil_class.clone() + } + /// Get the **System** class. + pub fn system_class(&self) -> SOMRef { + self.core.system_class.clone() + } + + /// Get the **Object** class. + pub fn object_class(&self) -> SOMRef { + self.core.object_class.clone() + } + + /// Get the **Symbol** class. + pub fn symbol_class(&self) -> SOMRef { + self.core.symbol_class.clone() + } + /// Get the **String** class. + pub fn string_class(&self) -> SOMRef { + self.core.string_class.clone() + } + /// Get the **Array** class. + pub fn array_class(&self) -> SOMRef { + self.core.array_class.clone() + } + + /// Get the **Integer** class. + pub fn integer_class(&self) -> SOMRef { + self.core.integer_class.clone() + } + /// Get the **Double** class. + pub fn double_class(&self) -> SOMRef { + self.core.double_class.clone() + } + + /// Get the **Block** class. + pub fn block_class(&self) -> SOMRef { + self.core.block_class.clone() + } + /// Get the **Block1** class. + pub fn block1_class(&self) -> SOMRef { + self.core.block1_class.clone() + } + /// Get the **Block2** class. + pub fn block2_class(&self) -> SOMRef { + self.core.block2_class.clone() + } + /// Get the **Block3** class. + pub fn block3_class(&self) -> SOMRef { + self.core.block3_class.clone() + } + + /// Get the **True** class. + pub fn true_class(&self) -> SOMRef { + self.core.true_class.clone() + } + /// Get the **False** class. + pub fn false_class(&self) -> SOMRef { + self.core.false_class.clone() + } + + /// Get the **Metaclass** class. + pub fn metaclass_class(&self) -> SOMRef { + 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() + } +} + +impl Universe { + /// Intern a symbol. + pub fn intern_symbol(&mut self, symbol: &str) -> Interned { + self.interner.intern(symbol) + } + + /// Lookup a symbol. + pub fn lookup_symbol(&self, symbol: Interned) -> &str { + self.interner.lookup(symbol) + } + + /// Search for a global binding. + pub fn lookup_global(&self, idx: Interned) -> Option { + self.globals.get(&idx).cloned() + } + + /// Assign a value to a global binding. + pub fn assign_global(&mut self, name: Interned, value: Value) -> Option<()> { + self.globals.insert(name, value)?; + Some(()) + } +} + +impl Universe { + /// Call `escapedBlock:` on the given value, if it is defined. + pub fn escaped_block( + &mut self, + interpreter: &mut Interpreter, + value: Value, + block: Rc, + ) -> Option<()> { + let method_name = self.intern_symbol("escapedBlock:"); + let method = value.lookup_method(self, method_name)?; + + let holder = method.holder().upgrade().unwrap(); + let kind = FrameKind::Method { + method, + holder, + self_value: value.clone(), + }; + + let frame = interpreter.push_frame(kind); + frame.borrow_mut().args.push(value); + frame.borrow_mut().args.push(Value::Block(block)); + + Some(()) + } + + /// Call `doesNotUnderstand:` on the given value, if it is defined. + pub fn does_not_understand( + &mut self, + interpreter: &mut Interpreter, + value: Value, + symbol: Interned, + args: Vec, + ) -> Option<()> { + let method_name = self.intern_symbol("doesNotUnderstand:arguments:"); + let method = value.lookup_method(self, method_name)?; + + let holder = method.holder().upgrade().unwrap(); + let kind = FrameKind::Method { + method, + holder, + self_value: value.clone(), + }; + + let frame = interpreter.push_frame(kind); + frame.borrow_mut().args.push(value); + frame.borrow_mut().args.push(Value::Symbol(symbol)); + let args = Value::Array(Rc::new(RefCell::new(args))); + frame.borrow_mut().args.push(args); + + Some(()) + } + + /// Call `unknownGlobal:` on the given value, if it is defined. + pub fn unknown_global( + &mut self, + interpreter: &mut Interpreter, + value: Value, + name: Interned, + ) -> Option<()> { + let method_name = self.intern_symbol("unknownGlobal:"); + let method = value.lookup_method(self, method_name)?; + + let holder = method.holder().upgrade().unwrap(); + let kind = FrameKind::Method { + method, + holder, + self_value: value.clone(), + }; + + let frame = interpreter.push_frame(kind); + frame.borrow_mut().args.push(value); + frame.borrow_mut().args.push(Value::Symbol(name)); + + Some(()) + } + + /// Call `System>>#initialize:` with the given name, if it is defined. + pub fn initialize(&mut self, interpreter: &mut Interpreter, args: Vec) -> Option<()> { + let method_name = self.interner.intern("initialize:"); + let method = Value::System.lookup_method(self, method_name)?; + + let kind = FrameKind::Method { + method, + holder: self.system_class(), + self_value: Value::System, + }; + + let frame = interpreter.push_frame(kind); + frame.borrow_mut().args.push(Value::System); + let args = Value::Array(Rc::new(RefCell::new(args))); + frame.borrow_mut().args.push(args); + + Some(()) + } +} + +fn set_super_class( + class: &SOMRef, + super_class: &SOMRef, + metaclass_class: &SOMRef, +) { + class.borrow_mut().set_super_class(super_class); + class + .borrow() + .class() + .borrow_mut() + .set_super_class(&super_class.borrow().class()); + class + .borrow() + .class() + .borrow_mut() + .set_class(metaclass_class); +} diff --git a/som-interpreter-bc/src/value.rs b/som-interpreter-bc/src/value.rs new file mode 100644 index 00000000..4684a5d2 --- /dev/null +++ b/som-interpreter-bc/src/value.rs @@ -0,0 +1,186 @@ +use std::fmt; +use std::rc::Rc; + +use num_bigint::BigInt; + +use crate::block::Block; +use crate::class::Class; +use crate::instance::Instance; +use crate::interner::Interned; +use crate::method::Method; +use crate::universe::Universe; +use crate::SOMRef; + +/// Represents an SOM value. +#[derive(Clone)] +pub enum Value { + /// The **nil** value. + Nil, + /// The **system** value. + System, + /// A boolean value (**true** or **false**). + Boolean(bool), + /// An integer value. + Integer(i64), + /// A big integer value (arbitrarily big). + BigInteger(BigInt), + /// An floating-point value. + Double(f64), + /// An interned symbol value. + Symbol(Interned), + /// A string value. + String(Rc), + /// An array of values. + Array(SOMRef>), + /// A block value, ready to be evaluated. + Block(Rc), + /// A generic (non-primitive) class instance. + Instance(SOMRef), + /// A bare class object. + Class(SOMRef), + /// A bare invokable. + Invokable(Rc), +} + +impl Value { + /// Get the class of the current value. + pub fn class(&self, universe: &Universe) -> SOMRef { + match self { + Self::Nil => universe.nil_class(), + Self::System => universe.system_class(), + Self::Boolean(true) => universe.true_class(), + Self::Boolean(false) => universe.false_class(), + Self::Integer(_) => universe.integer_class(), + Self::BigInteger(_) => universe.integer_class(), + Self::Double(_) => universe.double_class(), + Self::Symbol(_) => universe.symbol_class(), + Self::String(_) => universe.string_class(), + Self::Array(_) => universe.array_class(), + Self::Block(block) => block.class(universe), + Self::Instance(instance) => instance.borrow().class(), + Self::Class(class) => class.borrow().class(), + Self::Invokable(invokable) => invokable.class(universe), + } + } + + /// Search for a given method for this value. + pub fn lookup_method( + &self, + universe: &Universe, + signature: Interned, + ) -> Option> { + self.class(universe).borrow().lookup_method(signature) + } + + /// Search for a local binding within this value. + pub fn lookup_local(&self, idx: usize) -> Option { + match self { + Self::Instance(instance) => instance.borrow().lookup_local(idx), + Self::Class(class) => class.borrow().lookup_local(idx), + _ => None, + } + } + + /// Assign a value to a local binding within this value. + pub fn assign_local(&mut self, idx: usize, value: Self) -> Option<()> { + match self { + Self::Instance(instance) => instance.borrow_mut().assign_local(idx, value), + Self::Class(class) => class.borrow_mut().assign_local(idx, value), + _ => None, + } + } + + /// Get the string representation of this value. + pub fn to_string(&self, universe: &Universe) -> String { + match self { + Self::Nil => "nil".to_string(), + Self::System => "system".to_string(), + Self::Boolean(value) => value.to_string(), + Self::Integer(value) => value.to_string(), + Self::BigInteger(value) => value.to_string(), + Self::Double(value) => value.to_string(), + Self::Symbol(value) => { + let symbol = universe.lookup_symbol(*value); + if symbol.chars().any(|ch| ch.is_whitespace() || ch == '\'') { + format!("#'{}'", symbol.replace("'", "\\'")) + } else { + format!("#{}", symbol) + } + } + Self::String(value) => value.to_string(), + Self::Array(values) => { + // TODO: I think we can do better here (less allocations). + let strings: Vec = values + .borrow() + .iter() + .map(|value| value.to_string(universe)) + .collect(); + format!("#({})", strings.join(" ")) + } + Self::Block(block) => format!("instance of Block{}", block.nb_parameters() + 1), + Self::Instance(instance) => format!( + "instance of {} class", + instance.borrow().class().borrow().name(), + ), + Self::Class(class) => class.borrow().name().to_string(), + Self::Invokable(invokable) => invokable + .holder() + .upgrade() + .map(|holder| format!("{}>>#{}", holder.borrow().name(), invokable.signature())) + .unwrap_or_else(|| format!("??>>#{}", invokable.signature())), + } + } +} + +impl PartialEq for Value { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Nil, Self::Nil) | (Self::System, Self::System) => true, + (Self::Boolean(a), Self::Boolean(b)) => a.eq(b), + (Self::Integer(a), Self::Integer(b)) => a.eq(b), + (Self::Integer(a), Self::Double(b)) | (Self::Double(b), Self::Integer(a)) => { + (*a as f64).eq(b) + } + (Self::Double(a), Self::Double(b)) => a.eq(b), + (Self::BigInteger(a), Self::BigInteger(b)) => a.eq(b), + (Self::BigInteger(a), Self::Integer(b)) | (Self::Integer(b), Self::BigInteger(a)) => { + a.eq(&BigInt::from(*b)) + } + (Self::String(a), Self::String(b)) => a.eq(b), + (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::Block(a), Self::Block(b)) => Rc::ptr_eq(a, b), + (Self::Invokable(a), Self::Invokable(b)) => Rc::ptr_eq(a, b), + _ => 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::BigInteger(val) => f.debug_tuple("BigInteger").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(val) => { + let signature = val + .holder() + .upgrade() + .map(|holder| format!("{}>>#{}", holder.borrow().name(), val.signature())) + .unwrap_or_else(|| format!("??>>#{}", val.signature())); + f.debug_tuple("Invokable").field(&signature).finish() + } + } + } +} diff --git a/som-interpreter-bc/tests/basic_interpreter_tests.rs b/som-interpreter-bc/tests/basic_interpreter_tests.rs new file mode 100644 index 00000000..b384b610 --- /dev/null +++ b/som-interpreter-bc/tests/basic_interpreter_tests.rs @@ -0,0 +1,183 @@ +use std::path::PathBuf; + +use som_interpreter_bc::compiler; +use som_interpreter_bc::frame::FrameKind; +use som_interpreter_bc::interpreter::Interpreter; +use som_interpreter_bc::universe::Universe; +use som_interpreter_bc::value::Value; +use som_lexer::{Lexer, Token}; +use som_parser::lang; +use som_parser::Parser; + +fn setup_universe() -> Universe { + let classpath = vec![ + PathBuf::from("../core-lib/Smalltalk"), + PathBuf::from("../core-lib/TestSuite/BasicInterpreterTests"), + ]; + Universe::with_classpath(classpath).expect("could not setup test universe") +} + +#[test] +fn basic_interpreter_tests() { + let mut universe = setup_universe(); + + let return_class = Value::Class(universe.load_class("Return").unwrap()); + let compiler_simplification_class = + Value::Class(universe.load_class("CompilerSimplification").unwrap()); + + let method_name = universe.intern_symbol("run"); + + let tests: &[(&str, Value)] = &[ + // {"Self", "assignSuper", 42, ProgramDefinitionError.class}, + ("MethodCall test", Value::Integer(42)), + ("MethodCall test2", Value::Integer(42)), + ("NonLocalReturn test1", Value::Integer(42)), + ("NonLocalReturn test2", Value::Integer(43)), + ("NonLocalReturn test3", Value::Integer(3)), + ("NonLocalReturn test4", Value::Integer(42)), + ("NonLocalReturn test5", Value::Integer(22)), + ("Blocks testArg1", Value::Integer(42)), + ("Blocks testArg2", Value::Integer(77)), + ("Blocks testArgAndLocal", Value::Integer(8)), + ("Blocks testArgAndContext", Value::Integer(8)), + ("Return testReturnSelf", return_class.clone()), + ("Return testReturnSelfImplicitly", return_class.clone()), + ("Return testNoReturnReturnsSelf", return_class.clone()), + ( + "Return testBlockReturnsImplicitlyLastValue", + Value::Integer(4), + ), + ("IfTrueIfFalse test", Value::Integer(42)), + ("IfTrueIfFalse test2", Value::Integer(33)), + ("IfTrueIfFalse test3", Value::Integer(4)), + ( + "CompilerSimplification testReturnConstantSymbol", + Value::Symbol(universe.intern_symbol("constant")), + ), + ( + "CompilerSimplification testReturnConstantInt", + Value::Integer(42), + ), + ( + "CompilerSimplification testReturnSelf", + compiler_simplification_class.clone(), + ), + ( + "CompilerSimplification testReturnSelfImplicitly", + compiler_simplification_class.clone(), + ), + ( + "CompilerSimplification testReturnArgumentN", + Value::Integer(55), + ), + ( + "CompilerSimplification testReturnArgumentA", + Value::Integer(44), + ), + ( + "CompilerSimplification testSetField", + Value::Symbol(universe.intern_symbol("foo")), + ), + ("CompilerSimplification testGetField", Value::Integer(40)), + ("Hash testHash", Value::Integer(444)), + ("Arrays testEmptyToInts", Value::Integer(3)), + ("Arrays testPutAllInt", Value::Integer(5)), + ("Arrays testPutAllNil", Value::Class(universe.nil_class())), + ("Arrays testPutAllBlock", Value::Integer(3)), + ("Arrays testNewWithAll", Value::Integer(1)), + ("BlockInlining testNoInlining", Value::Integer(1)), + ("BlockInlining testOneLevelInlining", Value::Integer(1)), + ( + "BlockInlining testOneLevelInliningWithLocalShadowTrue", + Value::Integer(2), + ), + ( + "BlockInlining testOneLevelInliningWithLocalShadowFalse", + Value::Integer(1), + ), + ("BlockInlining testBlockNestedInIfTrue", Value::Integer(2)), + ("BlockInlining testBlockNestedInIfFalse", Value::Integer(42)), + ( + "BlockInlining testDeepNestedInlinedIfTrue", + Value::Integer(3), + ), + ( + "BlockInlining testDeepNestedInlinedIfFalse", + Value::Integer(42), + ), + ( + "BlockInlining testDeepNestedBlocksInInlinedIfTrue", + Value::Integer(5), + ), + ( + "BlockInlining testDeepNestedBlocksInInlinedIfFalse", + Value::Integer(43), + ), + ("BlockInlining testDeepDeepNestedTrue", Value::Integer(9)), + ("BlockInlining testDeepDeepNestedFalse", Value::Integer(43)), + ("BlockInlining testToDoNestDoNestIfTrue", Value::Integer(2)), + ("NonLocalVars testWriteDifferentTypes", Value::Double(3.75)), + ("ObjectCreation test", Value::Integer(1000000)), + ("Regressions testSymbolEquality", Value::Integer(1)), + ("Regressions testSymbolReferenceEquality", Value::Integer(1)), + ("BinaryOperation test", Value::Integer(3 + 8)), + ("NumberOfTests numberOfTests", Value::Integer(52)), + ]; + + for (counter, (expr, expected)) in tests.iter().enumerate() { + let mut interpreter = Interpreter::new(); + println!("testing: '{}'", expr); + + let line = format!( + "BasicInterpreterTest{} = ( run = ( ^ ( {} ) ) )", + counter, expr + ); + + let mut lexer = Lexer::new(line).skip_comments(true).skip_whitespace(true); + let tokens: Vec = lexer.by_ref().collect(); + assert!( + lexer.text().is_empty(), + "could not fully tokenize test expression" + ); + + let (class_def, rest) = lang::class_def().parse(tokens.as_slice()).unwrap(); + assert!( + rest.is_empty(), + "could not fully parse test expression: {:?}", + rest + ); + + let object_class = universe.object_class(); + let class = + compiler::compile_class(&mut universe.interner, &class_def, Some(&object_class)); + assert!(class.is_some(), "could not compile test expression"); + let class = class.unwrap(); + + let metaclass_class = universe.metaclass_class(); + class.borrow_mut().set_super_class(&object_class); + class + .borrow() + .class() + .borrow_mut() + .set_super_class(&object_class.borrow().class()); + class + .borrow() + .class() + .borrow_mut() + .set_class(&metaclass_class); + + let method = class + .borrow() + .lookup_method(method_name) + .expect("method not found ??"); + let kind = FrameKind::Method { + method, + holder: class.clone(), + self_value: Value::Class(class), + }; + interpreter.push_frame(kind); + if let Some(output) = interpreter.run(&mut universe) { + assert_eq!(&output, expected, "unexpected test output value"); + } + } +}