From 9aeb81691ecdccac8d88e2c60dd18d98f79bd82f Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Sun, 29 Jan 2023 17:33:10 -0800 Subject: [PATCH 01/31] First pass at reorganizing into workspace w/ multiple packages The impetus for this was wanting to add a package with `derive` macros, since those need to be in their own package, and that got me thinking that it might be good to break things up into logical packages and also learn about workspaces generally. The hardest part of this was breaking circular dependencies, and there's still one issue related to that that is unresolved... Most things work, but intrinsic functions that were previously passed a reference to the VM so they could make calls are broken because the VM reference is no longer passed into such functions. The VM is no longer passed in because 1. types shouldn't be aware of the VM and 2. the `feint-vm` package uses the `feint-builtins` in package, so `feint-builtins` can't also have `feint-vm` as a dependency. One solution that comes to mind is to return some kind of lazy object from intrinsic functions that the VM would then evaluate. This would only apply to the return values of intrinsic functions, since this is the only place where this is an issue. Something else that's wonky is how types indicate errors when an operation isn't defined. Previously, they would directly throw a `RuntimeErr`, which would cause the VM to halt (which was arguably incorrect anyway). Now, they all return `None` when an operation isn't defined on a type or between different types, and the associated error checking is handled in the VM. It might be better to instead make all the operations always return an object, which could be an `ErrObj` instance so the VM doesn't have to handle that (because it's somewhat awkward to handle them there). Type checking during compilation would be another way to fix this issue, but that would be a massive undertaking. Lastly, some things were moved to dubious locations in order to break dependency cycles. In particular, `Code` and `Inst` were moved to `feint-builtins`, in the `types` module, which doesn't seem like the right place for them. The issue here is that the `Func` and `Module` types need access to `Code`, but it can't live in `feint-vm` to avoid a circular dependency. Some related TODOs: - Remove `feint-vm` dep from `feint-cli` and make the driver translate `VMState`s to a driver result - Remove `feint-builtins` dep from `feint-cli` and add a `create_module` method to the driver - Add baked-in constants back to VM (singleton `nil`, `true`, etc) - Move `Code` to `feint-util`? - Rename `feint-util` to `feint-common`? --- Cargo.lock | 95 ++++- Cargo.toml | 30 +- examples/conditionals.fi | 8 +- examples/seq.fi | 11 + feint-builtins/Cargo.toml | 26 ++ build.rs => feint-builtins/build.rs | 11 - feint-builtins/src/lib.rs | 10 + feint-builtins/src/modules/mod.rs | 80 ++++ .../src}/modules/std/args.fi | 0 .../src}/modules/std/mod.rs | 0 .../src}/modules/std/proc.rs | 3 +- .../src}/modules/std/std.fi | 29 ++ .../src}/modules/std/std.rs | 15 +- .../src}/modules/std/system.fi | 0 .../src}/modules/std/test.fi | 0 feint-builtins/src/tests/mod.rs | 1 + {src => feint-builtins/src}/tests/types.rs | 20 +- {src => feint-builtins/src}/types/always.rs | 13 +- {src => feint-builtins/src}/types/base.rs | 139 +++--- {src => feint-builtins/src}/types/bool.rs | 37 +- .../src}/types/bound_func.rs | 15 +- {src => feint-builtins/src}/types/cell.rs | 17 +- {src => feint-builtins/src}/types/class.rs | 14 +- {src => feint-builtins/src}/types/closure.rs | 15 +- feint-builtins/src/types/code.rs | 399 ++++++++++++++++++ {src => feint-builtins/src}/types/custom.rs | 10 +- {src => feint-builtins/src}/types/err.rs | 62 +-- {src => feint-builtins/src}/types/err_type.rs | 22 +- {src => feint-builtins/src}/types/file.rs | 36 +- {src => feint-builtins/src}/types/float.rs | 68 ++- {src => feint-builtins/src}/types/func.rs | 19 +- .../src}/types/func_trait.rs | 6 +- {src => feint-builtins/src}/types/int.rs | 115 +++-- .../src}/types/intrinsic_func.rs | 20 +- {src => feint-builtins/src}/types/iterator.rs | 20 +- {src => feint-builtins/src}/types/list.rs | 91 ++-- {src => feint-builtins/src}/types/map.rs | 90 ++-- {src => feint-builtins/src}/types/mod.rs | 27 +- {src => feint-builtins/src}/types/module.rs | 42 +- {src => feint-builtins/src}/types/new.rs | 68 ++- {src => feint-builtins/src}/types/nil.rs | 17 +- {src => feint-builtins/src}/types/ns.rs | 0 {src => feint-builtins/src}/types/prop.rs | 14 +- feint-builtins/src/types/seq.rs | 125 ++++++ {src => feint-builtins/src}/types/str.rs | 131 +++--- {src => feint-builtins/src}/types/tuple.rs | 52 ++- {src => feint-builtins/src}/types/util.rs | 0 .../call.rs => feint-builtins/src/util.rs | 0 feint-cli/Cargo.toml | 30 ++ feint-cli/build.rs | 43 ++ {src => feint-cli/src}/cli.rs | 0 {src => feint-cli/src}/main.rs | 61 +-- {src => feint-cli/src}/repl.rs | 53 ++- feint-cli/src/tests/mod.rs | 1 + {src => feint-cli/src}/tests/repl.rs | 9 +- feint-code-gen/Cargo.toml | 10 + src/types/gen.rs => feint-code-gen/src/lib.rs | 41 +- feint-compiler/Cargo.toml | 20 + {src => feint-compiler/src}/ast/ast.rs | 37 +- feint-compiler/src/ast/mod.rs | 3 + {src => feint-compiler/src}/ast/visitors.rs | 0 .../src}/compiler/compiler.rs | 9 +- feint-compiler/src/compiler/mod.rs | 7 + .../src}/compiler/result.rs | 4 +- {src => feint-compiler/src}/compiler/scope.rs | 2 +- .../src}/compiler/visitor.rs | 37 +- {src => feint-compiler/src}/format.rs | 24 +- feint-compiler/src/lib.rs | 12 + {src => feint-compiler/src}/parser/mod.rs | 4 +- {src => feint-compiler/src}/parser/parser.rs | 3 +- .../src}/parser/precedence.rs | 0 {src => feint-compiler/src}/parser/result.rs | 3 +- .../src}/scanner/keywords.rs | 0 {src => feint-compiler/src}/scanner/mod.rs | 0 {src => feint-compiler/src}/scanner/result.rs | 3 +- .../src}/scanner/scanner.rs | 7 +- {src => feint-compiler/src}/scanner/token.rs | 5 +- {src => feint-compiler/src}/tests/ast.rs | 3 +- {src => feint-compiler/src}/tests/format.rs | 3 +- feint-compiler/src/tests/mod.rs | 4 + {src => feint-compiler/src}/tests/parser.rs | 5 +- {src => feint-compiler/src}/tests/scanner.rs | 4 +- feint-driver/Cargo.toml | 19 + src/exe.rs => feint-driver/src/driver.rs | 135 +++--- feint-driver/src/lib.rs | 8 + {src => feint-driver/src}/result.rs | 30 +- feint-driver/src/tests/driver.rs | 21 + feint-driver/src/tests/mod.rs | 2 + {src => feint-driver/src}/tests/run.rs | 16 +- feint-util/Cargo.toml | 10 + feint-util/src/lib.rs | 11 + {src => feint-util/src}/op.rs | 72 ++-- {src => feint-util/src}/source.rs | 4 +- {src/util => feint-util/src}/stack.rs | 2 +- {src/util => feint-util/src}/string.rs | 0 feint-util/src/tests/mod.rs | 1 + .../util.rs => feint-util/src/tests/stack.rs | 2 +- feint-vm/Cargo.toml | 21 + {src/vm => feint-vm/src}/context.rs | 9 +- {src => feint-vm/src}/dis.rs | 37 +- feint-vm/src/lib.rs | 15 + {src/vm => feint-vm/src}/result.rs | 37 +- src/tests/vm.rs => feint-vm/src/tests.rs | 8 +- {src/vm => feint-vm/src}/vm.rs | 263 ++++++------ src/ast/mod.rs | 5 - src/compiler/mod.rs | 7 - src/lib.rs | 26 -- src/modules/mod.rs | 43 -- src/tests/compiler.rs | 1 - src/tests/exe.rs | 20 - src/tests/mod.rs | 11 - src/types/result.rs | 9 - src/types/seq.rs | 134 ------ src/util/mod.rs | 7 - src/vm/code.rs | 224 ---------- src/vm/globals.rs | 89 ---- src/vm/inst.rs | 201 --------- src/vm/mod.rs | 19 - 118 files changed, 2015 insertions(+), 1974 deletions(-) create mode 100644 examples/seq.fi create mode 100644 feint-builtins/Cargo.toml rename build.rs => feint-builtins/build.rs (85%) create mode 100644 feint-builtins/src/lib.rs create mode 100644 feint-builtins/src/modules/mod.rs rename {src => feint-builtins/src}/modules/std/args.fi (100%) rename {src => feint-builtins/src}/modules/std/mod.rs (100%) rename {src => feint-builtins/src}/modules/std/proc.rs (90%) rename {src => feint-builtins/src}/modules/std/std.fi (76%) rename {src => feint-builtins/src}/modules/std/std.rs (84%) rename {src => feint-builtins/src}/modules/std/system.fi (100%) rename {src => feint-builtins/src}/modules/std/test.fi (100%) create mode 100644 feint-builtins/src/tests/mod.rs rename {src => feint-builtins/src}/tests/types.rs (92%) rename {src => feint-builtins/src}/types/always.rs (79%) rename {src => feint-builtins/src}/types/base.rs (85%) rename {src => feint-builtins/src}/types/bool.rs (61%) rename {src => feint-builtins/src}/types/bound_func.rs (90%) rename {src => feint-builtins/src}/types/cell.rs (79%) rename {src => feint-builtins/src}/types/class.rs (82%) rename {src => feint-builtins/src}/types/closure.rs (88%) create mode 100644 feint-builtins/src/types/code.rs rename {src => feint-builtins/src}/types/custom.rs (94%) rename {src => feint-builtins/src}/types/err.rs (75%) rename {src => feint-builtins/src}/types/err_type.rs (86%) rename {src => feint-builtins/src}/types/file.rs (81%) rename {src => feint-builtins/src}/types/float.rs (65%) rename {src => feint-builtins/src}/types/func.rs (89%) rename {src => feint-builtins/src}/types/func_trait.rs (96%) rename {src => feint-builtins/src}/types/int.rs (56%) rename {src => feint-builtins/src}/types/intrinsic_func.rs (84%) rename {src => feint-builtins/src}/types/iterator.rs (80%) rename {src => feint-builtins/src}/types/list.rs (75%) rename {src => feint-builtins/src}/types/map.rs (76%) rename {src => feint-builtins/src}/types/mod.rs (61%) rename {src => feint-builtins/src}/types/module.rs (81%) rename {src => feint-builtins/src}/types/new.rs (82%) rename {src => feint-builtins/src}/types/nil.rs (73%) rename {src => feint-builtins/src}/types/ns.rs (100%) rename {src => feint-builtins/src}/types/prop.rs (82%) create mode 100644 feint-builtins/src/types/seq.rs rename {src => feint-builtins/src}/types/str.rs (56%) rename {src => feint-builtins/src}/types/tuple.rs (75%) rename {src => feint-builtins/src}/types/util.rs (100%) rename src/util/call.rs => feint-builtins/src/util.rs (100%) create mode 100644 feint-cli/Cargo.toml create mode 100644 feint-cli/build.rs rename {src => feint-cli/src}/cli.rs (100%) rename {src => feint-cli/src}/main.rs (82%) rename {src => feint-cli/src}/repl.rs (85%) create mode 100644 feint-cli/src/tests/mod.rs rename {src => feint-cli/src}/tests/repl.rs (81%) create mode 100644 feint-code-gen/Cargo.toml rename src/types/gen.rs => feint-code-gen/src/lib.rs (89%) create mode 100644 feint-compiler/Cargo.toml rename {src => feint-compiler/src}/ast/ast.rs (96%) create mode 100644 feint-compiler/src/ast/mod.rs rename {src => feint-compiler/src}/ast/visitors.rs (100%) rename {src => feint-compiler/src}/compiler/compiler.rs (98%) create mode 100644 feint-compiler/src/compiler/mod.rs rename {src => feint-compiler/src}/compiler/result.rs (98%) rename {src => feint-compiler/src}/compiler/scope.rs (99%) rename {src => feint-compiler/src}/compiler/visitor.rs (97%) rename {src => feint-compiler/src}/format.rs (89%) create mode 100644 feint-compiler/src/lib.rs rename {src => feint-compiler/src}/parser/mod.rs (53%) rename {src => feint-compiler/src}/parser/parser.rs (99%) rename {src => feint-compiler/src}/parser/precedence.rs (100%) rename {src => feint-compiler/src}/parser/result.rs (98%) rename {src => feint-compiler/src}/scanner/keywords.rs (100%) rename {src => feint-compiler/src}/scanner/mod.rs (100%) rename {src => feint-compiler/src}/scanner/result.rs (98%) rename {src => feint-compiler/src}/scanner/scanner.rs (99%) rename {src => feint-compiler/src}/scanner/token.rs (99%) rename {src => feint-compiler/src}/tests/ast.rs (97%) rename {src => feint-compiler/src}/tests/format.rs (99%) create mode 100644 feint-compiler/src/tests/mod.rs rename {src => feint-compiler/src}/tests/parser.rs (98%) rename {src => feint-compiler/src}/tests/scanner.rs (99%) create mode 100644 feint-driver/Cargo.toml rename src/exe.rs => feint-driver/src/driver.rs (85%) create mode 100644 feint-driver/src/lib.rs rename {src => feint-driver/src}/result.rs (74%) create mode 100644 feint-driver/src/tests/driver.rs create mode 100644 feint-driver/src/tests/mod.rs rename {src => feint-driver/src}/tests/run.rs (90%) create mode 100644 feint-util/Cargo.toml create mode 100644 feint-util/src/lib.rs rename {src => feint-util/src}/op.rs (73%) rename {src => feint-util/src}/source.rs (97%) rename {src/util => feint-util/src}/stack.rs (98%) rename {src/util => feint-util/src}/string.rs (100%) create mode 100644 feint-util/src/tests/mod.rs rename src/tests/util.rs => feint-util/src/tests/stack.rs (98%) create mode 100644 feint-vm/Cargo.toml rename {src/vm => feint-vm/src}/context.rs (97%) rename {src => feint-vm/src}/dis.rs (81%) create mode 100644 feint-vm/src/lib.rs rename {src/vm => feint-vm/src}/result.rs (75%) rename src/tests/vm.rs => feint-vm/src/tests.rs (77%) rename {src/vm => feint-vm/src}/vm.rs (88%) delete mode 100644 src/ast/mod.rs delete mode 100644 src/compiler/mod.rs delete mode 100644 src/lib.rs delete mode 100644 src/modules/mod.rs delete mode 100644 src/tests/compiler.rs delete mode 100644 src/tests/exe.rs delete mode 100644 src/tests/mod.rs delete mode 100644 src/types/result.rs delete mode 100644 src/types/seq.rs delete mode 100644 src/util/mod.rs delete mode 100644 src/vm/code.rs delete mode 100644 src/vm/globals.rs delete mode 100644 src/vm/inst.rs delete mode 100644 src/vm/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 6092c96..f2222d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,9 +31,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -43,9 +43,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.1.1" +version = "4.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec7a4128863c188deefe750ac1d1dfe66c236909f845af04beed823638dc1b2" +checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" dependencies = [ "bitflags", "clap_lex", @@ -56,9 +56,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.1.0" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8955d4e8cd4f28f9a01c93a050194c4d131e73ca02f6636bcddbed867014d7" +checksum = "3d6540eedc41f8a5a76cf3d8d458057dcdf817be4158a55b5f861f7a5483de75" dependencies = [ "clap", ] @@ -195,9 +195,9 @@ dependencies = [ [[package]] name = "fd-lock" -version = "3.0.8" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb21c69b9fea5e15dbc1049e4b77145dd0ba1c84019c488102de0dc4ea4b0a27" +checksum = "28c0190ff0bd3b28bfdd4d0cf9f92faa12880fb0b8ae2054723dd6c76a4efd42" dependencies = [ "cfg-if", "rustix", @@ -205,24 +205,80 @@ dependencies = [ ] [[package]] -name = "feint" +name = "feint-builtins" version = "0.0.0" dependencies = [ "bitflags", + "feint-code-gen", + "feint-util", + "flate2", + "indexmap", + "num-bigint", + "num-traits", + "once_cell", + "tar", +] + +[[package]] +name = "feint-cli" +version = "0.0.0" +dependencies = [ "clap", "clap_complete", - "ctrlc", "dirs", "env_logger", - "flate2", - "indexmap", + "feint-builtins", + "feint-compiler", + "feint-driver", + "feint-vm", + "rustyline", +] + +[[package]] +name = "feint-code-gen" +version = "0.0.0" + +[[package]] +name = "feint-compiler" +version = "0.0.0" +dependencies = [ + "feint-builtins", + "feint-util", "log", "num-bigint", "num-traits", "once_cell", "regex", - "rustyline", - "tar", +] + +[[package]] +name = "feint-driver" +version = "0.0.0" +dependencies = [ + "feint-builtins", + "feint-code-gen", + "feint-compiler", + "feint-util", + "feint-vm", + "once_cell", +] + +[[package]] +name = "feint-util" +version = "0.0.0" + +[[package]] +name = "feint-vm" +version = "0.0.0" +dependencies = [ + "bitflags", + "ctrlc", + "env_logger", + "feint-builtins", + "feint-util", + "indexmap", + "log", + "num-traits", ] [[package]] @@ -370,10 +426,11 @@ dependencies = [ [[package]] name = "nix" -version = "0.24.3" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" dependencies = [ + "autocfg", "bitflags", "cfg-if", "libc", @@ -520,9 +577,9 @@ dependencies = [ [[package]] name = "rustyline" -version = "10.0.0" +version = "10.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1cd5ae51d3f7bf65d7969d579d502168ef578f289452bd8ccc91de28fda20e" +checksum = "c1e83c32c3f3c33b08496e0d1df9ea8c64d39adb8eb36a1ebb1440c690697aef" dependencies = [ "bitflags", "cfg-if", @@ -532,7 +589,7 @@ dependencies = [ "libc", "log", "memchr", - "nix 0.24.3", + "nix 0.25.1", "radix_trie", "scopeguard", "unicode-segmentation", diff --git a/Cargo.toml b/Cargo.toml index 6479de7..2e2be1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,29 @@ -[package] -name = "feint" +[workspace] +resolver = "2" +members = [ + "feint-builtins", + "feint-cli", + "feint-code-gen", + "feint-compiler", + "feint-util", + "feint-driver", + "feint-vm", +] + +[workspace.package] +edition = "2021" version = "0.0.0" -authors = ["Wyatt Baldwin "] description = "Programming language, bytecode-esque compiler, and stack-based VM" -edition = "2021" +authors = ["Wyatt Baldwin "] readme = "README.md" license-file = "LICENSE" homepage = "https://github.com/feint-lang/" repository = "https://github.com/feint-lang/feint" -[dependencies] +[workspace.dependencies] bitflags = "~1.3.2" clap = { version = "~4.1.1", features = ["env"] } +clap_complete = "~4.1.1" ctrlc = "~3.2.4" dirs = "~4.0.0" env_logger = "~0.10.0" @@ -22,11 +34,5 @@ num-bigint = "~0.4.3" num-traits = "~0.2.15" once_cell = "1.17.0" regex = "~1.7.1" -rustyline = "~10.0.0" -tar = { version = "0.4.38", default-features = false } - -[build-dependencies] -clap = { version = "~4.1.1", features = ["env"] } -clap_complete = "4.1.0" -flate2 = { version = "1.0.25", features = ["zlib"], default-features = false } +rustyline = "~10.1.1" tar = { version = "0.4.38", default-features = false } diff --git a/examples/conditionals.fi b/examples/conditionals.fi index 6d12dc2..b271650 100644 --- a/examples/conditionals.fi +++ b/examples/conditionals.fi @@ -41,13 +41,13 @@ else -> # Pattern matching obj = (1, 2) pat = @ -assert(obj == pat, (obj, pat), true) +assert(obj == pat, $"{obj} != {pat}", true) pat = (1, @) -assert(obj == pat, (obj, pat), true) +assert(obj == pat, $"{obj} != {pat}", true) pat = (@, 2) -assert(obj == pat, (obj, pat), true) +assert(obj == pat, $"{obj} != {pat}", true) -# This just demonstrates that if/else suite at the end of the file +# This just demonstrates that an if/else suite at the end of the file # doesn't cause an error. if false -> print_err(nil) diff --git a/examples/seq.fi b/examples/seq.fi new file mode 100644 index 0000000..c4f56e9 --- /dev/null +++ b/examples/seq.fi @@ -0,0 +1,11 @@ +tuple = ("a", "b", "c") +result = [] +tuple.each((item, i) => result.push((i, item))) +expected = [(0, "a"), (1, "b"), (2, "c")] +assert_eq(result, expected, true) + +list = ["a", "b", "c"] +result = [] +list.each((item, i) => result.push((i, item))) +expected = [(0, "a"), (1, "b"), (2, "c")] +assert_eq(result, expected, true) diff --git a/feint-builtins/Cargo.toml b/feint-builtins/Cargo.toml new file mode 100644 index 0000000..8b1c961 --- /dev/null +++ b/feint-builtins/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "feint-builtins" +edition.workspace = true +version.workspace = true +description.workspace = true +authors.workspace = true +readme.workspace = true +license-file.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +feint-code-gen = { path = "../feint-code-gen" } +feint-util = { path = "../feint-util" } + +bitflags.workspace = true +flate2.workspace = true +indexmap.workspace = true +num-bigint.workspace = true +num-traits.workspace = true +once_cell.workspace = true +tar = { workspace = true, default-features = false } + +[build-dependencies] +flate2 = { workspace = true, features = ["zlib"], default-features = false } +tar = { workspace = true, default-features = false } diff --git a/build.rs b/feint-builtins/build.rs similarity index 85% rename from build.rs rename to feint-builtins/build.rs index c47747c..e7991f5 100644 --- a/build.rs +++ b/feint-builtins/build.rs @@ -5,12 +5,9 @@ use std::io::Error; use std::path::Path; use std::process; -use clap_complete::{self, shells}; use flate2::{Compression, GzBuilder}; use tar::Builder as TarBuilder; -include!("src/cli.rs"); - fn main() -> Result<(), Error> { let out_dir = match env::var_os("OUT_DIR") { Some(out_dir) => out_dir, @@ -25,7 +22,6 @@ fn main() -> Result<(), Error> { let out_dir = Path::new(&out_dir); stamp(out_dir)?; - make_shell_completion_scripts(out_dir)?; make_module_archive(out_dir)?; Ok(()) @@ -39,13 +35,6 @@ fn stamp(out_dir: &Path) -> Result<(), Error> { Ok(()) } -fn make_shell_completion_scripts(out_dir: &Path) -> Result<(), Error> { - let mut cmd = build_cli(); - clap_complete::generate_to(shells::Bash, &mut cmd, "feint", out_dir)?; - clap_complete::generate_to(shells::Fish, &mut cmd, "feint", out_dir)?; - Ok(()) -} - fn make_module_archive(out_dir: &Path) -> Result<(), Error> { let archive_path = out_dir.join("modules.tgz"); let archive_file = File::create(archive_path)?; diff --git a/feint-builtins/src/lib.rs b/feint-builtins/src/lib.rs new file mode 100644 index 0000000..ebe10d1 --- /dev/null +++ b/feint-builtins/src/lib.rs @@ -0,0 +1,10 @@ +#[macro_use] +extern crate bitflags; + +pub mod modules; +pub mod types; + +mod util; + +#[cfg(test)] +mod tests; diff --git a/feint-builtins/src/modules/mod.rs b/feint-builtins/src/modules/mod.rs new file mode 100644 index 0000000..cfd7f04 --- /dev/null +++ b/feint-builtins/src/modules/mod.rs @@ -0,0 +1,80 @@ +use ::std::borrow::Cow; +use ::std::collections::HashMap; +use ::std::io::Read; +use ::std::path::Path; +use ::std::sync::{Arc, RwLock}; + +use flate2::read::GzDecoder; +use once_cell::sync::Lazy; +use tar::Archive as TarArchive; + +use feint_code_gen::{obj_ref, obj_ref_t}; + +use crate::types::map::Map; +use crate::types::{ObjectRef, ObjectTrait}; + +pub mod std; +pub use self::std::STD; + +/// This mirrors `system.modules`. It provides a way to access +/// modules in Rust code (e.g., in the VM). +pub static MODULES: Lazy = Lazy::new(|| obj_ref!(Map::default())); + +/// Add module to `std.system.modules`. +pub fn add_module(name: &str, module: ObjectRef) { + let modules = MODULES.write().unwrap(); + let modules = modules.down_to_map().unwrap(); + modules.insert(name, module); +} + +/// Get module from `system.modules`. +/// +/// XXX: Panics if the module doesn't exist (since that shouldn't be +/// possible). +pub fn get_module(name: &str) -> ObjectRef { + let modules = MODULES.read().unwrap(); + let modules = modules.down_to_map().unwrap(); + if let Some(module) = modules.get(name) { + module.clone() + } else { + panic!("Module not registered: {name}"); + } +} + +/// Get module from `system.modules`. +/// +/// XXX: This will return `None` if the module doesn't exist. Generally, +/// this should only be used during bootstrap. In most cases, +/// `get_module` should be used instead. +pub fn maybe_get_module(name: &str) -> Option { + let modules = MODULES.read().unwrap(); + let modules = modules.down_to_map().unwrap(); + modules.get(name) +} + +/// At build time, a compressed archive is created containing the +/// std .fi module files (see `build.rs`). +/// +/// At runtime, the module file data is read out and stored in a map +/// (lazily). When a std module is imported, the file data is read from +/// this map rather than reading from disk. +/// +/// The utility of this is that we don't need an install process that +/// copies the std module files into some location on the file system +/// based on the location of the current executable or anything like +/// that. +pub static STD_FI_MODULES: Lazy>> = Lazy::new(|| { + let archive_bytes: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/modules.tgz")); + let decoder = GzDecoder::new(archive_bytes); + let mut archive = TarArchive::new(decoder); + let mut modules = HashMap::new(); + for entry in archive.entries().unwrap() { + let mut entry = entry.unwrap(); + let path: Cow<'_, Path> = entry.path().unwrap(); + let path = path.to_str().unwrap().to_owned(); + let mut result = Vec::new(); + entry.read_to_end(&mut result).unwrap(); + modules.insert(path, result); + } + modules +}); diff --git a/src/modules/std/args.fi b/feint-builtins/src/modules/std/args.fi similarity index 100% rename from src/modules/std/args.fi rename to feint-builtins/src/modules/std/args.fi diff --git a/src/modules/std/mod.rs b/feint-builtins/src/modules/std/mod.rs similarity index 100% rename from src/modules/std/mod.rs rename to feint-builtins/src/modules/std/mod.rs diff --git a/src/modules/std/proc.rs b/feint-builtins/src/modules/std/proc.rs similarity index 90% rename from src/modules/std/proc.rs rename to feint-builtins/src/modules/std/proc.rs index 6db6506..40dd830 100644 --- a/src/modules/std/proc.rs +++ b/feint-builtins/src/modules/std/proc.rs @@ -3,7 +3,8 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use crate::types::gen::obj_ref_t; +use feint_code_gen::obj_ref_t; + use crate::types::{new, Module}; pub static PROC: Lazy = Lazy::new(|| { diff --git a/src/modules/std/std.fi b/feint-builtins/src/modules/std/std.fi similarity index 76% rename from src/modules/std/std.fi rename to feint-builtins/src/modules/std/std.fi index 71c5c9d..3c512cb 100644 --- a/src/modules/std/std.fi +++ b/feint-builtins/src/modules/std/std.fi @@ -92,3 +92,32 @@ assert: Bool | Err = (condition: Bool, ...) => $halt 1 err + +assert_eq: Bool | Err = (a: Any, b: Any, ...) => + "Check if items are equal and return error if not. + + # Args + + - a: Any + - b: Any + - halt?: Bool = false + + # Returns + + true: if the items are equal + Err: if the items are not equal and `halt` is unset + + > NOTE: If `halt` is set, the program will exit immediately with an + > error code. + + " + if a == b -> + true + else -> + err = Err.new(ErrType.assertion, $"{a} is not equal to {b}") + + if $args.get(0) -> + print_err(err) + $halt 1 + + err diff --git a/src/modules/std/std.rs b/feint-builtins/src/modules/std/std.rs similarity index 84% rename from src/modules/std/std.rs rename to feint-builtins/src/modules/std/std.rs index 0b52a28..41a735f 100644 --- a/src/modules/std/std.rs +++ b/feint-builtins/src/modules/std/std.rs @@ -3,10 +3,11 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use crate::types::{self, gen, new}; -use crate::vm::RuntimeErr; +use feint_code_gen::{obj_ref_t, use_arg, use_arg_str}; -pub static STD: Lazy = Lazy::new(|| { +use crate::types::{self, new}; + +pub static STD: Lazy = Lazy::new(|| { new::intrinsic_module( "std", "", @@ -46,12 +47,12 @@ pub static STD: Lazy = Lazy::new(|| { - name: Str ", - |_, args, _| { + |_, args| { let module = args[0].clone(); - let name_arg = gen::use_arg!(args, 1); - let name = gen::use_arg_str!(new_type, name, name_arg); + let name_arg = use_arg!(args, 1); + let name = use_arg_str!(new_type, name, name_arg); let class = new::custom_type(module, name); - Ok(class) + class }, ), ), diff --git a/src/modules/std/system.fi b/feint-builtins/src/modules/std/system.fi similarity index 100% rename from src/modules/std/system.fi rename to feint-builtins/src/modules/std/system.fi diff --git a/src/modules/std/test.fi b/feint-builtins/src/modules/std/test.fi similarity index 100% rename from src/modules/std/test.fi rename to feint-builtins/src/modules/std/test.fi diff --git a/feint-builtins/src/tests/mod.rs b/feint-builtins/src/tests/mod.rs new file mode 100644 index 0000000..6f8b3de --- /dev/null +++ b/feint-builtins/src/tests/mod.rs @@ -0,0 +1 @@ +mod types; diff --git a/src/tests/types.rs b/feint-builtins/src/tests/types.rs similarity index 92% rename from src/tests/types.rs rename to feint-builtins/src/tests/types.rs index fc2129e..cf8bc2d 100644 --- a/src/tests/types.rs +++ b/feint-builtins/src/tests/types.rs @@ -107,15 +107,9 @@ mod list { mod custom { use indexmap::IndexMap; - use crate::vm::{RuntimeObjResult, VM}; - use super::*; - fn instance( - type_obj: ObjectRef, - attrs: &[(&str, ObjectRef)], - vm: &mut VM, - ) -> RuntimeObjResult { + fn instance(type_obj: ObjectRef, attrs: &[(&str, ObjectRef)]) -> ObjectRef { let attrs = new::map(IndexMap::from_iter( attrs.into_iter().map(|(n, v)| (n.to_string(), v.clone())), )); @@ -123,22 +117,20 @@ mod custom { let new = new.read().unwrap(); let new = new.down_to_intrinsic_func().unwrap(); let new = new.func(); - new(type_obj.clone(), vec![attrs], vm) + new(type_obj.clone(), vec![attrs]) } #[test] fn test_custom() { - let vm = &mut VM::default(); - let mod1 = new::intrinsic_module("test1", "", "test module 1", &[]); let t1 = new::custom_type(mod1, "Custom1"); - let t1_obj1 = instance(t1.clone(), &[("value", new::nil())], vm).unwrap(); - let t1_obj2 = instance(t1.clone(), &[("value", new::nil())], vm).unwrap(); - let t1_obj3 = instance(t1.clone(), &[("value", new::nil())], vm).unwrap(); + let t1_obj1 = instance(t1.clone(), &[("value", new::nil())]); + let t1_obj2 = instance(t1.clone(), &[("value", new::nil())]); + let t1_obj3 = instance(t1.clone(), &[("value", new::nil())]); let mod2 = new::intrinsic_module("test2", "", "test module 2", &[]); let t2 = new::custom_type(mod2, "Custom2"); - let t2_obj1 = instance(t2.clone(), &[], vm).unwrap(); + let t2_obj1 = instance(t2.clone(), &[]); check_attr(t1.clone(), "$id"); check_attr(t1.clone(), "$type"); diff --git a/src/types/always.rs b/feint-builtins/src/types/always.rs similarity index 79% rename from src/types/always.rs rename to feint-builtins/src/types/always.rs index de96924..bc38d50 100644 --- a/src/types/always.rs +++ b/feint-builtins/src/types/always.rs @@ -4,7 +4,8 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use super::gen; +use feint_code_gen::*; + use super::new; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; @@ -13,10 +14,10 @@ use super::ns::Namespace; // AlwaysType Type ----------------------------------------------------- -gen::type_and_impls!(AlwaysType, Always); +type_and_impls!(AlwaysType, Always); -pub static ALWAYS_TYPE: Lazy = - Lazy::new(|| gen::obj_ref!(AlwaysType::new())); +pub static ALWAYS_TYPE: Lazy = + Lazy::new(|| obj_ref!(AlwaysType::new())); // Always Object ------------------------------------------------------- @@ -24,7 +25,7 @@ pub struct Always { ns: Namespace, } -gen::standard_object_impls!(Always); +standard_object_impls!(Always); impl Always { #[allow(clippy::new_without_default)] @@ -34,7 +35,7 @@ impl Always { } impl ObjectTrait for Always { - gen::object_trait_header!(ALWAYS_TYPE); + object_trait_header!(ALWAYS_TYPE); fn is_equal(&self, _rhs: &dyn ObjectTrait) -> bool { true diff --git a/src/types/base.rs b/feint-builtins/src/types/base.rs similarity index 85% rename from src/types/base.rs rename to feint-builtins/src/types/base.rs index f05dcb1..66ef95d 100644 --- a/src/types/base.rs +++ b/feint-builtins/src/types/base.rs @@ -6,12 +6,11 @@ use std::sync::{Arc, RwLock}; use num_bigint::BigInt; use num_traits::ToPrimitive; -use crate::dis::Disassembler; +use feint_code_gen::*; + use crate::modules::std::STD; -use crate::types::FuncTrait; -use crate::vm::{RuntimeBoolResult, RuntimeErr, RuntimeObjResult}; -use super::gen; +use super::func_trait::FuncTrait; use super::new; use super::ns::Namespace; @@ -38,8 +37,8 @@ use super::prop::{Prop, PropType}; use super::str::{Str, StrType}; use super::tuple::{Tuple, TupleType}; -pub type TypeRef = gen::obj_ref_t!(dyn TypeTrait); -pub type ObjectRef = gen::obj_ref_t!(dyn ObjectTrait); +pub type TypeRef = obj_ref_t!(dyn TypeTrait); +pub type ObjectRef = obj_ref_t!(dyn ObjectTrait); // Type Trait ---------------------------------------------------------- @@ -108,28 +107,18 @@ macro_rules! make_value_extractor { /// Create associated unary op function. macro_rules! make_unary_op { - ( $meth:ident, $op:literal, $result:ty ) => { - fn $meth(&self) -> $result { - Err(RuntimeErr::type_err(format!( - "Unary operator {} ({}) not implemented for {}", - $op, - stringify!($meth), - self.type_obj().read().unwrap() - ))) + ( $meth:ident, $op:literal, $ty:ty ) => { + fn $meth(&self) -> Option<$ty> { + None } }; } /// Create associated binary op function. macro_rules! make_bin_op { - ( $func:ident, $op:literal, $result:ty ) => { - fn $func(&self, _rhs: &dyn ObjectTrait) -> $result { - Err(RuntimeErr::type_err(format!( - "Binary operator {} ({}) not implemented for {}", - $op, - stringify!($func), - self.type_obj().read().unwrap() - ))) + ( $func:ident, $op:literal, $ty:ty ) => { + fn $func(&self, _rhs: &dyn ObjectTrait) -> Option<$ty> { + None } }; } @@ -213,38 +202,38 @@ pub trait ObjectTrait { return new::tuple(items); } - if name == "$dis" { - // User functions, bound functions wrapping user functions, - // and closures wrapping user functions can be disassembled. - if let Some(f) = self.down_to_func() { - let mut dis = Disassembler::new(); - dis.disassemble(f.code()); - } else if let Some(b) = self.down_to_bound_func() { - let f = b.func(); - let f = f.read().unwrap(); - if let Some(f) = f.down_to_func() { - let mut dis = Disassembler::new(); - dis.disassemble(f.code()); - } else { - eprintln!("Cannot disassemble bound func: {}", b); - } - } else if let Some(c) = self.down_to_closure() { - let f = c.func(); - let f = f.read().unwrap(); - if let Some(f) = f.down_to_func() { - let mut dis = Disassembler::new(); - dis.disassemble(f.code()); - } else { - eprintln!("Cannot disassemble closure: {}", c); - } - } else if let Some(m) = self.down_to_mod() { - let mut dis = Disassembler::new(); - dis.disassemble(m.code()); - } else { - eprintln!("Cannot disassemble object: {}", &*this.read().unwrap()); - } - return new::nil(); - } + // if name == "$dis" { + // // User functions, bound functions wrapping user functions, + // // and closures wrapping user functions can be disassembled. + // if let Some(f) = self.down_to_func() { + // let mut dis = Disassembler::new(); + // dis.disassemble(f.code()); + // } else if let Some(b) = self.down_to_bound_func() { + // let f = b.func(); + // let f = f.read().unwrap(); + // if let Some(f) = f.down_to_func() { + // let mut dis = Disassembler::new(); + // dis.disassemble(f.code()); + // } else { + // eprintln!("Cannot disassemble bound func: {}", b); + // } + // } else if let Some(c) = self.down_to_closure() { + // let f = c.func(); + // let f = f.read().unwrap(); + // if let Some(f) = f.down_to_func() { + // let mut dis = Disassembler::new(); + // dis.disassemble(f.code()); + // } else { + // eprintln!("Cannot disassemble closure: {}", c); + // } + // } else if let Some(m) = self.down_to_mod() { + // let mut dis = Disassembler::new(); + // dis.disassemble(m.code()); + // } else { + // eprintln!("Cannot disassemble object: {}", &*this.read().unwrap()); + // } + // return new::nil(); + // } // Instance attributes ----------------------------------------- // @@ -507,14 +496,14 @@ pub trait ObjectTrait { // Unary operations ------------------------------------------------ - make_unary_op!(negate, "-", RuntimeObjResult); - make_unary_op!(bool_val, "!!", RuntimeBoolResult); + make_unary_op!(negate, "-", ObjectRef); + make_unary_op!(bool_val, "!!", bool); - fn not(&self) -> RuntimeBoolResult { + fn not(&self) -> Option { match self.bool_val() { - Ok(true) => Ok(false), - Ok(false) => Ok(true), - err => err, + Some(true) => Some(false), + Some(false) => Some(true), + None => None, } } @@ -541,24 +530,18 @@ pub trait ObjectTrait { self.is(rhs) || rhs.is_always() } - make_bin_op!(and, "&&", RuntimeBoolResult); - make_bin_op!(or, "||", RuntimeBoolResult); - make_bin_op!(less_than, "<", RuntimeBoolResult); - make_bin_op!(greater_than, ">", RuntimeBoolResult); - - make_bin_op!(pow, "^", RuntimeObjResult); - make_bin_op!(modulo, "%", RuntimeObjResult); - make_bin_op!(mul, "*", RuntimeObjResult); - make_bin_op!(div, "/", RuntimeObjResult); - make_bin_op!(floor_div, "//", RuntimeObjResult); - make_bin_op!(add, "+", RuntimeObjResult); - make_bin_op!(sub, "-", RuntimeObjResult); - - // Call ------------------------------------------------------------ - - fn not_callable(&self) -> RuntimeErr { - RuntimeErr::not_callable(self.class().read().unwrap().full_name()) - } + make_bin_op!(and, "&&", bool); + make_bin_op!(or, "||", bool); + make_bin_op!(less_than, "<", bool); + make_bin_op!(greater_than, ">", bool); + + make_bin_op!(pow, "^", ObjectRef); + make_bin_op!(modulo, "%", ObjectRef); + make_bin_op!(mul, "*", ObjectRef); + make_bin_op!(div, "/", ObjectRef); + make_bin_op!(floor_div, "//", ObjectRef); + make_bin_op!(add, "+", ObjectRef); + make_bin_op!(sub, "-", ObjectRef); } // Display ------------------------------------------------------------- diff --git a/src/types/bool.rs b/feint-builtins/src/types/bool.rs similarity index 61% rename from src/types/bool.rs rename to feint-builtins/src/types/bool.rs index b7eeb3c..74df938 100644 --- a/src/types/bool.rs +++ b/feint-builtins/src/types/bool.rs @@ -4,9 +4,8 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use crate::vm::{RuntimeBoolResult, RuntimeErr}; +use feint_code_gen::*; -use super::gen; use super::new; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; @@ -15,10 +14,10 @@ use super::ns::Namespace; // Bool Type ----------------------------------------------------------- -gen::type_and_impls!(BoolType, Bool); +type_and_impls!(BoolType, Bool); -pub static BOOL_TYPE: Lazy = - Lazy::new(|| gen::obj_ref!(BoolType::new())); +pub static BOOL_TYPE: Lazy = + Lazy::new(|| obj_ref!(BoolType::new())); // Bool Object --------------------------------------------------------- @@ -27,7 +26,7 @@ pub struct Bool { value: bool, } -gen::standard_object_impls!(Bool); +standard_object_impls!(Bool); impl Bool { pub fn new(value: bool) -> Self { @@ -40,12 +39,12 @@ impl Bool { } impl ObjectTrait for Bool { - gen::object_trait_header!(BOOL_TYPE); + object_trait_header!(BOOL_TYPE); // Unary operations ----------------------------------------------- - fn bool_val(&self) -> RuntimeBoolResult { - Ok(*self.value()) + fn bool_val(&self) -> Option { + Some(*self.value()) } // Binary operations ----------------------------------------------- @@ -60,27 +59,19 @@ impl ObjectTrait for Bool { } } - fn and(&self, rhs: &dyn ObjectTrait) -> RuntimeBoolResult { + fn and(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(rhs) = rhs.down_to_bool() { - Ok(*self.value() && *rhs.value()) + Some(*self.value() && *rhs.value()) } else { - Err(RuntimeErr::type_err(format!( - "{} && {} not implemented", - self.class().read().unwrap(), - rhs.class().read().unwrap(), - ))) + None } } - fn or(&self, rhs: &dyn ObjectTrait) -> RuntimeBoolResult { + fn or(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(rhs) = rhs.down_to_bool() { - Ok(*self.value() || *rhs.value()) + Some(*self.value() || *rhs.value()) } else { - Err(RuntimeErr::type_err(format!( - "{} || {} not implemented", - self.class().read().unwrap(), - rhs.class().read().unwrap(), - ))) + None } } } diff --git a/src/types/bound_func.rs b/feint-builtins/src/types/bound_func.rs similarity index 90% rename from src/types/bound_func.rs rename to feint-builtins/src/types/bound_func.rs index 3c8bed4..721c817 100644 --- a/src/types/bound_func.rs +++ b/feint-builtins/src/types/bound_func.rs @@ -4,21 +4,22 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use super::gen; +use feint_code_gen::*; + use super::new; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; use super::func_trait::FuncTrait; use super::ns::Namespace; -use super::result::Params; +use super::Params; // Bound Function Type ------------------------------------------------- -gen::type_and_impls!(BoundFuncType, BoundFunc); +type_and_impls!(BoundFuncType, BoundFunc); -pub static BOUND_FUNC_TYPE: Lazy = - Lazy::new(|| gen::obj_ref!(BoundFuncType::new())); +pub static BOUND_FUNC_TYPE: Lazy = + Lazy::new(|| obj_ref!(BoundFuncType::new())); // BoundFunc Object ---------------------------------------------------------- @@ -31,7 +32,7 @@ pub struct BoundFunc { params: Params, } -gen::standard_object_impls!(BoundFunc); +standard_object_impls!(BoundFunc); impl BoundFunc { pub fn new(func: ObjectRef, this: ObjectRef) -> Self { @@ -101,7 +102,7 @@ impl FuncTrait for BoundFunc { } impl ObjectTrait for BoundFunc { - gen::object_trait_header!(BOUND_FUNC_TYPE); + object_trait_header!(BOUND_FUNC_TYPE); fn module(&self) -> ObjectRef { self.func().read().unwrap().module() diff --git a/src/types/cell.rs b/feint-builtins/src/types/cell.rs similarity index 79% rename from src/types/cell.rs rename to feint-builtins/src/types/cell.rs index 04f2639..8e045a0 100644 --- a/src/types/cell.rs +++ b/feint-builtins/src/types/cell.rs @@ -4,9 +4,8 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use crate::vm::RuntimeBoolResult; +use feint_code_gen::*; -use super::gen; use super::new; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; @@ -15,10 +14,10 @@ use super::ns::Namespace; // Cell Type ----------------------------------------------------------- -gen::type_and_impls!(CellType, Cell); +type_and_impls!(CellType, Cell); -pub static CELL_TYPE: Lazy = - Lazy::new(|| gen::obj_ref!(CellType::new())); +pub static CELL_TYPE: Lazy = + Lazy::new(|| obj_ref!(CellType::new())); // Cell Object --------------------------------------------------------- @@ -27,7 +26,7 @@ pub struct Cell { value: ObjectRef, } -gen::standard_object_impls!(Cell); +standard_object_impls!(Cell); impl Cell { #[allow(clippy::new_without_default)] @@ -51,10 +50,10 @@ impl Cell { } impl ObjectTrait for Cell { - gen::object_trait_header!(CELL_TYPE); + object_trait_header!(CELL_TYPE); - fn bool_val(&self) -> RuntimeBoolResult { - Ok(false) + fn bool_val(&self) -> Option { + Some(false) } } diff --git a/src/types/class.rs b/feint-builtins/src/types/class.rs similarity index 82% rename from src/types/class.rs rename to feint-builtins/src/types/class.rs index f26a1ec..44a92c2 100644 --- a/src/types/class.rs +++ b/feint-builtins/src/types/class.rs @@ -7,18 +7,18 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use super::gen; -use super::new; +use feint_code_gen::*; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; +use super::new; use super::ns::Namespace; // Type Type ----------------------------------------------------------- -gen::type_and_impls!(TypeType, Type); +type_and_impls!(TypeType, Type); -pub static TYPE_TYPE: Lazy = - Lazy::new(|| gen::obj_ref!(TypeType::new())); +pub static TYPE_TYPE: Lazy = + Lazy::new(|| obj_ref!(TypeType::new())); // Type Object --------------------------------------------------------- @@ -26,7 +26,7 @@ pub struct Type { ns: Namespace, } -gen::standard_object_impls!(Type); +standard_object_impls!(Type); impl Type { #[allow(clippy::new_without_default)] @@ -36,7 +36,7 @@ impl Type { } impl ObjectTrait for Type { - gen::object_trait_header!(TYPE_TYPE); + object_trait_header!(TYPE_TYPE); } // Display ------------------------------------------------------------- diff --git a/src/types/closure.rs b/feint-builtins/src/types/closure.rs similarity index 88% rename from src/types/closure.rs rename to feint-builtins/src/types/closure.rs index 7c561f2..712389b 100644 --- a/src/types/closure.rs +++ b/feint-builtins/src/types/closure.rs @@ -4,21 +4,20 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use super::gen; -use super::new; -use super::result::Params; +use feint_code_gen::*; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; use super::func_trait::FuncTrait; use super::ns::Namespace; +use super::{new, Params}; // Closure Type -------------------------------------------------------- -gen::type_and_impls!(ClosureType, Closure); +type_and_impls!(ClosureType, Closure); -pub static CLOSURE_TYPE: Lazy = - Lazy::new(|| gen::obj_ref!(ClosureType::new())); +pub static CLOSURE_TYPE: Lazy = + Lazy::new(|| obj_ref!(ClosureType::new())); // Closure Object ------------------------------------------------------ @@ -32,7 +31,7 @@ pub struct Closure { captured: ObjectRef, } -gen::standard_object_impls!(Closure); +standard_object_impls!(Closure); impl Closure { pub fn new(func_ref: ObjectRef, captured: ObjectRef) -> Self { @@ -87,7 +86,7 @@ impl FuncTrait for Closure { } impl ObjectTrait for Closure { - gen::object_trait_header!(CLOSURE_TYPE); + object_trait_header!(CLOSURE_TYPE); } // Display ------------------------------------------------------------- diff --git a/feint-builtins/src/types/code.rs b/feint-builtins/src/types/code.rs new file mode 100644 index 0000000..4f1c65b --- /dev/null +++ b/feint-builtins/src/types/code.rs @@ -0,0 +1,399 @@ +use std::ops::Index; +use std::slice::Iter; + +use feint_util::op::{BinaryOperator, CompareOperator, InplaceOperator, UnaryOperator}; +use feint_util::source::Location; +use feint_util::string::format_doc; + +use crate::types::{new, FuncTrait, ObjectRef}; + +type FreeVarEntry = ( + usize, // address + String, // name + Location, // source start + Location, // source end +); + +/// Code for a module or function. +#[derive(Debug)] +pub struct Code { + chunk: Vec, + constants: Vec, + // Vars defined outside of this unit of code. + free_vars: Vec, +} + +impl Default for Code { + fn default() -> Self { + Code::new(vec![], vec![], vec![]) + } +} + +impl Index for Code { + type Output = Inst; + + fn index(&self, index: usize) -> &Self::Output { + &self.chunk[index] + } +} + +impl PartialEq for Code { + fn eq(&self, other: &Self) -> bool { + if self.chunk != other.chunk { + return false; + } + if self.constants.len() != other.constants.len() { + return false; + } + if self.free_vars != other.free_vars { + return false; + } + for (c, d) in self.constants.iter().zip(other.constants.iter()) { + let c = c.read().unwrap(); + let d = d.read().unwrap(); + if !c.is_equal(&*d) { + return false; + } + } + true + } +} + +impl Code { + pub fn new( + chunk: Vec, + constants: Vec, + free_vars: Vec, + ) -> Self { + Self { chunk, constants, free_vars } + } + + /// Initialize code object with a list of instructions, also known + /// as a chunk. + pub fn with_chunk(chunk: Vec) -> Self { + Self::new(chunk, vec![], vec![]) + } + + /// Extend this `Code` object with another `Code` object: + /// + /// - Extend instructions, adjusting constant indexes + /// - Extend constants + /// - Free vars are ignored for now since this is mainly intended + /// for extending modules (where there are no free vars) and not + /// functions + /// + /// IMPORTANT: ALL instructions that hold a const index MUST be + /// updated here. + pub fn extend(&mut self, mut code: Self) { + use Inst::LoadConst; + let mut replacements = vec![]; + let const_offset = self.constants.len(); + for (addr, inst) in code.iter_chunk().enumerate() { + if let LoadConst(index) = inst { + replacements.push((addr, LoadConst(const_offset + index))); + } + } + for (addr, inst) in replacements { + code.replace_inst(addr, inst); + } + self.chunk.extend(code.chunk); + self.constants.extend(code.constants); + } + + /// Get docstring for code unit, if there is one. + pub fn get_doc(&self) -> ObjectRef { + if let Some(Inst::LoadConst(0)) = self.chunk.get(1) { + if let Some(obj_ref) = self.get_const(0) { + let obj = obj_ref.read().unwrap(); + if let Some(doc) = obj.get_str_val() { + return new::str(format_doc(doc)); + } + } + } + new::nil() + } + + // Instructions ---------------------------------------------------- + + pub fn len_chunk(&self) -> usize { + self.chunk.len() + } + + pub fn iter_chunk(&self) -> Iter<'_, Inst> { + self.chunk.iter() + } + + pub fn push_inst(&mut self, inst: Inst) { + self.chunk.push(inst) + } + + pub fn pop_inst(&mut self) -> Option { + self.chunk.pop() + } + + pub fn insert_inst(&mut self, index: usize, inst: Inst) { + self.chunk.insert(index, inst); + } + + pub fn replace_inst(&mut self, index: usize, inst: Inst) { + self.chunk[index] = inst; + } + + /// Explicit return statements need to jump to the end of the + /// function so that the function can be cleanly exited. + pub fn fix_up_explicit_returns(&mut self) { + let return_addr = self.len_chunk(); + for addr in 0..return_addr { + let inst = &self.chunk[addr]; + if let Inst::ReturnPlaceholder(inst_addr, depth) = inst { + let rel_addr = return_addr - inst_addr; + self.replace_inst(*inst_addr, Inst::Jump(rel_addr, true, depth - 1)); + } + } + } + + // Constants ------------------------------------------------------- + + pub fn add_const(&mut self, val_ref: ObjectRef) -> usize { + let val_guard = val_ref.read().unwrap(); + let val = &*val_guard; + + // XXX: Functions are immutable and comparable, but it feels + // potentially unsafe to treat them as such here. + let is_comparable = val.is_immutable() && !val.is_func(); + + for (index, other_ref) in self.iter_constants().enumerate() { + let other = other_ref.read().unwrap(); + let other_is_comparable = other.is_immutable() && !other.is_func(); + if is_comparable && other_is_comparable && other.is_equal(val) { + return index; + } + } + + let index = self.constants.len(); + drop(val_guard); + self.constants.push(val_ref); + index + } + + pub fn get_const(&self, index: usize) -> Option<&ObjectRef> { + self.constants.get(index) + } + + pub fn iter_constants(&self) -> Iter<'_, ObjectRef> { + self.constants.iter() + } + + pub fn get_main(&self) -> Option { + let maybe_index = self.constants.iter().position(|obj_ref| { + let obj = obj_ref.read().unwrap(); + if let Some(func) = obj.down_to_func() { + func.name() == "$main" + } else { + false + } + }); + maybe_index.map(|index| self.constants[index].clone()) + } + + // Vars ------------------------------------------------------------ + + pub fn free_vars(&self) -> &Vec { + &self.free_vars + } + + /// Add a free var, a reference to a var defined in an enclosing + /// scope. This also adds a placeholder instruction for the free + /// var that will replaced in the compiler's name resolution stage. + pub fn add_free_var>( + &mut self, + name: S, + start: Location, + end: Location, + ) { + let addr = self.len_chunk(); + let name = name.into(); + self.free_vars.push((addr, name.clone(), start, end)); + self.push_inst(Inst::FreeVarPlaceholder(addr, name)); + } +} + +/// NOTE: When adding or removing instructions, the PartialEq impl +/// below must also be updated. +#[derive(Debug)] +pub enum Inst { + NoOp, + + // Pop TOS and discard it. + Pop, + + ScopeStart, + ScopeEnd, + + StatementStart(Location, Location), + + // Other constants are local to a given code unit. + LoadConst(usize), + + DeclareVar(String), + AssignVar(String), + + // Args: name, offset + // + // `offset` is the number of scopes above the current scope to start + // the search. 0 means the current scope, 1 means the parent scope, + // and so on. + LoadVar(String, usize), + + // Load module global + LoadGlobal(String), + + // Load builtin + LoadBuiltin(String), + + // These are analogous to AssignVar and LoadVar. Assignment wraps + // the value in a cell so that it can be shared. Loading unwraps the + // value. + AssignCell(String), + LoadCell(String), + + // Load captured value to TOS (a special case of LoadCell). + LoadCaptured(String), + + // Jumps ----------------------------------------------------------- + // + // For all jump instructions, the first arg is the target address + // relative to the jump address. The second arg is a flag to + // indicate a forward or reverse jump. The third arg is the scope + // exit count. + // + // Relative addresses allow instructions to be inserted BEFORE any + // forward jumps or AFTER any backward jumps within a code segment. + // Mainly this is to allow instructions to be inserted at the + // beginning of functions. + + // Jump unconditionally. + Jump(usize, bool, usize), + + // Jump unconditionally and push nil onto stack. + JumpPushNil(usize, bool, usize), + + // If top of stack is true, jump to address. Otherwise, continue. + JumpIf(usize, bool, usize), + + // If top of stack is false, jump to address. Otherwise, continue. + JumpIfNot(usize, bool, usize), + + // If top of stack is NOT nil, jump to address. Otherwise, continue. + JumpIfNotNil(usize, bool, usize), + + UnaryOp(UnaryOperator), + BinaryOp(BinaryOperator), + CompareOp(CompareOperator), + InplaceOp(InplaceOperator), + + // Call function with N values from top of stack. The args are + // ordered such that the 1st arg is at TOS and other args are below + // it. + Call(usize), + + // RETURN is a jump target at the end of a function. Its only + // purpose is to serve as a jump target for explicit returns. + Return, + + // These make compound objects from the top N items on the stack. + MakeString(usize), + MakeTuple(usize), + MakeList(usize), + MakeMap(usize), + + // Capture set for function--a list of names for the function to + // capture. If empty, a regular function will be created. + CaptureSet(Vec), + + // Make function or closure depending on capture set. MAKE_FUNC + // expects the following entries at TOS: + // + // TOS capture_set: Map (added by CAPTURE_SET) + // func: Func (added by LOAD_CONST) + MakeFunc, + + LoadModule(String), + + Halt(u8), + HaltTop, + + // Placeholders ---------------------------------------------------- + // + // Placeholders are inserted during compilation and later updated. + // All placeholders must be replaced or a runtime error will be + // thrown. + Placeholder(usize, Box, String), // address, instruction, error message + FreeVarPlaceholder(usize, String), // address, var name + BreakPlaceholder(usize, usize), // jump address, scope depth + ContinuePlaceholder(usize, usize), // jump address, scope depth + + // NOTE: This is used for explicit return statements. It will be + // replaced with a jump to a RETURN target. + ReturnPlaceholder(usize, usize), // jump address, scope depth + + // Miscellaneous --------------------------------------------------- + + // Pop TOS and print it to stdout or stderr. Behavior is controlled + // by passing in flags. Pass `PrintFlags::default()` for the default + // behavior, which is to print to stdout with no newline. + Print(PrintFlags), + + DisplayStack(String), +} + +bitflags! { + #[derive(Default)] + pub struct PrintFlags: u32 { + const ERR = 0b00000001; // print to stderr + const NL = 0b00000010; // print a trailing newline. + const REPR = 0b00000100; // print repr using fmt::Debug + const NO_NIL = 0b00001000; // don't print obj if it's nil + } +} + +impl PartialEq for Inst { + fn eq(&self, other: &Self) -> bool { + use Inst::*; + + match (self, other) { + (NoOp, NoOp) => true, + (Pop, Pop) => true, + (ScopeStart, ScopeStart) => true, + (ScopeEnd, ScopeEnd) => true, + (StatementStart(..), StatementStart(..)) => true, + (LoadConst(a), LoadConst(b)) => a == b, + (DeclareVar(a), DeclareVar(b)) => a == b, + (AssignVar(a), AssignVar(b)) => a == b, + (LoadVar(a, i), LoadVar(b, j)) => (a, i) == (b, j), + (AssignCell(a), AssignCell(b)) => a == b, + (LoadCell(a), LoadCell(b)) => a == b, + (LoadCaptured(a), LoadCaptured(b)) => a == b, + (Jump(a, b, c), Jump(d, e, f)) => (a, b, c) == (d, e, f), + (JumpPushNil(a, b, c), JumpPushNil(d, e, f)) => (a, b, c) == (d, e, f), + (JumpIfNot(a, b, c), JumpIfNot(d, e, f)) => (a, b, c) == (d, e, f), + (UnaryOp(a), UnaryOp(b)) => a == b, + (BinaryOp(a), BinaryOp(b)) => a == b, + (CompareOp(a), CompareOp(b)) => a == b, + (InplaceOp(a), InplaceOp(b)) => a == b, + (Call(a), Call(b)) => a == b, + (Return, Return) => true, + (MakeString(a), MakeString(b)) => a == b, + (MakeTuple(a), MakeTuple(b)) => a == b, + (MakeList(a), MakeList(b)) => a == b, + (MakeMap(a), MakeMap(b)) => a == b, + (CaptureSet(a), CaptureSet(b)) => a == b, + (MakeFunc, MakeFunc) => true, + (LoadModule(a), LoadModule(b)) => a == b, + (Halt(a), Halt(b)) => a == b, + (HaltTop, HaltTop) => true, + (Print(a), Print(b)) => a == b, + _ => false, + } + } +} diff --git a/src/types/custom.rs b/feint-builtins/src/types/custom.rs similarity index 94% rename from src/types/custom.rs rename to feint-builtins/src/types/custom.rs index 2a8795d..1e48b64 100644 --- a/src/types/custom.rs +++ b/feint-builtins/src/types/custom.rs @@ -2,8 +2,8 @@ use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use super::gen; use super::new; +use feint_code_gen::*; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; @@ -40,7 +40,7 @@ impl CustomType { } } -gen::standard_object_impls!(CustomType); +standard_object_impls!(CustomType); impl TypeTrait for CustomType { fn name(&self) -> &str { @@ -98,14 +98,14 @@ impl ObjectTrait for CustomType { // Custom Object ------------------------------------------------------- pub struct CustomObj { - type_obj: gen::obj_ref_t!(CustomType), + type_obj: obj_ref_t!(CustomType), ns: Namespace, } -gen::standard_object_impls!(CustomObj); +standard_object_impls!(CustomObj); impl CustomObj { - pub fn new(type_obj: gen::obj_ref_t!(CustomType), attrs: Namespace) -> Self { + pub fn new(type_obj: obj_ref_t!(CustomType), attrs: Namespace) -> Self { Self { type_obj, ns: attrs } } } diff --git a/src/types/err.rs b/feint-builtins/src/types/err.rs similarity index 75% rename from src/types/err.rs rename to feint-builtins/src/types/err.rs index 6c384d6..253f704 100644 --- a/src/types/err.rs +++ b/feint-builtins/src/types/err.rs @@ -19,37 +19,36 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use crate::util::check_args; -use crate::vm::{RuntimeBoolResult, RuntimeErr}; +use feint_code_gen::*; -use super::gen; -use super::new; +use crate::util::check_args; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; use super::err_type::ErrKind; +use super::new; use super::ns::Namespace; // Err Type ------------------------------------------------------------ -gen::type_and_impls!(ErrType, Err); +type_and_impls!(ErrType, Err); -pub static ERR_TYPE: Lazy = Lazy::new(|| { - let type_ref = gen::obj_ref!(ErrType::new()); +pub static ERR_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(ErrType::new()); let mut type_obj = type_ref.write().unwrap(); type_obj.add_attrs(&[ // Class Methods ----------------------------------------------- - gen::meth!("new", type_ref, &["type", "msg"], "", |_, args, _| { + meth!("new", type_ref, &["type", "msg"], "", |_, args| { let name = "Err.new()"; let result = check_args(name, &args, false, 2, Some(2)); if let Err(err) = result { - return Ok(err); + return err; } - let type_arg = gen::use_arg!(args, 0); - let msg_arg = gen::use_arg!(args, 1); + let type_arg = use_arg!(args, 0); + let msg_arg = use_arg!(args, 1); let err_type = if let Some(err_type) = type_arg.down_to_err_type_obj() { err_type @@ -64,7 +63,7 @@ pub static ERR_TYPE: Lazy = Lazy::new(|| { // TODO: Figure out a solution for this, perhaps an err // type that is *not* user-constructible or a // nested err type? - return Ok(new::arg_err(arg_err_msg, new::nil())); + return new::arg_err(arg_err_msg, new::nil()); }; let kind = err_type.kind().clone(); @@ -73,21 +72,21 @@ pub static ERR_TYPE: Lazy = Lazy::new(|| { msg } else { let arg_err_msg = format!("{name} expected message to be a Str"); - return Ok(new::arg_err(arg_err_msg, new::nil())); + return new::arg_err(arg_err_msg, new::nil()); }; - Ok(new::err(kind, msg, new::nil())) + new::err(kind, msg, new::nil()) }), // Instance Attributes ----------------------------------------- - gen::prop!("type", type_ref, "", |this, _, _| { + prop!("type", type_ref, "", |this, _| { let this = this.read().unwrap(); let this = this.down_to_err().unwrap(); - Ok(this.kind.get_obj().unwrap()) + this.kind.get_obj().unwrap() }), - gen::prop!("message", type_ref, "", |this, _, _| { + prop!("message", type_ref, "", |this, _| { let this = this.read().unwrap(); let this = this.down_to_err().unwrap(); - Ok(new::str(&this.message)) + new::str(&this.message) }), ]); @@ -107,7 +106,7 @@ pub struct ErrObj { responds_to_bool: bool, } -gen::standard_object_impls!(ErrObj); +standard_object_impls!(ErrObj); impl ErrObj { pub fn new(kind: ErrKind, message: String, obj: ObjectRef) -> Self { @@ -138,30 +137,31 @@ impl ErrObj { } impl ObjectTrait for ErrObj { - gen::object_trait_header!(ERR_TYPE); + object_trait_header!(ERR_TYPE); - fn bool_val(&self) -> RuntimeBoolResult { + fn bool_val(&self) -> Option { if self.responds_to_bool { - Ok(self.bool_val) + Some(self.bool_val) } else { - Err(RuntimeErr::type_err(concat!( - "An Err object cannot be evaluated directly as a ", - "Bool. You must access it via the `.err` attribute of ", - "the result object.", - ))) + // Err(RuntimeErr::type_err(concat!( + // "An Err object cannot be evaluated directly as a ", + // "Bool. You must access it via the `.err` attribute of ", + // "the result object.", + // ))) + None } } - fn and(&self, rhs: &dyn ObjectTrait) -> RuntimeBoolResult { + fn and(&self, rhs: &dyn ObjectTrait) -> Option { let lhs = self.bool_val()?; let rhs = rhs.bool_val()?; - Ok(lhs && rhs) + Some(lhs && rhs) } - fn or(&self, rhs: &dyn ObjectTrait) -> RuntimeBoolResult { + fn or(&self, rhs: &dyn ObjectTrait) -> Option { let lhs = self.bool_val()?; let rhs = rhs.bool_val()?; - Ok(lhs || rhs) + Some(lhs && rhs) } } diff --git a/src/types/err_type.rs b/feint-builtins/src/types/err_type.rs similarity index 86% rename from src/types/err_type.rs rename to feint-builtins/src/types/err_type.rs index 40fdd3d..65e363a 100644 --- a/src/types/err_type.rs +++ b/feint-builtins/src/types/err_type.rs @@ -7,8 +7,8 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use super::gen; use super::new; +use feint_code_gen::*; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; @@ -23,6 +23,7 @@ pub enum ErrKind { FileNotFound, FileUnreadable, IndexOutOfBounds, + NotCallable, String, Type, Ok, @@ -38,6 +39,7 @@ static ERR_KINDS: Lazy> = Lazy::new(|| { FileNotFound, FileUnreadable, IndexOutOfBounds, + NotCallable, String, Type, Ok, @@ -55,6 +57,7 @@ impl ErrKind { FileNotFound => "file_not_found", FileUnreadable => "file_unreadable", IndexOutOfBounds => "index_out_of_bounds", + NotCallable => "not_callable", String => "string", Type => "type", Ok => "ok", @@ -69,23 +72,23 @@ impl ErrKind { // ErrType Type -------------------------------------------------------- -gen::type_and_impls!(ErrTypeType, ErrType); +type_and_impls!(ErrTypeType, ErrType); -pub static ERR_TYPE_TYPE: Lazy = Lazy::new(|| { - let type_ref = gen::obj_ref!(ErrTypeType::new()); +pub static ERR_TYPE_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(ErrTypeType::new()); let mut type_obj = type_ref.write().unwrap(); // Types as class attributes for kind in ERR_KINDS.iter() { - type_obj.add_attr(kind.name(), gen::obj_ref!(ErrTypeObj::new(kind.clone()))); + type_obj.add_attr(kind.name(), obj_ref!(ErrTypeObj::new(kind.clone()))); } type_obj.add_attrs(&[ // Instance Attributes ----------------------------------------- - gen::prop!("name", type_ref, "", |this, _, _| { + prop!("name", type_ref, "", |this, _| { let this = this.read().unwrap(); let this = this.as_any().downcast_ref::().unwrap(); - Ok(new::str(this.name())) + new::str(this.name()) }), ]); @@ -99,7 +102,7 @@ pub struct ErrTypeObj { kind: ErrKind, } -gen::standard_object_impls!(ErrTypeObj); +standard_object_impls!(ErrTypeObj); impl ErrTypeObj { pub fn new(kind: ErrKind) -> Self { @@ -116,7 +119,7 @@ impl ErrTypeObj { } impl ObjectTrait for ErrTypeObj { - gen::object_trait_header!(ERR_TYPE_TYPE); + object_trait_header!(ERR_TYPE_TYPE); fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { if self.is(rhs) || rhs.is_always() { @@ -142,6 +145,7 @@ impl fmt::Display for ErrKind { FileNotFound => "File not found", FileUnreadable => "File could not be read", IndexOutOfBounds => "Index out of bounds", + NotCallable => "Not callable", String => "String error", Type => "Type error", Ok => "OK (not an error)", diff --git a/src/types/file.rs b/feint-builtins/src/types/file.rs similarity index 81% rename from src/types/file.rs rename to feint-builtins/src/types/file.rs index e9f7a45..be5634f 100644 --- a/src/types/file.rs +++ b/feint-builtins/src/types/file.rs @@ -7,10 +7,8 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::{Lazy, OnceCell}; -use crate::vm::{RuntimeBoolResult, RuntimeErr}; - -use super::gen; use super::new; +use feint_code_gen::*; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; @@ -18,38 +16,38 @@ use super::ns::Namespace; // File Type ------------------------------------------------------------ -gen::type_and_impls!(FileType, File); +type_and_impls!(FileType, File); -pub static FILE_TYPE: Lazy = Lazy::new(|| { - let type_ref = gen::obj_ref!(FileType::new()); +pub static FILE_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(FileType::new()); let mut type_obj = type_ref.write().unwrap(); type_obj.add_attrs(&[ // Class Methods - gen::meth!("new", type_ref, &["file_name"], "", |_, args, _| { - let arg = gen::use_arg!(args, 0); + meth!("new", type_ref, &["file_name"], "", |_, args| { + let arg = use_arg!(args, 0); if let Some(file_name) = arg.get_str_val() { let path = Path::new(file_name); - Ok(if path.is_file() { + if path.is_file() { new::file(file_name) } else { new::file_not_found_err(file_name, new::nil()) - }) + } } else { let message = format!("File.new(file_name) expected string; got {arg}"); - Ok(new::arg_err(message, new::nil())) + new::arg_err(message, new::nil()) } }), // Instance Attributes - gen::prop!("text", type_ref, "", |this, _, _| { + prop!("text", type_ref, "", |this, _| { let this = this.read().unwrap(); let this = this.down_to_file().unwrap(); - Ok(this.text()) + this.text() }), - gen::prop!("lines", type_ref, "", |this, _, _| { + prop!("lines", type_ref, "", |this, _| { let this = this.read().unwrap(); let this = &mut this.down_to_file().unwrap(); - Ok(this.lines()) + this.lines() }), ]); @@ -66,7 +64,7 @@ pub struct File { lines: OnceCell, } -gen::standard_object_impls!(File); +standard_object_impls!(File); impl File { pub fn new(file_name: String) -> Self { @@ -116,10 +114,10 @@ impl File { } impl ObjectTrait for File { - gen::object_trait_header!(FILE_TYPE); + object_trait_header!(FILE_TYPE); - fn bool_val(&self) -> RuntimeBoolResult { - Ok(false) + fn bool_val(&self) -> Option { + Some(false) } } diff --git a/src/types/float.rs b/feint-builtins/src/types/float.rs similarity index 65% rename from src/types/float.rs rename to feint-builtins/src/types/float.rs index 8905a92..4b37b26 100644 --- a/src/types/float.rs +++ b/feint-builtins/src/types/float.rs @@ -5,9 +5,7 @@ use std::sync::{Arc, RwLock}; use num_traits::ToPrimitive; use once_cell::sync::Lazy; -use crate::vm::{RuntimeBoolResult, RuntimeErr, RuntimeObjResult}; - -use super::gen; +use feint_code_gen::*; use super::new; use super::util::{eq_int_float, gt_int_float, lt_int_float}; @@ -18,16 +16,16 @@ use super::ns::Namespace; // Float Type ---------------------------------------------------------- -gen::type_and_impls!(FloatType, Float); +type_and_impls!(FloatType, Float); -pub static FLOAT_TYPE: Lazy = Lazy::new(|| { - let type_ref = gen::obj_ref!(FloatType::new()); +pub static FLOAT_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(FloatType::new()); let mut type_obj = type_ref.write().unwrap(); type_obj.add_attrs(&[ // Class Methods ----------------------------------------------- - gen::meth!("new", type_ref, &["value"], "", |_, args, _| { - let arg = gen::use_arg!(args, 0); + meth!("new", type_ref, &["value"], "", |this, args| { + let arg = use_arg!(args, 0); let float = if let Some(val) = arg.get_float_val() { new::float(*val) } else if let Some(val) = arg.get_int_val() { @@ -35,10 +33,10 @@ pub static FLOAT_TYPE: Lazy = Lazy::new(|| { } else if let Some(val) = arg.get_str_val() { new::float_from_string(val) } else { - let message = format!("Float new expected string or float; got {arg}"); - return Err(RuntimeErr::type_err(message)); + let msg = format!("Float.new() expected string or float; got {arg}"); + new::type_err(msg, this) }; - Ok(float) + float }), ]); @@ -49,20 +47,20 @@ pub static FLOAT_TYPE: Lazy = Lazy::new(|| { macro_rules! make_op { ( $meth:ident, $op:tt, $message:literal, $trunc:literal ) => { - fn $meth(&self, rhs: &dyn ObjectTrait) -> RuntimeObjResult { + fn $meth(&self, rhs: &dyn ObjectTrait) -> Option { let value = if let Some(rhs) = rhs.down_to_float() { *rhs.value() } else if let Some(rhs) = rhs.down_to_int() { rhs.value().to_f64().unwrap() } else { - return Err(RuntimeErr::type_err(format!($message, rhs.class().read().unwrap()))); + return None }; let mut value = &self.value $op value; if $trunc { value = value.trunc(); } let value = new::float(value); - Ok(value) + Some(value) } }; } @@ -72,7 +70,7 @@ pub struct Float { value: f64, } -gen::standard_object_impls!(Float); +standard_object_impls!(Float); impl Float { pub fn new(value: f64) -> Self { @@ -85,10 +83,10 @@ impl Float { } impl ObjectTrait for Float { - gen::object_trait_header!(FLOAT_TYPE); + object_trait_header!(FLOAT_TYPE); - fn negate(&self) -> RuntimeObjResult { - Ok(new::float(-*self.value())) + fn negate(&self) -> Option { + Some(new::float(-*self.value())) } fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { @@ -103,49 +101,37 @@ impl ObjectTrait for Float { } } - fn less_than(&self, rhs: &dyn ObjectTrait) -> RuntimeBoolResult { + fn less_than(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(rhs) = rhs.down_to_float() { - Ok(self.value() < rhs.value()) + Some(self.value() < rhs.value()) } else if let Some(rhs) = rhs.down_to_int() { - Ok(lt_int_float(rhs, self)) + Some(lt_int_float(rhs, self)) } else { - Err(RuntimeErr::type_err(format!( - "Could not compare {} to {}: <", - self.class().read().unwrap(), - rhs.class().read().unwrap() - ))) + None } } - fn greater_than(&self, rhs: &dyn ObjectTrait) -> RuntimeBoolResult { + fn greater_than(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(rhs) = rhs.down_to_float() { - Ok(self.value() > rhs.value()) + Some(self.value() > rhs.value()) } else if let Some(rhs) = rhs.down_to_int() { - Ok(gt_int_float(rhs, self)) + Some(gt_int_float(rhs, self)) } else { - Err(RuntimeErr::type_err(format!( - "Could not compare {} to {}: >", - self.class().read().unwrap(), - rhs.class().read().unwrap() - ))) + return None; } } - fn pow(&self, rhs: &dyn ObjectTrait) -> RuntimeObjResult { + fn pow(&self, rhs: &dyn ObjectTrait) -> Option { let exp = if let Some(rhs) = rhs.down_to_float() { *rhs.value() } else if let Some(rhs) = rhs.down_to_int() { rhs.value().to_f64().unwrap() } else { - return Err(RuntimeErr::type_err(format!( - "Could not raise {} by {}", - self.class().read().unwrap(), - rhs.class().read().unwrap() - ))); + return None; }; let value = self.value().powf(exp); let value = new::float(value); - Ok(value) + Some(value) } make_op!(modulo, %, "Could not divide {} with Float", false); diff --git a/src/types/func.rs b/feint-builtins/src/types/func.rs similarity index 89% rename from src/types/func.rs rename to feint-builtins/src/types/func.rs index 79c30ec..a723261 100644 --- a/src/types/func.rs +++ b/feint-builtins/src/types/func.rs @@ -4,24 +4,23 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::{Lazy, OnceCell}; -use crate::modules::get_module; -use crate::vm::Code; +use feint_code_gen::*; -use super::gen; -use super::new; -use super::result::Params; +use crate::modules::get_module; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; +use super::code::Code; use super::func_trait::FuncTrait; use super::ns::Namespace; +use super::{new, Params}; // Function Type ------------------------------------------------------- -gen::type_and_impls!(FuncType, Func); +type_and_impls!(FuncType, Func); -pub static FUNC_TYPE: Lazy = - Lazy::new(|| gen::obj_ref!(FuncType::new())); +pub static FUNC_TYPE: Lazy = + Lazy::new(|| obj_ref!(FuncType::new())); // Func Object ---------------------------------------------------------- @@ -34,7 +33,7 @@ pub struct Func { code: Code, } -gen::standard_object_impls!(Func); +standard_object_impls!(Func); impl Func { pub fn new(module_name: String, name: String, params: Params, code: Code) -> Self { @@ -94,7 +93,7 @@ impl FuncTrait for Func { } impl ObjectTrait for Func { - gen::object_trait_header!(FUNC_TYPE); + object_trait_header!(FUNC_TYPE); fn module(&self) -> ObjectRef { self.module.get_or_init(|| get_module(&self.module_name)).clone() diff --git a/src/types/func_trait.rs b/feint-builtins/src/types/func_trait.rs similarity index 96% rename from src/types/func_trait.rs rename to feint-builtins/src/types/func_trait.rs index b619973..307f701 100644 --- a/src/types/func_trait.rs +++ b/feint-builtins/src/types/func_trait.rs @@ -1,8 +1,8 @@ use std::fmt; -use crate::types::{Namespace, ObjectRef}; - -use super::result::Params; +use super::base::ObjectRef; +use super::ns::Namespace; +use super::Params; // Function Trait ------------------------------------------------------ diff --git a/src/types/int.rs b/feint-builtins/src/types/int.rs similarity index 56% rename from src/types/int.rs rename to feint-builtins/src/types/int.rs index fb66030..e710b7d 100644 --- a/src/types/int.rs +++ b/feint-builtins/src/types/int.rs @@ -7,9 +7,7 @@ use num_traits::{FromPrimitive, ToPrimitive}; use once_cell::sync::Lazy; -use crate::vm::{RuntimeBoolResult, RuntimeErr, RuntimeObjResult}; - -use super::gen; +use feint_code_gen::*; use super::new; use super::util::{eq_int_float, gt_int_float, lt_int_float}; @@ -24,17 +22,17 @@ static DOC: &str = " Intrinsic Int type "; -gen::type_and_impls!(IntType, Int); +type_and_impls!(IntType, Int); -pub static INT_TYPE: Lazy = Lazy::new(|| { - let type_ref = gen::obj_ref!(IntType::new()); +pub static INT_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(IntType::new()); let mut type_obj = type_ref.write().unwrap(); type_obj.add_attrs(&[ ("$doc", new::str(DOC)), // Class Methods ----------------------------------------------- - gen::meth!("new", type_ref, &["value"], "", |_, args, _| { - let arg = gen::use_arg!(args, 0); + meth!("new", type_ref, &["value"], "", |this, args| { + let arg = use_arg!(args, 0); let int = if let Some(val) = arg.get_int_val() { new::int(val.clone()) } else if let Some(val) = arg.get_float_val() { @@ -42,10 +40,10 @@ pub static INT_TYPE: Lazy = Lazy::new(|| { } else if let Some(val) = arg.get_str_val() { new::int_from_string(val) } else { - let message = format!("Int.new() expected number or string; got {arg}"); - return Err(RuntimeErr::type_err(message)); + let msg = format!("Int.new() expected number or string; got {arg}"); + new::type_err(msg, this) }; - Ok(int) + int }), ]); @@ -55,20 +53,18 @@ pub static INT_TYPE: Lazy = Lazy::new(|| { // Int Object ---------------------------------------------------------- macro_rules! make_op { - ( $meth:ident, $op:tt, $message:literal ) => { - fn $meth(&self, rhs: &dyn ObjectTrait) -> RuntimeObjResult { + ( $meth:ident, $op:tt ) => { + fn $meth(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(rhs) = rhs.down_to_int() { // XXX: Return Int let value = self.value() $op rhs.value(); - let value = new::int(value); - Ok(value) + Some(new::int(value)) } else if let Some(rhs) = rhs.down_to_float() { // XXX: Return Float let value = self.value().to_f64().unwrap() $op rhs.value(); - let value = new::float(value); - Ok(value) + Some(new::float(value)) } else { - Err(RuntimeErr::type_err(format!($message, rhs.class().read().unwrap()))) + None } } }; @@ -79,7 +75,7 @@ pub struct Int { value: BigInt, } -gen::standard_object_impls!(Int); +standard_object_impls!(Int); impl Int { pub fn new(value: BigInt) -> Self { @@ -91,27 +87,24 @@ impl Int { } // Cast both LHS and RHS to f64 and divide them - fn div_f64(&self, rhs: &dyn ObjectTrait) -> Result { + fn div_f64(&self, rhs: &dyn ObjectTrait) -> Option { let lhs_val = self.value().to_f64().unwrap(); let rhs_val = if let Some(rhs) = rhs.down_to_int() { rhs.value().to_f64().unwrap() } else if let Some(rhs) = rhs.down_to_float() { *rhs.value() } else { - return Err(RuntimeErr::type_err(format!( - "Could not divide {} into Int", - rhs.class().read().unwrap() - ))); + return None; }; - Ok(lhs_val / rhs_val) + Some(lhs_val / rhs_val) } } impl ObjectTrait for Int { - gen::object_trait_header!(INT_TYPE); + object_trait_header!(INT_TYPE); - fn negate(&self) -> RuntimeObjResult { - Ok(new::int(-self.value.clone())) + fn negate(&self) -> Option { + Some(new::int(-self.value.clone())) } fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { @@ -126,76 +119,68 @@ impl ObjectTrait for Int { } } - fn less_than(&self, rhs: &dyn ObjectTrait) -> RuntimeBoolResult { + fn less_than(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(rhs) = rhs.down_to_int() { - Ok(self.value() < rhs.value()) + Some(self.value() < rhs.value()) } else if let Some(rhs) = rhs.down_to_float() { - Ok(lt_int_float(self, rhs)) + Some(lt_int_float(self, rhs)) } else { - Err(RuntimeErr::type_err(format!( - "Could not compare {} to {}: >", - self.class().read().unwrap(), - rhs.class().read().unwrap() - ))) + None } } - fn greater_than(&self, rhs: &dyn ObjectTrait) -> RuntimeBoolResult { + fn greater_than(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(rhs) = rhs.down_to_int() { - Ok(self.value() > rhs.value()) + Some(self.value() > rhs.value()) } else if let Some(rhs) = rhs.down_to_float() { - Ok(gt_int_float(self, rhs)) + Some(gt_int_float(self, rhs)) } else { - Err(RuntimeErr::type_err(format!( - "Could not compare {} to {}: >", - self.class().read().unwrap(), - rhs.class().read().unwrap() - ))) + None } } - fn pow(&self, rhs: &dyn ObjectTrait) -> RuntimeObjResult { + fn pow(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(rhs) = rhs.down_to_int() { // XXX: Return Int let base = self.value(); let exp = rhs.value().to_u32().unwrap(); let value = base.pow(exp); let value = new::int(value); - Ok(value) + Some(value) } else if let Some(rhs) = rhs.down_to_float() { // XXX: Return Float let base = self.value().to_f64().unwrap(); let exp = *rhs.value(); let value = base.powf(exp); let value = new::float(value); - Ok(value) + Some(value) } else { - Err(RuntimeErr::type_err(format!( - "Could not raise {} by {}", - self.class().read().unwrap(), - rhs.class().read().unwrap() - ))) + None } } - make_op!(modulo, %, "Could not divide {} with Int"); - make_op!(mul, *, "Could not multiply {} with Int"); - make_op!(add, +, "Could not add {} to Int"); - make_op!(sub, -, "Could not subtract {} from Int"); + make_op!(modulo, %); + make_op!(mul, *); + make_op!(add, +); + make_op!(sub, -); // Int division *always* returns a Float - fn div(&self, rhs: &dyn ObjectTrait) -> RuntimeObjResult { - let value = self.div_f64(rhs)?; - let value = new::float(value); - Ok(value) + fn div(&self, rhs: &dyn ObjectTrait) -> Option { + if let Some(value) = self.div_f64(rhs) { + Some(new::float(value)) + } else { + None + } } // Int *floor* division *always* returns an Int - fn floor_div(&self, rhs: &dyn ObjectTrait) -> RuntimeObjResult { - let value = self.div_f64(rhs)?; - let value = BigInt::from_f64(value).unwrap(); - let value = new::int(value); - Ok(value) + fn floor_div(&self, rhs: &dyn ObjectTrait) -> Option { + if let Some(value) = self.div_f64(rhs) { + let value = BigInt::from_f64(value).unwrap(); + Some(new::int(value)) + } else { + None + } } } diff --git a/src/types/intrinsic_func.rs b/feint-builtins/src/types/intrinsic_func.rs similarity index 84% rename from src/types/intrinsic_func.rs rename to feint-builtins/src/types/intrinsic_func.rs index 09d7932..71eeb50 100644 --- a/src/types/intrinsic_func.rs +++ b/feint-builtins/src/types/intrinsic_func.rs @@ -5,26 +5,24 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::{Lazy, OnceCell}; -use crate::modules::get_module; -use crate::vm::VM; +use feint_code_gen::*; -use super::gen; -use super::new; -use super::result::{Args, CallResult, Params}; +use crate::modules::get_module; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; use super::func_trait::FuncTrait; use super::ns::Namespace; +use super::{new, Args, CallResult, Params}; -pub type IntrinsicFn = fn(ObjectRef, Args, &mut VM) -> CallResult; +pub type IntrinsicFn = fn(ObjectRef, Args) -> CallResult; // Intrinsic Function Type --------------------------------------------- -gen::type_and_impls!(IntrinsicFuncType, IntrinsicFunc); +type_and_impls!(IntrinsicFuncType, IntrinsicFunc); -pub static INTRINSIC_FUNC_TYPE: Lazy = - Lazy::new(|| gen::obj_ref!(IntrinsicFuncType::new())); +pub static INTRINSIC_FUNC_TYPE: Lazy = + Lazy::new(|| obj_ref!(IntrinsicFuncType::new())); // IntrinsicFunc Object ------------------------------------------------ @@ -38,7 +36,7 @@ pub struct IntrinsicFunc { func: IntrinsicFn, } -gen::standard_object_impls!(IntrinsicFunc); +standard_object_impls!(IntrinsicFunc); impl IntrinsicFunc { pub fn new( @@ -98,7 +96,7 @@ impl FuncTrait for IntrinsicFunc { } impl ObjectTrait for IntrinsicFunc { - gen::object_trait_header!(INTRINSIC_FUNC_TYPE); + object_trait_header!(INTRINSIC_FUNC_TYPE); fn module(&self) -> ObjectRef { self.module.get_or_init(|| get_module(&self.module_name)).clone() diff --git a/src/types/iterator.rs b/feint-builtins/src/types/iterator.rs similarity index 80% rename from src/types/iterator.rs rename to feint-builtins/src/types/iterator.rs index dde5707..228bf0c 100644 --- a/src/types/iterator.rs +++ b/feint-builtins/src/types/iterator.rs @@ -4,8 +4,8 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use super::gen; use super::new; +use feint_code_gen::*; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; @@ -13,23 +13,23 @@ use super::ns::Namespace; // IteratorType Type --------------------------------------------------- -gen::type_and_impls!(IteratorType, Iterator); +type_and_impls!(IteratorType, Iterator); -pub static ITERATOR_TYPE: Lazy = Lazy::new(|| { - let type_ref = gen::obj_ref!(IteratorType::new()); +pub static ITERATOR_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(IteratorType::new()); let mut type_obj = type_ref.write().unwrap(); type_obj.add_attrs(&[ // Instance Methods -------------------------------------------- - gen::meth!("next", type_ref, &[], "", |this, _, _| { + meth!("next", type_ref, &[], "", |this, _| { let mut this = this.write().unwrap(); let this = this.down_to_iterator_mut().unwrap(); - Ok(this.next()) + this.next() }), - gen::meth!("peek", type_ref, &[], "", |this, _, _| { + meth!("peek", type_ref, &[], "", |this, _| { let this = this.write().unwrap(); let this = this.down_to_iterator().unwrap(); - Ok(this.peek()) + this.peek() }), ]); @@ -44,7 +44,7 @@ pub struct FIIterator { current: usize, } -gen::standard_object_impls!(FIIterator); +standard_object_impls!(FIIterator); impl FIIterator { pub fn new(wrapped: Vec) -> Self { @@ -77,7 +77,7 @@ impl FIIterator { } impl ObjectTrait for FIIterator { - gen::object_trait_header!(ITERATOR_TYPE); + object_trait_header!(ITERATOR_TYPE); } // Display ------------------------------------------------------------- diff --git a/src/types/list.rs b/feint-builtins/src/types/list.rs similarity index 75% rename from src/types/list.rs rename to feint-builtins/src/types/list.rs index b98ac94..9de7f2f 100644 --- a/src/types/list.rs +++ b/feint-builtins/src/types/list.rs @@ -4,9 +4,7 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use crate::vm::{RuntimeErr, RuntimeResult}; - -use super::gen; +use feint_code_gen::*; use super::new; @@ -17,32 +15,32 @@ use super::seq; // List Type ----------------------------------------------------------- -gen::type_and_impls!(ListType, List); +type_and_impls!(ListType, List); -pub static LIST_TYPE: Lazy = Lazy::new(|| { - let type_ref = gen::obj_ref!(ListType::new()); +pub static LIST_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(ListType::new()); let mut type_obj = type_ref.write().unwrap(); type_obj.add_attrs(&[ // Instance Attributes ----------------------------------------- - gen::prop!("length", type_ref, "", |this, _, _| { + prop!("length", type_ref, "", |this, _| { let this = this.read().unwrap(); let this = this.down_to_list().unwrap(); - Ok(new::int(this.len())) + new::int(this.len()) }), - gen::prop!("is_empty", type_ref, "", |this, _, _| { + prop!("is_empty", type_ref, "", |this, _| { let this = this.read().unwrap(); let this = this.down_to_list().unwrap(); - Ok(new::bool(this.len() == 0)) + new::bool(this.len() == 0) }), - gen::prop!("sum", type_ref, "", |this, _, _| { + prop!("sum", type_ref, "", |this, _| { let this = this.read().unwrap(); let this = this.down_to_list().unwrap(); let items = &this.items.read().unwrap(); seq::sum(items) }), // Instance Methods -------------------------------------------- - gen::meth!( + meth!( "each", type_ref, &["each_fn"], @@ -56,76 +54,71 @@ pub static LIST_TYPE: Lazy = Lazy::new(|| { index of the item. ", - |this_obj, args, vm| { + |this_obj, args| { let this = this_obj.read().unwrap(); let this = this.down_to_list().unwrap(); let items = &this.items.read().unwrap(); - seq::each(&this_obj, items, &args, vm) + seq::each(&this_obj, items, &args) } ), - gen::meth!( + meth!( "extend", type_ref, &["items"], "Push items and return this.", - |this, args, _| { + |this, args| { let return_val = this.clone(); let this = this.read().unwrap(); let this = this.down_to_list().unwrap(); - this.extend(args[0].clone())?; - Ok(return_val) + if let Some(err) = this.extend(args[0].clone()) { + return err; + } + return_val } ), - gen::meth!("get", type_ref, &["index"], "", |this, args, _| { + meth!("get", type_ref, &["index"], "", |this, args| { let this = this.read().unwrap(); let this = this.down_to_list().unwrap(); - let index = gen::use_arg_usize!(get, index, args, 0); + let index = use_arg_usize!(get, index, args, 0); let result = match this.get(index) { Some(obj) => obj, None => new::nil(), }; - Ok(result) + result }), - gen::meth!("has", type_ref, &["member"], "", |this, args, _| { + meth!("has", type_ref, &["member"], "", |this, args| { let this = this.read().unwrap(); let this = this.down_to_list().unwrap(); let items = &this.items.read().unwrap(); seq::has(items, &args) }), - gen::meth!("join", type_ref, &["sep"], "", |this, args, _| { + meth!("join", type_ref, &["sep"], "", |this, args| { let this = this.read().unwrap(); let this = this.down_to_list().unwrap(); let items = &this.items.read().unwrap(); seq::join(items, &args) }), - gen::meth!("map", type_ref, &["map_fn"], "", |this_obj, args, vm| { + meth!("map", type_ref, &["map_fn"], "", |this_obj, args| { let this = this_obj.read().unwrap(); let this = this.down_to_list().unwrap(); let items = &this.items.read().unwrap(); - seq::map(&this_obj, items, &args, vm) + seq::map(&this_obj, items, &args) }), - gen::meth!("pop", type_ref, &[], "", |this, _, _| { + meth!("pop", type_ref, &[], "", |this, _| { let this = this.read().unwrap(); let this = this.down_to_list().unwrap(); - let result = match this.pop() { + match this.pop() { Some(obj) => obj, None => new::nil(), - }; - Ok(result) - }), - gen::meth!( - "push", - type_ref, - &["item"], - "Push item and return it.", - |this, args, _| { - let this = this.read().unwrap(); - let this = this.down_to_list().unwrap(); - let arg = args[0].clone(); - this.push(arg.clone()); - Ok(arg) } - ), + }), + meth!("push", type_ref, &["item"], "Push item and return it.", |this, args| { + let this = this.read().unwrap(); + let this = this.down_to_list().unwrap(); + let arg = args[0].clone(); + this.push(arg.clone()); + arg + }), ]); type_ref.clone() @@ -138,7 +131,7 @@ pub struct List { items: RwLock>, } -gen::standard_object_impls!(List); +standard_object_impls!(List); impl List { pub fn new(items: Vec) -> Self { @@ -155,8 +148,8 @@ impl List { items.push(item); } - pub fn extend(&self, obj: ObjectRef) -> RuntimeResult { - let obj = obj.read().unwrap(); + pub fn extend(&self, obj_ref: ObjectRef) -> Option { + let obj = obj_ref.read().unwrap(); let items = &mut self.items.write().unwrap(); if let Some(list) = obj.down_to_list() { let new_items = list.items.read().unwrap(); @@ -173,9 +166,9 @@ impl List { "List.extend() expected List or Tuple; got {}", obj.class().read().unwrap() ); - return Err(RuntimeErr::type_err(msg)); + return Some(new::type_err(msg, obj_ref.clone())); } - Ok(()) + None } pub fn pop(&self) -> Option { @@ -198,7 +191,7 @@ impl List { } impl ObjectTrait for List { - gen::object_trait_header!(LIST_TYPE); + object_trait_header!(LIST_TYPE); fn get_item(&self, index: usize, this: ObjectRef) -> ObjectRef { if let Some(item) = self.get(index) { @@ -250,7 +243,7 @@ impl fmt::Display for List { }) .collect(); let items_str = items.join(", "); - write!(f, "[{}]", items_str) + write!(f, "[{items_str}]") } } diff --git a/src/types/map.rs b/feint-builtins/src/types/map.rs similarity index 76% rename from src/types/map.rs rename to feint-builtins/src/types/map.rs index 69a8572..7571734 100644 --- a/src/types/map.rs +++ b/feint-builtins/src/types/map.rs @@ -5,10 +5,8 @@ use std::sync::{Arc, RwLock}; use indexmap::IndexMap; use once_cell::sync::Lazy; -use crate::vm::RuntimeErr; - -use super::gen; use super::new; +use feint_code_gen::*; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; @@ -16,26 +14,26 @@ use super::ns::Namespace; // Map Type ------------------------------------------------------------ -gen::type_and_impls!(MapType, Map); +type_and_impls!(MapType, Map); -pub static MAP_TYPE: Lazy = Lazy::new(|| { - let type_ref = gen::obj_ref!(MapType::new()); +pub static MAP_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(MapType::new()); let mut type_obj = type_ref.write().unwrap(); type_obj.add_attrs(&[ // Instance Attributes ----------------------------------------- - gen::prop!("length", type_ref, "", |this, _, _| { + prop!("length", type_ref, "", |this, _| { let this = this.read().unwrap(); let this = this.down_to_map().unwrap(); - Ok(new::int(this.len())) + new::int(this.len()) }), - gen::prop!("is_empty", type_ref, "", |this, _, _| { + prop!("is_empty", type_ref, "", |this, _| { let this = this.read().unwrap(); let this = this.down_to_map().unwrap(); - Ok(new::bool(this.is_empty())) + new::bool(this.is_empty()) }), // Instance Methods -------------------------------------------- - gen::meth!( + meth!( "add", type_ref, &["key", "val"], @@ -47,17 +45,17 @@ pub static MAP_TYPE: Lazy = Lazy::new(|| { - value: Any ", - |this, args, _| { + |this, args| { let this = this.read().unwrap(); let this = this.down_to_map().unwrap(); - let arg = gen::use_arg!(args, 0); - let key = gen::use_arg_str!(get, key, arg); + let arg = use_arg!(args, 0); + let key = use_arg_str!(get, key, arg); let val = args[1].clone(); this.insert(key, val); - Ok(new::nil()) + new::nil() } ), - gen::meth!( + meth!( "each", type_ref, &["each_fn"], @@ -81,13 +79,13 @@ pub static MAP_TYPE: Lazy = Lazy::new(|| { ``` ", - |this_obj, args, vm| { + |this_obj, args| { let this = this_obj.read().unwrap(); let this = this.down_to_map().unwrap(); let entries = &this.entries.read().unwrap(); if entries.is_empty() { - return Ok(new::nil()); + return new::nil(); } let each_fn = &args[0]; @@ -98,28 +96,25 @@ pub static MAP_TYPE: Lazy = Lazy::new(|| { f.arity() } } else { - return Ok(new::arg_err( - "each/1 expects a function", - this_obj.clone(), - )); + return new::arg_err("each/1 expects a function", this_obj.clone()); }; - for (i, (key, val)) in entries.iter().enumerate() { - let each = each_fn.clone(); - let key = new::str(key); - if n_args == 1 { - vm.call(each, vec![key])?; - } else if n_args == 2 { - vm.call(each, vec![key, val.clone()])?; - } else { - vm.call(each, vec![key, val.clone(), new::int(i)])?; - } - } - - Ok(new::nil()) + // for (i, (key, val)) in entries.iter().enumerate() { + // let each = each_fn.clone(); + // let key = new::str(key); + // if n_args == 1 { + // vm.call(each, vec![key])?; + // } else if n_args == 2 { + // vm.call(each, vec![key, val.clone()])?; + // } else { + // vm.call(each, vec![key, val.clone(), new::int(i)])?; + // } + // } + + new::nil() } ), - gen::meth!( + meth!( "get", type_ref, &["key"], @@ -139,25 +134,24 @@ pub static MAP_TYPE: Lazy = Lazy::new(|| { > store `nil` values. ", - |this, args, _| { + |this, args| { let this = this.read().unwrap(); let this = this.down_to_map().unwrap(); - let arg = gen::use_arg!(args, 0); - let key = gen::use_arg_str!(get, key, arg); - let result = match this.get(key) { + let arg = use_arg!(args, 0); + let key = use_arg_str!(get, key, arg); + match this.get(key) { Some(obj) => obj, None => new::nil(), - }; - Ok(result) + } } ), - gen::meth!("has", type_ref, &["member"], "", |this, args, _| { + meth!("has", type_ref, &["member"], "", |this, args| { let this = this.read().unwrap(); let this = this.down_to_map().unwrap(); - let arg = gen::use_arg!(args, 0); - let key = gen::use_arg_str!(get, key, arg); + let arg = use_arg!(args, 0); + let key = use_arg_str!(get, key, arg); let result = this.contains_key(key); - Ok(new::bool(result)) + new::bool(result) }), ]); @@ -171,7 +165,7 @@ pub struct Map { entries: RwLock>, } -gen::standard_object_impls!(Map); +standard_object_impls!(Map); impl Default for Map { fn default() -> Self { @@ -219,7 +213,7 @@ impl Map { } impl ObjectTrait for Map { - gen::object_trait_header!(MAP_TYPE); + object_trait_header!(MAP_TYPE); fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { if self.is(rhs) || rhs.is_always() { diff --git a/src/types/mod.rs b/feint-builtins/src/types/mod.rs similarity index 61% rename from src/types/mod.rs rename to feint-builtins/src/types/mod.rs index fdd7578..6d925f0 100644 --- a/src/types/mod.rs +++ b/feint-builtins/src/types/mod.rs @@ -1,18 +1,17 @@ -// Objects -pub(crate) use base::{ObjectRef, ObjectTrait}; -pub(crate) use map::Map; +pub mod code; +pub mod new; -// Namespacing -pub(crate) use module::Module; -pub(crate) use ns::Namespace; +pub use base::{ObjectRef, ObjectTrait}; +pub use func::Func; +pub use func_trait::FuncTrait; +pub use intrinsic_func::IntrinsicFunc; +pub use map::Map; +pub use module::Module; -// Functions -pub(crate) use func::Func; -pub(crate) use func_trait::FuncTrait; -pub(crate) use intrinsic_func::IntrinsicFunc; - -pub(crate) mod new; -pub(crate) use result::{Args, Params, ThisOpt}; +pub type ThisOpt = Option; +pub type Params = Vec; +pub type Args = Vec; +pub type CallResult = ObjectRef; mod base; mod func_trait; @@ -33,7 +32,6 @@ pub(crate) mod err_type; pub(crate) mod file; pub(crate) mod float; pub(crate) mod func; -pub(crate) mod gen; pub(crate) mod int; pub(crate) mod intrinsic_func; pub(crate) mod iterator; @@ -42,7 +40,6 @@ pub(crate) mod map; pub(crate) mod module; pub(crate) mod nil; pub(crate) mod prop; -pub(crate) mod result; pub(crate) mod seq; pub(crate) mod str; pub(crate) mod tuple; diff --git a/src/types/module.rs b/feint-builtins/src/types/module.rs similarity index 81% rename from src/types/module.rs rename to feint-builtins/src/types/module.rs index 31bfdaa..3110b44 100644 --- a/src/types/module.rs +++ b/feint-builtins/src/types/module.rs @@ -4,26 +4,26 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use crate::util::check_args; -use crate::vm::{Code, RuntimeErr}; +use feint_code_gen::*; -use super::gen; -use super::new; +use crate::util::check_args; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; +use super::code::Code; use super::map::Map; +use super::new; use super::ns::Namespace; // Module Type --------------------------------------------------------- -gen::type_and_impls!(ModuleType, Module); +type_and_impls!(ModuleType, Module); -pub static MODULE_TYPE: Lazy = Lazy::new(|| { - let type_ref = gen::obj_ref!(ModuleType::new()); +pub static MODULE_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(ModuleType::new()); let mut type_obj = type_ref.write().unwrap(); - type_obj.add_attrs(&[gen::meth!( + type_obj.add_attrs(&[meth!( "new", type_ref, &["name", "path", "doc", "attrs"], @@ -39,20 +39,20 @@ pub static MODULE_TYPE: Lazy = Lazy::new(|| { # Returns Module", - |_, args, _| { + |_, args| { if let Err(err) = check_args("new", &args, false, 4, Some(4)) { - return Ok(err); + return err; }; - let name_arg = gen::use_arg!(args, 0); - let path_arg = gen::use_arg!(args, 1); - let doc_arg = gen::use_arg!(args, 2); - let attrs_arg = gen::use_arg!(args, 3); + let name_arg = use_arg!(args, 0); + let path_arg = use_arg!(args, 1); + let doc_arg = use_arg!(args, 2); + let attrs_arg = use_arg!(args, 3); - let name = gen::use_arg_str!(new, name, name_arg); - let path = gen::use_arg_str!(new, path, path_arg); - let doc = gen::use_arg_str!(new, doc, doc_arg); - let attrs = gen::use_arg_map!(new, attrs, attrs_arg); + let name = use_arg_str!(new, name, name_arg); + let path = use_arg_str!(new, path, path_arg); + let doc = use_arg_str!(new, doc, doc_arg); + let attrs = use_arg_map!(new, attrs, attrs_arg); let module = Module::with_map_entries( attrs, @@ -62,7 +62,7 @@ pub static MODULE_TYPE: Lazy = Lazy::new(|| { Some(doc.to_owned()), ); - Ok(gen::obj_ref!(module)) + obj_ref!(module) } )]); @@ -78,7 +78,7 @@ pub struct Module { code: Code, } -gen::standard_object_impls!(Module); +standard_object_impls!(Module); impl Module { /// NOTE: The `$doc` attribute should only be passed for intrinsic @@ -164,7 +164,7 @@ impl Module { } impl ObjectTrait for Module { - gen::object_trait_header!(MODULE_TYPE); + object_trait_header!(MODULE_TYPE); } // Display ------------------------------------------------------------- diff --git a/src/types/new.rs b/feint-builtins/src/types/new.rs similarity index 82% rename from src/types/new.rs rename to feint-builtins/src/types/new.rs index 024baee..83b3bce 100644 --- a/src/types/new.rs +++ b/feint-builtins/src/types/new.rs @@ -9,16 +9,18 @@ use num_traits::{FromPrimitive, Num, Signed, ToPrimitive}; use indexmap::IndexMap; use once_cell::sync::Lazy; -use crate::util::format_doc; -use crate::vm::{globals, Code, RuntimeErr}; +use feint_code_gen::{obj_ref, obj_ref_t}; +use feint_util::string::format_doc; use super::base::{ObjectRef, ObjectTrait}; -use super::gen::{obj_ref, obj_ref_t, use_arg}; -use super::result::Params; +use super::Params; +use super::always::Always; +use super::bool::Bool; use super::bound_func::BoundFunc; use super::cell::Cell; use super::closure::Closure; +use super::code::Code; use super::custom::{CustomObj, CustomType}; use super::err::ErrObj; use super::err_type::ErrKind; @@ -31,6 +33,7 @@ use super::iterator::FIIterator; use super::list::List; use super::map::Map; use super::module::Module; +use super::nil::Nil; use super::ns::Namespace; use super::prop::Prop; use super::str::Str; @@ -38,28 +41,53 @@ use super::tuple::Tuple; // Global singletons --------------------------------------------------- +static NIL: Lazy = Lazy::new(|| obj_ref!(Nil::new())); +static TRUE: Lazy = Lazy::new(|| obj_ref!(Bool::new(true))); +static FALSE: Lazy = Lazy::new(|| obj_ref!(Bool::new(false))); +static ALWAYS: Lazy = Lazy::new(|| obj_ref!(Always::new())); + +static EMPTY_STR: Lazy = + Lazy::new(|| obj_ref!(Str::new("".to_owned()))); + +static NEWLINE: Lazy = + Lazy::new(|| obj_ref!(Str::new("\n".to_owned()))); + +static EMPTY_TUPLE: Lazy = + Lazy::new(|| obj_ref!(Tuple::new(vec![]))); + +static SHARED_INT_MAX: usize = 256; +static SHARED_INT_MAX_BIGINT: Lazy = Lazy::new(|| BigInt::from(SHARED_INT_MAX)); +static SHARED_INTS: Lazy> = Lazy::new(|| { + (0..=SHARED_INT_MAX).map(|i| obj_ref!(Int::new(BigInt::from(i)))).collect() +}); + #[inline] pub fn nil() -> ObjectRef { - globals::NIL.clone() + NIL.clone() } #[inline] pub fn bool(val: bool) -> ObjectRef { if val { - globals::TRUE.clone() + TRUE.clone() } else { - globals::FALSE.clone() + FALSE.clone() } } +#[inline] +pub fn always() -> ObjectRef { + ALWAYS.clone() +} + #[inline] pub fn empty_str() -> ObjectRef { - globals::EMPTY_STR.clone() + EMPTY_STR.clone() } #[inline] pub fn empty_tuple() -> ObjectRef { - globals::EMPTY_TUPLE.clone() + EMPTY_TUPLE.clone() } // Intrinsic type constructors --------------------------------- @@ -153,6 +181,10 @@ pub fn index_out_of_bounds_err(index: usize, obj: ObjectRef) -> ObjectRef { err(ErrKind::IndexOutOfBounds, index.to_string(), obj) } +pub fn not_callable_err(obj: ObjectRef) -> ObjectRef { + err(ErrKind::NotCallable, format!("{}", obj.read().unwrap()), obj) +} + pub fn string_err>(msg: S, obj: ObjectRef) -> ObjectRef { err(ErrKind::String, msg, obj) } @@ -196,9 +228,9 @@ pub fn func>( pub fn int>(value: I) -> ObjectRef { let value = value.into(); - if value.is_positive() && &value <= Lazy::force(&globals::SHARED_INT_MAX_BIGINT) { + if value.is_positive() && &value <= Lazy::force(&SHARED_INT_MAX_BIGINT) { let index = value.to_usize().unwrap(); - globals::SHARED_INTS[index].clone() + SHARED_INTS[index].clone() } else { obj_ref!(Int::new(value)) } @@ -239,9 +271,9 @@ pub fn prop(getter: ObjectRef) -> ObjectRef { pub fn str>(val: S) -> ObjectRef { let val = val.into(); if val.is_empty() { - globals::EMPTY_STR.clone() + EMPTY_STR.clone() } else if val == "\n" { - globals::NEWLINE.clone() + NEWLINE.clone() } else { obj_ref!(Str::new(val)) } @@ -249,7 +281,7 @@ pub fn str>(val: S) -> ObjectRef { pub fn tuple(items: Vec) -> ObjectRef { if items.is_empty() { - globals::EMPTY_TUPLE.clone() + EMPTY_TUPLE.clone() } else { obj_ref!(Tuple::new(items)) } @@ -283,9 +315,11 @@ pub fn custom_type(module: ObjectRef, name: &str) -> ObjectRef { - attributes: Map ", - |this, args, _| { - let attrs_arg = use_arg!(args, 0); + |this, args| { + let attrs_arg = args.get(0).unwrap(); + let attrs_arg = attrs_arg.read().unwrap(); let attrs = attrs_arg.down_to_map().unwrap(); + let mut ns = Namespace::default(); ns.extend_from_map(attrs); @@ -308,7 +342,7 @@ pub fn custom_type(module: ObjectRef, name: &str) -> ObjectRef { }; let instance = CustomObj::new(type_obj, ns); - Ok(obj_ref!(instance)) + obj_ref!(instance) }, ), ); diff --git a/src/types/nil.rs b/feint-builtins/src/types/nil.rs similarity index 73% rename from src/types/nil.rs rename to feint-builtins/src/types/nil.rs index 032c00d..b65a7b4 100644 --- a/src/types/nil.rs +++ b/feint-builtins/src/types/nil.rs @@ -4,10 +4,8 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use crate::vm::RuntimeBoolResult; - -use super::gen; use super::new; +use feint_code_gen::*; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; @@ -15,10 +13,9 @@ use super::ns::Namespace; // Nil Type ------------------------------------------------------------ -gen::type_and_impls!(NilType, Nil); +type_and_impls!(NilType, Nil); -pub static NIL_TYPE: Lazy = - Lazy::new(|| gen::obj_ref!(NilType::new())); +pub static NIL_TYPE: Lazy = Lazy::new(|| obj_ref!(NilType::new())); // Nil Object ---------------------------------------------------------- @@ -26,7 +23,7 @@ pub struct Nil { ns: Namespace, } -gen::standard_object_impls!(Nil); +standard_object_impls!(Nil); impl Nil { #[allow(clippy::new_without_default)] @@ -36,10 +33,10 @@ impl Nil { } impl ObjectTrait for Nil { - gen::object_trait_header!(NIL_TYPE); + object_trait_header!(NIL_TYPE); - fn bool_val(&self) -> RuntimeBoolResult { - Ok(false) + fn bool_val(&self) -> Option { + Some(false) } } diff --git a/src/types/ns.rs b/feint-builtins/src/types/ns.rs similarity index 100% rename from src/types/ns.rs rename to feint-builtins/src/types/ns.rs diff --git a/src/types/prop.rs b/feint-builtins/src/types/prop.rs similarity index 82% rename from src/types/prop.rs rename to feint-builtins/src/types/prop.rs index 5f9309e..c73e215 100644 --- a/src/types/prop.rs +++ b/feint-builtins/src/types/prop.rs @@ -6,19 +6,19 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use super::gen; -use super::new; +use feint_code_gen::*; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; +use super::new; use super::ns::Namespace; // Prop Type ----------------------------------------------------------- -gen::type_and_impls!(PropType, Prop); +type_and_impls!(PropType, Prop); -pub static PROP_TYPE: Lazy = - Lazy::new(|| gen::obj_ref!(PropType::new())); +pub static PROP_TYPE: Lazy = + Lazy::new(|| obj_ref!(PropType::new())); // Prop Object --------------------------------------------------------- @@ -27,7 +27,7 @@ pub struct Prop { getter: ObjectRef, } -gen::standard_object_impls!(Prop); +standard_object_impls!(Prop); impl Prop { pub fn new(getter: ObjectRef) -> Self { @@ -40,7 +40,7 @@ impl Prop { } impl ObjectTrait for Prop { - gen::object_trait_header!(PROP_TYPE); + object_trait_header!(PROP_TYPE); } // Display ------------------------------------------------------------- diff --git a/feint-builtins/src/types/seq.rs b/feint-builtins/src/types/seq.rs new file mode 100644 index 0000000..ed47696 --- /dev/null +++ b/feint-builtins/src/types/seq.rs @@ -0,0 +1,125 @@ +//! Common sequence operations + +use num_bigint::BigInt; + +use feint_code_gen::{use_arg, use_arg_str}; + +use super::base::ObjectRef; +use super::{new, Args}; + +pub fn each(this: &ObjectRef, items: &[ObjectRef], args: &Args) -> ObjectRef { + if items.is_empty() { + return new::nil(); + } + + let each_fn = &args[0]; + let f = each_fn.read().unwrap(); + let n_args = if let Some(f) = f.as_func() { + if f.has_var_args() { + 2 + } else { + f.arity() + } + } else { + return new::arg_err("each/1 expects a function", this.clone()); + }; + + // for (i, item) in items.iter().enumerate() { + // let each = each_fn.clone(); + // let item = item.clone(); + // if n_args == 1 { + // vm.call(each, vec![item])?; + // } else { + // vm.call(each, vec![item, new::int(i)])?; + // } + // } + + new::nil() +} + +pub fn has(items: &[ObjectRef], args: &Args) -> ObjectRef { + if items.is_empty() { + return new::bool(false); + } + let member = use_arg!(args, 0); + for item in items.iter() { + if member.is_equal(&*item.read().unwrap()) { + return new::bool(true); + } + } + new::bool(false) +} + +pub fn join(items: &[ObjectRef], args: &Args) -> ObjectRef { + if items.is_empty() { + return new::empty_str(); + } + + let n_items = items.len(); + let last_i = n_items - 1; + let arg = use_arg!(args, 0); + let sep = use_arg_str!(join, sep, arg); + + // XXX: Guessing at average word length + let capacity = n_items * 5 + ((last_i) * sep.len()); + let mut string = String::with_capacity(capacity); + + for (i, item) in items.iter().enumerate() { + let item = item.read().unwrap(); + let str = item.to_string(); + string.push_str(&str); + if i != last_i { + string.push_str(sep); + } + } + + new::str(string) +} + +pub fn map(this: &ObjectRef, items: &[ObjectRef], args: &Args) -> ObjectRef { + if items.is_empty() { + return new::empty_tuple(); + } + + let map_fn = &args[0]; + let f = map_fn.read().unwrap(); + let n_args = if let Some(f) = f.as_func() { + if f.has_var_args() { + 2 + } else { + f.arity() + } + } else { + return new::arg_err("map/1 expects a function", this.clone()); + }; + + let mut results = vec![]; + // for (i, item) in items.iter().enumerate() { + // let map = map_fn.clone(); + // let item = item.clone(); + // if n_args == 1 { + // vm.call(map, vec![item])?; + // } else { + // vm.call(map, vec![item, new::int(i)])?; + // } + // results.push(vm.pop_obj()?); + // } + + new::tuple(results) +} + +pub fn sum(items: &[ObjectRef]) -> ObjectRef { + let mut sum = new::int(BigInt::from(0)); + for item in items.iter() { + sum = { + let a = sum.read().unwrap(); + let b = item.read().unwrap(); + if let Some(new_sum) = (*a).add(&*b) { + new_sum + } else { + return new::type_err("Could not add object to sum", item.clone()); + } + } + } + sum +} diff --git a/src/types/str.rs b/feint-builtins/src/types/str.rs similarity index 56% rename from src/types/str.rs rename to feint-builtins/src/types/str.rs index f0fcc20..48365e2 100644 --- a/src/types/str.rs +++ b/feint-builtins/src/types/str.rs @@ -4,89 +4,90 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use crate::format::render_template; -use crate::vm::{RuntimeBoolResult, RuntimeErr, RuntimeObjResult}; - -use super::gen::{self, use_arg, use_arg_str, use_arg_usize}; -use super::new; +use feint_code_gen::*; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; +use super::new; use super::ns::Namespace; // Str Type ------------------------------------------------------------ -gen::type_and_impls!(StrType, Str); +type_and_impls!(StrType, Str); -pub static STR_TYPE: Lazy = Lazy::new(|| { - let type_ref = gen::obj_ref!(StrType::new()); +pub static STR_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(StrType::new()); let mut type_obj = type_ref.write().unwrap(); type_obj.add_attrs(&[ // Class Methods ----------------------------------------------- - gen::meth!("new", type_ref, &["value"], "", |_, args, _| { + meth!("new", type_ref, &["value"], "", |_, args| { let arg = use_arg!(args, 0); - Ok(if arg.is_str() { args[0].clone() } else { new::str(arg.to_string()) }) + if arg.is_str() { + args[0].clone() + } else { + new::str(arg.to_string()) + } }), // Instance Attributes ----------------------------------------- - gen::prop!("length", type_ref, "", |this, _, _| { + prop!("length", type_ref, "", |this, _| { let this = this.read().unwrap(); let value = this.get_str_val().unwrap(); - Ok(new::int(value.len())) + new::int(value.len()) }), // Instance Methods -------------------------------------------- - gen::meth!("starts_with", type_ref, &["prefix"], "", |this, args, _| { + meth!("starts_with", type_ref, &["prefix"], "", |this, args| { let this = this.read().unwrap(); let value = this.get_str_val().unwrap(); let arg = use_arg!(args, 0); let prefix = use_arg_str!(starts_with, prefix, arg); - Ok(new::bool(value.starts_with(prefix))) + new::bool(value.starts_with(prefix)) }), - gen::meth!("ends_with", type_ref, &["suffix"], "", |this, args, _| { + meth!("ends_with", type_ref, &["suffix"], "", |this, args| { let this = this.read().unwrap(); let value = this.get_str_val().unwrap(); let arg = use_arg!(args, 0); let suffix = use_arg_str!(ends_with, suffix, arg); - Ok(new::bool(value.ends_with(suffix))) + new::bool(value.ends_with(suffix)) }), - gen::meth!("upper", type_ref, &[], "", |this, _, _| { + meth!("upper", type_ref, &[], "", |this, _| { let this = this.read().unwrap(); let value = this.get_str_val().unwrap(); - Ok(new::str(value.to_uppercase())) + new::str(value.to_uppercase()) }), - gen::meth!("lower", type_ref, &[], "", |this, _, _| { + meth!("lower", type_ref, &[], "", |this, _| { let this = this.read().unwrap(); let value = this.get_str_val().unwrap(); - Ok(new::str(value.to_lowercase())) + new::str(value.to_lowercase()) }), - gen::meth!( - "render", - type_ref, - &["context"], - "Render string as template - - Templates may contain `{{ name }}` vars which will be replaced with the - values provided in the context map. - - # Args - - - context: Map A map containing values to be rendered into the - template. - - ", - |this, args, _| { - let context = args[0].clone(); - let result = render_template(this.clone(), context)?; - Ok(result) - } - ), - gen::meth!("repeat", type_ref, &["count"], "", |this, args, _| { + // meth!( + // "render", + // type_ref, + // &["context"], + // "Render string as template + // + // Templates may contain `{{ name }}` vars which will be replaced with the + // values provided in the context map. + // + // # Args + // + // - context: Map A map containing values to be rendered into the + // template. + // + // ", + // |this, args| { + // let context = args[0].clone(); + // let result = render_template(this.clone(), context)?; + // Ok(result) + // } + // ), + meth!("repeat", type_ref, &["count"], "", |this, args| { let this = this.read().unwrap(); let value = this.get_str_val().unwrap(); let count = use_arg_usize!(get, index, args, 0); - Ok(new::str(value.repeat(count))) + new::str(value.repeat(count)) }), - gen::meth!("replace", type_ref, &["old", "new"], "", |this, args, _| { + meth!("replace", type_ref, &["old", "new"], "", |this, args| { let this = this.read().unwrap(); let value = this.get_str_val().unwrap(); let arg1 = use_arg!(args, 0); @@ -94,19 +95,19 @@ pub static STR_TYPE: Lazy = Lazy::new(|| { let old = use_arg_str!(replace, old, arg1); let new = use_arg_str!(replace, new, arg2); let result = value.replace(old, new); - Ok(new::str(result)) + new::str(result) }), - gen::meth!("remove_prefix", type_ref, &["prefix"], "", |this_ref, args, _| { + meth!("remove_prefix", type_ref, &["prefix"], "", |this_ref, args| { let this = this_ref.read().unwrap(); let val = this.get_str_val().unwrap(); let arg = use_arg!(args, 0); let prefix = use_arg_str!(starts_with, prefix, arg); - Ok(if let Some(new_val) = val.strip_prefix(prefix) { + if let Some(new_val) = val.strip_prefix(prefix) { new::str(new_val) } else { drop(this); this_ref - }) + } }), ]); @@ -120,7 +121,7 @@ pub struct Str { value: String, } -gen::standard_object_impls!(Str); +standard_object_impls!(Str); impl Str { pub fn new(value: String) -> Self { @@ -139,7 +140,7 @@ impl Str { } impl ObjectTrait for Str { - gen::object_trait_header!(STR_TYPE); + object_trait_header!(STR_TYPE); fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { if self.is(rhs) || rhs.is_always() { @@ -151,7 +152,7 @@ impl ObjectTrait for Str { } } - fn add(&self, rhs: &dyn ObjectTrait) -> RuntimeObjResult { + fn add(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(rhs) = rhs.down_to_str() { let a = self.value(); let b = rhs.value(); @@ -159,37 +160,25 @@ impl ObjectTrait for Str { value.push_str(a); value.push_str(b); let value = new::str(value); - Ok(value) + Some(value) } else { - Err(RuntimeErr::type_err(format!( - "Cannot concatenate {} to {}", - self.class().read().unwrap(), - rhs.class().read().unwrap(), - ))) + None } } - fn less_than(&self, rhs: &dyn ObjectTrait) -> RuntimeBoolResult { + fn less_than(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(rhs) = rhs.down_to_str() { - Ok(self.value() < rhs.value()) + Some(self.value() < rhs.value()) } else { - Err(RuntimeErr::type_err(format!( - "Cannot compare {} to {}: <", - self.class().read().unwrap(), - rhs.class().read().unwrap(), - ))) + None } } - fn greater_than(&self, rhs: &dyn ObjectTrait) -> RuntimeBoolResult { + fn greater_than(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(rhs) = rhs.down_to_str() { - Ok(self.value() > rhs.value()) + Some(self.value() > rhs.value()) } else { - Err(RuntimeErr::type_err(format!( - "Cannot compare {} to {}: >", - self.class().read().unwrap(), - rhs.class().read().unwrap(), - ))) + None } } } diff --git a/src/types/tuple.rs b/feint-builtins/src/types/tuple.rs similarity index 75% rename from src/types/tuple.rs rename to feint-builtins/src/types/tuple.rs index 844db46..8458523 100644 --- a/src/types/tuple.rs +++ b/feint-builtins/src/types/tuple.rs @@ -5,9 +5,8 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use crate::vm::RuntimeErr; +use feint_code_gen::*; -use super::gen; use super::new; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; @@ -17,31 +16,31 @@ use super::seq; // Tuple Type ---------------------------------------------------------- -gen::type_and_impls!(TupleType, Tuple); +type_and_impls!(TupleType, Tuple); -pub static TUPLE_TYPE: Lazy = Lazy::new(|| { - let type_ref = gen::obj_ref!(TupleType::new()); +pub static TUPLE_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(TupleType::new()); let mut type_obj = type_ref.write().unwrap(); type_obj.add_attrs(&[ // Instance Attributes ----------------------------------------- - gen::prop!("length", type_ref, "", |this, _, _| { + prop!("length", type_ref, "", |this, _| { let this = this.read().unwrap(); let this = this.down_to_tuple().unwrap(); - Ok(new::int(this.len())) + new::int(this.len()) }), - gen::prop!("is_empty", type_ref, "", |this, _, _| { + prop!("is_empty", type_ref, "", |this, _| { let this = this.read().unwrap(); let this = this.down_to_tuple().unwrap(); - Ok(new::bool(this.len() == 0)) + new::bool(this.len() == 0) }), - gen::prop!("sum", type_ref, "", |this, _, _| { + prop!("sum", type_ref, "", |this, _| { let this = this.read().unwrap(); let this = this.down_to_tuple().unwrap(); seq::sum(&this.items) }), // Instance Methods -------------------------------------------- - gen::meth!( + meth!( "each", type_ref, &["each_fn"], @@ -55,41 +54,40 @@ pub static TUPLE_TYPE: Lazy = Lazy::new(|| { index of the item. ", - |this_obj, args, vm| { + |this_obj, args| { let this = this_obj.read().unwrap(); let this = this.down_to_tuple().unwrap(); - seq::each(&this_obj, &this.items, &args, vm) + seq::each(&this_obj, &this.items, &args) } ), - gen::meth!("get", type_ref, &["index"], "", |this, args, _| { + meth!("get", type_ref, &["index"], "", |this, args| { let this = this.read().unwrap(); let this = this.down_to_tuple().unwrap(); - let index = gen::use_arg_usize!(get, index, args, 0); - let result = match this.get(index) { + let index = use_arg_usize!(get, index, args, 0); + match this.get(index) { Some(obj) => obj, None => new::nil(), - }; - Ok(result) + } }), - gen::meth!("has", type_ref, &["member"], "", |this, args, _| { + meth!("has", type_ref, &["member"], "", |this, args| { let this = this.read().unwrap(); let this = this.down_to_tuple().unwrap(); seq::has(&this.items, &args) }), - gen::meth!("iter", type_ref, &[], "", |this_ref, _, _| { + meth!("iter", type_ref, &[], "", |this_ref, _| { let this = this_ref.read().unwrap(); let this = this.down_to_tuple().unwrap(); - Ok(new::iterator(this.items.clone())) + new::iterator(this.items.clone()) }), - gen::meth!("join", type_ref, &["sep"], "", |this, args, _| { + meth!("join", type_ref, &["sep"], "", |this, args| { let this = this.read().unwrap(); let this = this.down_to_tuple().unwrap(); seq::join(&this.items, &args) }), - gen::meth!("map", type_ref, &["map_fn"], "", |this_obj, args, vm| { + meth!("map", type_ref, &["map_fn"], "", |this_obj, args| { let this = this_obj.read().unwrap(); let this = this.down_to_tuple().unwrap(); - seq::map(&this_obj, &this.items, &args, vm) + seq::map(&this_obj, &this.items, &args) }), ]); @@ -103,7 +101,7 @@ pub struct Tuple { items: Vec, } -gen::standard_object_impls!(Tuple); +standard_object_impls!(Tuple); impl Tuple { pub fn new(items: Vec) -> Self { @@ -128,7 +126,7 @@ impl Tuple { } impl ObjectTrait for Tuple { - gen::object_trait_header!(TUPLE_TYPE); + object_trait_header!(TUPLE_TYPE); fn get_item(&self, index: usize, this: ObjectRef) -> ObjectRef { if let Some(item) = self.items.get(index) { @@ -169,7 +167,7 @@ impl fmt::Display for Tuple { self.iter().map(|item| format!("{:?}", &*item.read().unwrap())).collect(); let items_str = items.join(", "); let trailing_comma = if num_items == 1 { "," } else { "" }; - write!(f, "({}{})", items_str, trailing_comma) + write!(f, "({items_str}{trailing_comma})") } } diff --git a/src/types/util.rs b/feint-builtins/src/types/util.rs similarity index 100% rename from src/types/util.rs rename to feint-builtins/src/types/util.rs diff --git a/src/util/call.rs b/feint-builtins/src/util.rs similarity index 100% rename from src/util/call.rs rename to feint-builtins/src/util.rs diff --git a/feint-cli/Cargo.toml b/feint-cli/Cargo.toml new file mode 100644 index 0000000..5f167d6 --- /dev/null +++ b/feint-cli/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "feint-cli" +edition.workspace = true +version.workspace = true +description.workspace = true +authors.workspace = true +readme.workspace = true +license-file.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +feint-builtins = { path = "../feint-builtins" } +feint-compiler = { path = "../feint-compiler" } +feint-driver = { path = "../feint-driver" } +feint-vm = { path = "../feint-vm" } + +clap.workspace = true +clap_complete.workspace = true +dirs.workspace = true +env_logger.workspace = true +rustyline.workspace = true + +[build-dependencies] +clap.workspace = true +clap_complete.workspace = true + +[[bin]] +name = "feint" +path = "src/main.rs" diff --git a/feint-cli/build.rs b/feint-cli/build.rs new file mode 100644 index 0000000..8ba6b0f --- /dev/null +++ b/feint-cli/build.rs @@ -0,0 +1,43 @@ +use std::env; +use std::fs::File; +use std::io::Error; +use std::path::Path; +use std::process; + +use clap_complete::{self, shells}; + +include!("src/cli.rs"); + +fn main() -> Result<(), Error> { + let out_dir = match env::var_os("OUT_DIR") { + Some(out_dir) => out_dir, + None => { + eprintln!("OUT_DIR env var not set"); + eprintln!("Cannot proceed"); + eprintln!("Aborting"); + process::exit(1); + } + }; + + let out_dir = Path::new(&out_dir); + + stamp(out_dir)?; + make_shell_completion_scripts(out_dir)?; + + Ok(()) +} + +/// Adds a stamp file to the build output directory so that the latest +/// build can be found by other tools. +fn stamp(out_dir: &Path) -> Result<(), Error> { + let stamp_path = Path::new(out_dir).join("feint.stamp"); + File::create(stamp_path)?; + Ok(()) +} + +fn make_shell_completion_scripts(out_dir: &Path) -> Result<(), Error> { + let mut cmd = build_cli(); + clap_complete::generate_to(shells::Bash, &mut cmd, "feint", out_dir)?; + clap_complete::generate_to(shells::Fish, &mut cmd, "feint", out_dir)?; + Ok(()) +} diff --git a/src/cli.rs b/feint-cli/src/cli.rs similarity index 100% rename from src/cli.rs rename to feint-cli/src/cli.rs diff --git a/src/main.rs b/feint-cli/src/main.rs similarity index 82% rename from src/main.rs rename to feint-cli/src/main.rs index 6211c30..b6e9ab6 100644 --- a/src/main.rs +++ b/feint-cli/src/main.rs @@ -1,3 +1,9 @@ +mod cli; +mod repl; + +#[cfg(test)] +mod tests; + use std::ffi::OsStr; use std::fs; use std::path::{Path, PathBuf}; @@ -5,11 +11,10 @@ use std::process::ExitCode; use clap::{parser::ValueSource, ArgMatches}; -use feint::cli; -use feint::exe::Executor; -use feint::repl::Repl; -use feint::result::ExeResult; -use feint::vm::{CallDepth, VMState, DEFAULT_MAX_CALL_DEPTH}; +use feint_driver::{Driver, DriverResult}; +use feint_vm::VMState; + +use repl::Repl; /// Interpret a file if one is specified. Otherwise, run the REPL. fn main() -> ExitCode { @@ -21,7 +26,13 @@ fn main() -> ExitCode { let debug = *matches.get_one::("debug").unwrap(); let max_call_depth = match matches.value_source("max_call_depth") { - Some(ValueSource::DefaultValue) => DEFAULT_MAX_CALL_DEPTH, + Some(ValueSource::DefaultValue) => { + if cfg!(debug_assertions) { + 256 + } else { + 1024 + } + } _ => max_call_depth, }; @@ -38,7 +49,7 @@ fn main() -> ExitCode { } /// Subcommand: run -fn handle_run(matches: &ArgMatches, max_call_depth: CallDepth, debug: bool) -> u8 { +fn handle_run(matches: &ArgMatches, max_call_depth: usize, debug: bool) -> u8 { let file_name = matches.get_one::("FILE_NAME"); let code = matches.get_one::("code"); let dis = *matches.get_one::("dis").unwrap(); @@ -63,44 +74,44 @@ fn handle_run(matches: &ArgMatches, max_call_depth: CallDepth, debug: bool) -> u // error. let incremental = !(code.is_some() || file_name.is_some()); - let mut exe = Executor::new(max_call_depth, argv, incremental, dis, debug); + let mut driver = Driver::new(max_call_depth, argv, incremental, dis, debug); - if let Err(err) = exe.bootstrap() { - return handle_exe_result(Err(err)); + if let Err(err) = driver.bootstrap() { + return handle_driver_result(Err(err)); } - let exe_result = if let Some(code) = code { - exe.execute_text(code) + let result = if let Some(code) = code { + driver.execute_text(code) } else if let Some(file_name) = file_name { if file_name == "-" { - exe.execute_stdin() + driver.execute_stdin() } else if let Some(path) = get_script_file_path(file_name) { - exe.execute_file(path.as_path()) + driver.execute_file(path.as_path()) } else { - exe.execute_module_as_script(file_name) + driver.execute_module_as_script(file_name) } } else { let history_path = create_repl_history_file(&save_repl_history, history_path); - exe.install_sigint_handler(); - let mut repl = Repl::new(history_path, exe); + driver.install_sigint_handler(); + let mut repl = Repl::new(history_path, driver); repl.run() }; - handle_exe_result(exe_result) + handle_driver_result(result) } /// Subcommand: test -fn handle_test(matches: &ArgMatches, max_call_depth: CallDepth, debug: bool) -> u8 { +fn handle_test(matches: &ArgMatches, max_call_depth: usize, debug: bool) -> u8 { let argv: Vec = matches .get_many::("argv") .unwrap_or_default() .map(|v| v.to_string()) .collect(); - let mut exe = Executor::new(max_call_depth, argv, false, false, debug); - if let Err(err) = exe.bootstrap() { - return handle_exe_result(Err(err)); + let mut driver = Driver::new(max_call_depth, argv, false, false, debug); + if let Err(err) = driver.bootstrap() { + return handle_driver_result(Err(err)); } - handle_exe_result(exe.execute_module_as_script("std.test")) + handle_driver_result(driver.execute_module_as_script("std.test")) } // Utilities ----------------------------------------------------------- @@ -184,8 +195,8 @@ fn create_repl_history_file(cond: &bool, path: Option<&String>) -> Option u8 { - match exe_result { +fn handle_driver_result(result: DriverResult) -> u8 { + match result { Ok(vm_state) => match vm_state { VMState::Running => { eprintln!("VM should be idle or halted, not running"); diff --git a/src/repl.rs b/feint-cli/src/repl.rs similarity index 85% rename from src/repl.rs rename to feint-cli/src/repl.rs index 6222503..b672ef8 100644 --- a/src/repl.rs +++ b/feint-cli/src/repl.rs @@ -4,39 +4,36 @@ use std::path::PathBuf; use rustyline::config::Configurer; use rustyline::error::ReadlineError; -use crate::compiler::CompErrKind; -use crate::dis; -use crate::exe::Executor; -use crate::parser::ParseErrKind; -use crate::result::{ExeErr, ExeErrKind, ExeResult}; -use crate::scanner::ScanErrKind; -use crate::types::{new, ObjectRef, ObjectTrait}; -use crate::vm::VMState; +use feint_builtins::types::{new, ObjectRef, ObjectTrait}; +use feint_compiler::{CompErrKind, ParseErrKind, ScanErrKind}; +use feint_driver::result::{DriverErr, DriverErrKind, DriverResult}; +use feint_driver::Driver; +use feint_vm::{Disassembler, VMState}; pub struct Repl { module: ObjectRef, reader: rustyline::Editor<()>, history_path: Option, - executor: Executor, + driver: Driver, } impl Repl { - pub fn new(history_path: Option, executor: Executor) -> Self { + pub fn new(history_path: Option, driver: Driver) -> Self { let module = new::intrinsic_module("$repl", "$repl", "FeInt REPL module", &[]); let mut reader = rustyline::Editor::<()>::new().expect("Could initialize readline"); reader.set_indent_size(4); reader.set_tab_stop(4); - Repl { module, reader, history_path, executor } + Repl { module, reader, history_path, driver } } - pub fn run(&mut self) -> ExeResult { + pub fn run(&mut self) -> DriverResult { println!("Welcome to the FeInt REPL (read/eval/print loop)"); println!("Type a line of code, then hit Enter to evaluate it"); self.load_history(); println!("Type .exit or .quit to exit"); - self.executor.add_module("$repl", self.module.clone()); + self.driver.add_module("$repl", self.module.clone()); let result = loop { match self.read_line("→ ", true) { @@ -61,8 +58,8 @@ impl Repl { // Unexpected error encountered while attempting to read // a line. Err(err) => { - let msg = format!("Could not read line: {}", err); - break Err(ExeErr::new(ExeErrKind::ReplErr(msg))); + let msg = format!("Could not read line: {err}"); + break Err(DriverErr::new(DriverErrKind::ReplErr(msg))); } } }; @@ -85,9 +82,9 @@ impl Repl { } /// Evaluate text. Returns `None` to indicate to the main loop to - /// continue reading and evaluating input. Returns an `ExeResult` to - /// indicate to the main loop to exit. - pub fn eval(&mut self, text: &str, continue_on_err: bool) -> Option { + /// continue reading and evaluating input. Returns an `DriverResult` + /// to indicate to the main loop to exit. + pub fn eval(&mut self, text: &str, continue_on_err: bool) -> Option { self.add_history_entry(text); if matches!(text, ".exit" | ".quit") { @@ -96,7 +93,7 @@ impl Repl { return None; } - let result = self.executor.execute_repl(text, self.module.clone()); + let result = self.driver.execute_repl(text, self.module.clone()); match result { Ok(vm_state) => { @@ -145,7 +142,7 @@ impl Repl { } } else { let msg = format!("{}", read_line_result.unwrap_err()); - break Some(Err(ExeErr::new(ExeErrKind::ReplErr(msg)))); + break Some(Err(DriverErr::new(DriverErrKind::ReplErr(msg)))); } } } @@ -187,11 +184,11 @@ impl Repl { ".dis" => { let module = self.module.read().unwrap(); let module = module.down_to_mod().unwrap(); - let mut disassembler = dis::Disassembler::new(); + let mut disassembler = Disassembler::new(); disassembler.disassemble(module.code()); } ".stack" => { - self.executor.display_stack(); + self.driver.display_stack(); } ".emacs" => { self.reader.set_edit_mode(rustyline::config::EditMode::Emacs); @@ -204,8 +201,8 @@ impl Repl { true } - fn continue_on_err(&self, err: &ExeErr) -> bool { - if let ExeErrKind::ScanErr(kind) = &err.kind { + fn continue_on_err(&self, err: &DriverErr) -> bool { + if let DriverErrKind::ScanErr(kind) = &err.kind { use ScanErrKind::*; return matches!( kind, @@ -214,10 +211,10 @@ impl Repl { | UnmatchedOpeningBracket(_) | UnterminatedStr(_) ); - } else if let ExeErrKind::ParseErr(kind) = &err.kind { + } else if let DriverErrKind::ParseErr(kind) = &err.kind { use ParseErrKind::*; return matches!(kind, ExpectedBlock(_)); - } else if let ExeErrKind::CompErr(kind) = &err.kind { + } else if let DriverErrKind::CompErr(kind) = &err.kind { use CompErrKind::*; return matches!(kind, LabelNotFoundInScope(..)); } @@ -230,7 +227,7 @@ impl Repl { println!("REPL history will be saved to {}", path.to_string_lossy()); match self.reader.load_history(path.as_path()) { Ok(_) => (), - Err(err) => eprintln!("Could not load REPL history: {}", err), + Err(err) => eprintln!("Could not load REPL history: {err}"), } } None => (), @@ -244,7 +241,7 @@ impl Repl { match self.reader.save_history(path.as_path()) { Ok(_) => (), Err(err) => { - eprintln!("WARNING: Could not save REPL history: {}", err) + eprintln!("WARNING: Could not save REPL history: {err}") } } } diff --git a/feint-cli/src/tests/mod.rs b/feint-cli/src/tests/mod.rs new file mode 100644 index 0000000..dae8daa --- /dev/null +++ b/feint-cli/src/tests/mod.rs @@ -0,0 +1 @@ +mod repl; diff --git a/src/tests/repl.rs b/feint-cli/src/tests/repl.rs similarity index 81% rename from src/tests/repl.rs rename to feint-cli/src/tests/repl.rs index 83556a9..cb9d1d9 100644 --- a/src/tests/repl.rs +++ b/feint-cli/src/tests/repl.rs @@ -1,4 +1,5 @@ -use crate::exe::Executor; +use feint_driver::Driver; + use crate::repl::Repl; #[test] @@ -36,11 +37,11 @@ fn eval_if_with_no_block() { // Utilities ----------------------------------------------------------- fn eval(input: &str) { - let mut exe = Executor::new(16, vec![], false, false, false); - if let Err(err) = exe.bootstrap() { + let mut driver = Driver::new(16, vec![], false, false, false); + if let Err(err) = driver.bootstrap() { panic!("{err}"); } - let mut repl = Repl::new(None, exe); + let mut repl = Repl::new(None, driver); match repl.eval(input, false) { Some(Ok(_)) => assert!(false), Some(Err(_)) => assert!(false), diff --git a/feint-code-gen/Cargo.toml b/feint-code-gen/Cargo.toml new file mode 100644 index 0000000..0253dc0 --- /dev/null +++ b/feint-code-gen/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "feint-code-gen" +edition.workspace = true +version.workspace = true +description.workspace = true +authors.workspace = true +readme.workspace = true +license-file.workspace = true +homepage.workspace = true +repository.workspace = true diff --git a/src/types/gen.rs b/feint-code-gen/src/lib.rs similarity index 89% rename from src/types/gen.rs rename to feint-code-gen/src/lib.rs index 0154533..53ee467 100644 --- a/src/types/gen.rs +++ b/feint-code-gen/src/lib.rs @@ -1,20 +1,17 @@ +#[macro_export] macro_rules! obj_ref_t { ( $ty:ty ) => { Arc> }; } +#[macro_export] macro_rules! obj_ref { ( $obj:expr ) => { Arc::new(RwLock::new($obj)) }; } -pub(crate) use obj_ref; -pub(crate) use obj_ref_t; - -// Types --------------------------------------------------------------- - /// Generate an intrinsic type definition. This includes the type's /// struct and impl as well as the TypeTrait, Send, and Sync impls. /// @@ -25,6 +22,7 @@ pub(crate) use obj_ref_t; /// /// $name: ident /// The type's object name. E.g., `Nil` +#[macro_export] macro_rules! type_and_impls { ( $type_name:ident, $name:ident ) => { pub struct $type_name { @@ -109,6 +107,7 @@ macro_rules! type_and_impls { } /// Generate standard obj impls. +#[macro_export] macro_rules! standard_object_impls { ( $name:ident ) => { unsafe impl Send for $name {} @@ -123,6 +122,7 @@ macro_rules! standard_object_impls { /// /// $class: ident /// The singleton type instance. E.g. `NIL_TYPE`. +#[macro_export] macro_rules! object_trait_header { ( $class:ident ) => { fn as_any(&self) -> &dyn Any { @@ -155,10 +155,6 @@ macro_rules! object_trait_header { }; } -pub(crate) use object_trait_header; -pub(crate) use standard_object_impls; -pub(crate) use type_and_impls; - // Methods ------------------------------------------------------------- /// Make a class or instance method for an intrinsic type. @@ -189,6 +185,7 @@ pub(crate) use type_and_impls; /// Returns a 2-tuple containing the method name and the intrinsic /// function object itself. This makes it easy to add the method to the /// type's namespace by calling `ns.add_obj(meth!(...))`. +#[macro_export] macro_rules! meth { ( $name:literal, $this_type:expr, $params:expr, $doc:literal, $func:expr ) => { ( @@ -207,6 +204,7 @@ macro_rules! meth { /// This is similar to `meth!` but it creates a property instead of a /// method and has no `$params` arg. +#[macro_export] macro_rules! prop { ( $name:literal, $this_type:expr, $doc:literal, $func:expr ) => { ( @@ -229,13 +227,15 @@ macro_rules! prop { /// /// $args: Args /// $index: usize +#[macro_export] macro_rules! use_arg { ( $args:ident, $index:literal ) => {{ if $index < $args.len() { $args[$index].read().unwrap() } else { - // NOTE: This should never happen from user code. - return Err(RuntimeErr::index_out_of_bounds("Arg", $index)); + let msg = + format!("{}() didn't receive enough args", stringify!($func_name)); + return new::arg_err(msg, new::nil()); } }}; } @@ -243,6 +243,7 @@ macro_rules! use_arg { // The use_arg_ macros convert the supplied arg to a value of the // given type if possible or return an Err if not. +#[macro_export] macro_rules! use_arg_str { ( $func_name:ident, $arg_name:ident, $arg:ident ) => {{ if let Some(val) = $arg.get_str_val() { @@ -253,11 +254,12 @@ macro_rules! use_arg_str { stringify!($func_name), stringify!($arg_name) ); - return Ok(new::arg_err(msg, new::nil())); + return new::arg_err(msg, new::nil()); } }}; } +#[macro_export] macro_rules! use_arg_map { ( $func_name:ident, $arg_name:ident, $arg:ident ) => {{ if let Some(val) = $arg.get_map_val() { @@ -268,11 +270,12 @@ macro_rules! use_arg_map { stringify!($func_name), stringify!($arg_name) ); - return Ok(new::arg_err(msg, new::nil())); + return new::arg_err(msg, new::nil()); } }}; } +#[macro_export] macro_rules! use_arg_usize { ( $func_name:ident, $arg_name:ident, $args:ident, $index:literal ) => {{ if $index < $args.len() { @@ -285,20 +288,12 @@ macro_rules! use_arg_usize { stringify!($func_name), stringify!($arg_name) ); - return Ok(new::arg_err(msg, new::nil())); + return new::arg_err(msg, new::nil()); } } else { - // NOTE: This should never happen from user code. let msg = format!("{}() didn't receive enough args", stringify!($func_name)); - return Err(RuntimeErr::index_out_of_bounds(msg, $index)); + return new::arg_err(msg, new::nil()); } }}; } - -pub(crate) use meth; -pub(crate) use prop; -pub(crate) use use_arg; -pub(crate) use use_arg_map; -pub(crate) use use_arg_str; -pub(crate) use use_arg_usize; diff --git a/feint-compiler/Cargo.toml b/feint-compiler/Cargo.toml new file mode 100644 index 0000000..36a82aa --- /dev/null +++ b/feint-compiler/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "feint-compiler" +edition.workspace = true +version.workspace = true +description.workspace = true +authors.workspace = true +readme.workspace = true +license-file.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +feint-builtins = { path = "../feint-builtins" } +feint-util = { path = "../feint-util" } + +log.workspace = true +num-bigint.workspace = true +num-traits.workspace = true +once_cell.workspace = true +regex.workspace = true diff --git a/src/ast/ast.rs b/feint-compiler/src/ast/ast.rs similarity index 96% rename from src/ast/ast.rs rename to feint-compiler/src/ast/ast.rs index 499b1d1..ce4e2ac 100644 --- a/src/ast/ast.rs +++ b/feint-compiler/src/ast/ast.rs @@ -2,13 +2,14 @@ use std::fmt; use num_bigint::BigInt; -use crate::op::{ +use feint_builtins::types::Params; +use feint_util::op::{ BinaryOperator, CompareOperator, InplaceOperator, ShortCircuitCompareOperator, UnaryOperator, }; +use feint_util::source::Location; + use crate::scanner::Token; -use crate::source::Location; -use crate::types::Params; /// Module - A self-contained list of statements. #[derive(PartialEq)] @@ -24,11 +25,8 @@ impl Module { impl fmt::Debug for Module { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let items: Vec = self - .statements - .iter() - .map(|statement| format!("{:?}", statement)) - .collect(); + let items: Vec = + self.statements.iter().map(|statement| format!("{statement:?}")).collect(); write!(f, "{}", items.join(" ; ")) } } @@ -134,7 +132,7 @@ impl fmt::Debug for StatementKind { Self::Return(expr) => write!(f, "return {expr:?}"), Self::Halt(expr) => write!(f, "$halt {expr:?}"), Self::Print(expr) => write!(f, "$print {expr:?}"), - Self::Expr(expr) => write!(f, "{:?}", expr), + Self::Expr(expr) => write!(f, "{expr:?}"), } } } @@ -305,7 +303,7 @@ impl Expr { start: Location, end: Location, ) -> Self { - let kind = if let Ok(op) = UnaryOperator::from_token(op_token) { + let kind = if let Ok(op) = UnaryOperator::from_token(op_token.as_str()) { ExprKind::UnaryOp(op, Box::new(a)) } else { panic!("Unknown unary operator: {op_token}"); @@ -321,13 +319,15 @@ impl Expr { end: Location, ) -> Self { use ExprKind::{BinaryOp, CompareOp, InplaceOp, ShortCircuitCompareOp}; - let kind = if let Ok(op) = BinaryOperator::from_token(op_token) { + let kind = if let Ok(op) = BinaryOperator::from_token(op_token.as_str()) { BinaryOp(Box::new(a), op, Box::new(b)) - } else if let Ok(op) = CompareOperator::from_token(op_token) { + } else if let Ok(op) = CompareOperator::from_token(op_token.as_str()) { CompareOp(Box::new(a), op, Box::new(b)) - } else if let Ok(op) = ShortCircuitCompareOperator::from_token(op_token) { + } else if let Ok(op) = + ShortCircuitCompareOperator::from_token(op_token.as_str()) + { ShortCircuitCompareOp(Box::new(a), op, Box::new(b)) - } else if let Ok(op) = InplaceOperator::from_token(op_token) { + } else if let Ok(op) = InplaceOperator::from_token(op_token.as_str()) { InplaceOp(Box::new(a), op, Box::new(b)) } else { panic!("Unknown binary operator: {op_token}"); @@ -478,11 +478,8 @@ impl StatementBlock { impl fmt::Debug for StatementBlock { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let items: Vec = self - .statements - .iter() - .map(|statement| format!("{:?}", statement)) - .collect(); + let items: Vec = + self.statements.iter().map(|statement| format!("{statement:?}")).collect(); write!(f, "-> {}", items.join(" ; ")) } } @@ -660,6 +657,6 @@ impl fmt::Debug for IdentKind { Self::SpecialIdent(name) => name, Self::TypeIdent(name) => name, }; - write!(f, "{}", name) + write!(f, "{name}") } } diff --git a/feint-compiler/src/ast/mod.rs b/feint-compiler/src/ast/mod.rs new file mode 100644 index 0000000..7ae2518 --- /dev/null +++ b/feint-compiler/src/ast/mod.rs @@ -0,0 +1,3 @@ +pub mod ast; +pub use ast::*; +pub mod visitors; diff --git a/src/ast/visitors.rs b/feint-compiler/src/ast/visitors.rs similarity index 100% rename from src/ast/visitors.rs rename to feint-compiler/src/ast/visitors.rs diff --git a/src/compiler/compiler.rs b/feint-compiler/src/compiler/compiler.rs similarity index 98% rename from src/compiler/compiler.rs rename to feint-compiler/src/compiler/compiler.rs index 3959381..0ab91be 100644 --- a/src/compiler/compiler.rs +++ b/feint-compiler/src/compiler/compiler.rs @@ -1,11 +1,12 @@ //! Compiler. use std::collections::HashSet; +use feint_builtins::modules::STD; +use feint_builtins::types::code::{Code, Inst}; +use feint_builtins::types::{new, Module}; +use feint_util::stack::Stack; + use crate::ast; -use crate::modules::std::STD; -use crate::types::{new, Module}; -use crate::util::Stack; -use crate::vm::{Code, Inst}; use super::result::{CompErr, CompResult, VisitResult}; use super::visitor::CompilerVisitor; diff --git a/feint-compiler/src/compiler/mod.rs b/feint-compiler/src/compiler/mod.rs new file mode 100644 index 0000000..f686aa9 --- /dev/null +++ b/feint-compiler/src/compiler/mod.rs @@ -0,0 +1,7 @@ +pub use compiler::Compiler; +pub use result::{CompErr, CompErrKind}; + +mod compiler; +mod result; +mod scope; +mod visitor; diff --git a/src/compiler/result.rs b/feint-compiler/src/compiler/result.rs similarity index 98% rename from src/compiler/result.rs rename to feint-compiler/src/compiler/result.rs index d5f7fb3..63db871 100644 --- a/src/compiler/result.rs +++ b/feint-compiler/src/compiler/result.rs @@ -1,5 +1,5 @@ -use crate::source::Location; -use crate::types::Module; +use feint_builtins::types::Module; +use feint_util::source::Location; pub type CompResult = Result; pub type VisitResult = Result<(), CompErr>; diff --git a/src/compiler/scope.rs b/feint-compiler/src/compiler/scope.rs similarity index 99% rename from src/compiler/scope.rs rename to feint-compiler/src/compiler/scope.rs index 2d643df..12875eb 100644 --- a/src/compiler/scope.rs +++ b/feint-compiler/src/compiler/scope.rs @@ -285,7 +285,7 @@ impl Scope { if let Some(pos) = self.jumps.iter().position(|(n, _)| n == name) { &self.jumps[pos].1 } else { - panic!("Jump does not exist in scope: {}", name) + panic!("Jump does not exist in scope: {name}") } } }; diff --git a/src/compiler/visitor.rs b/feint-compiler/src/compiler/visitor.rs similarity index 97% rename from src/compiler/visitor.rs rename to feint-compiler/src/compiler/visitor.rs index b7ea1f1..3a59d02 100644 --- a/src/compiler/visitor.rs +++ b/feint-compiler/src/compiler/visitor.rs @@ -3,15 +3,16 @@ use std::collections::HashSet; use std::fmt; use std::fmt::Formatter; -use crate::ast; -use crate::modules::std::STD; -use crate::op::{ +use feint_builtins::modules::STD; +use feint_builtins::types::code::{Code, Inst, PrintFlags}; +use feint_builtins::types::{new, ObjectRef}; +use feint_util::op::{ BinaryOperator, CompareOperator, InplaceOperator, ShortCircuitCompareOperator, UnaryOperator, }; -use crate::source::Location; -use crate::types::{new, ObjectRef}; -use crate::vm::{globals, Code, Inst, PrintFlags}; +use feint_util::source::Location; + +use crate::ast; use super::result::{CompErr, VisitResult}; use super::scope::{Scope, ScopeKind, ScopeTree}; @@ -358,11 +359,7 @@ impl CompilerVisitor { Kind::Always => self.push_always(), Kind::Ellipsis => self.push_nil(), Kind::Int(value) => { - if let Some(index) = globals::shared_int_index(&value) { - self.push_global_const(index) - } else { - self.add_const(new::int(value)); - } + self.add_const(new::int(value)); } Kind::Float(value) => { self.add_const(new::float(value)); @@ -852,35 +849,31 @@ impl CompilerVisitor { // Global constants ------------------------------------------------ fn push_nil(&mut self) { - self.push(Inst::LoadNil); + self.add_const(new::nil()); } fn push_true(&mut self) { - self.push(Inst::LoadTrue); + self.add_const(new::bool(true)); } fn push_false(&mut self) { - self.push(Inst::LoadFalse); + self.add_const(new::bool(false)); } fn push_always(&mut self) { - self.push(Inst::LoadAlways); + self.add_const(new::always()); } fn push_empty_str(&mut self) { - self.push(Inst::LoadEmptyStr); + self.add_const(new::empty_str()); } fn push_newline(&mut self) { - self.push(Inst::LoadNewline); + self.add_const(new::empty_str()); } fn push_empty_tuple(&mut self) { - self.push(Inst::LoadEmptyTuple); - } - - fn push_global_const(&mut self, index: usize) { - self.push(Inst::LoadGlobalConst(index)); + self.add_const(new::empty_tuple()); } // Code unit constants --------------------------------------------- diff --git a/src/format.rs b/feint-compiler/src/format.rs similarity index 89% rename from src/format.rs rename to feint-compiler/src/format.rs index a36548d..5215da1 100644 --- a/src/format.rs +++ b/feint-compiler/src/format.rs @@ -1,7 +1,7 @@ +use feint_builtins::types::{new, ObjectRef}; +use feint_util::source::source_from_text; + use crate::scanner::{ScanTokensResult, Scanner, Token, TokenWithLocation as TWL}; -use crate::source::source_from_text; -use crate::types::{new, ObjectRef}; -use crate::vm::RuntimeObjResult; #[derive(Clone, Debug, PartialEq)] pub enum FormatStrToken { @@ -110,10 +110,7 @@ pub fn scan_format_string( Ok(tokens) } -pub fn render_template( - template_ref: ObjectRef, - context_ref: ObjectRef, -) -> RuntimeObjResult { +pub fn render_template(template_ref: ObjectRef, context_ref: ObjectRef) -> ObjectRef { use Token::{EndOfStatement, Ident}; let template = template_ref.read().unwrap(); @@ -123,10 +120,7 @@ pub fn render_template( let context = if let Some(context) = context.down_to_map() { context } else { - return Ok(new::string_err( - "Expected context to be a map", - template_ref.clone(), - )); + return new::string_err("Expected context to be a map", template_ref.clone()); }; let scan_result = scan_format_string(template.as_str(), Some(("{{", "}}"))); @@ -135,7 +129,7 @@ pub fn render_template( Ok(tokens) => tokens, Err(err) => { let msg = format!("Could not parse template: {err:?}"); - return Ok(new::string_err(msg, template_ref.clone())); + return new::string_err(msg, template_ref.clone()); } }; @@ -154,7 +148,7 @@ pub fn render_template( output.push_str(val.as_str()); } else { let msg = format!("Name not found in context: {name}"); - return Ok(new::string_err(msg, template_ref.clone())); + return new::string_err(msg, template_ref.clone()); } } _ => { @@ -163,11 +157,11 @@ pub fn render_template( let msg = format!( "Template is contains an invalid expression: {tokens:?}" ); - return Ok(new::string_err(msg, template_ref.clone())); + return new::string_err(msg, template_ref.clone()); } }, } } - Ok(new::str(output)) + new::str(output) } diff --git a/feint-compiler/src/lib.rs b/feint-compiler/src/lib.rs new file mode 100644 index 0000000..c331f94 --- /dev/null +++ b/feint-compiler/src/lib.rs @@ -0,0 +1,12 @@ +pub use compiler::{CompErr, CompErrKind, Compiler}; +pub use parser::{ParseErr, ParseErrKind, Parser}; +pub use scanner::{ScanErr, ScanErrKind, Scanner, Token, TokenWithLocation}; + +pub mod ast; +pub mod compiler; +pub mod format; +pub mod parser; +pub mod scanner; + +#[cfg(test)] +mod tests; diff --git a/src/parser/mod.rs b/feint-compiler/src/parser/mod.rs similarity index 53% rename from src/parser/mod.rs rename to feint-compiler/src/parser/mod.rs index 4ae6de2..fc96e1f 100644 --- a/src/parser/mod.rs +++ b/feint-compiler/src/parser/mod.rs @@ -1,5 +1,5 @@ -pub(crate) use parser::Parser; -pub(crate) use result::{ParseErr, ParseErrKind}; +pub use parser::Parser; +pub use result::{ParseErr, ParseErrKind}; mod parser; mod precedence; diff --git a/src/parser/parser.rs b/feint-compiler/src/parser/parser.rs similarity index 99% rename from src/parser/parser.rs rename to feint-compiler/src/parser/parser.rs index 29493fa..9a08b34 100644 --- a/src/parser/parser.rs +++ b/feint-compiler/src/parser/parser.rs @@ -2,11 +2,12 @@ use std::collections::VecDeque; use std::iter::{Iterator, Peekable}; +use feint_util::source::Location; + use crate::ast; use crate::format::FormatStrToken; use crate::parser::result::StatementResult; use crate::scanner::{ScanErr, ScanTokenResult, Token, TokenWithLocation}; -use crate::source::Location; use super::precedence::{ get_binary_precedence, get_unary_precedence, is_right_associative, diff --git a/src/parser/precedence.rs b/feint-compiler/src/parser/precedence.rs similarity index 100% rename from src/parser/precedence.rs rename to feint-compiler/src/parser/precedence.rs diff --git a/src/parser/result.rs b/feint-compiler/src/parser/result.rs similarity index 98% rename from src/parser/result.rs rename to feint-compiler/src/parser/result.rs index 4192a50..a083d0d 100644 --- a/src/parser/result.rs +++ b/feint-compiler/src/parser/result.rs @@ -1,6 +1,7 @@ +use feint_util::source::Location; + use crate::ast; use crate::scanner::{ScanErr, Token, TokenWithLocation}; -use crate::source::Location; pub type BoolResult = Result; pub type ParseResult = Result; diff --git a/src/scanner/keywords.rs b/feint-compiler/src/scanner/keywords.rs similarity index 100% rename from src/scanner/keywords.rs rename to feint-compiler/src/scanner/keywords.rs diff --git a/src/scanner/mod.rs b/feint-compiler/src/scanner/mod.rs similarity index 100% rename from src/scanner/mod.rs rename to feint-compiler/src/scanner/mod.rs diff --git a/src/scanner/result.rs b/feint-compiler/src/scanner/result.rs similarity index 98% rename from src/scanner/result.rs rename to feint-compiler/src/scanner/result.rs index eb07766..ed63f31 100644 --- a/src/scanner/result.rs +++ b/feint-compiler/src/scanner/result.rs @@ -2,8 +2,9 @@ use std::num::ParseFloatError; use num_bigint::ParseBigIntError; +use feint_util::source::Location; + use crate::format::FormatStrErr; -use crate::source::Location; use super::{Token, TokenWithLocation}; diff --git a/src/scanner/scanner.rs b/feint-compiler/src/scanner/scanner.rs similarity index 99% rename from src/scanner/scanner.rs rename to feint-compiler/src/scanner/scanner.rs index 27fb6bf..0d48481 100644 --- a/src/scanner/scanner.rs +++ b/feint-compiler/src/scanner/scanner.rs @@ -6,10 +6,11 @@ use num_traits::Num; use once_cell::sync::Lazy; use regex::Regex; +use feint_util::source::{Location, Source}; +use feint_util::stack::Stack; + use crate::format::scan_format_string; use crate::scanner::result::AddTokenResult; -use crate::source::{Location, Source}; -use crate::util::Stack; use super::keywords::KEYWORDS; use super::result::ScanErrKind as ErrKind; @@ -787,7 +788,7 @@ impl<'a, T: BufRead> Scanner<'a, T> { Some('o') | Some('O') => 8, Some('x') | Some('X') => 16, Some(t) if t.is_ascii_alphabetic() => { - panic!("Unsupported numeric type: {}", t); + panic!("Unsupported numeric type: {t}"); } _ => 10, } diff --git a/src/scanner/token.rs b/feint-compiler/src/scanner/token.rs similarity index 99% rename from src/scanner/token.rs rename to feint-compiler/src/scanner/token.rs index b1112fa..b15c277 100644 --- a/src/scanner/token.rs +++ b/feint-compiler/src/scanner/token.rs @@ -2,8 +2,9 @@ use std::fmt; use num_bigint::BigInt; +use feint_util::source::Location; + use crate::format::FormatStrToken; -use crate::source::Location; #[derive(Clone, Debug, PartialEq)] pub enum Token { @@ -153,7 +154,7 @@ impl Token { Self::Comma => ",", Self::DollarDollar => "$$", - Self::DollarNot => "$", + Self::DollarNot => "$!", Self::EqualEqualEqual => "===", Self::NotEqualEqual => "!==", Self::EqualEqual => "==", diff --git a/src/tests/ast.rs b/feint-compiler/src/tests/ast.rs similarity index 97% rename from src/tests/ast.rs rename to feint-compiler/src/tests/ast.rs index fd93c71..d12ec5d 100644 --- a/src/tests/ast.rs +++ b/feint-compiler/src/tests/ast.rs @@ -1,8 +1,9 @@ use num_bigint::BigInt; +use feint_util::source::Location; + use crate::ast::*; use crate::scanner::Token; -use crate::source::Location; #[test] #[rustfmt::skip] diff --git a/src/tests/format.rs b/feint-compiler/src/tests/format.rs similarity index 99% rename from src/tests/format.rs rename to feint-compiler/src/tests/format.rs index 3473ffa..d42e8ce 100644 --- a/src/tests/format.rs +++ b/feint-compiler/src/tests/format.rs @@ -1,10 +1,11 @@ use num_bigint::BigInt; +use feint_util::source::Location; + use crate::format::FormatStrErr::*; use crate::format::FormatStrToken::*; use crate::format::*; use crate::scanner::{Token, TokenWithLocation}; -use crate::source::Location; fn scan_ok( string: &str, diff --git a/feint-compiler/src/tests/mod.rs b/feint-compiler/src/tests/mod.rs new file mode 100644 index 0000000..2ec9e04 --- /dev/null +++ b/feint-compiler/src/tests/mod.rs @@ -0,0 +1,4 @@ +mod ast; +mod format; +mod parser; +mod scanner; diff --git a/src/tests/parser.rs b/feint-compiler/src/tests/parser.rs similarity index 98% rename from src/tests/parser.rs rename to feint-compiler/src/tests/parser.rs index c679628..7a26c53 100644 --- a/src/tests/parser.rs +++ b/feint-compiler/src/tests/parser.rs @@ -1,9 +1,10 @@ use num_bigint::BigInt; +use feint_util::op::BinaryOperator; +use feint_util::source::{source_from_text, Location}; + use crate::ast; -use crate::op::BinaryOperator; use crate::scanner::Scanner; -use crate::source::{source_from_text, Location}; use crate::parser::*; diff --git a/src/tests/scanner.rs b/feint-compiler/src/tests/scanner.rs similarity index 99% rename from src/tests/scanner.rs rename to feint-compiler/src/tests/scanner.rs index 000fe07..5093e8c 100644 --- a/src/tests/scanner.rs +++ b/feint-compiler/src/tests/scanner.rs @@ -1,6 +1,8 @@ +use std::convert::From; + use num_bigint::BigInt; -use crate::source::{source_from_text, Location}; +use feint_util::source::{source_from_text, Location}; use crate::scanner::*; diff --git a/feint-driver/Cargo.toml b/feint-driver/Cargo.toml new file mode 100644 index 0000000..64aae9e --- /dev/null +++ b/feint-driver/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "feint-driver" +edition.workspace = true +version.workspace = true +description.workspace = true +authors.workspace = true +readme.workspace = true +license-file.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +feint-builtins = { path = "../feint-builtins" } +feint-code-gen = { path = "../feint-code-gen" } +feint-compiler = { path = "../feint-compiler" } +feint-util = { path = "../feint-util" } +feint-vm = { path = "../feint-vm" } + +once_cell.workspace = true diff --git a/src/exe.rs b/feint-driver/src/driver.rs similarity index 85% rename from src/exe.rs rename to feint-driver/src/driver.rs index 5efc944..73a3569 100644 --- a/src/exe.rs +++ b/feint-driver/src/driver.rs @@ -1,62 +1,33 @@ //! Front end for executing code from a source on a VM. -use std::borrow::Cow; -use std::collections::{HashMap, VecDeque}; +use std::collections::VecDeque; use std::fs::canonicalize; -use std::io::{BufRead, Read}; +use std::io::BufRead; use std::path::Path; use std::sync::{Arc, RwLock}; -use flate2::read::GzDecoder; -use once_cell::sync::Lazy; -use tar::Archive as TarArchive; - -use crate::compiler::{CompErr, CompErrKind, Compiler}; -use crate::modules::std::{self as stdlib, STD}; -use crate::modules::{add_module, maybe_get_module, MODULES}; -use crate::parser::{ParseErr, ParseErrKind, Parser}; -use crate::result::ExeErrKind::ModuleNotFound; -use crate::result::{ExeErr, ExeErrKind, ExeResult}; -use crate::scanner::{ScanErr, ScanErrKind, Scanner, Token, TokenWithLocation}; -use crate::source::{ +use feint_builtins::modules::{ + add_module, maybe_get_module, std as stdlib, MODULES, STD, STD_FI_MODULES, +}; +use feint_builtins::types::code::{Inst, PrintFlags}; +use feint_builtins::types::{new, Module, ObjectRef, ObjectTrait}; +use feint_code_gen::obj_ref; +use feint_compiler::{ + ast, CompErr, CompErrKind, Compiler, ParseErr, ParseErrKind, Parser, ScanErr, + ScanErrKind, Scanner, Token, TokenWithLocation, +}; +use feint_util::source::{ source_from_bytes, source_from_file, source_from_stdin, source_from_text, Location, Source, }; -use crate::types::gen::obj_ref; -use crate::types::{new, Module, ObjectRef, ObjectTrait}; -use crate::vm::{ - CallDepth, Inst, PrintFlags, ModuleExecutionContext, RuntimeErr, RuntimeErrKind, - VMExeResult, VMState, VM, +use feint_vm::{ + dis, CallDepth, ModuleExecutionContext, RuntimeErr, RuntimeErrKind, RuntimeResult, + VMState, VM, }; -use crate::{ast, dis}; - -/// At build time, a compressed archive is created containing the -/// std .fi module files (see `build.rs`). -/// -/// At runtime, the module file data is read out and stored in a map -/// (lazily). When a std module is imported, the file data is read from -/// this map rather than reading from disk. -/// -/// The utility of this is that we don't need an install process that -/// copies the std module files into some location on the file system -/// based on the location of the current executable or anything like -/// that. -static STD_FI_MODULES: Lazy>> = Lazy::new(|| { - let archive_bytes: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/modules.tgz")); - let decoder = GzDecoder::new(archive_bytes); - let mut archive = TarArchive::new(decoder); - let mut modules = HashMap::new(); - for entry in archive.entries().unwrap() { - let mut entry = entry.unwrap(); - let path: Cow<'_, Path> = entry.path().unwrap(); - let path = path.to_str().unwrap().to_owned(); - let mut result = Vec::new(); - entry.read_to_end(&mut result).unwrap(); - modules.insert(path, result); - } - modules -}); -pub struct Executor { +use super::result::DriverErrKind::ModuleNotFound; +use super::result::{DriverErr, DriverErrKind, DriverResult}; + +pub struct Driver { vm: VM, argv: Vec, incremental: bool, @@ -66,7 +37,7 @@ pub struct Executor { imports: VecDeque, } -impl Executor { +impl Driver { pub fn new( max_call_depth: CallDepth, argv: Vec, @@ -103,7 +74,7 @@ impl Executor { // Bootstrap ------------------------------------------------------- /// Bootstrap and return error on failure. - pub fn bootstrap(&mut self) -> Result<(), ExeErr> { + pub fn bootstrap(&mut self) -> Result<(), DriverErr> { // Add the `std` module with builtins first because any other // module may rely on it, including `system`. self.extend_intrinsic_module(STD.clone(), "std")?; @@ -134,7 +105,7 @@ impl Executor { &mut self, base_module: ObjectRef, name: &str, - ) -> Result<(), ExeErr> { + ) -> Result<(), DriverErr> { let fi_module = self.load_module(name)?; let fi_module = fi_module.read().unwrap(); let fi_module = fi_module.down_to_mod().unwrap(); @@ -152,7 +123,7 @@ impl Executor { /// compiled all at once and executed as a script. In the REPL, code /// is compiled incrementally as it's entered, which makes it /// somewhat more complex to deal with. - pub fn execute_repl(&mut self, text: &str, module: ObjectRef) -> ExeResult { + pub fn execute_repl(&mut self, text: &str, module: ObjectRef) -> DriverResult { self.current_file_name = "".to_owned(); // XXX: Nested scopes are necessary to avoid deadlocks. @@ -172,7 +143,7 @@ impl Executor { let mut code = comp_result.map_err(|err| { self.handle_comp_err(&err, source); - ExeErr::new(ExeErrKind::CompErr(err.kind)) + DriverErr::new(DriverErrKind::CompErr(err.kind)) })?; // Assign TOS to _, print it, then pop it to clear the stack @@ -190,7 +161,7 @@ impl Executor { Some(inst) => format!("{inst:?}"), None => "[EMPTY CHUNK]".to_owned(), }; - panic!("Expected module chunk to end with POP; got {}", last_inst); + panic!("Expected module chunk to end with POP; got {last_inst}"); } { @@ -217,7 +188,7 @@ impl Executor { } /// Execute source from file as script. - pub fn execute_file(&mut self, file_path: &Path) -> ExeResult { + pub fn execute_file(&mut self, file_path: &Path) -> DriverResult { match source_from_file(file_path) { Ok(mut source) => { self.set_current_file_name(file_path); @@ -225,20 +196,20 @@ impl Executor { } Err(err) => { let message = format!("{}: {err}", file_path.display()); - Err(ExeErr::new(ExeErrKind::CouldNotReadSourceFile(message))) + Err(DriverErr::new(DriverErrKind::CouldNotReadSourceFile(message))) } } } /// Execute stdin as script. - pub fn execute_stdin(&mut self) -> ExeResult { + pub fn execute_stdin(&mut self) -> DriverResult { self.current_file_name = "".to_owned(); let mut source = source_from_stdin(); self.execute_script_from_source(&mut source) } /// Execute text as script. - pub fn execute_text(&mut self, text: &str) -> ExeResult { + pub fn execute_text(&mut self, text: &str) -> DriverResult { self.current_file_name = "".to_owned(); let mut source = source_from_text(text); self.execute_script_from_source(&mut source) @@ -250,7 +221,7 @@ impl Executor { fn execute_script_from_source( &mut self, source: &mut Source, - ) -> ExeResult { + ) -> DriverResult { let module = self.compile_module("$main", source)?; let module_ref = obj_ref!(module); self.add_module("$main", module_ref.clone()); @@ -259,7 +230,7 @@ impl Executor { self.execute_module(module, 0, source, true) } - pub fn execute_module_as_script(&mut self, name: &str) -> ExeResult { + pub fn execute_module_as_script(&mut self, name: &str) -> DriverResult { let module = self.get_or_add_module(name)?; let module = module.read().unwrap(); let module = module.down_to_mod().unwrap(); @@ -276,7 +247,7 @@ impl Executor { start: usize, source: &mut Source, is_main: bool, - ) -> ExeResult { + ) -> DriverResult { if self.dis && is_main { let mut disassembler = dis::Disassembler::new(); disassembler.disassemble(module.code()); @@ -317,7 +288,7 @@ impl Executor { Ok(()) => Ok(self.vm.state.clone()), Err(err) => { if let RuntimeErrKind::Exit(_) = err.kind { - Err(ExeErr::new(ExeErrKind::RuntimeErr(err.kind))) + Err(DriverErr::new(DriverErrKind::RuntimeErr(err.kind))) } else { let start = self.vm.loc().0; let line = source @@ -325,7 +296,7 @@ impl Executor { .unwrap_or(""); self.print_err_line(start.line, line); self.handle_runtime_err(&err); - Err(ExeErr::new(ExeErrKind::RuntimeErr(err.kind))) + Err(DriverErr::new(DriverErrKind::RuntimeErr(err.kind))) } } } @@ -337,7 +308,7 @@ impl Executor { fn parse_source( &mut self, source: &mut Source, - ) -> Result { + ) -> Result { let scanner = Scanner::new(source); let mut parser = Parser::new(scanner); match parser.parse() { @@ -348,10 +319,10 @@ impl Executor { Err(err) => { if let ParseErrKind::ScanErr(scan_err) = err.kind { self.handle_scan_err(&scan_err, source); - Err(ExeErr::new(ExeErrKind::ScanErr(scan_err.kind))) + Err(DriverErr::new(DriverErrKind::ScanErr(scan_err.kind))) } else { self.handle_parse_err(&err, source); - Err(ExeErr::new(ExeErrKind::ParseErr(err.kind))) + Err(DriverErr::new(DriverErrKind::ParseErr(err.kind))) } } } @@ -364,14 +335,14 @@ impl Executor { &mut self, name: &str, source: &mut Source, - ) -> Result { + ) -> Result { let ast_module = self.parse_source(source)?; let mut compiler = Compiler::default(); let module = compiler .compile_module(name, self.current_file_name.as_str(), ast_module) .map_err(|err| { self.handle_comp_err(&err, source); - ExeErr::new(ExeErrKind::CompErr(err.kind)) + DriverErr::new(DriverErrKind::CompErr(err.kind)) })?; Ok(module) } @@ -382,7 +353,7 @@ impl Executor { /// /// XXX: This will load the module regardless of whether it has /// already been loaded. - fn load_module(&mut self, name: &str) -> Result { + fn load_module(&mut self, name: &str) -> Result { // TODO: Handle non-std modules if let Some(file_data) = STD_FI_MODULES.get(name) { self.set_current_file_name(Path::new(&format!("<{name}>"))); @@ -394,7 +365,7 @@ impl Executor { } Ok(obj_ref!(module)) } else { - Err(ExeErr::new(ModuleNotFound(name.to_owned()))) + Err(DriverErr::new(ModuleNotFound(name.to_owned()))) } } @@ -404,17 +375,17 @@ impl Executor { } /// Get module from `MODULES` (the `system.modules` mirror). - fn get_module(&mut self, name: &str) -> Result { + fn get_module(&mut self, name: &str) -> Result { if let Some(module) = maybe_get_module(name) { Ok(module) } else { - Err(ExeErr::new(ModuleNotFound(name.to_owned()))) + Err(DriverErr::new(ModuleNotFound(name.to_owned()))) } } /// Get module or load it from file system and add it to both /// `MODULES` and `system.modules`. - fn get_or_add_module(&mut self, name: &str) -> Result { + fn get_or_add_module(&mut self, name: &str) -> Result { if let Ok(module) = self.get_module(name) { Ok(module) } else { @@ -436,7 +407,7 @@ impl Executor { } /// Load modules imported by the current module. - fn load_imported_modules(&mut self) -> Result<(), ExeErr> { + fn load_imported_modules(&mut self) -> Result<(), DriverErr> { while let Some(name) = self.imports.pop_front() { self.get_or_add_module(&name)?; } @@ -485,7 +456,7 @@ impl Executor { let col = loc.col; let mut message = match &err.kind { UnexpectedChar(c) => { - format!("Syntax error: Unexpected character at column {}: '{}'", col, c) + format!("Syntax error: Unexpected character at column {col}: '{c}'") } UnmatchedOpeningBracket(_) => { format!("Unmatched open bracket at {loc}") @@ -508,7 +479,7 @@ impl Executor { format!("Syntax error: Invalid label: {msg}") } FormatStrErr(err) => { - use crate::format::FormatStrErr::*; + use feint_compiler::format::FormatStrErr::*; match err { EmptyExpr(pos) => { loc = Location::new(loc.line, loc.col + pos + 2); @@ -592,7 +563,7 @@ impl Executor { "Parse error: extra match arm found after default match arm".to_string() } SyntaxErr(loc) => format!("Syntax error at {loc}"), - kind => format!("Unhandled parse error: {:?}", kind), + kind => format!("Unhandled parse error: {kind:?}"), }; if self.debug { message = format!("PARSE ERROR: {message}"); @@ -663,7 +634,7 @@ impl Executor { NameErr(message) => format!("Name error: {message}"), TypeErr(message) => format!("Type error: {message}"), NotCallable(type_name) => format!("Object is not callable: {type_name}"), - kind => format!("Unhandled runtime error: {}", kind), + kind => format!("Unhandled runtime error: {kind}"), }; if self.debug { message = format!("RUNTIME ERROR: {message}"); @@ -673,13 +644,13 @@ impl Executor { // Miscellaneous --------------------------------------------------- - pub(crate) fn display_stack(&self) { + pub fn display_stack(&self) { eprintln!("{:=<79}", "STACK "); self.vm.display_stack(); } - fn display_vm_state(&self, result: &VMExeResult) { + fn display_vm_state(&self, result: &RuntimeResult) { eprintln!("\n{:=<79}", "VM STATE "); - eprintln!("{:?}", result); + eprintln!("{result:?}"); } } diff --git a/feint-driver/src/lib.rs b/feint-driver/src/lib.rs new file mode 100644 index 0000000..303f3b8 --- /dev/null +++ b/feint-driver/src/lib.rs @@ -0,0 +1,8 @@ +pub mod driver; +pub mod result; + +pub use driver::Driver; +pub use result::DriverResult; + +#[cfg(test)] +mod tests; diff --git a/src/result.rs b/feint-driver/src/result.rs similarity index 74% rename from src/result.rs rename to feint-driver/src/result.rs index 1369f3e..5714a05 100644 --- a/src/result.rs +++ b/feint-driver/src/result.rs @@ -1,27 +1,27 @@ use core::fmt; use std::fmt::Formatter; -use crate::compiler::CompErrKind; -use crate::parser::ParseErrKind; -use crate::scanner::ScanErrKind; -use crate::vm::{RuntimeErrKind, VMState}; +use feint_compiler::{CompErrKind, ParseErrKind, ScanErrKind}; +use feint_vm::{RuntimeErrKind, VMState}; -/// Result type used by top level program executor. -pub type ExeResult = Result; +pub type CallDepth = usize; + +/// Result type used by top level program driver. +pub type DriverResult = Result; #[derive(Debug)] -pub struct ExeErr { - pub kind: ExeErrKind, +pub struct DriverErr { + pub kind: DriverErrKind, } -impl ExeErr { - pub fn new(kind: ExeErrKind) -> Self { +impl DriverErr { + pub fn new(kind: DriverErrKind) -> Self { Self { kind } } /// Return exit code if this error wraps a runtime exit error. pub fn exit_code(&self) -> Option { - if let ExeErrKind::RuntimeErr(RuntimeErrKind::Exit(code)) = self.kind { + if let DriverErrKind::RuntimeErr(RuntimeErrKind::Exit(code)) = self.kind { Some(code) } else { None @@ -30,7 +30,7 @@ impl ExeErr { } #[derive(Debug)] -pub enum ExeErrKind { +pub enum DriverErrKind { Bootstrap(String), ModuleDirNotFound(String), ModuleNotFound(String), @@ -42,15 +42,15 @@ pub enum ExeErrKind { ReplErr(String), } -impl fmt::Display for ExeErr { +impl fmt::Display for DriverErr { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", self.kind) } } -impl fmt::Display for ExeErrKind { +impl fmt::Display for DriverErrKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - use ExeErrKind::*; + use DriverErrKind::*; let msg = match self { Bootstrap(msg) => format!("Bootstrap process failed: {msg}"), ModuleDirNotFound(path) => format!( diff --git a/feint-driver/src/tests/driver.rs b/feint-driver/src/tests/driver.rs new file mode 100644 index 0000000..9a8cea2 --- /dev/null +++ b/feint-driver/src/tests/driver.rs @@ -0,0 +1,21 @@ +use feint_vm::RuntimeErrKind; + +use crate::driver::Driver; +use crate::result::{DriverErrKind, DriverResult}; + +fn execute(source: &str) -> DriverResult { + let mut driver = Driver::new(16, vec![], false, false, false); + driver.bootstrap()?; + driver.execute_text(source) +} + +#[test] +fn test_too_much_recursion() { + let result = execute("f = () => f()\nf()"); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert!(matches!( + err.kind, + DriverErrKind::RuntimeErr(RuntimeErrKind::RecursionDepthExceeded(_)) + )); +} diff --git a/feint-driver/src/tests/mod.rs b/feint-driver/src/tests/mod.rs new file mode 100644 index 0000000..75c97aa --- /dev/null +++ b/feint-driver/src/tests/mod.rs @@ -0,0 +1,2 @@ +mod driver; +mod run; diff --git a/src/tests/run.rs b/feint-driver/src/tests/run.rs similarity index 90% rename from src/tests/run.rs rename to feint-driver/src/tests/run.rs index d4e1e99..f2fa7b4 100644 --- a/src/tests/run.rs +++ b/feint-driver/src/tests/run.rs @@ -1,17 +1,17 @@ -use crate::exe::Executor; -use crate::result::ExeResult; +use crate::driver::Driver; +use crate::result::DriverResult; -fn run_text(text: &str) -> ExeResult { - let mut exe = Executor::new(16, vec![], false, false, false); - exe.bootstrap()?; - exe.execute_text(text) +fn run_text(text: &str) -> DriverResult { + let mut driver = Driver::new(16, vec![], false, false, false); + driver.bootstrap()?; + driver.execute_text(text) } -fn assert_result_is_ok(result: ExeResult) { +fn assert_result_is_ok(result: DriverResult) { assert!(result.is_ok(), "{:?}", result.err()); } -fn assert_result_is_err(result: ExeResult) { +fn assert_result_is_err(result: DriverResult) { assert!(result.is_err(), "{:?}", result); } diff --git a/feint-util/Cargo.toml b/feint-util/Cargo.toml new file mode 100644 index 0000000..e572b66 --- /dev/null +++ b/feint-util/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "feint-util" +edition.workspace = true +version.workspace = true +description.workspace = true +authors.workspace = true +readme.workspace = true +license-file.workspace = true +homepage.workspace = true +repository.workspace = true diff --git a/feint-util/src/lib.rs b/feint-util/src/lib.rs new file mode 100644 index 0000000..9cb6624 --- /dev/null +++ b/feint-util/src/lib.rs @@ -0,0 +1,11 @@ +//! # FeInt +//! +//! FeInt is a stack-based bytecode interpreter. + +pub mod op; +pub mod source; +pub mod stack; +pub mod string; + +#[cfg(test)] +mod tests; diff --git a/src/op.rs b/feint-util/src/op.rs similarity index 73% rename from src/op.rs rename to feint-util/src/op.rs index 497ed81..5830d4b 100644 --- a/src/op.rs +++ b/feint-util/src/op.rs @@ -3,8 +3,6 @@ //! easier to handle different types of operations in the VM. use std::fmt; -use crate::scanner::Token; - /// Unary operators. #[derive(Clone, Eq, PartialEq)] pub enum UnaryOperator { @@ -15,12 +13,12 @@ pub enum UnaryOperator { } impl UnaryOperator { - pub fn from_token(token: &Token) -> Result { + pub fn from_token(token: &str) -> Result { let op = match token { - Token::Plus => Self::Plus, - Token::Minus => Self::Negate, - Token::Bang => Self::Not, - Token::BangBang => Self::AsBool, + "+" => Self::Plus, + "-" => Self::Negate, + "!" => Self::Not, + "!!" => Self::AsBool, _ => return Err(format!("Unknown unary operator: {token}")), }; Ok(op) @@ -59,16 +57,16 @@ pub enum BinaryOperator { } impl BinaryOperator { - pub fn from_token(token: &Token) -> Result { + pub fn from_token(token: &str) -> Result { let op = match token { - Token::Caret => Self::Pow, - Token::Star => Self::Mul, - Token::Slash => Self::Div, - Token::DoubleSlash => Self::FloorDiv, - Token::Percent => Self::Mod, - Token::Plus => Self::Add, - Token::Minus => Self::Sub, - Token::Dot => Self::Dot, + "^" => Self::Pow, + "*" => Self::Mul, + "/" => Self::Div, + "//" => Self::FloorDiv, + "%" => Self::Mod, + "+" => Self::Add, + "-" => Self::Sub, + "." => Self::Dot, _ => return Err(format!("Unknown binary operator: {token}")), }; Ok(op) @@ -113,18 +111,18 @@ pub enum CompareOperator { } impl CompareOperator { - pub fn from_token(token: &Token) -> Result { + pub fn from_token(token: &str) -> Result { let op = match token { - Token::DollarDollar => Self::Is, - Token::DollarNot => Self::IsNot, - Token::EqualEqualEqual => Self::IsTypeEqual, - Token::NotEqualEqual => Self::IsNotTypeEqual, - Token::EqualEqual => Self::IsEqual, - Token::NotEqual => Self::NotEqual, - Token::LessThan => Self::LessThan, - Token::LessThanOrEqual => Self::LessThanOrEqual, - Token::GreaterThan => Self::GreaterThan, - Token::GreaterThanOrEqual => Self::GreaterThanOrEqual, + "$$" => Self::Is, + "$!" => Self::IsNot, + "===" => Self::IsTypeEqual, + "!==" => Self::IsNotTypeEqual, + "==" => Self::IsEqual, + "!=" => Self::NotEqual, + "<" => Self::LessThan, + "<=" => Self::LessThanOrEqual, + ">" => Self::GreaterThan, + ">=" => Self::GreaterThanOrEqual, _ => return Err(format!("Unknown comparison operator: {token}")), }; Ok(op) @@ -134,7 +132,7 @@ impl CompareOperator { impl fmt::Display for CompareOperator { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let string = match self { - Self::Is => "$", + Self::Is => "$$", Self::IsNot => "$!", Self::IsTypeEqual => "===", Self::IsNotTypeEqual => "!==", @@ -164,11 +162,11 @@ pub enum ShortCircuitCompareOperator { } impl ShortCircuitCompareOperator { - pub fn from_token(token: &Token) -> Result { + pub fn from_token(token: &str) -> Result { let op = match token { - Token::And => Self::And, - Token::Or => Self::Or, - Token::NilOr => Self::NilOr, + "&&" => Self::And, + "||" => Self::Or, + "??" => Self::NilOr, _ => { return Err(format!( "Unknown short-circuiting comparison operator: {token}" @@ -206,12 +204,12 @@ pub enum InplaceOperator { } impl InplaceOperator { - pub fn from_token(token: &Token) -> Result { + pub fn from_token(token: &str) -> Result { let op = match token { - Token::MulEqual => Self::Mul, - Token::DivEqual => Self::Div, - Token::PlusEqual => Self::Add, - Token::MinusEqual => Self::Sub, + "*=" => Self::Mul, + "/=" => Self::Div, + "+=" => Self::Add, + "-=" => Self::Sub, _ => return Err(format!("Unknown inplace operator: {token}")), }; Ok(op) diff --git a/src/source.rs b/feint-util/src/source.rs similarity index 97% rename from src/source.rs rename to feint-util/src/source.rs index f0fd770..6bd7123 100644 --- a/src/source.rs +++ b/feint-util/src/source.rs @@ -119,7 +119,7 @@ impl Source { } Ok(n) => { if n > MAX_LINE_LENGTH_USIZE { - panic!("Line is too long (> {})", MAX_LINE_LENGTH); + panic!("Line is too long (> {MAX_LINE_LENGTH})"); } self.line_no += 1; self.col = 0; @@ -141,7 +141,7 @@ impl Source { } } Err(err) => { - panic!("Could not read line from source: {}", err); + panic!("Could not read line from source: {err}"); } }; } diff --git a/src/util/stack.rs b/feint-util/src/stack.rs similarity index 98% rename from src/util/stack.rs rename to feint-util/src/stack.rs index b6930cc..fe44713 100644 --- a/src/util/stack.rs +++ b/feint-util/src/stack.rs @@ -85,7 +85,7 @@ impl Stack { impl fmt::Display for Stack { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for index in self.iter() { - write!(f, "{}", index)?; + write!(f, "{index}")?; } write!(f, "") } diff --git a/src/util/string.rs b/feint-util/src/string.rs similarity index 100% rename from src/util/string.rs rename to feint-util/src/string.rs diff --git a/feint-util/src/tests/mod.rs b/feint-util/src/tests/mod.rs new file mode 100644 index 0000000..e19fd3d --- /dev/null +++ b/feint-util/src/tests/mod.rs @@ -0,0 +1 @@ +mod stack; diff --git a/src/tests/util.rs b/feint-util/src/tests/stack.rs similarity index 98% rename from src/tests/util.rs rename to feint-util/src/tests/stack.rs index 676b61d..46f99d4 100644 --- a/src/tests/util.rs +++ b/feint-util/src/tests/stack.rs @@ -1,4 +1,4 @@ -use crate::util::Stack; +use crate::stack::Stack; #[test] fn new_stack_is_empty() { diff --git a/feint-vm/Cargo.toml b/feint-vm/Cargo.toml new file mode 100644 index 0000000..943e986 --- /dev/null +++ b/feint-vm/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "feint-vm" +edition.workspace = true +version.workspace = true +description.workspace = true +authors.workspace = true +readme.workspace = true +license-file.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +feint-builtins = { path = "../feint-builtins" } +feint-util = { path = "../feint-util" } + +bitflags.workspace = true +ctrlc.workspace = true +env_logger.workspace = true +indexmap.workspace = true +log.workspace = true +num-traits.workspace = true diff --git a/src/vm/context.rs b/feint-vm/src/context.rs similarity index 97% rename from src/vm/context.rs rename to feint-vm/src/context.rs index 1e1a181..2a53854 100644 --- a/src/vm/context.rs +++ b/feint-vm/src/context.rs @@ -1,9 +1,10 @@ //! VM runtime context. use indexmap::IndexMap; -use crate::modules::std::STD; -use crate::types::{new, ObjectRef, ObjectTrait}; -use crate::vm::RuntimeObjResult; +use feint_builtins::modules::STD; +use feint_builtins::types::{new, ObjectRef, ObjectTrait}; + +use crate::RuntimeObjResult; use super::result::{RuntimeErr, RuntimeResult}; @@ -43,7 +44,7 @@ impl ModuleExecutionContext { } #[inline] - pub(crate) fn globals(&self) -> &Namespace { + pub fn globals(&self) -> &Namespace { &self.ns_stack[0] } diff --git a/src/dis.rs b/feint-vm/src/dis.rs similarity index 81% rename from src/dis.rs rename to feint-vm/src/dis.rs index c1153d4..71c771b 100644 --- a/src/dis.rs +++ b/feint-vm/src/dis.rs @@ -1,6 +1,6 @@ use std::fmt; -use crate::vm::{globals, Code, Inst}; +use feint_builtins::types::code::{Code, Inst}; pub struct Disassembler { curr_line_no: usize, @@ -37,7 +37,7 @@ impl Disassembler { if let Some(func) = obj.down_to_func() { println!(); let heading = format!("{func:?} "); - println!("{:=<79}", heading); + println!("{heading:=<79}"); self.disassemble(func.code()); } } @@ -54,25 +54,6 @@ impl Disassembler { match inst { NoOp => self.align("NOOP", "ø"), Pop => self.align("POP", ""), - LoadGlobalConst(index) => { - let op_code = "LOAD_GLOBAL_CONST"; - let index = *index; - if let Some(obj) = globals::get_global_constant(index) { - self.align(op_code, format!("{index} ({})", obj.read().unwrap())) - } else { - self.align( - op_code, - format!("{index} ([global constant does not exist])"), - ) - } - } - LoadNil => self.align("LOAD_NIL", "nil"), - LoadTrue => self.align("LOAD_TRUE", "true"), - LoadFalse => self.align("LOAD_FALSE", "false"), - LoadAlways => self.align("LOAD_ALWAYS", "@"), - LoadEmptyStr => self.align("LOAD_EMPTY_STR", "\"\""), - LoadNewline => self.align("LOAD_NEWLINE", "\"\\n\""), - LoadEmptyTuple => self.align("LOAD_EMPTY_TUPLE", "()"), ScopeStart => self.align("SCOPE_START", ""), ScopeEnd => self.align("SCOPE_END", ""), StatementStart(start, _) => { @@ -80,13 +61,13 @@ impl Disassembler { self.curr_line_no = start.line; self.align("STATEMENT_START", "") } - LoadConst(index) => { - let constant = match code.get_const(*index) { - Ok(obj) => obj.read().unwrap().to_string(), - Err(err) => err.to_string(), - }; - self.align("LOAD_CONST", format!("{index} ({constant:?})")) - } + LoadConst(index) => match code.get_const(*index) { + Some(obj) => { + let obj_str = obj.read().unwrap().to_string(); + self.align("LOAD_CONST", format!("{index} ({obj_str:?})")) + } + None => self.align("LOAD_CONST", ""), + }, DeclareVar(name) => self.align("DECLARE_VAR", name), AssignVar(name) => self.align("ASSIGN_VAR", name), LoadVar(name, offset) => { diff --git a/feint-vm/src/lib.rs b/feint-vm/src/lib.rs new file mode 100644 index 0000000..6ad32f7 --- /dev/null +++ b/feint-vm/src/lib.rs @@ -0,0 +1,15 @@ +pub use context::ModuleExecutionContext; +pub use dis::Disassembler; +pub use result::{ + CallDepth, RuntimeBoolResult, RuntimeErr, RuntimeErrKind, RuntimeObjResult, + RuntimeResult, VMState, +}; +pub use vm::VM; + +pub mod context; +pub mod dis; +pub mod result; +pub mod vm; + +#[cfg(test)] +mod tests; diff --git a/src/vm/result.rs b/feint-vm/src/result.rs similarity index 75% rename from src/vm/result.rs rename to feint-vm/src/result.rs index a0a1765..bcecfe8 100644 --- a/src/vm/result.rs +++ b/feint-vm/src/result.rs @@ -1,12 +1,9 @@ use std::fmt; use std::fmt::Formatter; -use crate::compiler::CompErr; -use crate::parser::ParseErr; -use crate::types::ObjectRef; +use feint_builtins::types::ObjectRef; pub type CallDepth = usize; -pub type VMExeResult = Result<(), RuntimeErr>; pub type RuntimeResult = Result<(), RuntimeErr>; pub type RuntimeObjResult = Result; pub type RuntimeBoolResult = Result; @@ -34,8 +31,6 @@ pub enum ValueStackKind { ReturnVal(ObjectRef), } -// Runtime errors ------------------------------------------------------ - #[derive(Clone, Debug)] pub struct RuntimeErr { pub kind: RuntimeErrKind, @@ -46,18 +41,6 @@ impl RuntimeErr { Self { kind } } - pub fn config_name_not_known>(name: S) -> Self { - Self::new(RuntimeErrKind::ConfigNameNotKnown(name.into())) - } - - pub fn config_value_not_set>(name: S) -> Self { - Self::new(RuntimeErrKind::ConfigValueNotSet(name.into())) - } - - pub fn config_value_is_not_valid>(name: S, msg: S) -> Self { - Self::new(RuntimeErrKind::ConfigValueIsNotValid(name.into(), msg.into())) - } - pub fn exit(code: u8) -> Self { Self::new(RuntimeErrKind::Exit(code)) } @@ -131,9 +114,6 @@ impl fmt::Display for RuntimeErr { #[derive(Clone, Debug)] pub enum RuntimeErrKind { - ConfigNameNotKnown(String), - ConfigValueNotSet(String), - ConfigValueIsNotValid(String, String), Exit(u8), AssertionFailed(String), EmptyStack, @@ -145,8 +125,6 @@ pub enum RuntimeErrKind { ConstantNotFound(usize), CapturedVarNotFound(String), ExpectedVar(String), - ParseErr(ParseErr), - CompErr(CompErr), UnhandledInstruction(String), TypeErr(String), NameErr(String), @@ -158,17 +136,6 @@ pub enum RuntimeErrKind { impl fmt::Display for RuntimeErrKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - use RuntimeErrKind::*; - let str = match self { - ConfigNameNotKnown(name) => { - format!("Unknown FeInt config name: {name}") - } - ConfigValueNotSet(name) => format!("FeInt config value not set: {name}"), - ConfigValueIsNotValid(name, val) => { - format!("FeInt config value not valid for {name}: {val}") - } - _ => format!("{self:?}"), - }; - write!(f, "{str}") + write!(f, "{self:?}") } } diff --git a/src/tests/vm.rs b/feint-vm/src/tests.rs similarity index 77% rename from src/tests/vm.rs rename to feint-vm/src/tests.rs index 7b5135d..b740ba5 100644 --- a/src/tests/vm.rs +++ b/feint-vm/src/tests.rs @@ -1,6 +1,8 @@ -use crate::op::BinaryOperator; -use crate::types::{new, Module}; -use crate::vm::*; +use feint_builtins::types::code::{Code, Inst}; +use feint_builtins::types::{new, Module}; +use feint_util::op::BinaryOperator; + +use crate::*; #[test] fn execute_simple_program() { diff --git a/src/vm/vm.rs b/feint-vm/src/vm.rs similarity index 88% rename from src/vm/vm.rs rename to feint-vm/src/vm.rs index def7e30..fed0d58 100644 --- a/src/vm/vm.rs +++ b/feint-vm/src/vm.rs @@ -12,27 +12,21 @@ use ctrlc; use indexmap::IndexMap; use num_traits::ToPrimitive; -use crate::modules::get_module; -use crate::op::{BinaryOperator, CompareOperator, InplaceOperator, UnaryOperator}; -use crate::source::Location; -use crate::types::{ +use feint_builtins::modules::get_module; +use feint_builtins::types::code::{Code, Inst, PrintFlags}; +use feint_builtins::types::{ new, Args, Func, FuncTrait, IntrinsicFunc, Module, ObjectRef, ThisOpt, }; -use crate::util::Stack; +use feint_util::op::{BinaryOperator, CompareOperator, InplaceOperator, UnaryOperator}; +use feint_util::source::Location; +use feint_util::stack::Stack; -use super::code::Code; use super::context::ModuleExecutionContext; -use super::globals; -use super::inst::{Inst, PrintFlags}; use super::result::{ CallDepth, PeekObjResult, PeekResult, PopNObjResult, PopNResult, PopObjResult, - PopResult, RuntimeErr, RuntimeObjResult, RuntimeResult, VMExeResult, VMState, - ValueStackKind, + PopResult, RuntimeErr, RuntimeObjResult, RuntimeResult, VMState, ValueStackKind, }; -pub const DEFAULT_MAX_CALL_DEPTH: CallDepth = - if cfg!(debug_assertions) { 256 } else { 1024 }; - struct CallFrame { stack_pointer: usize, this_opt: ThisOpt, @@ -61,9 +55,8 @@ impl CallFrame { } pub struct VM { - pub(crate) ctx: ModuleExecutionContext, - pub(crate) state: VMState, - global_constants: Vec, + pub ctx: ModuleExecutionContext, + pub state: VMState, // The scope stack contains pointers into the value stack. When a // scope is entered, the current, pre-scope stack position is // recorded. When a scope is exited, its corresponding pointer is @@ -90,7 +83,8 @@ unsafe impl Sync for VM {} impl Default for VM { fn default() -> Self { - VM::new(ModuleExecutionContext::default(), DEFAULT_MAX_CALL_DEPTH) + let call_depth = if cfg!(debug_assertions) { 256 } else { 1024 }; + VM::new(ModuleExecutionContext::default(), call_depth) } } @@ -99,7 +93,6 @@ impl VM { VM { ctx, state: VMState::Idle(None), - global_constants: globals::get_global_constants(), scope_stack: Stack::with_capacity(max_call_depth), value_stack: Stack::with_capacity(max_call_depth * 8), call_stack: Stack::with_capacity(max_call_depth), @@ -110,12 +103,12 @@ impl VM { } } - pub fn execute_module(&mut self, module: &Module, start: usize) -> VMExeResult { + pub fn execute_module(&mut self, module: &Module, start: usize) -> RuntimeResult { self.reset(); self.execute_code(module, module.code(), start) } - pub fn execute_func(&mut self, func: &Func, start: usize) -> VMExeResult { + pub fn execute_func(&mut self, func: &Func, start: usize) -> RuntimeResult { let module = func.module(); let module = module.read().unwrap(); let module = module.down_to_mod().unwrap(); @@ -136,7 +129,7 @@ impl VM { module: &Module, code: &Code, mut ip: usize, - ) -> VMExeResult { + ) -> RuntimeResult { use Inst::*; self.set_running(); @@ -163,32 +156,6 @@ impl VM { Pop => { self.pop()?; } - // Well-known global constants - LoadNil => { - self.push_global_const(globals::NIL_INDEX)?; - } - LoadTrue => { - self.push_global_const(globals::TRUE_INDEX)?; - } - LoadFalse => { - self.push_global_const(globals::FALSE_INDEX)?; - } - LoadAlways => { - self.push_global_const(globals::ALWAYS_INDEX)?; - } - LoadEmptyStr => { - self.push_global_const(globals::EMPTY_STR_INDEX)?; - } - LoadNewline => { - self.push_global_const(globals::NEWLINE_INDEX)?; - } - LoadEmptyTuple => { - self.push_global_const(globals::EMPTY_TUPLE_INDEX)?; - } - // Other global constants (shared ints) - LoadGlobalConst(index) => { - self.push_global_const(*index)?; - } // Scopes ScopeStart => { self.enter_scope(); @@ -200,8 +167,8 @@ impl VM { self.loc = (*start, *end); } LoadConst(index) => { - let obj = code.get_const(*index)?.clone(); - self.push(ValueStackKind::Constant(obj, *index)); + let obj = code.get_const(*index).unwrap(); + self.push(ValueStackKind::Constant(obj.clone(), *index)); } // Modules LoadModule(name) => { @@ -326,7 +293,7 @@ impl VM { } } JumpPushNil(addr, forward, scope_exit_count) => { - self.push_global_const(0)?; + self.push_temp(new::nil()); self.exit_scopes(*scope_exit_count); if *forward { jump_ip = Some(ip + *addr); @@ -338,32 +305,45 @@ impl VM { self.exit_scopes(*scope_exit_count); let obj = self.peek_obj()?; let obj = obj.read().unwrap(); - if obj.bool_val()? { - if *forward { - jump_ip = Some(ip + *addr); - } else { - jump_ip = Some(ip - *addr); + if let Some(should_jump) = obj.bool_val() { + if should_jump { + if *forward { + jump_ip = Some(ip + *addr); + } else { + jump_ip = Some(ip - *addr); + } } + } else { + // XXX: Should not be a hard error? + return Err(RuntimeErr::type_err(format!( + "Cannot evaluate as bool: {obj}" + ))); } } JumpIfNot(addr, forward, scope_exit_count) => { self.exit_scopes(*scope_exit_count); let obj = self.peek_obj()?; let obj = obj.read().unwrap(); - if !obj.bool_val()? { - if *forward { - jump_ip = Some(ip + *addr); - } else { - jump_ip = Some(ip - *addr); + if let Some(should_not_jump) = obj.bool_val() { + if !should_not_jump { + if *forward { + jump_ip = Some(ip + *addr); + } else { + jump_ip = Some(ip - *addr); + } } + } else { + // XXX: Should not be a hard error? + return Err(RuntimeErr::type_err(format!( + "Cannot evaluate as bool: {obj}" + ))); } } JumpIfNotNil(addr, forward, scope_exit_count) => { self.exit_scopes(*scope_exit_count); let obj = self.peek_obj()?; let obj = obj.read().unwrap(); - let nil = &self.global_constants[globals::NIL_INDEX]; - if !obj.is(&*nil.read().unwrap()) { + if obj.is_nil() { if *forward { jump_ip = Some(ip + *addr); } else { @@ -593,13 +573,13 @@ impl VM { self.state = VMState::Idle(obj); } - fn halt(&mut self, exit_code: u8) -> VMExeResult { + fn halt(&mut self, exit_code: u8) -> RuntimeResult { self.reset(); self.state = VMState::Halted(exit_code); Err(RuntimeErr::exit(exit_code)) } - pub fn halt_top(&mut self) -> VMExeResult { + pub fn halt_top(&mut self) -> RuntimeResult { let obj = self.pop_obj()?; let obj = obj.read().unwrap(); let return_code = match obj.get_int_val() { @@ -626,12 +606,31 @@ impl VM { let a = a_ref.read().unwrap(); let result = match op { Plus => { - drop(a); - a_ref // no-op + // no-op + self.push_temp(a_ref.clone()); + return Ok(()); + } + Negate => { + if let Some(result) = a.negate() { + result + } else { + new::type_err(format!("- not defined for {a}"), a_ref.clone()) + } + } + AsBool => { + if let Some(result) = a.bool_val() { + new::bool(result) + } else { + new::type_err(format!("!! not defined for {a}"), a_ref.clone()) + } + } + Not => { + if let Some(result) = a.not() { + new::bool(result) + } else { + new::type_err(format!("! not defined for {a}"), a_ref.clone()) + } } - Negate => a.negate()?, - AsBool => new::bool(a.bool_val()?), - Not => new::bool(a.not()?), }; self.push_temp(result); Ok(()) @@ -648,13 +647,13 @@ impl VM { let b = b_ref.read().unwrap(); let b = &*b; let result = match op { - Pow => a.pow(b)?, - Mul => a.mul(b)?, - Div => a.div(b)?, - FloorDiv => a.floor_div(b)?, - Mod => a.modulo(b)?, - Add => a.add(b)?, - Sub => a.sub(b)?, + Pow => a.pow(b), + Mul => a.mul(b), + Div => a.div(b), + FloorDiv => a.floor_div(b), + Mod => a.modulo(b), + Add => a.add(b), + Sub => a.sub(b), Dot => { let obj_ref = if let Some(name) = b.get_str_val() { let mut result = a.get_attr(name, a_ref.clone()); @@ -688,33 +687,45 @@ impl VM { }; let obj = obj_ref.read().unwrap(); - if obj.is_intrinsic_func() || obj.is_func() || obj.is_closure() { - // If `b` in `a.b` is a function, bind `b` to `a`. + let result = + if obj.is_intrinsic_func() || obj.is_func() || obj.is_closure() { + // If `b` in `a.b` is a function, bind `b` to `a`. - // TODO: Check whether `a` is a type or an instance. + // TODO: Check whether `a` is a type or an instance. - new::bound_func(obj_ref.clone(), a_ref.clone()) - } else if let Some(prop) = obj.down_to_prop() { - // If `b` in `a.b` is a property, bind `b`'s getter - // to `a` then call the bound getter. + new::bound_func(obj_ref.clone(), a_ref.clone()) + } else if let Some(prop) = obj.down_to_prop() { + // If `b` in `a.b` is a property, bind `b`'s getter + // to `a` then call the bound getter. - // TODO: Check whether `a` is a type or an instance - // and return the property itself when `a` is - // a type. + // TODO: Check whether `a` is a type or an instance + // and return the property itself when `a` is + // a type. - let func = new::bound_func(prop.getter(), a_ref.clone()); - if a.is_type_object() { - func + let func = new::bound_func(prop.getter(), a_ref.clone()); + if a.is_type_object() { + func + } else { + return self.call(func, vec![]); + } } else { - return self.call(func, vec![]); - } - } else { - drop(obj); - obj_ref - } + obj_ref.clone() + }; + + self.push_temp(result); + return Ok(()); } }; - self.push_temp(result); + + if let Some(result) = result { + self.push_temp(result); + } else { + self.push_temp(new::type_err( + format!("Operation not defined for {b}"), + a_ref.clone(), + )); + } + Ok(()) } @@ -735,10 +746,10 @@ impl VM { IsNotTypeEqual => !a.is_type_equal(b), IsEqual => a.is_equal(b), NotEqual => !a.is_equal(b), - LessThan => a.less_than(b)?, - LessThanOrEqual => a.less_than(b)? || a.is_equal(b), - GreaterThan => a.greater_than(b)?, - GreaterThanOrEqual => a.greater_than(b)? || a.is_equal(b), + LessThan => a.less_than(b).unwrap_or(false), + LessThanOrEqual => a.less_than(b).unwrap_or(false) || a.is_equal(b), + GreaterThan => a.greater_than(b).unwrap_or(false), + GreaterThanOrEqual => a.greater_than(b).unwrap_or(false) || a.is_equal(b), }; self.push_temp(new::bool(result)); Ok(()) @@ -755,11 +766,22 @@ impl VM { let b = b_ref.read().unwrap(); let b = &*b; let result = match op { - InplaceOperator::Mul => a.mul(b)?, - InplaceOperator::Div => a.div(b)?, - InplaceOperator::Add => a.add(b)?, - InplaceOperator::Sub => a.sub(b)?, + InplaceOperator::Mul => a.mul(b), + InplaceOperator::Div => a.div(b), + InplaceOperator::Add => a.add(b), + InplaceOperator::Sub => a.sub(b), + }; + + let result = if let Some(result) = result { + result + } else { + self.push_temp(new::type_err( + format!("Operation not defined for {b}"), + a_ref.clone(), + )); + return Ok(()); }; + if let ValueStackKind::Var(_, depth, name) = a_kind { self.ctx.assign_var_at_depth(depth, name.as_str(), result.clone())?; self.push_temp(result); @@ -770,7 +792,7 @@ impl VM { cell.set_value(result.clone()); self.push_temp(result); } else { - return Err(RuntimeErr::expected_var(format!("Binary op: {}", op))); + return Err(RuntimeErr::expected_var(format!("Binary op: {op}"))); } Ok(()) } @@ -920,10 +942,12 @@ impl VM { ); self.call_closure(func_ref.clone(), this_opt, args) } else { - Err(func_obj.not_callable()) + self.push_temp(new::not_callable_err(callable_ref.clone())); + Ok(()) } } else { - Err(callable.not_callable()) + self.push_temp(new::not_callable_err(callable_ref.clone())); + Ok(()) } } @@ -935,18 +959,10 @@ impl VM { ) -> RuntimeResult { let args = self.check_call_args(func, &this_opt, args)?; self.push_call_frame(this_opt.clone(), None)?; - let result = (func.func())(self.find_this(), args, self); - match result { - Ok(return_val) => { - self.push_return_val(return_val); - self.pop_call_frame()?; - Ok(()) - } - Err(err) => { - self.reset(); - Err(err) - } - } + let return_val = (func.func())(self.find_this(), args); + self.push_return_val(return_val); + self.pop_call_frame()?; + Ok(()) } pub fn call_func( @@ -1088,15 +1104,6 @@ impl VM { self.value_stack.push(kind); } - fn push_global_const(&mut self, index: usize) -> RuntimeResult { - if let Some(obj) = self.global_constants.get(index) { - self.push(ValueStackKind::GlobalConstant(obj.clone(), index)); - Ok(()) - } else { - Err(RuntimeErr::constant_not_found(index)) - } - } - fn push_var(&mut self, depth: usize, name: String) -> RuntimeResult { let obj_ref = self.ctx.get_var_at_depth(depth, name.as_str())?; // XXX: This is a workaround for function args being created diff --git a/src/ast/mod.rs b/src/ast/mod.rs deleted file mode 100644 index a0b76b9..0000000 --- a/src/ast/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub(crate) use ast::*; - -pub(crate) mod visitors; - -mod ast; diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs deleted file mode 100644 index 496cb26..0000000 --- a/src/compiler/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub(crate) use compiler::Compiler; -pub(crate) use result::{CompErr, CompErrKind}; - -mod compiler; -mod result; -mod scope; -mod visitor; diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index cd64332..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! # FeInt -//! -//! FeInt is a stack-based bytecode interpreter. -#[macro_use] -extern crate bitflags; - -pub mod cli; -pub mod dis; -pub mod exe; -pub mod op; -pub mod repl; -pub mod result; -pub mod source; -pub mod vm; - -mod ast; -mod compiler; -mod format; -mod modules; -mod parser; -mod scanner; -mod types; -mod util; - -#[cfg(test)] -mod tests; diff --git a/src/modules/mod.rs b/src/modules/mod.rs deleted file mode 100644 index 07068bf..0000000 --- a/src/modules/mod.rs +++ /dev/null @@ -1,43 +0,0 @@ -use ::std::sync::{Arc, RwLock}; -use once_cell::sync::Lazy; - -use crate::types::gen::{obj_ref, obj_ref_t}; -use crate::types::{Map, ObjectRef, ObjectTrait}; - -pub mod std; - -/// This mirrors `system.modules`. It provides a way to access -/// modules in Rust code (e.g., in the VM). -pub static MODULES: Lazy = Lazy::new(|| obj_ref!(Map::default())); - -/// Add module to `std.system.modules`. -pub fn add_module(name: &str, module: ObjectRef) { - let modules = MODULES.write().unwrap(); - let modules = modules.down_to_map().unwrap(); - modules.insert(name, module); -} - -/// Get module from `system.modules`. -/// -/// XXX: Panics if the module doesn't exist (since that shouldn't be -/// possible). -pub fn get_module(name: &str) -> ObjectRef { - let modules = MODULES.read().unwrap(); - let modules = modules.down_to_map().unwrap(); - if let Some(module) = modules.get(name) { - module.clone() - } else { - panic!("Module not registered: {name}"); - } -} - -/// Get module from `system.modules`. -/// -/// XXX: This will return `None` if the module doesn't exist. Generally, -/// this should only be used during bootstrap. In most cases, -/// `get_module` should be used instead. -pub fn maybe_get_module(name: &str) -> Option { - let modules = MODULES.read().unwrap(); - let modules = modules.down_to_map().unwrap(); - modules.get(name) -} diff --git a/src/tests/compiler.rs b/src/tests/compiler.rs deleted file mode 100644 index 8b13789..0000000 --- a/src/tests/compiler.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/tests/exe.rs b/src/tests/exe.rs deleted file mode 100644 index c81f555..0000000 --- a/src/tests/exe.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::exe::Executor; -use crate::result::{ExeErrKind, ExeResult}; -use crate::vm::RuntimeErrKind; - -fn execute(source: &str) -> ExeResult { - let mut exe = Executor::new(16, vec![], false, false, false); - exe.bootstrap()?; - exe.execute_text(source) -} - -#[test] -fn test_too_much_recursion() { - let result = execute("f = () => f()\nf()"); - assert!(result.is_err()); - let err = result.unwrap_err(); - assert!(matches!( - err.kind, - ExeErrKind::RuntimeErr(RuntimeErrKind::RecursionDepthExceeded(_)) - )); -} diff --git a/src/tests/mod.rs b/src/tests/mod.rs deleted file mode 100644 index 20cfdf1..0000000 --- a/src/tests/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -mod ast; -mod compiler; -mod exe; -mod format; -mod parser; -mod repl; -mod run; -mod scanner; -mod types; -mod util; -mod vm; diff --git a/src/types/result.rs b/src/types/result.rs deleted file mode 100644 index 0da4e9c..0000000 --- a/src/types/result.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::vm::RuntimeErr; - -use super::base::ObjectRef; - -// TODO: Move call-related types elsewhere -pub type ThisOpt = Option; -pub type Params = Vec; -pub type Args = Vec; -pub type CallResult = Result; diff --git a/src/types/seq.rs b/src/types/seq.rs deleted file mode 100644 index 89163a9..0000000 --- a/src/types/seq.rs +++ /dev/null @@ -1,134 +0,0 @@ -//! Common sequence operations - -use num_bigint::BigInt; - -use crate::vm::{RuntimeErr, RuntimeObjResult, VM}; - -use super::gen::{use_arg, use_arg_str}; -use super::new; - -use super::base::ObjectRef; -use super::result::Args; - -pub fn each( - this: &ObjectRef, - items: &[ObjectRef], - args: &Args, - vm: &mut VM, -) -> RuntimeObjResult { - if items.is_empty() { - return Ok(new::nil()); - } - - let each_fn = &args[0]; - let f = each_fn.read().unwrap(); - let n_args = if let Some(f) = f.as_func() { - if f.has_var_args() { - 2 - } else { - f.arity() - } - } else { - return Ok(new::arg_err("each/1 expects a function", this.clone())); - }; - - for (i, item) in items.iter().enumerate() { - let each = each_fn.clone(); - let item = item.clone(); - if n_args == 1 { - vm.call(each, vec![item])?; - } else { - vm.call(each, vec![item, new::int(i)])?; - } - } - - Ok(new::nil()) -} - -pub fn has(items: &[ObjectRef], args: &Args) -> RuntimeObjResult { - if items.is_empty() { - return Ok(new::bool(false)); - } - let member = use_arg!(args, 0); - for item in items.iter() { - if member.is_equal(&*item.read().unwrap()) { - return Ok(new::bool(true)); - } - } - Ok(new::bool(false)) -} - -pub fn join(items: &[ObjectRef], args: &Args) -> RuntimeObjResult { - if items.is_empty() { - return Ok(new::empty_str()); - } - - let n_items = items.len(); - let last_i = n_items - 1; - let arg = use_arg!(args, 0); - let sep = use_arg_str!(join, sep, arg); - - // XXX: Guessing at average word length - let capacity = n_items * 5 + ((last_i) * sep.len()); - let mut string = String::with_capacity(capacity); - - for (i, item) in items.iter().enumerate() { - let item = item.read().unwrap(); - let str = item.to_string(); - string.push_str(&str); - if i != last_i { - string.push_str(sep); - } - } - - Ok(new::str(string)) -} - -pub fn map( - this: &ObjectRef, - items: &[ObjectRef], - args: &Args, - vm: &mut VM, -) -> RuntimeObjResult { - if items.is_empty() { - return Ok(new::empty_tuple()); - } - - let map_fn = &args[0]; - let f = map_fn.read().unwrap(); - let n_args = if let Some(f) = f.as_func() { - if f.has_var_args() { - 2 - } else { - f.arity() - } - } else { - return Ok(new::arg_err("map/1 expects a function", this.clone())); - }; - - let mut results = vec![]; - for (i, item) in items.iter().enumerate() { - let map = map_fn.clone(); - let item = item.clone(); - if n_args == 1 { - vm.call(map, vec![item])?; - } else { - vm.call(map, vec![item, new::int(i)])?; - } - results.push(vm.pop_obj()?); - } - - Ok(new::tuple(results)) -} - -pub fn sum(items: &[ObjectRef]) -> RuntimeObjResult { - let mut sum = new::int(BigInt::from(0)); - for item in items.iter() { - let a = sum.read().unwrap(); - let b = item.read().unwrap(); - let new_sum = (*a).add(&*b)?; - drop(a); - sum = new_sum; - } - Ok(sum) -} diff --git a/src/util/mod.rs b/src/util/mod.rs deleted file mode 100644 index 4163d95..0000000 --- a/src/util/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub(crate) use call::check_args; -pub(crate) use stack::Stack; -pub(crate) use string::format_doc; - -mod call; -mod stack; -mod string; diff --git a/src/vm/code.rs b/src/vm/code.rs deleted file mode 100644 index 764adb3..0000000 --- a/src/vm/code.rs +++ /dev/null @@ -1,224 +0,0 @@ -use std::ops::Index; -use std::slice::Iter; - -use crate::source::Location; -use crate::types::{new, FuncTrait, ObjectRef}; -use crate::util::format_doc; - -use super::inst::Inst; -use super::result::RuntimeErr; - -type FreeVarEntry = ( - usize, // address - String, // name - Location, // source start - Location, // source end -); - -/// Code for a module or function. -#[derive(Debug)] -pub struct Code { - chunk: Vec, - constants: Vec, - // Vars defined outside of this unit of code. - free_vars: Vec, -} - -impl Default for Code { - fn default() -> Self { - Code::new(vec![], vec![], vec![]) - } -} - -impl Index for Code { - type Output = Inst; - - fn index(&self, index: usize) -> &Self::Output { - &self.chunk[index] - } -} - -impl PartialEq for Code { - fn eq(&self, other: &Self) -> bool { - if self.chunk != other.chunk { - return false; - } - if self.constants.len() != other.constants.len() { - return false; - } - if self.free_vars != other.free_vars { - return false; - } - for (c, d) in self.constants.iter().zip(other.constants.iter()) { - let c = c.read().unwrap(); - let d = d.read().unwrap(); - if !c.is_equal(&*d) { - return false; - } - } - true - } -} - -impl Code { - pub fn new( - chunk: Vec, - constants: Vec, - free_vars: Vec, - ) -> Self { - Self { chunk, constants, free_vars } - } - - /// Initialize code object with a list of instructions, also known - /// as a chunk. - pub fn with_chunk(chunk: Vec) -> Self { - Self::new(chunk, vec![], vec![]) - } - - /// Extend this `Code` object with another `Code` object: - /// - /// - Extend instructions, adjusting constant indexes - /// - Extend constants - /// - Free vars are ignored for now since this is mainly intended - /// for extending modules (where there are no free vars) and not - /// functions - /// - /// IMPORTANT: ALL instructions that hold a const index MUST be - /// updated here. - pub fn extend(&mut self, mut code: Self) { - use Inst::LoadConst; - let mut replacements = vec![]; - let const_offset = self.constants.len(); - for (addr, inst) in code.iter_chunk().enumerate() { - if let LoadConst(index) = inst { - replacements.push((addr, LoadConst(const_offset + index))); - } - } - for (addr, inst) in replacements { - code.replace_inst(addr, inst); - } - self.chunk.extend(code.chunk); - self.constants.extend(code.constants); - } - - /// Get docstring for code unit, if there is one. - pub fn get_doc(&self) -> ObjectRef { - if let Some(Inst::LoadConst(0)) = self.chunk.get(1) { - if let Ok(obj_ref) = self.get_const(0) { - let obj = obj_ref.read().unwrap(); - if let Some(doc) = obj.get_str_val() { - return new::str(format_doc(doc)); - } - } - } - new::nil() - } - - // Instructions ---------------------------------------------------- - - pub fn len_chunk(&self) -> usize { - self.chunk.len() - } - - pub fn iter_chunk(&self) -> Iter<'_, Inst> { - self.chunk.iter() - } - - pub fn push_inst(&mut self, inst: Inst) { - self.chunk.push(inst) - } - - pub fn pop_inst(&mut self) -> Option { - self.chunk.pop() - } - - pub fn insert_inst(&mut self, index: usize, inst: Inst) { - self.chunk.insert(index, inst); - } - - pub fn replace_inst(&mut self, index: usize, inst: Inst) { - self.chunk[index] = inst; - } - - /// Explicit return statements need to jump to the end of the - /// function so that the function can be cleanly exited. - pub fn fix_up_explicit_returns(&mut self) { - let return_addr = self.len_chunk(); - for addr in 0..return_addr { - let inst = &self.chunk[addr]; - if let Inst::ReturnPlaceholder(inst_addr, depth) = inst { - let rel_addr = return_addr - inst_addr; - self.replace_inst(*inst_addr, Inst::Jump(rel_addr, true, depth - 1)); - } - } - } - - // Constants ------------------------------------------------------- - - pub fn add_const(&mut self, val_ref: ObjectRef) -> usize { - let val_guard = val_ref.read().unwrap(); - let val = &*val_guard; - - // XXX: Functions are immutable and comparable, but it feels - // potentially unsafe to treat them as such here. - let is_comparable = val.is_immutable() && !val.is_func(); - - for (index, other_ref) in self.iter_constants().enumerate() { - let other = other_ref.read().unwrap(); - let other_is_comparable = other.is_immutable() && !other.is_func(); - if is_comparable && other_is_comparable && other.is_equal(val) { - return index; - } - } - - let index = self.constants.len(); - drop(val_guard); - self.constants.push(val_ref); - index - } - - pub fn get_const(&self, index: usize) -> Result<&ObjectRef, RuntimeErr> { - if let Some(obj) = self.constants.get(index) { - Ok(obj) - } else { - Err(RuntimeErr::constant_not_found(index)) - } - } - - pub fn iter_constants(&self) -> Iter<'_, ObjectRef> { - self.constants.iter() - } - - pub fn get_main(&self) -> Option { - let maybe_index = self.constants.iter().position(|obj_ref| { - let obj = obj_ref.read().unwrap(); - if let Some(func) = obj.down_to_func() { - func.name() == "$main" - } else { - false - } - }); - maybe_index.map(|index| self.constants[index].clone()) - } - - // Vars ------------------------------------------------------------ - - pub fn free_vars(&self) -> &Vec { - &self.free_vars - } - - /// Add a free var, a reference to a var defined in an enclosing - /// scope. This also adds a placeholder instruction for the free - /// var that will replaced in the compiler's name resolution stage. - pub fn add_free_var>( - &mut self, - name: S, - start: Location, - end: Location, - ) { - let addr = self.len_chunk(); - let name = name.into(); - self.free_vars.push((addr, name.clone(), start, end)); - self.push_inst(Inst::FreeVarPlaceholder(addr, name)); - } -} diff --git a/src/vm/globals.rs b/src/vm/globals.rs deleted file mode 100644 index 403c13d..0000000 --- a/src/vm/globals.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! This module defines global constants that are shared across all -//! code units. These can (and should) be loaded by index in the VM -//! using dedicated instructions (`LOAD_NIL`, `LOAD_GLOBAL_CONST`, etc). - -use std::sync::{Arc, RwLock}; - -use num_bigint::BigInt; -use num_traits::{Signed, ToPrimitive, Zero}; - -use once_cell::sync::Lazy; - -use crate::types::gen::{obj_ref, obj_ref_t}; -use crate::types::ObjectRef; - -use crate::types::always::Always; -use crate::types::bool::Bool; -use crate::types::int::Int; -use crate::types::nil::Nil; -use crate::types::str::Str; -use crate::types::tuple::Tuple; - -pub static NIL: Lazy = Lazy::new(|| obj_ref!(Nil::new())); -pub static TRUE: Lazy = Lazy::new(|| obj_ref!(Bool::new(true))); -pub static FALSE: Lazy = Lazy::new(|| obj_ref!(Bool::new(false))); -pub static ALWAYS: Lazy = Lazy::new(|| obj_ref!(Always::new())); - -pub static EMPTY_STR: Lazy = - Lazy::new(|| obj_ref!(Str::new("".to_owned()))); - -pub static NEWLINE: Lazy = - Lazy::new(|| obj_ref!(Str::new("\n".to_owned()))); - -pub static EMPTY_TUPLE: Lazy = - Lazy::new(|| obj_ref!(Tuple::new(vec![]))); - -pub static SHARED_INT_MAX: usize = 256; -pub static SHARED_INT_MAX_BIGINT: Lazy = - Lazy::new(|| BigInt::from(SHARED_INT_MAX)); -pub static SHARED_INTS: Lazy> = Lazy::new(|| { - (0..=SHARED_INT_MAX).map(|i| obj_ref!(Int::new(BigInt::from(i)))).collect() -}); - -pub const NIL_INDEX: usize = 0; -pub const TRUE_INDEX: usize = 1; -pub const FALSE_INDEX: usize = 2; -pub const ALWAYS_INDEX: usize = 3; -pub const EMPTY_STR_INDEX: usize = 4; -pub const NEWLINE_INDEX: usize = 5; -pub const EMPTY_TUPLE_INDEX: usize = 6; -pub const SHARED_INT_INDEX: usize = 7; - -/// Get the global constants. -/// -/// NOTE: This is only intended to be called _once_. -pub fn get_global_constants() -> Vec { - let mut global_constants: Vec = vec![ - NIL.clone(), - TRUE.clone(), - FALSE.clone(), - ALWAYS.clone(), - EMPTY_STR.clone(), - NEWLINE.clone(), - EMPTY_TUPLE.clone(), - ]; - for int in SHARED_INTS.iter() { - global_constants.push(int.clone()); - } - global_constants -} - -/// Get the global constant index for the `int` if it's in the shared -/// int range. -pub fn shared_int_index(int: &BigInt) -> Option { - if int.is_zero() { - Some(SHARED_INT_INDEX) - } else if int.is_positive() && int <= Lazy::force(&SHARED_INT_MAX_BIGINT) { - Some(int.to_usize().unwrap() + SHARED_INT_INDEX) - } else { - None - } -} - -/// Get the global constant at `index`. -/// -/// NOTE: This is only intended for use in testing. -pub(crate) fn get_global_constant(index: usize) -> Option { - let global_constants = get_global_constants(); - global_constants.get(index).cloned() -} diff --git a/src/vm/inst.rs b/src/vm/inst.rs deleted file mode 100644 index 7dac9c6..0000000 --- a/src/vm/inst.rs +++ /dev/null @@ -1,201 +0,0 @@ -use crate::op::{BinaryOperator, CompareOperator, InplaceOperator, UnaryOperator}; -use crate::source::Location; - -/// NOTE: When adding or removing instructions, the PartialEq impl -/// below must also be updated. -#[derive(Debug)] -pub enum Inst { - NoOp, - - // Pop TOS and discard it. - Pop, - - // Global constants are shared globally by all code units. - LoadGlobalConst(usize), - - // Special global constants with a known index. - LoadNil, // 0 - LoadTrue, // 1 - LoadFalse, // 2 - LoadAlways, // 3 - LoadEmptyStr, // 4 - LoadNewline, // 5 - LoadEmptyTuple, // 6 - - ScopeStart, - ScopeEnd, - - StatementStart(Location, Location), - - // Other constants are local to a given code unit. - LoadConst(usize), - - DeclareVar(String), - AssignVar(String), - - // Args: name, offset - // - // `offset` is the number of scopes above the current scope to start - // the search. 0 means the current scope, 1 means the parent scope, - // and so on. - LoadVar(String, usize), - - // Load module global - LoadGlobal(String), - - // Load builtin - LoadBuiltin(String), - - // These are analogous to AssignVar and LoadVar. Assignment wraps - // the value in a cell so that it can be shared. Loading unwraps the - // value. - AssignCell(String), - LoadCell(String), - - // Load captured value to TOS (a special case of LoadCell). - LoadCaptured(String), - - // Jumps ----------------------------------------------------------- - // - // For all jump instructions, the first arg is the target address - // relative to the jump address. The second arg is a flag to - // indicate a forward or reverse jump. The third arg is the scope - // exit count. - // - // Relative addresses allow instructions to be inserted BEFORE any - // forward jumps or AFTER any backward jumps within a code segment. - // Mainly this is to allow instructions to be inserted at the - // beginning of functions. - - // Jump unconditionally. - Jump(usize, bool, usize), - - // Jump unconditionally and push nil onto stack. - JumpPushNil(usize, bool, usize), - - // If top of stack is true, jump to address. Otherwise, continue. - JumpIf(usize, bool, usize), - - // If top of stack is false, jump to address. Otherwise, continue. - JumpIfNot(usize, bool, usize), - - // If top of stack is NOT nil, jump to address. Otherwise, continue. - JumpIfNotNil(usize, bool, usize), - - UnaryOp(UnaryOperator), - BinaryOp(BinaryOperator), - CompareOp(CompareOperator), - InplaceOp(InplaceOperator), - - // Call function with N values from top of stack. The args are - // ordered such that the 1st arg is at TOS and other args are below - // it. - Call(usize), - - // RETURN is a jump target at the end of a function. Its only - // purpose is to serve as a jump target for explicit returns. - Return, - - // These make compound objects from the top N items on the stack. - MakeString(usize), - MakeTuple(usize), - MakeList(usize), - MakeMap(usize), - - // Capture set for function--a list of names for the function to - // capture. If empty, a regular function will be created. - CaptureSet(Vec), - - // Make function or closure depending on capture set. MAKE_FUNC - // expects the following entries at TOS: - // - // TOS capture_set: Map (added by CAPTURE_SET) - // func: Func (added by LOAD_CONST) - MakeFunc, - - LoadModule(String), - - Halt(u8), - HaltTop, - - // Placeholders ---------------------------------------------------- - // - // Placeholders are inserted during compilation and later updated. - // All placeholders must be replaced or a runtime error will be - // thrown. - Placeholder(usize, Box, String), // address, instruction, error message - FreeVarPlaceholder(usize, String), // address, var name - BreakPlaceholder(usize, usize), // jump address, scope depth - ContinuePlaceholder(usize, usize), // jump address, scope depth - - // NOTE: This is used for explicit return statements. It will be - // replaced with a jump to a RETURN target. - ReturnPlaceholder(usize, usize), // jump address, scope depth - - // Miscellaneous --------------------------------------------------- - - // Pop TOS and print it to stdout or stderr. Behavior is controlled - // by passing in flags. Pass `PrintFlags::default()` for the default - // behavior, which is to print to stdout with no newline. - Print(PrintFlags), - - DisplayStack(String), -} - -bitflags! { - #[derive(Default)] - pub struct PrintFlags: u32 { - const ERR = 0b00000001; // print to stderr - const NL = 0b00000010; // print a trailing newline. - const REPR = 0b00000100; // print repr using fmt::Debug - const NO_NIL = 0b00001000; // don't print obj if it's nil - } -} - -impl PartialEq for Inst { - fn eq(&self, other: &Self) -> bool { - use Inst::*; - - match (self, other) { - (NoOp, NoOp) => true, - (Pop, Pop) => true, - (LoadGlobalConst(a), LoadGlobalConst(b)) => a == b, - (LoadNil, LoadNil) => true, - (LoadTrue, LoadTrue) => true, - (LoadFalse, LoadFalse) => true, - (LoadAlways, LoadAlways) => true, - (LoadEmptyStr, LoadEmptyStr) => true, - (LoadEmptyTuple, LoadEmptyTuple) => true, - (ScopeStart, ScopeStart) => true, - (ScopeEnd, ScopeEnd) => true, - (StatementStart(..), StatementStart(..)) => true, - (LoadConst(a), LoadConst(b)) => a == b, - (DeclareVar(a), DeclareVar(b)) => a == b, - (AssignVar(a), AssignVar(b)) => a == b, - (LoadVar(a, i), LoadVar(b, j)) => (a, i) == (b, j), - (AssignCell(a), AssignCell(b)) => a == b, - (LoadCell(a), LoadCell(b)) => a == b, - (LoadCaptured(a), LoadCaptured(b)) => a == b, - (Jump(a, b, c), Jump(d, e, f)) => (a, b, c) == (d, e, f), - (JumpPushNil(a, b, c), JumpPushNil(d, e, f)) => (a, b, c) == (d, e, f), - (JumpIfNot(a, b, c), JumpIfNot(d, e, f)) => (a, b, c) == (d, e, f), - (UnaryOp(a), UnaryOp(b)) => a == b, - (BinaryOp(a), BinaryOp(b)) => a == b, - (CompareOp(a), CompareOp(b)) => a == b, - (InplaceOp(a), InplaceOp(b)) => a == b, - (Call(a), Call(b)) => a == b, - (Return, Return) => true, - (MakeString(a), MakeString(b)) => a == b, - (MakeTuple(a), MakeTuple(b)) => a == b, - (MakeList(a), MakeList(b)) => a == b, - (MakeMap(a), MakeMap(b)) => a == b, - (CaptureSet(a), CaptureSet(b)) => a == b, - (MakeFunc, MakeFunc) => true, - (LoadModule(a), LoadModule(b)) => a == b, - (Halt(a), Halt(b)) => a == b, - (HaltTop, HaltTop) => true, - (Print(a), Print(b)) => a == b, - _ => false, - } - } -} diff --git a/src/vm/mod.rs b/src/vm/mod.rs deleted file mode 100644 index 52f5e99..0000000 --- a/src/vm/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -pub use result::VMState; -pub use result::{CallDepth, RuntimeErr}; -pub use vm::{DEFAULT_MAX_CALL_DEPTH, VM}; - -pub(crate) use code::Code; -pub(crate) use context::ModuleExecutionContext; -pub(crate) use inst::Inst; -pub(crate) use inst::PrintFlags; -pub(crate) use result::{ - RuntimeBoolResult, RuntimeErrKind, RuntimeObjResult, RuntimeResult, VMExeResult, -}; - -pub(crate) mod globals; - -mod code; -mod context; -mod inst; -mod result; -mod vm; From e845455a59ba42b3babcec78af6852a597c7ea99 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Mon, 30 Jan 2023 17:42:46 -0800 Subject: [PATCH 02/31] Fix less than & greater than comparisons between floats and ints In the case of `f < i` and `f > i` (LHS float, RHS int), there was a bug where the actual operation performed was `i < f` and `i > f`. I'm guessing this was due to an errant copypasta (along with a lack of test coverage). Added new utility functions for these cases and updated the names of the existing functions and their args for clarity. NOTE: This is a manual cherry pick of f624692ab1a0 from dev. --- feint-builtins/src/types/float.rs | 6 +++--- feint-builtins/src/types/int.rs | 6 +++--- feint-builtins/src/types/util.rs | 28 ++++++++++++++++++++-------- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/feint-builtins/src/types/float.rs b/feint-builtins/src/types/float.rs index 4b37b26..744197c 100644 --- a/feint-builtins/src/types/float.rs +++ b/feint-builtins/src/types/float.rs @@ -8,7 +8,7 @@ use once_cell::sync::Lazy; use feint_code_gen::*; use super::new; -use super::util::{eq_int_float, gt_int_float, lt_int_float}; +use super::util::{eq_int_float, float_gt_int, float_lt_int}; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; @@ -105,7 +105,7 @@ impl ObjectTrait for Float { if let Some(rhs) = rhs.down_to_float() { Some(self.value() < rhs.value()) } else if let Some(rhs) = rhs.down_to_int() { - Some(lt_int_float(rhs, self)) + Some(float_lt_int(self, rhs)) } else { None } @@ -115,7 +115,7 @@ impl ObjectTrait for Float { if let Some(rhs) = rhs.down_to_float() { Some(self.value() > rhs.value()) } else if let Some(rhs) = rhs.down_to_int() { - Some(gt_int_float(rhs, self)) + Some(float_gt_int(self, rhs)) } else { return None; } diff --git a/feint-builtins/src/types/int.rs b/feint-builtins/src/types/int.rs index e710b7d..fb67dba 100644 --- a/feint-builtins/src/types/int.rs +++ b/feint-builtins/src/types/int.rs @@ -10,7 +10,7 @@ use once_cell::sync::Lazy; use feint_code_gen::*; use super::new; -use super::util::{eq_int_float, gt_int_float, lt_int_float}; +use super::util::{eq_int_float, int_gt_float, int_lt_float}; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; use super::class::TYPE_TYPE; @@ -123,7 +123,7 @@ impl ObjectTrait for Int { if let Some(rhs) = rhs.down_to_int() { Some(self.value() < rhs.value()) } else if let Some(rhs) = rhs.down_to_float() { - Some(lt_int_float(self, rhs)) + Some(int_lt_float(self, rhs)) } else { None } @@ -133,7 +133,7 @@ impl ObjectTrait for Int { if let Some(rhs) = rhs.down_to_int() { Some(self.value() > rhs.value()) } else if let Some(rhs) = rhs.down_to_float() { - Some(gt_int_float(self, rhs)) + Some(int_gt_float(self, rhs)) } else { None } diff --git a/feint-builtins/src/types/util.rs b/feint-builtins/src/types/util.rs index b74a06e..5bdd633 100644 --- a/feint-builtins/src/types/util.rs +++ b/feint-builtins/src/types/util.rs @@ -16,14 +16,26 @@ pub fn eq_int_float(int: &Int, float: &Float) -> bool { } } -/// Compare Int and Float for less than. -pub fn lt_int_float(int: &Int, float: &Float) -> bool { - let int_as_float = int.value().to_f64().unwrap(); - int_as_float < *float.value() +/// Check Float < Int +pub fn float_lt_int(lhs_float: &Float, rhs_int: &Int) -> bool { + let rhs_as_float = rhs_int.value().to_f64().unwrap(); + *lhs_float.value() < rhs_as_float } -/// Compare Int and Float for greater than. -pub fn gt_int_float(int: &Int, float: &Float) -> bool { - let int_as_float = int.value().to_f64().unwrap(); - int_as_float > *float.value() +/// Check Int < Float +pub fn int_lt_float(lhs_int: &Int, rhs_float: &Float) -> bool { + let lhs_as_float = lhs_int.value().to_f64().unwrap(); + lhs_as_float < *rhs_float.value() +} + +/// Check Float > Int +pub fn float_gt_int(lhs_float: &Float, rhs_int: &Int) -> bool { + let rhs_as_float = rhs_int.value().to_f64().unwrap(); + *lhs_float.value() > rhs_as_float +} + +/// Check Int > Float +pub fn int_gt_float(lhs_int: &Int, rhs_float: &Float) -> bool { + let lhs_as_float = lhs_int.value().to_f64().unwrap(); + lhs_as_float > *rhs_float.value() } From 8befadaa923e7b041d60d0785d0534dd0d3e0d4e Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Mon, 30 Jan 2023 18:55:36 -0800 Subject: [PATCH 03/31] Fix some issues around printing - Make `$print ("\n",)` flush - Make the `NO_NIL` flag only applicable when printing to stdout - Rename `ERR` flag to `STDERR` for clarity - Add comment documenting `$print` to builtin `print()` - Fix compilation of standalone newlines; they were being compiled as empty strings - Make REPL print results to stdout (why were they being printed to stderr?) - Escape backslash-escaped sequences when print Str repr (NOTE: there's some assymetry here because FeInt doesn't handle all escape sequences, particularly unicode, but since this is just for debugging, it should be fine for now) --- feint-builtins/src/modules/std/std.fi | 34 +++++++++++++++++++-- feint-builtins/src/types/code.rs | 8 ++--- feint-builtins/src/types/new.rs | 5 ++++ feint-builtins/src/types/str.rs | 9 +++++- feint-compiler/src/compiler/visitor.rs | 4 +-- feint-driver/src/driver.rs | 5 +--- feint-vm/src/vm.rs | 41 ++++++++++++++++++-------- 7 files changed, 80 insertions(+), 26 deletions(-) diff --git a/feint-builtins/src/modules/std/std.fi b/feint-builtins/src/modules/std/std.fi index 3c512cb..c532cad 100644 --- a/feint-builtins/src/modules/std/std.fi +++ b/feint-builtins/src/modules/std/std.fi @@ -4,22 +4,52 @@ print = (...) => "Print representation of zero or more objects to stdout. + The objects will be converted to their string representations, + joined together with spaces, and printed out with a trailing + newline. + # Args - objects?: Any[] " + # $print args: + # + # - object: Any + # A single object to print + # + # - stderr: Bool + # Print to stderr instead of stdout + # + # - nl: Bool + # Print a newline (which will cause a flush) + # + # - repr: Bool + # Print object repr rather than string (use Debug impl) + # + # - no_nil: Bool + # Don't print object if it's nil (only applicable when printing + # to stdout) + # + # NOTE: Though the syntax below looks call-like, $print is NOT a + # function. It's compiled to a PRINT instruction, and its + # operand MUST be a tuple (i.e., a trailing comma MUST be + # included when none of the flags are specified). i = 0 loop i < $args.length -> $print ($args.i, false) $print (" ", false) i += 1 - $print ("", false, true) + $print ("\n", false) print_err = (...) => "Print representation of zero or more objects to stderr. + The objects will be converted to their string representations, + joined together with spaces, and printed out with a trailing + newline. + # Args - objects?: Any[] @@ -30,7 +60,7 @@ print_err = (...) => $print ($args.i, true) $print (" ", true) i += 1 - $print ("", true, true) + $print ("\n", true) type: Type = (obj: Any) => diff --git a/feint-builtins/src/types/code.rs b/feint-builtins/src/types/code.rs index 4f1c65b..b414be9 100644 --- a/feint-builtins/src/types/code.rs +++ b/feint-builtins/src/types/code.rs @@ -350,10 +350,10 @@ pub enum Inst { bitflags! { #[derive(Default)] pub struct PrintFlags: u32 { - const ERR = 0b00000001; // print to stderr - const NL = 0b00000010; // print a trailing newline. - const REPR = 0b00000100; // print repr using fmt::Debug - const NO_NIL = 0b00001000; // don't print obj if it's nil + const STDERR = 0b00000001; // print to stderr instead of stdout + const NL = 0b00000010; // print a trailing newline + const REPR = 0b00000100; // print repr using fmt::Debug + const NO_NIL = 0b00001000; // don't print obj if it's nil (if !STDERR) } } diff --git a/feint-builtins/src/types/new.rs b/feint-builtins/src/types/new.rs index 83b3bce..5577c1b 100644 --- a/feint-builtins/src/types/new.rs +++ b/feint-builtins/src/types/new.rs @@ -85,6 +85,11 @@ pub fn empty_str() -> ObjectRef { EMPTY_STR.clone() } +#[inline] +pub fn newline() -> ObjectRef { + NEWLINE.clone() +} + #[inline] pub fn empty_tuple() -> ObjectRef { EMPTY_TUPLE.clone() diff --git a/feint-builtins/src/types/str.rs b/feint-builtins/src/types/str.rs index 48365e2..58736b0 100644 --- a/feint-builtins/src/types/str.rs +++ b/feint-builtins/src/types/str.rs @@ -1,5 +1,6 @@ use std::any::Any; use std::fmt; +use std::str::EscapeDefault; use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; @@ -137,6 +138,12 @@ impl Str { pub fn value(&self) -> &str { self.value.as_str() } + + /// XXX: Make this correspond to how FeInt escapes strings and + /// improve FeInt's handling of escapes, particular unicode. + fn escape(&self) -> EscapeDefault<'_> { + self.value.escape_default() + } } impl ObjectTrait for Str { @@ -193,6 +200,6 @@ impl fmt::Display for Str { impl fmt::Debug for Str { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "\"{}\"", self.value) + write!(f, "\"{}\"", self.escape()) } } diff --git a/feint-compiler/src/compiler/visitor.rs b/feint-compiler/src/compiler/visitor.rs index 3a59d02..752cfdb 100644 --- a/feint-compiler/src/compiler/visitor.rs +++ b/feint-compiler/src/compiler/visitor.rs @@ -239,7 +239,7 @@ impl CompilerVisitor { let obj = args[0].clone(); // XXX: This is kinda gnarly. if n_args > 1 && args[1].is_true() { - flags.insert(PrintFlags::ERR); + flags.insert(PrintFlags::STDERR); } if n_args > 2 && args[2].is_true() { flags.insert(PrintFlags::NL); @@ -869,7 +869,7 @@ impl CompilerVisitor { } fn push_newline(&mut self) { - self.add_const(new::empty_str()); + self.add_const(new::newline()); } fn push_empty_tuple(&mut self) { diff --git a/feint-driver/src/driver.rs b/feint-driver/src/driver.rs index 73a3569..90fdbae 100644 --- a/feint-driver/src/driver.rs +++ b/feint-driver/src/driver.rs @@ -149,10 +149,7 @@ impl Driver { // Assign TOS to _, print it, then pop it to clear the stack let last_inst = code.pop_inst(); if let Some(Inst::Pop) = last_inst { - let print_flags = PrintFlags::ERR - | PrintFlags::NL - | PrintFlags::REPR - | PrintFlags::NO_NIL; + let print_flags = PrintFlags::NL | PrintFlags::REPR | PrintFlags::NO_NIL; code.push_inst(Inst::DeclareVar("_".to_owned())); code.push_inst(Inst::AssignVar("_".to_owned())); code.push_inst(Inst::Print(print_flags)); diff --git a/feint-vm/src/vm.rs b/feint-vm/src/vm.rs index fed0d58..e41447d 100644 --- a/feint-vm/src/vm.rs +++ b/feint-vm/src/vm.rs @@ -800,27 +800,42 @@ impl VM { fn handle_print(&mut self, flags: &PrintFlags) -> RuntimeResult { if let Ok(obj) = self.pop_obj() { let obj = obj.read().unwrap(); - if flags.contains(PrintFlags::NO_NIL) && obj.is_nil() { - // do nothing - } else if flags.contains(PrintFlags::ERR) { - if flags.contains(PrintFlags::REPR) { - eprint!("{:?}", &*obj); + + let print_to_stdout = !flags.contains(PrintFlags::STDERR); + let no_nil = flags.contains(PrintFlags::NO_NIL); + + if print_to_stdout && no_nil && obj.is_nil() { + return Ok(()); + } + + let is_nl = obj.get_str_val() == Some("\n"); + let print_nl = flags.contains(PrintFlags::NL) && !is_nl; + let print_repr = flags.contains(PrintFlags::REPR); + + if print_to_stdout { + if print_repr { + print!("{:?}", &*obj); + } else if is_nl { + println!(); } else { - eprint!("{obj}"); + print!("{obj}"); } - if flags.contains(PrintFlags::NL) { - eprintln!(); + if print_nl { + println!(); } } else { - if flags.contains(PrintFlags::REPR) { - print!("{:?}", &*obj); + if print_repr { + eprint!("{:?}", &*obj); + } else if is_nl { + eprintln!(); } else { - print!("{obj}"); + eprint!("{obj}"); } - if flags.contains(PrintFlags::NL) { - println!(); + if print_nl { + eprintln!(); } } + Ok(()) } else { Err(RuntimeErr::empty_stack()) From a4ca80992e4874575c71729f0ee1a2ff045ff173 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Mon, 30 Jan 2023 13:57:30 -0800 Subject: [PATCH 04/31] Add Mandlebrot benchmark Ported from https://github.com/RustPython/RustPython/blob/main/benches/benchmarks/mandelbrot.py, although I'm not sure if that is the original source. Performance isn't great, but it's also not too terrible. Fixing the reassignment hack might help with this. --- examples/mandlebrot.fi | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 examples/mandlebrot.fi diff --git a/examples/mandlebrot.fi b/examples/mandlebrot.fi new file mode 100644 index 0000000..68f1224 --- /dev/null +++ b/examples/mandlebrot.fi @@ -0,0 +1,56 @@ +# Ported from https://github.com/RustPython/RustPython/blob/main/benches/benchmarks/mandelbrot.py +# (not sure if it's that's the original source). +# +# Python runs this in ~200ms on my laptop. This version takes over +# a second using a release build. +# +# The most notable issue is that a hack has to be used to reassign outer +# vars--compute new value into temporary, reset var using `*= 0` hack, +# reassign var to temporary value using `+=` hack. Given the number of +# computations in the inner loop, this likely causes serious performance +# degradation. +$main = (...) => + w = 50.0 + h = 50.0 + y = 0.0 + + loop y < h -> + x = 0.0 + + loop x < w -> + zr = 0.0 + zi = 0.0 + tr = 0.0 + ti = 0.0 + cr = 2 * x / w - 1.5 + ci = 2 * y / h - 1.0 + i = 0 + + loop i < 50 && tr + ti <= 4 -> + new_zi = 2 * zr * zi + ci + zi *= 0.0 + zi += new_zi + + new_zr = tr - ti + cr + zr *= 0 + zr += new_zr + + new_tr = zr * zr + tr *= 0 + tr += new_tr + + new_ti = zi * zi + ti *= 0 + ti += new_ti + + i += 1 + + # NOTE: Using $print instead of print to avoid function call + # overhead in this hot loop. Additionally, print + # currently always adds a newline. + $print(if tr + ti <= 4 -> "*" else -> ".",) + + x += 1 + + $print("\n",) + y += 1 From c9d74c92f32a23c8022f3a80e0a95be5db54e1a8 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Mon, 30 Jan 2023 23:22:33 -0800 Subject: [PATCH 05/31] Fix bug in Source that would only allow 4096 bytes total to be read This happened due to a misunderstand/misuse of `Take` and was only now discovered due to continued work on `std.args.parse()`, since it grew enough so that `std.args` module hit the 4096 byte limit. --- feint-util/src/source.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/feint-util/src/source.rs b/feint-util/src/source.rs index 6bd7123..a7e3486 100644 --- a/feint-util/src/source.rs +++ b/feint-util/src/source.rs @@ -1,6 +1,6 @@ use std::collections::VecDeque; use std::fs::File; -use std::io::{BufRead, BufReader, Cursor, Take}; +use std::io::{BufRead, BufReader, Cursor}; use std::path::Path; use std::{fmt, io}; @@ -64,7 +64,7 @@ pub fn source_from_stdin() -> Source> { /// - Tracks current line and column. /// - Panics when lines are too long. pub struct Source { - stream: Take, + stream: T, /// String buffer the source reader reads lines into. buffer: String, /// The queue of characters for the current line. @@ -79,9 +79,9 @@ pub struct Source { } impl Source { - pub fn new(source: T) -> Self { + pub fn new(stream: T) -> Self { let mut source = Source { - stream: source.take(MAX_LINE_LENGTH + 1), + stream, buffer: String::with_capacity(INITIAL_CAPACITY), queue: VecDeque::with_capacity(INITIAL_CAPACITY), line_no: 0, From 5dd4a771f71cedc315b76f2437ea69cdc84e8e9c Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Mon, 30 Jan 2023 21:01:35 -0800 Subject: [PATCH 06/31] Improve std.args.parse() This is exhibiting some strange scanning/parsing behavior --- feint-builtins/src/modules/std/args.fi | 30 ++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/feint-builtins/src/modules/std/args.fi b/feint-builtins/src/modules/std/args.fi index 25458db..a8c4932 100644 --- a/feint-builtins/src/modules/std/args.fi +++ b/feint-builtins/src/modules/std/args.fi @@ -18,9 +18,14 @@ parse: Map = ( # Returns - A Map containing args or errors. + A `Map` containing args or errors, unless help is requested, in + which case the program's usage will be printed and `nil` will be + returned. " + if argv.iter.err -> + return argv.iter + add_help = spec.get("add_help") ?? true arg_specs = spec.get("args") @@ -111,15 +116,36 @@ parse: Map = ( it = flag_specs.iter() loop (spec = it.next()) $! nil -> name = spec.get("name") + default = spec.get("default") ?? false val = match flags.get(name) -> true -> true false -> false - nil -> false + nil -> default result.add(name, val) if flags.get("help") -> print($"usage: {program_name}\n") print(description) + + if arg_specs $! nil -> + print("\n# Args\n") + it = arg_specs.iter() + loop (spec = it.next()) $! nil -> + print(spec.get("name")) + + if option_specs $! nil -> + print("\n# Options\n") + it = option_specs.iter() + loop (spec = it.next()) $! nil -> + print(spec.get("name")) + + if flag_specs $! nil -> + print("\n# Flags\n") + it = flag_specs.iter() + loop (spec = it.next()) != nil -> + print(spec.get("name")) + + nil else if errors.is_empty -> result From 9fe5fad7da356bc7343932a91bf18cdd264afab6 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Mon, 30 Jan 2023 21:02:19 -0800 Subject: [PATCH 07/31] Improve Mandlebrot benchmark example --- examples/mandlebrot.fi | 54 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/examples/mandlebrot.fi b/examples/mandlebrot.fi index 68f1224..ab2eae4 100644 --- a/examples/mandlebrot.fi +++ b/examples/mandlebrot.fi @@ -9,9 +9,50 @@ # reassign var to temporary value using `+=` hack. Given the number of # computations in the inner loop, this likely causes serious performance # degradation. +import std.args +import std.system + + +die = (code, msg) => + print_err(msg) + system.exit(code, msg) + + $main = (...) => - w = 50.0 - h = 50.0 + "Mandlebrot + + # Args + + - w: Float + Width + + - h: Float + Height + + - print: Bool + Flag indicating whether to print output to terminal + + " + args = args.parse("mandlebrot", $args, $main.$doc, { + "args": ( + {"name": "w", "default": 20.0, "type": Float}, + {"name": "h", "default": 10.0, "type": Float}, + ), + "flags": ( + {"name": "print", "default": true}, + ) + }) + + if args.err -> + die(1, args.err) + + if args $$ nil -> + system.exit(0) + + w = args.get("w") ?? die(1, "Expected width to be a Float") + h = args.get("h") ?? die(1, "Expected height to be a Float") + do_print = args.get("print") + y = 0.0 loop y < h -> @@ -26,7 +67,7 @@ $main = (...) => ci = 2 * y / h - 1.0 i = 0 - loop i < 50 && tr + ti <= 4 -> + loop i < w && tr + ti <= 4 -> new_zi = 2 * zr * zi + ci zi *= 0.0 zi += new_zi @@ -48,9 +89,12 @@ $main = (...) => # NOTE: Using $print instead of print to avoid function call # overhead in this hot loop. Additionally, print # currently always adds a newline. - $print(if tr + ti <= 4 -> "*" else -> ".",) + if do_print -> + $print(if tr + ti <= 4 -> "*" else -> ".",) x += 1 - $print("\n",) + if do_print -> + $print("\n",) + y += 1 From a24dacdf0f882991d7dc2466bdbdf4ada83b0a85 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Mon, 30 Jan 2023 19:18:04 -0800 Subject: [PATCH 08/31] Fix inverted logic of JUMP_IF_NOT_NIL handling in VM --- feint-vm/src/vm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feint-vm/src/vm.rs b/feint-vm/src/vm.rs index e41447d..9bf5f0e 100644 --- a/feint-vm/src/vm.rs +++ b/feint-vm/src/vm.rs @@ -343,7 +343,7 @@ impl VM { self.exit_scopes(*scope_exit_count); let obj = self.peek_obj()?; let obj = obj.read().unwrap(); - if obj.is_nil() { + if !obj.is_nil() { if *forward { jump_ip = Some(ip + *addr); } else { From 98fcc157101cb50e127f0de68281dd974a7176f6 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Mon, 30 Jan 2023 19:35:17 -0800 Subject: [PATCH 09/31] Make Float.new() return err on bad input instead of panicking --- feint-builtins/src/types/new.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/feint-builtins/src/types/new.rs b/feint-builtins/src/types/new.rs index 5577c1b..bd30db0 100644 --- a/feint-builtins/src/types/new.rs +++ b/feint-builtins/src/types/new.rs @@ -216,10 +216,13 @@ pub fn float(value: f64) -> ObjectRef { obj_ref!(Float::new(value)) } -pub fn float_from_string>(value: S) -> ObjectRef { - let value = value.into(); - let value = value.parse::().unwrap(); - float(value) +pub fn float_from_string>(val: S) -> ObjectRef { + let val = val.into(); + if let Ok(val) = val.parse::() { + float(val) + } else { + type_err("Could not convert string to Float", str(val)) + } } pub fn func>( From 7bdc11777af7d8ddc7c923106ba600284395f9d6 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Mon, 30 Jan 2023 23:20:11 -0800 Subject: [PATCH 10/31] When jumping, only exit scopes if jump actually happens This applies to the various `JUMP_IF` instructions, which would always exit scopes even if the jump didn't happen. The reason this hasn't caused any problems is because currently all such are always compiled such that they don't exit any scopes. Thinking on this a bit, I'm not sure any of the jump instructions other than `JUMP` actually need to exit scopes, since it's only necessary to exit scopes when jumping *out* of a block and `JUMP` is the only instruction that does that currently. --- feint-vm/src/vm.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/feint-vm/src/vm.rs b/feint-vm/src/vm.rs index 9bf5f0e..ffcd14d 100644 --- a/feint-vm/src/vm.rs +++ b/feint-vm/src/vm.rs @@ -302,11 +302,11 @@ impl VM { } } JumpIf(addr, forward, scope_exit_count) => { - self.exit_scopes(*scope_exit_count); let obj = self.peek_obj()?; let obj = obj.read().unwrap(); if let Some(should_jump) = obj.bool_val() { if should_jump { + self.exit_scopes(*scope_exit_count); if *forward { jump_ip = Some(ip + *addr); } else { @@ -321,11 +321,11 @@ impl VM { } } JumpIfNot(addr, forward, scope_exit_count) => { - self.exit_scopes(*scope_exit_count); let obj = self.peek_obj()?; let obj = obj.read().unwrap(); if let Some(should_not_jump) = obj.bool_val() { if !should_not_jump { + self.exit_scopes(*scope_exit_count); if *forward { jump_ip = Some(ip + *addr); } else { @@ -340,10 +340,10 @@ impl VM { } } JumpIfNotNil(addr, forward, scope_exit_count) => { - self.exit_scopes(*scope_exit_count); let obj = self.peek_obj()?; let obj = obj.read().unwrap(); if !obj.is_nil() { + self.exit_scopes(*scope_exit_count); if *forward { jump_ip = Some(ip + *addr); } else { From d194b7739518f569596b710d373bc6321aef3e44 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Tue, 31 Jan 2023 15:22:20 -0800 Subject: [PATCH 11/31] Fix release-related aliases --- aliases | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/aliases b/aliases index 94d5382..f9eb135 100644 --- a/aliases +++ b/aliases @@ -1,5 +1,4 @@ set debug_binary ./target/debug/feint -set release_binary ./target/release/feint # build alias b="cargo build" @@ -27,9 +26,9 @@ alias d="cargo run -- --dis --debug" alias l="RUST_LOG=feint=trace cargo run --" # run release binary -alias rr="cargo build --release && $release_binary --" -alias cr="cargo build --release && $release_binary -- --code" -alias dr="cargo build --release && $release_binary -- --dis --debug" +alias rr="cargo run --release --" +alias cr="cargo run --release -- --code" +alias dr="cargo run --release -- --dis --debug" # profile alias p="cargo flamegraph --root --" From 5aa5d24df0f53a0f3567cd228aa22520f6258dbd Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Tue, 31 Jan 2023 15:28:19 -0800 Subject: [PATCH 12/31] Print specific driver errors in main() and REPL These errors aren't caught and handled by the driver, so we need to show them somewhere or it confusingly looks like no error occurred in certain cases when an error did occur. Amends 4a69cc3ee916 --- feint-cli/src/main.rs | 12 +++++++++++- feint-cli/src/repl.rs | 10 ++++++++++ feint-driver/src/lib.rs | 2 +- feint-driver/src/result.rs | 15 +++++++++------ 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/feint-cli/src/main.rs b/feint-cli/src/main.rs index b6e9ab6..9bbd8d9 100644 --- a/feint-cli/src/main.rs +++ b/feint-cli/src/main.rs @@ -11,7 +11,7 @@ use std::process::ExitCode; use clap::{parser::ValueSource, ArgMatches}; -use feint_driver::{Driver, DriverResult}; +use feint_driver::{Driver, DriverErrKind, DriverResult}; use feint_vm::VMState; use repl::Repl; @@ -210,6 +210,16 @@ fn handle_driver_result(result: DriverResult) -> u8 { if let Some(exit_code) = err.exit_code() { exit_code } else { + if matches!( + &err.kind, + DriverErrKind::Bootstrap(_) + | DriverErrKind::CouldNotReadSourceFile(_) + | DriverErrKind::ModuleDirNotFound(_) + | DriverErrKind::ModuleNotFound(_) + | DriverErrKind::ReplErr(_) + ) { + eprintln!("{err}"); + } 255 } } diff --git a/feint-cli/src/repl.rs b/feint-cli/src/repl.rs index b672ef8..bd5850a 100644 --- a/feint-cli/src/repl.rs +++ b/feint-cli/src/repl.rs @@ -114,6 +114,16 @@ impl Repl { // to add more lines *if* the error can potentially be // recovered from by adding more input. if !(continue_on_err && self.continue_on_err(&err)) { + if matches!( + &err.kind, + DriverErrKind::Bootstrap(_) + | DriverErrKind::CouldNotReadSourceFile(_) + | DriverErrKind::ModuleDirNotFound(_) + | DriverErrKind::ModuleNotFound(_) + | DriverErrKind::ReplErr(_) + ) { + eprintln!("{err}"); + } return None; } } diff --git a/feint-driver/src/lib.rs b/feint-driver/src/lib.rs index 303f3b8..a3d481f 100644 --- a/feint-driver/src/lib.rs +++ b/feint-driver/src/lib.rs @@ -2,7 +2,7 @@ pub mod driver; pub mod result; pub use driver::Driver; -pub use result::DriverResult; +pub use result::{DriverErr, DriverErrKind, DriverResult}; #[cfg(test)] mod tests; diff --git a/feint-driver/src/result.rs b/feint-driver/src/result.rs index 5714a05..e0c27a3 100644 --- a/feint-driver/src/result.rs +++ b/feint-driver/src/result.rs @@ -31,15 +31,18 @@ impl DriverErr { #[derive(Debug)] pub enum DriverErrKind { + // These errors are NOT handled by the driver. They should be + // handled by the user of the driver (e.g., in main, REPL). Bootstrap(String), + CouldNotReadSourceFile(String), ModuleDirNotFound(String), ModuleNotFound(String), - CouldNotReadSourceFile(String), + ReplErr(String), + // These errors ARE handled by the driver. ScanErr(ScanErrKind), ParseErr(ParseErrKind), CompErr(CompErrKind), RuntimeErr(RuntimeErrKind), - ReplErr(String), } impl fmt::Display for DriverErr { @@ -53,6 +56,9 @@ impl fmt::Display for DriverErrKind { use DriverErrKind::*; let msg = match self { Bootstrap(msg) => format!("Bootstrap process failed: {msg}"), + CouldNotReadSourceFile(file_name) => { + format!("Could not read source file: {file_name}") + } ModuleDirNotFound(path) => format!( concat!( "Module directory not found: {}\n", @@ -63,14 +69,11 @@ impl fmt::Display for DriverErrKind { ModuleNotFound(name) => { format!("Module not found: {name}") } - CouldNotReadSourceFile(file_name) => { - format!("Could not read source file: {file_name}") - } + ReplErr(msg) => format!("REPL error: {msg}"), ScanErr(kind) => format!("Scan error: {kind:?}"), ParseErr(kind) => format!("Parse error: {kind:?}"), CompErr(kind) => format!("Compilation error: {kind:?}"), RuntimeErr(kind) => format!("Runtime error: {kind:?}"), - ReplErr(msg) => format!("REPL error: {msg}"), }; write!(f, "{msg}") } From 4e641af8f28594a81789fb8cff6755e6125d20d4 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Tue, 31 Jan 2023 15:41:03 -0800 Subject: [PATCH 13/31] Add $debug keyword/statement type This is similar to $print, but it always prints to stderr, always includes a trailing newline, and is elided when debug mode is active. --- feint-compiler/src/ast/ast.rs | 6 +++++ feint-compiler/src/compiler/compiler.rs | 26 ++++++++++--------- feint-compiler/src/compiler/visitor.rs | 33 +++++++++++++++++++------ feint-compiler/src/parser/parser.rs | 20 ++++++++++++++- feint-compiler/src/scanner/keywords.rs | 1 + feint-compiler/src/scanner/token.rs | 6 +++-- feint-driver/src/driver.rs | 4 +-- 7 files changed, 72 insertions(+), 24 deletions(-) diff --git a/feint-compiler/src/ast/ast.rs b/feint-compiler/src/ast/ast.rs index ce4e2ac..8d8a709 100644 --- a/feint-compiler/src/ast/ast.rs +++ b/feint-compiler/src/ast/ast.rs @@ -49,6 +49,7 @@ pub enum StatementKind { Return(Expr), Halt(Expr), Print(Expr), + Debug(Expr), Expr(Expr), } @@ -94,6 +95,10 @@ impl Statement { Self::new(StatementKind::Print(expr), start, end) } + pub fn new_debug(expr: Expr, start: Location, end: Location) -> Self { + Self::new(StatementKind::Debug(expr), start, end) + } + pub fn new_expr(expr: Expr, start: Location, end: Location) -> Self { Self::new(StatementKind::Expr(expr), start, end) } @@ -132,6 +137,7 @@ impl fmt::Debug for StatementKind { Self::Return(expr) => write!(f, "return {expr:?}"), Self::Halt(expr) => write!(f, "$halt {expr:?}"), Self::Print(expr) => write!(f, "$print {expr:?}"), + Self::Debug(expr) => write!(f, "$debug {expr:?}"), Self::Expr(expr) => write!(f, "{expr:?}"), } } diff --git a/feint-compiler/src/compiler/compiler.rs b/feint-compiler/src/compiler/compiler.rs index 0ab91be..8092315 100644 --- a/feint-compiler/src/compiler/compiler.rs +++ b/feint-compiler/src/compiler/compiler.rs @@ -31,22 +31,21 @@ pub struct Compiler { // are known to exist but aren't available to the compiler (e.g., in // the REPL). global_names: HashSet, + debug: bool, } -impl Default for Compiler { - fn default() -> Self { +impl Compiler { + pub fn new(global_names: HashSet, debug: bool) -> Self { + Self { visitor_stack: Stack::new(), global_names, debug } + } + + pub fn for_module(debug: bool) -> Self { let mut global_names = HashSet::default(); global_names.insert("$full_name".to_owned()); global_names.insert("$name".to_owned()); global_names.insert("$path".to_owned()); global_names.insert("$doc".to_owned()); - Self::new(global_names) - } -} - -impl Compiler { - pub fn new(global_names: HashSet) -> Self { - Self { visitor_stack: Stack::new(), global_names } + Self::new(global_names, debug) } /// Compile AST module node to module object. @@ -66,8 +65,11 @@ impl Compiler { module_name: &str, module: ast::Module, ) -> Result { - let mut visitor = - CompilerVisitor::for_module(module_name, self.global_names.clone()); + let mut visitor = CompilerVisitor::for_module( + module_name, + self.global_names.clone(), + self.debug, + ); visitor.visit_module(module)?; self.global_names = self .global_names @@ -115,7 +117,7 @@ impl Compiler { let params = node.params.clone(); let mut visitor = - CompilerVisitor::for_func(func_name, self.global_names.clone()); + CompilerVisitor::for_func(func_name, self.global_names.clone(), self.debug); visitor.visit_func(node)?; // Unresolved names are assumed to be globals or builtins. diff --git a/feint-compiler/src/compiler/visitor.rs b/feint-compiler/src/compiler/visitor.rs index 752cfdb..56a0bb1 100644 --- a/feint-compiler/src/compiler/visitor.rs +++ b/feint-compiler/src/compiler/visitor.rs @@ -30,8 +30,9 @@ type FuncNode = ( /// construct a `Module` or `Func` from this `Code` object. pub struct CompilerVisitor { initial_scope_kind: ScopeKind, - global_names: HashSet, name: String, + global_names: HashSet, + debug: bool, pub(crate) code: Code, pub(crate) scope_tree: ScopeTree, pub(crate) scope_depth: usize, @@ -43,12 +44,14 @@ impl CompilerVisitor { initial_scope_kind: ScopeKind, name: &str, global_names: HashSet, + debug: bool, ) -> Self { assert!(matches!(initial_scope_kind, ScopeKind::Module | ScopeKind::Func)); Self { initial_scope_kind, name: name.to_owned(), global_names, + debug, code: Code::default(), scope_tree: ScopeTree::new(initial_scope_kind), scope_depth: 0, @@ -56,12 +59,20 @@ impl CompilerVisitor { } } - pub(crate) fn for_module(name: &str, global_names: HashSet) -> Self { - Self::new(ScopeKind::Module, name, global_names) + pub(crate) fn for_module( + name: &str, + global_names: HashSet, + debug: bool, + ) -> Self { + Self::new(ScopeKind::Module, name, global_names, debug) } - pub(crate) fn for_func(name: &str, global_names: HashSet) -> Self { - Self::new(ScopeKind::Func, name, global_names) + pub(crate) fn for_func( + name: &str, + global_names: HashSet, + debug: bool, + ) -> Self { + Self::new(ScopeKind::Func, name, global_names, debug) } // Entry Point Visitors -------------------------------------------- @@ -190,6 +201,7 @@ impl CompilerVisitor { Kind::Return(expr) => self.visit_return(expr)?, Kind::Halt(expr) => self.visit_halt(expr)?, Kind::Print(expr) => self.visit_print(expr)?, + Kind::Debug(expr) => self.visit_debug(expr)?, Kind::Expr(expr) => self.visit_expr(expr, None)?, } Ok(()) @@ -257,12 +269,19 @@ impl CompilerVisitor { } } let msg = concat!( - "$print expects to receive at least 1 arg and no more ", - "than 5 args: (object, ...flags)" + "$print expects to receive a Tuple with at least 1 arg ", + "and no more than 5 args: (object, ...flags)" ); Err(CompErr::print(msg, expr.start, expr.end)) } + fn visit_debug(&mut self, expr: ast::Expr) -> VisitResult { + if self.debug || cfg!(debug_assertions) { + self.visit_print(expr)?; + } + Ok(()) + } + fn visit_return(&mut self, expr: ast::Expr) -> VisitResult { self.visit_expr(expr, None)?; self.push(Inst::ReturnPlaceholder(self.len(), self.scope_depth)); diff --git a/feint-compiler/src/parser/parser.rs b/feint-compiler/src/parser/parser.rs index 9a08b34..d72bf5e 100644 --- a/feint-compiler/src/parser/parser.rs +++ b/feint-compiler/src/parser/parser.rs @@ -84,7 +84,8 @@ impl> Parser { log::trace!("BEGIN STATEMENT level {level}"); self.statement_level += 1; use Token::{ - Break, Continue, EndOfStatement, Halt, Import, Jump, Label, Print, Return, + Break, Continue, Debug, EndOfStatement, Halt, Import, Jump, Label, Print, + Return, }; let token = self.expect_next_token()?; let start = token.start; @@ -95,6 +96,7 @@ impl> Parser { Jump => self.jump(start)?, Label(name) => self.label(name, start)?, Return => self.return_(start)?, + Debug => self.debug(start)?, Halt => self.halt(start)?, Print => self.print(start)?, _ => { @@ -174,6 +176,22 @@ impl> Parser { Ok(ast::Statement::new_print(expr, start, end)) } + /// Handle `$debug`. + fn debug(&mut self, start: Location) -> StatementResult { + let expr = self.expr(0)?; + let end = expr.end; + let args = ast::Expr::new_tuple( + vec![ + expr, + ast::Expr::new_true(start, end), + ast::Expr::new_true(start, end), + ], + start, + end, + ); + Ok(ast::Statement::new_debug(args, start, end)) + } + /// Handle `import`. fn import(&mut self, start: Location) -> StatementResult { if self.func_level != 0 { diff --git a/feint-compiler/src/scanner/keywords.rs b/feint-compiler/src/scanner/keywords.rs index 6b1cabe..5dc1995 100644 --- a/feint-compiler/src/scanner/keywords.rs +++ b/feint-compiler/src/scanner/keywords.rs @@ -26,6 +26,7 @@ pub static KEYWORDS: Lazy> = Lazy::new(|| { ("return", Return), ("$halt", Halt), ("$print", Print), + ("$debug", Debug), ] .iter() .cloned() diff --git a/feint-compiler/src/scanner/token.rs b/feint-compiler/src/scanner/token.rs index b15c277..2dd465c 100644 --- a/feint-compiler/src/scanner/token.rs +++ b/feint-compiler/src/scanner/token.rs @@ -89,8 +89,9 @@ pub enum Token { Return, // return Jump, // jump label Label(String), // :label: - Halt, - Print, + Halt, // $halt + Print, // $print + Debug, // $debug // Import/export --------------------------------------------------- Import, // import @@ -197,6 +198,7 @@ impl Token { Self::Label(_name) => "label", Self::Halt => "$halt", Self::Print => "$print", + Self::Debug => "$debug", // Import/export --------------------------------------------------- Self::Import => "import", diff --git a/feint-driver/src/driver.rs b/feint-driver/src/driver.rs index 90fdbae..19e8fda 100644 --- a/feint-driver/src/driver.rs +++ b/feint-driver/src/driver.rs @@ -138,7 +138,7 @@ impl Driver { let source = &mut source_from_text(text); let ast_module = self.parse_source(source)?; - let mut compiler = Compiler::new(global_names); + let mut compiler = Compiler::new(global_names, self.debug); let comp_result = compiler.compile_module_to_code("$repl", ast_module); let mut code = comp_result.map_err(|err| { @@ -334,7 +334,7 @@ impl Driver { source: &mut Source, ) -> Result { let ast_module = self.parse_source(source)?; - let mut compiler = Compiler::default(); + let mut compiler = Compiler::for_module(self.debug); let module = compiler .compile_module(name, self.current_file_name.as_str(), ast_module) .map_err(|err| { From 7fe833132e49e8a91e3788a6cd1f7beb71d89219 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Tue, 31 Jan 2023 15:48:56 -0800 Subject: [PATCH 14/31] Fix builtin type() so it returns object rather than printing it --- feint-builtins/src/modules/std/std.fi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feint-builtins/src/modules/std/std.fi b/feint-builtins/src/modules/std/std.fi index c532cad..b0e77a4 100644 --- a/feint-builtins/src/modules/std/std.fi +++ b/feint-builtins/src/modules/std/std.fi @@ -65,7 +65,7 @@ print_err = (...) => type: Type = (obj: Any) => "Get the type of an object." - print(obj.$type) + obj.$type id = (obj: Any) => From 99ac6a6f7770cfa9fe2b9375423c7763f2dd11ef Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Tue, 31 Jan 2023 15:50:45 -0800 Subject: [PATCH 15/31] Improve string representations of function types Only include the ID, binding, etc in the Debug representation. In the regular representation, just show name and arity. --- feint-builtins/src/types/bound_func.rs | 14 +++++++------- feint-builtins/src/types/closure.rs | 2 +- feint-builtins/src/types/func.rs | 4 ++-- feint-builtins/src/types/intrinsic_func.rs | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/feint-builtins/src/types/bound_func.rs b/feint-builtins/src/types/bound_func.rs index 721c817..e203cd8 100644 --- a/feint-builtins/src/types/bound_func.rs +++ b/feint-builtins/src/types/bound_func.rs @@ -113,17 +113,17 @@ impl ObjectTrait for BoundFunc { impl fmt::Display for BoundFunc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{} *BOUND* to {:?}", - self.func.read().unwrap(), - &*self.this.read().unwrap() - ) + write!(f, "{}", self.func.read().unwrap()) } } impl fmt::Debug for BoundFunc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{self}") + write!( + f, + "{:?} *BOUND* to {:?}", + &*self.func.read().unwrap(), + &*self.this.read().unwrap() + ) } } diff --git a/feint-builtins/src/types/closure.rs b/feint-builtins/src/types/closure.rs index 712389b..e56e127 100644 --- a/feint-builtins/src/types/closure.rs +++ b/feint-builtins/src/types/closure.rs @@ -99,6 +99,6 @@ impl fmt::Display for Closure { impl fmt::Debug for Closure { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{self}") + write!(f, "[closure] {:?}", &*self.func.read().unwrap()) } } diff --git a/feint-builtins/src/types/func.rs b/feint-builtins/src/types/func.rs index a723261..330ad98 100644 --- a/feint-builtins/src/types/func.rs +++ b/feint-builtins/src/types/func.rs @@ -114,12 +114,12 @@ impl ObjectTrait for Func { impl fmt::Display for Func { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", FuncTrait::format_string(self, Some(self.id()))) + write!(f, "{}", FuncTrait::format_string(self, None)) } } impl fmt::Debug for Func { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{self}") + write!(f, "{}", FuncTrait::format_string(self, Some(self.id()))) } } diff --git a/feint-builtins/src/types/intrinsic_func.rs b/feint-builtins/src/types/intrinsic_func.rs index 71eeb50..b4f956f 100644 --- a/feint-builtins/src/types/intrinsic_func.rs +++ b/feint-builtins/src/types/intrinsic_func.rs @@ -107,12 +107,12 @@ impl ObjectTrait for IntrinsicFunc { impl fmt::Display for IntrinsicFunc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", FuncTrait::format_string(self, Some(self.id()))) + write!(f, "{}", FuncTrait::format_string(self, None)) } } impl fmt::Debug for IntrinsicFunc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{self}") + write!(f, "{}", FuncTrait::format_string(self, Some(self.id()))) } } From 2e81740a1f1d52377fc3aadd98246a48d7eed479 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Tue, 31 Jan 2023 16:15:52 -0800 Subject: [PATCH 16/31] Add $params, $arity, $has_var_args attrs to function types Also, simplify how the bound func is handled in `BoundFunc` by utilizing `FuncTrait`. These attributes are intended to be used for inspection in FeInt code. --- feint-builtins/src/types/bound_func.rs | 49 +++++++++++++--------- feint-builtins/src/types/closure.rs | 7 +++- feint-builtins/src/types/func.rs | 14 ++++++- feint-builtins/src/types/func_trait.rs | 15 +++++-- feint-builtins/src/types/intrinsic_func.rs | 14 ++++++- 5 files changed, 71 insertions(+), 28 deletions(-) diff --git a/feint-builtins/src/types/bound_func.rs b/feint-builtins/src/types/bound_func.rs index e203cd8..6fb8b46 100644 --- a/feint-builtins/src/types/bound_func.rs +++ b/feint-builtins/src/types/bound_func.rs @@ -35,35 +35,44 @@ pub struct BoundFunc { standard_object_impls!(BoundFunc); impl BoundFunc { - pub fn new(func: ObjectRef, this: ObjectRef) -> Self { - let f = func.read().unwrap(); - - let (module_name, name, params, doc) = - if let Some(f) = f.down_to_intrinsic_func() { - (f.module_name(), f.name(), f.params(), f.get_doc()) - } else if let Some(f) = f.down_to_func() { - (f.module_name(), f.name(), f.params(), f.get_doc()) - } else if let Some(f) = f.down_to_closure() { - (f.module_name(), f.name(), f.params(), f.get_doc()) - } else { - panic!("Unexpected bound func type: {f}") - }; - - let module_name = module_name.to_owned(); - let name = name.to_owned(); - let params = params.clone(); - - drop(f); + pub fn new(func_ref: ObjectRef, this: ObjectRef) -> Self { + let (module_name, name, doc, params, params_tuple, arity, has_var_args) = { + let func_guard = func_ref.read().unwrap(); + + let func: &dyn FuncTrait = + if let Some(func) = func_guard.down_to_intrinsic_func() { + func + } else if let Some(func) = func_guard.down_to_func() { + func + } else if let Some(func) = func_guard.down_to_closure() { + func + } else { + panic!("Unexpected bound func type: {func_guard}") + }; + + ( + func.module_name().to_owned(), + func.name().to_owned(), + func.get_doc(), + func.params().clone(), + func.get_params(), + func.arity(), + func.has_var_args(), + ) + }; Self { ns: Namespace::with_entries(&[ ("$module_name", new::str(&module_name)), ("$full_name", new::str(format!("{module_name}.{name}"))), ("$name", new::str(&name)), + ("$params", params_tuple), ("$doc", doc), + ("$arity", new::int(arity)), + ("$has_var_args", new::bool(has_var_args)), ]), module_name, - func, + func: func_ref, this, name, params, diff --git a/feint-builtins/src/types/closure.rs b/feint-builtins/src/types/closure.rs index e56e127..3c1bf36 100644 --- a/feint-builtins/src/types/closure.rs +++ b/feint-builtins/src/types/closure.rs @@ -38,7 +38,12 @@ impl Closure { let func = func_ref.read().unwrap(); let func = func.down_to_func().unwrap(); Self { - ns: Namespace::with_entries(&[("$doc", func.get_doc())]), + ns: Namespace::with_entries(&[ + ("$params", func.get_params()), + ("$doc", func.get_doc()), + ("$arity", new::int(func.arity())), + ("$has_var_args", new::bool(func.has_var_args())), + ]), module_name: func.module_name().to_owned(), name: func.name().to_owned(), params: func.params().clone(), diff --git a/feint-builtins/src/types/func.rs b/feint-builtins/src/types/func.rs index 330ad98..48b4e4c 100644 --- a/feint-builtins/src/types/func.rs +++ b/feint-builtins/src/types/func.rs @@ -37,12 +37,15 @@ standard_object_impls!(Func); impl Func { pub fn new(module_name: String, name: String, params: Params, code: Code) -> Self { - Self { + let params_tuple = new::tuple(params.iter().map(new::str).collect()); + + let mut instance = Self { ns: Namespace::with_entries(&[ // Instance Attributes ("$module_name", new::str(&module_name)), ("$full_name", new::str(format!("{module_name}.{name}"))), ("$name", new::str(&name)), + ("$params", params_tuple), ("$doc", code.get_doc()), ]), module_name, @@ -50,7 +53,14 @@ impl Func { name, params, code, - } + }; + + let arity = (&instance as &dyn FuncTrait).arity(); + let has_var_args = (&instance as &dyn FuncTrait).has_var_args(); + instance.ns_mut().insert("$arity", new::int(arity)); + instance.ns_mut().insert("$has_var_args", new::bool(has_var_args)); + + instance } pub fn arg_names(&self) -> Vec<&str> { diff --git a/feint-builtins/src/types/func_trait.rs b/feint-builtins/src/types/func_trait.rs index 307f701..5119ada 100644 --- a/feint-builtins/src/types/func_trait.rs +++ b/feint-builtins/src/types/func_trait.rs @@ -13,8 +13,18 @@ pub trait FuncTrait { fn name(&self) -> &String; fn params(&self) -> &Params; + fn get_params(&self) -> ObjectRef { + self.ns() + .get("$params") + .expect("Function type should have a $params attribute") + .clone() + } + fn get_doc(&self) -> ObjectRef { - self.ns().get("$doc").unwrap().clone() + self.ns() + .get("$doc") + .expect("Function type should have a $doc attribute") + .clone() } /// Returns the required number of args. @@ -34,8 +44,7 @@ pub trait FuncTrait { } /// If the function has var args, this returns the index of the var - /// args in the args list (which is also equal to the required - /// number of args). + /// args in the args list. fn var_args_index(&self) -> Option { let params = self.params(); if let Some(name) = params.last() { diff --git a/feint-builtins/src/types/intrinsic_func.rs b/feint-builtins/src/types/intrinsic_func.rs index b4f956f..13a8f5f 100644 --- a/feint-builtins/src/types/intrinsic_func.rs +++ b/feint-builtins/src/types/intrinsic_func.rs @@ -47,12 +47,15 @@ impl IntrinsicFunc { doc: ObjectRef, func: IntrinsicFn, ) -> Self { - Self { + let params_tuple = new::tuple(params.iter().map(new::str).collect()); + + let mut instance = Self { ns: Namespace::with_entries(&[ // Instance Attributes ("$module_name", new::str(module_name.as_str())), ("$full_name", new::str(format!("{module_name}.{name}"))), ("$name", new::str(name.as_str())), + ("$params", params_tuple), ("$doc", doc), ]), module_name, @@ -61,7 +64,14 @@ impl IntrinsicFunc { this_type, params, func, - } + }; + + let arity = (&instance as &dyn FuncTrait).arity(); + let has_var_args = (&instance as &dyn FuncTrait).has_var_args(); + instance.ns_mut().insert("$arity", new::int(arity)); + instance.ns_mut().insert("$has_var_args", new::bool(has_var_args)); + + instance } pub fn this_type(&self) -> Option { From c8ee3091a2b26739eb1be7296b6b05006f9e6b57 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Tue, 31 Jan 2023 15:53:25 -0800 Subject: [PATCH 17/31] Reimplement each and map methods in FeInt This sidesteps the issue noted in 9aeb81691ecd regarding intrinsic functions no longer having access to the VM. Additionally, it's neat to implement more of FeInt in itself and it's good dog food. --- feint-builtins/src/modules/std/args.fi | 2 +- feint-builtins/src/modules/std/list.fi | 82 +++++++++++++++++++++++++ feint-builtins/src/modules/std/map.fi | 56 +++++++++++++++++ feint-builtins/src/modules/std/std.fi | 18 ++++++ feint-builtins/src/modules/std/tuple.fi | 82 +++++++++++++++++++++++++ feint-builtins/src/types/list.rs | 43 ++++--------- feint-builtins/src/types/map.rs | 68 +++----------------- feint-builtins/src/types/mod.rs | 54 ++++++++-------- feint-builtins/src/types/seq.rs | 62 ------------------- feint-builtins/src/types/tuple.rs | 31 +--------- feint-driver/src/driver.rs | 28 ++++++++- 11 files changed, 315 insertions(+), 211 deletions(-) create mode 100644 feint-builtins/src/modules/std/list.fi create mode 100644 feint-builtins/src/modules/std/map.fi create mode 100644 feint-builtins/src/modules/std/tuple.fi diff --git a/feint-builtins/src/modules/std/args.fi b/feint-builtins/src/modules/std/args.fi index a8c4932..ea0b995 100644 --- a/feint-builtins/src/modules/std/args.fi +++ b/feint-builtins/src/modules/std/args.fi @@ -151,6 +151,6 @@ parse: Map = ( result else -> ess = if errors.length == 1 -> "" else -> "s" - print_err($"{errors.length} error{ess} encountered while parsing args:\n") + print_err($"{errors.length} error{ess} encountered while parsing args\n") errors.each((name, err) => print_err($"{name}: {err.message}")) Err.new(ErrType.arg, "") diff --git a/feint-builtins/src/modules/std/list.fi b/feint-builtins/src/modules/std/list.fi new file mode 100644 index 0000000..26284f0 --- /dev/null +++ b/feint-builtins/src/modules/std/list.fi @@ -0,0 +1,82 @@ +# Method: List.each() +each = (fn: Func) => + "Apply function to each List item. + + # Args + + - fn: Func + A function that will be passed each item in turn and the index of + the item. + + # Returns + + If all calls to `fn` are successful, `nil` is returned. If a call is + unsuccessful, the `Err` from that call is returned. + + " + meth = List.each + + result = check_arg_type(meth, "this", this, (List,)) + if result.err -> + return result + + result = check_arg_type(meth, "fn", fn, (Func, IntrinsicFunc)) + if result.err -> + return result + + n_args = match fn.$arity -> + 0 -> return Err.new(ErrType.arg, $"fn of {meth} should take at least 1 arg") + 1 -> 1 + * -> 2 + + if this.is_empty -> + return nil + + i = 0 + loop i < this.length -> + result = if n_args == 1 -> fn(this.i) else -> fn(this.i, i) + if result.err -> + return result + i += 1 + + +# Method: List.map() +map: List = (fn: Func) => + "Apply function to each List item and collect results. + + # Args + + - fn: Func + A function that will be passed each item in turn and the index of + the item. + + # Returns + + A list containing the result of each call, which could be an `Err`. + + " + meth = List.map + + result = check_arg_type(meth, "this", this, (List,)) + if result.err -> + return result + + result = check_arg_type(meth, "fn", fn, (Func, IntrinsicFunc)) + if result.err -> + return result + + results = [] + + if this.is_empty -> + return results + + n_args = match fn.$arity -> + 0 -> return Err.new(ErrType.arg, $"fn of {meth} should take at least 1 arg") + 1 -> 1 + * -> 2 + + i = 0 + loop i < this.length -> + result = if n_args == 1 -> fn(this.i) else -> fn(this.i, i) + result.push(result) + i += 1 diff --git a/feint-builtins/src/modules/std/map.fi b/feint-builtins/src/modules/std/map.fi new file mode 100644 index 0000000..00d664e --- /dev/null +++ b/feint-builtins/src/modules/std/map.fi @@ -0,0 +1,56 @@ +# Method: Map.each() +each = (fn: Func) => + "Apply function to each Map item. + + # Args + + - fn: Func + A function that will be passed the key and value of each entry in + turn. An index is also passed. + + # Returns + + If all calls to `fn` are successful, `nil` is returned. If a call is + unsuccessful, the `Err` from that call is returned. + + ``` + → map = {'a': 'a', 'b': 'b'} + {'a' => 'a', 'b' => 'b'} + → fn = (k, v, i) => print($'{i + 1}. {k} = {v}') + function fn/2 @ + → map.each(fn) + 1. a = a + 2. b = b + ``` + + " + meth = Map.each + + result = check_arg_type(meth, "this", this, (Map,)) + if result.err -> + return result + + result = check_arg_type(meth, "fn", fn, (Func, IntrinsicFunc)) + if result.err -> + return result + + if this.is_empty -> + return nil + + n_args = match fn.$arity -> + 0 -> return Err.new(ErrType.arg, $"fn of {meth} should take at least 1 arg") + 1 -> 1 + 2 -> 2 + * -> 3 + + i = 0 + it = this.iter() + loop (entry = it.next()) $! nil -> + key = entry.0 + result = match n_args -> + 1 -> fn(key) + 2 -> fn(key, entry.1) + 3 -> fn(key, entry.1, i) + if result.err -> + return result + i += 1 diff --git a/feint-builtins/src/modules/std/std.fi b/feint-builtins/src/modules/std/std.fi index b0e77a4..26a5019 100644 --- a/feint-builtins/src/modules/std/std.fi +++ b/feint-builtins/src/modules/std/std.fi @@ -123,6 +123,7 @@ assert: Bool | Err = (condition: Bool, ...) => err + assert_eq: Bool | Err = (a: Any, b: Any, ...) => "Check if items are equal and return error if not. @@ -151,3 +152,20 @@ assert_eq: Bool | Err = (a: Any, b: Any, ...) => $halt 1 err + + +# NOTE: This is used for manual type-checking of function args, which +# isn't great. +check_arg_type = (func: Func, name: Str, arg: Any, expected_types: Tuple) => + i = 0 + loop i < expected_types.length -> + if arg.$type $$ expected_types.i -> + return nil + i += 1 + + err = Err.new( + ErrType.arg, + $"{name} of {func} expected type {expected_types.0.$name}; got {arg.$type.$name}", + ) + $debug err + err diff --git a/feint-builtins/src/modules/std/tuple.fi b/feint-builtins/src/modules/std/tuple.fi new file mode 100644 index 0000000..9b52945 --- /dev/null +++ b/feint-builtins/src/modules/std/tuple.fi @@ -0,0 +1,82 @@ +# Method: Tuple.each() +each = (fn: Func) => + "Apply function to each Tuple item. + + # Args + + - fn: Func + A function that will be passed each item in turn and the index of + the item. + + # Returns + + If all calls to `fn` are successful, `nil` is returned. If a call is + unsuccessful, the `Err` from that call is returned. + + " + meth = Tuple.each + + result = check_arg_type(meth, "this", this, (Tuple,)) + if result.err -> + return result + + result = check_arg_type(meth, "fn", fn, (Func, IntrinsicFunc)) + if result.err -> + return result + + n_args = match fn.$arity -> + 0 -> return Err.new(ErrType.arg, $"fn of {meth} should take at least 1 arg") + 1 -> 1 + * -> 2 + + if this.is_empty -> + return nil + + i = 0 + loop i < this.length -> + result = if n_args == 1 -> fn(this.i) else -> fn(this.i, i) + if result.err -> + return result + i += 1 + + +# Method: Tuple.map() +map: List = (fn: Func) => + "Apply function to each Tuple item and collect results. + + # Args + + - fn: Func + A function that will be passed each item in turn and the index of + the item. + + # Returns + + A list containing the result of each call, which could be an `Err`. + + " + meth = Tuple.map + + result = check_arg_type(meth, "this", this, (Tuple,)) + if result.err -> + return result + + result = check_arg_type(meth, "fn", fn, (Func, IntrinsicFunc)) + if result.err -> + return result + + results = [] + + if this.is_empty -> + return results + + n_args = match fn.$arity -> + 0 -> return Err.new(ErrType.arg, $"fn of {meth} should take at least 1 arg") + 1 -> 1 + * -> 2 + + i = 0 + loop i < this.length -> + result = if n_args == 1 -> fn(this.i) else -> fn(this.i, i) + result.push(result) + i += 1 diff --git a/feint-builtins/src/types/list.rs b/feint-builtins/src/types/list.rs index 9de7f2f..87198f2 100644 --- a/feint-builtins/src/types/list.rs +++ b/feint-builtins/src/types/list.rs @@ -40,27 +40,6 @@ pub static LIST_TYPE: Lazy = Lazy::new(|| { seq::sum(items) }), // Instance Methods -------------------------------------------- - meth!( - "each", - type_ref, - &["each_fn"], - "Apply function to each List item. - - # Args - - - func: Func - - A function that will be passed each item in turn and, optionally, the - index of the item. - - ", - |this_obj, args| { - let this = this_obj.read().unwrap(); - let this = this.down_to_list().unwrap(); - let items = &this.items.read().unwrap(); - seq::each(&this_obj, items, &args) - } - ), meth!( "extend", type_ref, @@ -92,18 +71,18 @@ pub static LIST_TYPE: Lazy = Lazy::new(|| { let items = &this.items.read().unwrap(); seq::has(items, &args) }), + meth!("iter", type_ref, &[], "", |this_ref, _| { + let this = this_ref.read().unwrap(); + let this = this.down_to_list().unwrap(); + let items = this.items.read().unwrap(); + new::iterator(items.clone()) + }), meth!("join", type_ref, &["sep"], "", |this, args| { let this = this.read().unwrap(); let this = this.down_to_list().unwrap(); let items = &this.items.read().unwrap(); seq::join(items, &args) }), - meth!("map", type_ref, &["map_fn"], "", |this_obj, args| { - let this = this_obj.read().unwrap(); - let this = this.down_to_list().unwrap(); - let items = &this.items.read().unwrap(); - seq::map(&this_obj, items, &args) - }), meth!("pop", type_ref, &[], "", |this, _| { let this = this.read().unwrap(); let this = this.down_to_list().unwrap(); @@ -138,17 +117,17 @@ impl List { Self { ns: Namespace::default(), items: RwLock::new(items) } } - pub fn len(&self) -> usize { + fn len(&self) -> usize { let items = self.items.read().unwrap(); items.len() } - pub fn push(&self, item: ObjectRef) { + fn push(&self, item: ObjectRef) { let items = &mut self.items.write().unwrap(); items.push(item); } - pub fn extend(&self, obj_ref: ObjectRef) -> Option { + fn extend(&self, obj_ref: ObjectRef) -> Option { let obj = obj_ref.read().unwrap(); let items = &mut self.items.write().unwrap(); if let Some(list) = obj.down_to_list() { @@ -171,7 +150,7 @@ impl List { None } - pub fn pop(&self) -> Option { + fn pop(&self) -> Option { let items = &mut self.items.write().unwrap(); if let Some(item) = items.pop() { Some(item.clone()) @@ -180,7 +159,7 @@ impl List { } } - pub fn get(&self, index: usize) -> Option { + fn get(&self, index: usize) -> Option { let items = self.items.read().unwrap(); if let Some(item) = items.get(index) { Some(item.clone()) diff --git a/feint-builtins/src/types/map.rs b/feint-builtins/src/types/map.rs index 7571734..a4a7c08 100644 --- a/feint-builtins/src/types/map.rs +++ b/feint-builtins/src/types/map.rs @@ -55,65 +55,6 @@ pub static MAP_TYPE: Lazy = Lazy::new(|| { new::nil() } ), - meth!( - "each", - type_ref, - &["each_fn"], - "Apply function to each Map entry. - - # Args - - - func: Func - - A function that will be passed the key and value of each entry in - turn. - - ``` - → map = {'a': 'a', 'b': 'b'} - {'a' => 'a', 'b' => 'b'} - → fn = (k, v) => print($'{k} = {v}') - function fn/2 @ - → map.each(fn) - a = a - b = b - ``` - - ", - |this_obj, args| { - let this = this_obj.read().unwrap(); - let this = this.down_to_map().unwrap(); - let entries = &this.entries.read().unwrap(); - - if entries.is_empty() { - return new::nil(); - } - - let each_fn = &args[0]; - let n_args = if let Some(f) = each_fn.read().unwrap().as_func() { - if f.has_var_args() { - 3 - } else { - f.arity() - } - } else { - return new::arg_err("each/1 expects a function", this_obj.clone()); - }; - - // for (i, (key, val)) in entries.iter().enumerate() { - // let each = each_fn.clone(); - // let key = new::str(key); - // if n_args == 1 { - // vm.call(each, vec![key])?; - // } else if n_args == 2 { - // vm.call(each, vec![key, val.clone()])?; - // } else { - // vm.call(each, vec![key, val.clone(), new::int(i)])?; - // } - // } - - new::nil() - } - ), meth!( "get", type_ref, @@ -153,6 +94,15 @@ pub static MAP_TYPE: Lazy = Lazy::new(|| { let result = this.contains_key(key); new::bool(result) }), + meth!("iter", type_ref, &[], "", |this_ref, _| { + let this = this_ref.read().unwrap(); + let this = this.down_to_map().unwrap(); + let mut items = vec![]; + for (name, val) in this.entries.read().unwrap().iter() { + items.push(new::tuple(vec![new::str(name), val.clone()])) + } + new::iterator(items) + }), ]); type_ref.clone() diff --git a/feint-builtins/src/types/mod.rs b/feint-builtins/src/types/mod.rs index 6d925f0..14b17c1 100644 --- a/feint-builtins/src/types/mod.rs +++ b/feint-builtins/src/types/mod.rs @@ -1,6 +1,3 @@ -pub mod code; -pub mod new; - pub use base::{ObjectRef, ObjectTrait}; pub use func::Func; pub use func_trait::FuncTrait; @@ -19,28 +16,31 @@ mod func_trait; // Namespace (not a type) pub(crate) mod ns; +pub mod code; +pub mod new; + // Intrinsic Types -pub(crate) mod always; -pub(crate) mod bool; -pub(crate) mod bound_func; -pub(crate) mod cell; -pub(crate) mod class; -pub(crate) mod closure; -pub(crate) mod custom; -pub(crate) mod err; -pub(crate) mod err_type; -pub(crate) mod file; -pub(crate) mod float; -pub(crate) mod func; -pub(crate) mod int; -pub(crate) mod intrinsic_func; -pub(crate) mod iterator; -pub(crate) mod list; -pub(crate) mod map; -pub(crate) mod module; -pub(crate) mod nil; -pub(crate) mod prop; -pub(crate) mod seq; -pub(crate) mod str; -pub(crate) mod tuple; -pub(crate) mod util; +pub mod always; +pub mod bool; +pub mod bound_func; +pub mod cell; +pub mod class; +pub mod closure; +pub mod custom; +pub mod err; +pub mod err_type; +pub mod file; +pub mod float; +pub mod func; +pub mod int; +pub mod intrinsic_func; +pub mod iterator; +pub mod list; +pub mod map; +pub mod module; +pub mod nil; +pub mod prop; +pub mod seq; +pub mod str; +pub mod tuple; +pub mod util; diff --git a/feint-builtins/src/types/seq.rs b/feint-builtins/src/types/seq.rs index ed47696..20157a8 100644 --- a/feint-builtins/src/types/seq.rs +++ b/feint-builtins/src/types/seq.rs @@ -7,36 +7,6 @@ use feint_code_gen::{use_arg, use_arg_str}; use super::base::ObjectRef; use super::{new, Args}; -pub fn each(this: &ObjectRef, items: &[ObjectRef], args: &Args) -> ObjectRef { - if items.is_empty() { - return new::nil(); - } - - let each_fn = &args[0]; - let f = each_fn.read().unwrap(); - let n_args = if let Some(f) = f.as_func() { - if f.has_var_args() { - 2 - } else { - f.arity() - } - } else { - return new::arg_err("each/1 expects a function", this.clone()); - }; - - // for (i, item) in items.iter().enumerate() { - // let each = each_fn.clone(); - // let item = item.clone(); - // if n_args == 1 { - // vm.call(each, vec![item])?; - // } else { - // vm.call(each, vec![item, new::int(i)])?; - // } - // } - - new::nil() -} - pub fn has(items: &[ObjectRef], args: &Args) -> ObjectRef { if items.is_empty() { return new::bool(false); @@ -76,38 +46,6 @@ pub fn join(items: &[ObjectRef], args: &Args) -> ObjectRef { new::str(string) } -pub fn map(this: &ObjectRef, items: &[ObjectRef], args: &Args) -> ObjectRef { - if items.is_empty() { - return new::empty_tuple(); - } - - let map_fn = &args[0]; - let f = map_fn.read().unwrap(); - let n_args = if let Some(f) = f.as_func() { - if f.has_var_args() { - 2 - } else { - f.arity() - } - } else { - return new::arg_err("map/1 expects a function", this.clone()); - }; - - let mut results = vec![]; - // for (i, item) in items.iter().enumerate() { - // let map = map_fn.clone(); - // let item = item.clone(); - // if n_args == 1 { - // vm.call(map, vec![item])?; - // } else { - // vm.call(map, vec![item, new::int(i)])?; - // } - // results.push(vm.pop_obj()?); - // } - - new::tuple(results) -} - pub fn sum(items: &[ObjectRef]) -> ObjectRef { let mut sum = new::int(BigInt::from(0)); for item in items.iter() { diff --git a/feint-builtins/src/types/tuple.rs b/feint-builtins/src/types/tuple.rs index 8458523..8297b67 100644 --- a/feint-builtins/src/types/tuple.rs +++ b/feint-builtins/src/types/tuple.rs @@ -40,26 +40,6 @@ pub static TUPLE_TYPE: Lazy = Lazy::new(|| { seq::sum(&this.items) }), // Instance Methods -------------------------------------------- - meth!( - "each", - type_ref, - &["each_fn"], - "Apply function to each Tuple item. - - # Args - - - func: Func - - A function that will be passed each item in turn and, optionally, the - index of the item. - - ", - |this_obj, args| { - let this = this_obj.read().unwrap(); - let this = this.down_to_tuple().unwrap(); - seq::each(&this_obj, &this.items, &args) - } - ), meth!("get", type_ref, &["index"], "", |this, args| { let this = this.read().unwrap(); let this = this.down_to_tuple().unwrap(); @@ -84,11 +64,6 @@ pub static TUPLE_TYPE: Lazy = Lazy::new(|| { let this = this.down_to_tuple().unwrap(); seq::join(&this.items, &args) }), - meth!("map", type_ref, &["map_fn"], "", |this_obj, args| { - let this = this_obj.read().unwrap(); - let this = this.down_to_tuple().unwrap(); - seq::map(&this_obj, &this.items, &args) - }), ]); type_ref.clone() @@ -108,15 +83,15 @@ impl Tuple { Self { ns: Namespace::default(), items } } - pub fn iter(&self) -> Iter<'_, ObjectRef> { + pub(crate) fn iter(&self) -> Iter<'_, ObjectRef> { self.items.iter() } - pub fn len(&self) -> usize { + pub(crate) fn len(&self) -> usize { self.items.len() } - pub fn get(&self, index: usize) -> Option { + fn get(&self, index: usize) -> Option { if let Some(item) = self.items.get(index) { Some(item.clone()) } else { diff --git a/feint-driver/src/driver.rs b/feint-driver/src/driver.rs index 19e8fda..4a5b205 100644 --- a/feint-driver/src/driver.rs +++ b/feint-driver/src/driver.rs @@ -8,8 +8,11 @@ use std::sync::{Arc, RwLock}; use feint_builtins::modules::{ add_module, maybe_get_module, std as stdlib, MODULES, STD, STD_FI_MODULES, }; -use feint_builtins::types::code::{Inst, PrintFlags}; -use feint_builtins::types::{new, Module, ObjectRef, ObjectTrait}; +use feint_builtins::types::{ + self, + code::{Inst, PrintFlags}, + new, Module, ObjectRef, ObjectTrait, +}; use feint_code_gen::obj_ref; use feint_compiler::{ ast, CompErr, CompErrKind, Compiler, ParseErr, ParseErrKind, Parser, ScanErr, @@ -96,6 +99,10 @@ impl Driver { self.add_module("std.proc", stdlib::PROC.clone()); + self.extend_intrinsic_type(types::list::LIST_TYPE.clone(), "std.list")?; + self.extend_intrinsic_type(types::map::MAP_TYPE.clone(), "std.map")?; + self.extend_intrinsic_type(types::tuple::TUPLE_TYPE.clone(), "std.tuple")?; + Ok(()) } @@ -116,6 +123,23 @@ impl Driver { Ok(()) } + /// Extend intrinsic type with global objects from corresponding + /// FeInt module. + fn extend_intrinsic_type( + &mut self, + type_ref: ObjectRef, + module_name: &str, + ) -> Result<(), DriverErr> { + let mut ty = type_ref.write().unwrap(); + let fi_module = self.get_or_add_module(module_name)?; + let fi_module = fi_module.read().unwrap(); + let fi_module = fi_module.down_to_mod().unwrap(); + for (name, val) in fi_module.iter_globals() { + ty.ns_mut().insert(name, val.clone()); + } + Ok(()) + } + // Execute --------------------------------------------------------- /// Execute text entered in REPL. REPL execution is different from From c4615846c864087799b799d1d678719124d7a609 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Tue, 31 Jan 2023 16:51:06 -0800 Subject: [PATCH 18/31] Show program name when displaying errors in std.args.parse() --- feint-builtins/src/modules/std/args.fi | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/feint-builtins/src/modules/std/args.fi b/feint-builtins/src/modules/std/args.fi index ea0b995..413f039 100644 --- a/feint-builtins/src/modules/std/args.fi +++ b/feint-builtins/src/modules/std/args.fi @@ -151,6 +151,8 @@ parse: Map = ( result else -> ess = if errors.length == 1 -> "" else -> "s" - print_err($"{errors.length} error{ess} encountered while parsing args\n") + print_err( + $"{program_name}: {errors.length} error{ess} encountered while parsing args\n" + ) errors.each((name, err) => print_err($"{name}: {err.message}")) Err.new(ErrType.arg, "") From eb31f75d7a091481bf62d21bdc423922fe180e29 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Tue, 31 Jan 2023 16:51:41 -0800 Subject: [PATCH 19/31] Tweak Mandlebrot example --- examples/mandlebrot.fi | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/mandlebrot.fi b/examples/mandlebrot.fi index ab2eae4..29af1b9 100644 --- a/examples/mandlebrot.fi +++ b/examples/mandlebrot.fi @@ -13,9 +13,10 @@ import std.args import std.system -die = (code, msg) => - print_err(msg) - system.exit(code, msg) +die = (code, ...) => + if $args.length -> + print_err($args.join(" ")) + system.exit(code) $main = (...) => @@ -44,7 +45,7 @@ $main = (...) => }) if args.err -> - die(1, args.err) + die(1) if args $$ nil -> system.exit(0) From de2e120c76055b511686bd34c434e4c446b6dda4 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Thu, 2 Feb 2023 19:15:28 -0800 Subject: [PATCH 20/31] builtins: Move some things around in new mod --- feint-builtins/src/modules/std/proc.rs | 2 +- feint-builtins/src/modules/std/std.rs | 2 +- feint-builtins/src/tests/types.rs | 4 +- feint-builtins/src/types/new.rs | 64 +++++++++++++------------- feint-cli/src/repl.rs | 2 +- 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/feint-builtins/src/modules/std/proc.rs b/feint-builtins/src/modules/std/proc.rs index 40dd830..d398d41 100644 --- a/feint-builtins/src/modules/std/proc.rs +++ b/feint-builtins/src/modules/std/proc.rs @@ -8,7 +8,7 @@ use feint_code_gen::obj_ref_t; use crate::types::{new, Module}; pub static PROC: Lazy = Lazy::new(|| { - new::intrinsic_module( + new::module( "std.proc", "", "Proc module", diff --git a/feint-builtins/src/modules/std/std.rs b/feint-builtins/src/modules/std/std.rs index 41a735f..0eec819 100644 --- a/feint-builtins/src/modules/std/std.rs +++ b/feint-builtins/src/modules/std/std.rs @@ -8,7 +8,7 @@ use feint_code_gen::{obj_ref_t, use_arg, use_arg_str}; use crate::types::{self, new}; pub static STD: Lazy = Lazy::new(|| { - new::intrinsic_module( + new::module( "std", "", "std module (builtins)", diff --git a/feint-builtins/src/tests/types.rs b/feint-builtins/src/tests/types.rs index cf8bc2d..26e425e 100644 --- a/feint-builtins/src/tests/types.rs +++ b/feint-builtins/src/tests/types.rs @@ -122,13 +122,13 @@ mod custom { #[test] fn test_custom() { - let mod1 = new::intrinsic_module("test1", "", "test module 1", &[]); + let mod1 = new::module("test1", "", "test module 1", &[]); let t1 = new::custom_type(mod1, "Custom1"); let t1_obj1 = instance(t1.clone(), &[("value", new::nil())]); let t1_obj2 = instance(t1.clone(), &[("value", new::nil())]); let t1_obj3 = instance(t1.clone(), &[("value", new::nil())]); - let mod2 = new::intrinsic_module("test2", "", "test module 2", &[]); + let mod2 = new::module("test2", "", "test module 2", &[]); let t2 = new::custom_type(mod2, "Custom2"); let t2_obj1 = instance(t2.clone(), &[]); diff --git a/feint-builtins/src/types/new.rs b/feint-builtins/src/types/new.rs index bd30db0..4b28f5e 100644 --- a/feint-builtins/src/types/new.rs +++ b/feint-builtins/src/types/new.rs @@ -95,12 +95,25 @@ pub fn empty_tuple() -> ObjectRef { EMPTY_TUPLE.clone() } -// Intrinsic type constructors --------------------------------- +// Modules ------------------------------------------------------------- -pub fn bound_func(func: ObjectRef, this: ObjectRef) -> ObjectRef { - obj_ref!(BoundFunc::new(func, this)) +pub fn module( + name: &str, + path: &str, + doc: &str, + entries: &[(&str, ObjectRef)], +) -> obj_ref_t!(Module) { + obj_ref!(Module::with_entries( + entries, + name.to_owned(), + path.to_owned(), + Code::default(), + Some(doc.to_owned()) + )) } +// Function------------------------------------------------------------- + pub fn intrinsic_func( module_name: &str, name: &str, @@ -121,19 +134,21 @@ pub fn intrinsic_func( )) } -pub fn intrinsic_module( - name: &str, - path: &str, - doc: &str, - entries: &[(&str, ObjectRef)], -) -> obj_ref_t!(Module) { - obj_ref!(Module::with_entries( - entries, - name.to_owned(), - path.to_owned(), - Code::default(), - Some(doc.to_owned()) - )) +pub fn func>( + module_name: S, + func_name: S, + params: Params, + code: Code, +) -> ObjectRef { + obj_ref!(Func::new(module_name.into(), func_name.into(), params, code)) +} + +pub fn bound_func(func: ObjectRef, this: ObjectRef) -> ObjectRef { + obj_ref!(BoundFunc::new(func, this)) +} + +pub fn closure(func: ObjectRef, captured: ObjectRef) -> ObjectRef { + obj_ref!(Closure::new(func, captured)) } pub fn cell() -> ObjectRef { @@ -144,8 +159,8 @@ pub fn cell_with_value(value: ObjectRef) -> ObjectRef { obj_ref!(Cell::with_value(value)) } -pub fn closure(func: ObjectRef, captured: ObjectRef) -> ObjectRef { - obj_ref!(Closure::new(func, captured)) +pub fn argv_tuple(argv: &[String]) -> ObjectRef { + obj_ref!(Tuple::new(argv.iter().map(str).collect())) } // Errors -------------------------------------------------------------- @@ -225,15 +240,6 @@ pub fn float_from_string>(val: S) -> ObjectRef { } } -pub fn func>( - module_name: S, - func_name: S, - params: Params, - code: Code, -) -> ObjectRef { - obj_ref!(Func::new(module_name.into(), func_name.into(), params, code)) -} - pub fn int>(value: I) -> ObjectRef { let value = value.into(); if value.is_positive() && &value <= Lazy::force(&SHARED_INT_MAX_BIGINT) { @@ -295,10 +301,6 @@ pub fn tuple(items: Vec) -> ObjectRef { } } -pub fn argv_tuple(argv: &[String]) -> ObjectRef { - obj_ref!(Tuple::new(argv.iter().map(str).collect())) -} - // Custom type constructor --------------------------------------------- pub fn custom_type(module: ObjectRef, name: &str) -> ObjectRef { diff --git a/feint-cli/src/repl.rs b/feint-cli/src/repl.rs index bd5850a..6d28c89 100644 --- a/feint-cli/src/repl.rs +++ b/feint-cli/src/repl.rs @@ -19,7 +19,7 @@ pub struct Repl { impl Repl { pub fn new(history_path: Option, driver: Driver) -> Self { - let module = new::intrinsic_module("$repl", "$repl", "FeInt REPL module", &[]); + let module = new::module("$repl", "", "FeInt REPL module", &[]); let mut reader = rustyline::Editor::<()>::new().expect("Could initialize readline"); reader.set_indent_size(4); From 3d3b2231a429eeee2ff4662b02aee14bc22dcf53 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Thu, 2 Feb 2023 19:22:33 -0800 Subject: [PATCH 21/31] Remove vm dep from cli --- Cargo.lock | 1 - feint-cli/Cargo.toml | 1 - feint-cli/src/main.rs | 42 ++++------- feint-cli/src/repl.rs | 142 +++++++++++++++++------------------- feint-cli/src/tests/repl.rs | 6 +- feint-driver/src/driver.rs | 54 +++++++++----- feint-driver/src/result.rs | 5 +- 7 files changed, 123 insertions(+), 128 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f2222d4..505ea68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -230,7 +230,6 @@ dependencies = [ "feint-builtins", "feint-compiler", "feint-driver", - "feint-vm", "rustyline", ] diff --git a/feint-cli/Cargo.toml b/feint-cli/Cargo.toml index 5f167d6..fc52682 100644 --- a/feint-cli/Cargo.toml +++ b/feint-cli/Cargo.toml @@ -13,7 +13,6 @@ repository.workspace = true feint-builtins = { path = "../feint-builtins" } feint-compiler = { path = "../feint-compiler" } feint-driver = { path = "../feint-driver" } -feint-vm = { path = "../feint-vm" } clap.workspace = true clap_complete.workspace = true diff --git a/feint-cli/src/main.rs b/feint-cli/src/main.rs index 9bbd8d9..0684216 100644 --- a/feint-cli/src/main.rs +++ b/feint-cli/src/main.rs @@ -12,7 +12,6 @@ use std::process::ExitCode; use clap::{parser::ValueSource, ArgMatches}; use feint_driver::{Driver, DriverErrKind, DriverResult}; -use feint_vm::VMState; use repl::Repl; @@ -196,34 +195,23 @@ fn create_repl_history_file(cond: &bool, path: Option<&String>) -> Option u8 { - match result { - Ok(vm_state) => match vm_state { - VMState::Running => { - eprintln!("VM should be idle or halted, not running"); - 255 - } - VMState::Idle(_) => 0, - VMState::Halted(0) => 0, - VMState::Halted(code) => code, - }, - Err(err) => { - if let Some(exit_code) = err.exit_code() { - exit_code - } else { - if matches!( - &err.kind, - DriverErrKind::Bootstrap(_) - | DriverErrKind::CouldNotReadSourceFile(_) - | DriverErrKind::ModuleDirNotFound(_) - | DriverErrKind::ModuleNotFound(_) - | DriverErrKind::ReplErr(_) - ) { - eprintln!("{err}"); - } - 255 + result.unwrap_or_else(|err| { + if let Some(code) = err.exit_code() { + code + } else { + if matches!( + &err.kind, + DriverErrKind::Bootstrap(_) + | DriverErrKind::CouldNotReadSourceFile(_) + | DriverErrKind::ModuleDirNotFound(_) + | DriverErrKind::ModuleNotFound(_) + | DriverErrKind::ReplErr(_) + ) { + eprintln!("{err}"); } + 255 } - } + }) } /// Get path for str, expanding leading ~ to user home directory. The diff --git a/feint-cli/src/repl.rs b/feint-cli/src/repl.rs index 6d28c89..78a8480 100644 --- a/feint-cli/src/repl.rs +++ b/feint-cli/src/repl.rs @@ -6,9 +6,8 @@ use rustyline::error::ReadlineError; use feint_builtins::types::{new, ObjectRef, ObjectTrait}; use feint_compiler::{CompErrKind, ParseErrKind, ScanErrKind}; -use feint_driver::result::{DriverErr, DriverErrKind, DriverResult}; +use feint_driver::result::{DriverErr, DriverErrKind, DriverOptResult, DriverResult}; use feint_driver::Driver; -use feint_vm::{Disassembler, VMState}; pub struct Repl { module: ObjectRef, @@ -18,12 +17,15 @@ pub struct Repl { } impl Repl { - pub fn new(history_path: Option, driver: Driver) -> Self { + pub fn new(history_path: Option, mut driver: Driver) -> Self { let module = new::module("$repl", "", "FeInt REPL module", &[]); + driver.add_module("$repl", module.clone()); + let mut reader = - rustyline::Editor::<()>::new().expect("Could initialize readline"); + rustyline::Editor::<()>::new().expect("Could not initialize readline"); reader.set_indent_size(4); reader.set_tab_stop(4); + Repl { module, reader, history_path, driver } } @@ -33,8 +35,6 @@ impl Repl { self.load_history(); println!("Type .exit or .quit to exit"); - self.driver.add_module("$repl", self.module.clone()); - let result = loop { match self.read_line("→ ", true) { Ok(None) => { @@ -43,8 +43,11 @@ impl Repl { Ok(Some(input)) => { // Evaluate the input. If eval returns a result of // any kind (ok or err), shut down the REPL. - if let Some(result) = self.eval(input.as_str(), true) { - break result; + let result = self.eval(input.as_str(), true); + match result { + Ok(Some(code)) => break Ok(code), + Ok(None) => (), + Err(err) => break Err(err), } } // User hit Ctrl-C @@ -52,9 +55,7 @@ impl Repl { println!("Use Ctrl-D or .exit to exit"); } // User hit Ctrl-D - Err(ReadlineError::Eof) => { - break Ok(VMState::Halted(0)); - } + Err(ReadlineError::Eof) => break Ok(0), // Unexpected error encountered while attempting to read // a line. Err(err) => { @@ -63,6 +64,7 @@ impl Repl { } } }; + result } @@ -82,79 +84,70 @@ impl Repl { } /// Evaluate text. Returns `None` to indicate to the main loop to - /// continue reading and evaluating input. Returns an `DriverResult` - /// to indicate to the main loop to exit. - pub fn eval(&mut self, text: &str, continue_on_err: bool) -> Option { + /// continue reading and evaluating input. Returns `Some(u8)` to + /// indicate to the main loop to exit. + pub fn eval(&mut self, text: &str, continue_on_err: bool) -> DriverOptResult { self.add_history_entry(text); if matches!(text, ".exit" | ".quit") { - return Some(Ok(VMState::Halted(0))); + return Ok(Some(0)); } else if self.handle_command(text) { - return None; + return Ok(None); } - let result = self.driver.execute_repl(text, self.module.clone()); - - match result { - Ok(vm_state) => { - return match vm_state { - VMState::Running => None, - VMState::Idle(_) => None, - // Halted: - state => Some(Ok(state)), - }; + // If there's an error executing the current input, try to add + // more lines *if* the error can potentially be recovered from + // by adding more input. + self.driver.execute_repl(text, self.module.clone()).or_else(|err| { + if let Some(code) = err.exit_code() { + return Ok(Some(code)); } - Err(err) => { - // If the special Exit err is returned, exit. - if let Some(code) = err.exit_code() { - return Some(Ok(VMState::Halted(code))); - } - // If there's an error executing the current input, try - // to add more lines *if* the error can potentially be - // recovered from by adding more input. - if !(continue_on_err && self.continue_on_err(&err)) { - if matches!( - &err.kind, - DriverErrKind::Bootstrap(_) - | DriverErrKind::CouldNotReadSourceFile(_) - | DriverErrKind::ModuleDirNotFound(_) - | DriverErrKind::ModuleNotFound(_) - | DriverErrKind::ReplErr(_) - ) { - eprintln!("{err}"); + if continue_on_err && self.continue_on_err(&err) { + // Add input until 2 successive blank lines are entered. + let mut input = text.to_owned(); + let mut blank_line_count = 0; + loop { + match self.read_line("+ ", false) { + Ok(None) => unreachable!( + "read_line should never return None in this context" + ), + Ok(Some(new_input)) => { + input.push('\n'); + if new_input.is_empty() { + if blank_line_count > 0 { + break self.eval(input.as_str(), false); + } + blank_line_count += 1; + } else { + input.push_str(new_input.as_str()); + if blank_line_count > 0 { + break self.eval(input.as_str(), false); + } + blank_line_count = 0; + } + } + Err(err) => { + break Err(DriverErr::new(DriverErrKind::ReplErr( + format!("{err}"), + ))); + } } - return None; - } - } - } - - // Add input until 2 successive blank lines are entered. - let mut input = text.to_owned(); - let mut blank_line_count = 0; - loop { - let read_line_result = self.read_line("+ ", false); - if let Ok(None) = read_line_result { - unreachable!(); - } else if let Ok(Some(new_input)) = read_line_result { - input.push('\n'); - if new_input.is_empty() { - if blank_line_count > 0 { - break self.eval(input.as_str(), false); - } - blank_line_count += 1; - } else { - input.push_str(new_input.as_str()); - if blank_line_count > 0 { - break self.eval(input.as_str(), false); - } - blank_line_count = 0; } } else { - let msg = format!("{}", read_line_result.unwrap_err()); - break Some(Err(DriverErr::new(DriverErrKind::ReplErr(msg)))); + if matches!( + &err.kind, + DriverErrKind::Bootstrap(_) + | DriverErrKind::CouldNotReadSourceFile(_) + | DriverErrKind::ModuleDirNotFound(_) + | DriverErrKind::ModuleNotFound(_) + | DriverErrKind::ReplErr(_) + ) { + eprintln!("{err}"); + } + Ok(None) } - } + }) } fn handle_command(&mut self, text: &str) -> bool { @@ -192,10 +185,7 @@ impl Repl { } } ".dis" => { - let module = self.module.read().unwrap(); - let module = module.down_to_mod().unwrap(); - let mut disassembler = Disassembler::new(); - disassembler.disassemble(module.code()); + self.driver.disassemble_module(self.module.clone()); } ".stack" => { self.driver.display_stack(); diff --git a/feint-cli/src/tests/repl.rs b/feint-cli/src/tests/repl.rs index cb9d1d9..ba056e1 100644 --- a/feint-cli/src/tests/repl.rs +++ b/feint-cli/src/tests/repl.rs @@ -43,8 +43,8 @@ fn eval(input: &str) { } let mut repl = Repl::new(None, driver); match repl.eval(input, false) { - Some(Ok(_)) => assert!(false), - Some(Err(_)) => assert!(false), - None => assert!(true), // eval returns None on valid input + Ok(Some(_)) => assert!(false), + Ok(None) => assert!(true), // eval returns None on valid input + Err(_) => assert!(false), } } diff --git a/feint-driver/src/driver.rs b/feint-driver/src/driver.rs index 4a5b205..80e398b 100644 --- a/feint-driver/src/driver.rs +++ b/feint-driver/src/driver.rs @@ -23,12 +23,11 @@ use feint_util::source::{ Source, }; use feint_vm::{ - dis, CallDepth, ModuleExecutionContext, RuntimeErr, RuntimeErrKind, RuntimeResult, - VMState, VM, + CallDepth, Disassembler, ModuleExecutionContext, RuntimeErr, RuntimeErrKind, + RuntimeResult, VMState, VM, }; -use super::result::DriverErrKind::ModuleNotFound; -use super::result::{DriverErr, DriverErrKind, DriverResult}; +use super::result::{DriverErr, DriverErrKind, DriverOptResult, DriverResult}; pub struct Driver { vm: VM, @@ -147,7 +146,7 @@ impl Driver { /// compiled all at once and executed as a script. In the REPL, code /// is compiled incrementally as it's entered, which makes it /// somewhat more complex to deal with. - pub fn execute_repl(&mut self, text: &str, module: ObjectRef) -> DriverResult { + pub fn execute_repl(&mut self, text: &str, module: ObjectRef) -> DriverOptResult { self.current_file_name = "".to_owned(); // XXX: Nested scopes are necessary to avoid deadlocks. @@ -191,7 +190,7 @@ impl Driver { module.code_mut().extend(code); } - let vm_state = { + { let module = module.read().unwrap(); let module = module.down_to_mod().unwrap(); self.execute_module(module, start, source, false)? @@ -205,7 +204,10 @@ impl Driver { } } - Ok(vm_state) + match &self.vm.state { + VMState::Running | VMState::Idle(_) => Ok(None), + VMState::Halted(code) => Ok(Some(*code)), + } } /// Execute source from file as script. @@ -270,12 +272,12 @@ impl Driver { is_main: bool, ) -> DriverResult { if self.dis && is_main { - let mut disassembler = dis::Disassembler::new(); + let mut disassembler = Disassembler::new(); disassembler.disassemble(module.code()); if self.debug { self.display_stack(); } - return Ok(VMState::Halted(0)); + return Ok(0); } self.load_imported_modules()?; @@ -305,11 +307,19 @@ impl Driver { self.display_vm_state(&result); } - match result { - Ok(()) => Ok(self.vm.state.clone()), - Err(err) => { + result + .map(|_| match &self.vm.state { + VMState::Running => { + eprintln!("VM should be idle or halted, not running"); + 255 + } + VMState::Idle(_) => 0, + VMState::Halted(0) => 0, + VMState::Halted(code) => *code, + }) + .map_err(|err| { if let RuntimeErrKind::Exit(_) = err.kind { - Err(DriverErr::new(DriverErrKind::RuntimeErr(err.kind))) + DriverErr::new(DriverErrKind::RuntimeErr(err.kind)) } else { let start = self.vm.loc().0; let line = source @@ -317,10 +327,9 @@ impl Driver { .unwrap_or(""); self.print_err_line(start.line, line); self.handle_runtime_err(&err); - Err(DriverErr::new(DriverErrKind::RuntimeErr(err.kind))) + DriverErr::new(DriverErrKind::RuntimeErr(err.kind)) } - } - } + }) } // Parsing --------------------------------------------------------- @@ -386,7 +395,7 @@ impl Driver { } Ok(obj_ref!(module)) } else { - Err(DriverErr::new(ModuleNotFound(name.to_owned()))) + Err(DriverErr::new(DriverErrKind::ModuleNotFound(name.to_owned()))) } } @@ -400,7 +409,7 @@ impl Driver { if let Some(module) = maybe_get_module(name) { Ok(module) } else { - Err(DriverErr::new(ModuleNotFound(name.to_owned()))) + Err(DriverErr::new(DriverErrKind::ModuleNotFound(name.to_owned()))) } } @@ -435,6 +444,15 @@ impl Driver { Ok(()) } + // Disassembly ----------------------------------------------------- + + pub fn disassemble_module(&self, module: ObjectRef) { + let module = module.read().unwrap(); + let module = module.down_to_mod().unwrap(); + let mut disassembler = Disassembler::new(); + disassembler.disassemble(module.code()); + } + // Error Handling -------------------------------------------------- fn print_err_line(&self, line_no: usize, line: &str) { diff --git a/feint-driver/src/result.rs b/feint-driver/src/result.rs index e0c27a3..6bd36d3 100644 --- a/feint-driver/src/result.rs +++ b/feint-driver/src/result.rs @@ -2,12 +2,13 @@ use core::fmt; use std::fmt::Formatter; use feint_compiler::{CompErrKind, ParseErrKind, ScanErrKind}; -use feint_vm::{RuntimeErrKind, VMState}; +use feint_vm::RuntimeErrKind; pub type CallDepth = usize; /// Result type used by top level program driver. -pub type DriverResult = Result; +pub type DriverResult = Result; +pub type DriverOptResult = Result, DriverErr>; #[derive(Debug)] pub struct DriverErr { From 38ed6484f597fddb9a493a71f7e5056e54d7a833 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Thu, 2 Feb 2023 21:01:48 -0800 Subject: [PATCH 22/31] Implement reassignment via <- `<-` is used for reassigning a var defined in an OUTER scope, excluding globals. `=` always creates a new var in the current scope, similar to using `let` in Rust. Within the SAME scope, `=` can be used for reassignment, but since there are no `let` or `mut` keywords in FeInt, there's no way to indicate in an INNER scope that an assignment should reassign a var defined in an OUTER scope. Therefore, a new operator is necessary for this purpose, which is especially usefully in certain loops--e.g., when a flag needs to be set or an accumulator needs to be updated. In terms of performance, using reassignment in the Mandelbrot example makes a noticeable difference of about 150ms per run using the following command on an M1 Mac: time rr examples/mandlebrot.fi 50 50 Using reassignment, this runs in ~500ms. The previous version, which used in-place update hacks, took about ~650ms. --- README.md | 18 ++++------ examples/mandlebrot.fi | 20 +++-------- examples/reassignment.fi | 47 +++++++++++++++++++++++++ feint-builtins/src/modules/std/std.fi | 30 ++++++++++++++++ feint-builtins/src/types/code.rs | 10 ++++-- feint-compiler/src/ast/ast.rs | 14 ++++++++ feint-compiler/src/compiler/compiler.rs | 2 +- feint-compiler/src/compiler/result.rs | 6 ++++ feint-compiler/src/compiler/visitor.rs | 41 +++++++++++++++++++-- feint-compiler/src/parser/parser.rs | 6 ++++ feint-compiler/src/parser/precedence.rs | 3 +- feint-compiler/src/scanner/scanner.rs | 2 +- feint-compiler/src/scanner/token.rs | 4 +-- feint-driver/src/driver.rs | 5 ++- feint-vm/src/context.rs | 8 +++-- feint-vm/src/dis.rs | 8 +++-- feint-vm/src/vm.rs | 15 ++++---- 17 files changed, 189 insertions(+), 50 deletions(-) create mode 100644 examples/reassignment.fi diff --git a/README.md b/README.md index 3fea10b..d8046da 100644 --- a/README.md +++ b/README.md @@ -265,20 +265,14 @@ loop i <- 1...10 -> i # Loop until condition is met -# -# TODO: There's a bug in this example since `loop cond` checks the OUTER -# `cond`. This is due to how scopes work--the inner assignment -# creates a new scope-local var rather than reassigning the outer -# `cond`. A possible fix would be to add an `outer` keyword. +# +# NOTE the use of `cond <- false` rather than `cond = false`. This is +# necessary because `cond = false` would create a new var in the loop's +# scope such that the loop would never exit (since it checks the OUTER +# `cond`). cond = true loop cond -> - cond = false - -# Another fix for the above example would be to pull the outer cond into -# the loop's scope as a local variable: -cond = true -loop cond = cond -> - cond = false + cond <- false ``` ## Jumps diff --git a/examples/mandlebrot.fi b/examples/mandlebrot.fi index 29af1b9..f50ea2e 100644 --- a/examples/mandlebrot.fi +++ b/examples/mandlebrot.fi @@ -69,22 +69,10 @@ $main = (...) => i = 0 loop i < w && tr + ti <= 4 -> - new_zi = 2 * zr * zi + ci - zi *= 0.0 - zi += new_zi - - new_zr = tr - ti + cr - zr *= 0 - zr += new_zr - - new_tr = zr * zr - tr *= 0 - tr += new_tr - - new_ti = zi * zi - ti *= 0 - ti += new_ti - + zi <- 2 * zr * zi + ci + zr <- tr - ti + cr + tr <- zr * zr + ti <- zi * zi i += 1 # NOTE: Using $print instead of print to avoid function call diff --git a/examples/reassignment.fi b/examples/reassignment.fi new file mode 100644 index 0000000..96bdfa9 --- /dev/null +++ b/examples/reassignment.fi @@ -0,0 +1,47 @@ +# Reassignment using <- is allowed only to vars defined in an OUTER +# scope, EXCLUDING globals. + +x = 1 + +# This would throw a compilation error. +# x <- 2 + +# So would this. +# block -> +# x <- 2 + + +# This is okay though. The rationale for this is that it provides a way +# to dynamically initialize a global without leaking temporaries into +# the global scope. +y = block -> + a = true + loop a -> + a <- false + +assert_is(y, false, true) + + +f = () => + # This would throw throw a compilation error because x isn't defined + # in the function's scope. + # x <- 2 + ... + + +g = (x) => + # This would throw throw a compilation error because the parameter x + # is defined in the same scope. + # x <- 2 + + block -> + x <- 2 + + cond = true + loop cond -> + # NOTE: Using = instead of <- here would cause an infinite loop. + cond <- false + + +$main = (...) => + g(1) diff --git a/feint-builtins/src/modules/std/std.fi b/feint-builtins/src/modules/std/std.fi index 26a5019..e4e94ec 100644 --- a/feint-builtins/src/modules/std/std.fi +++ b/feint-builtins/src/modules/std/std.fi @@ -154,6 +154,36 @@ assert_eq: Bool | Err = (a: Any, b: Any, ...) => err +assert_is: Bool | Err = (a: Any, b: Any, ...) => + "Check if items are the same object and return error if not. + + # Args + + - a: Any + - b: Any + - halt?: Bool = false + + # Returns + + true: if the items are the same object + Err: if the items are not the same and `halt` is unset + + > NOTE: If `halt` is set, the program will exit immediately with an + > error code. + + " + if a $$ b -> + true + else -> + err = Err.new(ErrType.assertion, $"{a} is not {b}") + + if $args.get(0) -> + print_err(err) + $halt 1 + + err + + # NOTE: This is used for manual type-checking of function args, which # isn't great. check_arg_type = (func: Func, name: Str, arg: Any, expected_types: Tuple) => diff --git a/feint-builtins/src/types/code.rs b/feint-builtins/src/types/code.rs index b414be9..34cd4e0 100644 --- a/feint-builtins/src/types/code.rs +++ b/feint-builtins/src/types/code.rs @@ -236,7 +236,13 @@ pub enum Inst { LoadConst(usize), DeclareVar(String), - AssignVar(String), + + // Args: name, offset + // + // `offset` is the number of scopes above the current scope, where + // the var is defined. 0 means the current scope, 1 means the parent + // scope, and so on. + AssignVar(String, usize), // Args: name, offset // @@ -369,7 +375,7 @@ impl PartialEq for Inst { (StatementStart(..), StatementStart(..)) => true, (LoadConst(a), LoadConst(b)) => a == b, (DeclareVar(a), DeclareVar(b)) => a == b, - (AssignVar(a), AssignVar(b)) => a == b, + (AssignVar(a, i), AssignVar(b, j)) => (a, i) == (b, j), (LoadVar(a, i), LoadVar(b, j)) => (a, i) == (b, j), (AssignCell(a), AssignCell(b)) => a == b, (LoadCell(a), LoadCell(b)) => a == b, diff --git a/feint-compiler/src/ast/ast.rs b/feint-compiler/src/ast/ast.rs index 8d8a709..5aec528 100644 --- a/feint-compiler/src/ast/ast.rs +++ b/feint-compiler/src/ast/ast.rs @@ -166,6 +166,7 @@ pub enum ExprKind { Call(Call), DeclarationAndAssignment(Box, Box), Assignment(Box, Box), + Reassignment(Box, Box), UnaryOp(UnaryOperator, Box), BinaryOp(Box, BinaryOperator, Box), CompareOp(Box, CompareOperator, Box), @@ -284,6 +285,18 @@ impl Expr { end, ) } + pub fn new_reassignment( + lhs_expr: Expr, + value_expr: Expr, + start: Location, + end: Location, + ) -> Self { + Self::new( + ExprKind::Reassignment(Box::new(lhs_expr), Box::new(value_expr)), + start, + end, + ) + } pub fn new_func( params: Params, @@ -452,6 +465,7 @@ impl fmt::Debug for ExprKind { write!(f, "{ident:?} = {expr:?}") } Self::Assignment(ident, expr) => write!(f, "{ident:?} = {expr:?}"), + Self::Reassignment(ident, expr) => write!(f, "{ident:?} <- {expr:?}"), Self::Block(block) => write!(f, "block {block:?}"), Self::Conditional(branches, default) => { write!(f, "{branches:?} {default:?}") diff --git a/feint-compiler/src/compiler/compiler.rs b/feint-compiler/src/compiler/compiler.rs index 8092315..732ab1a 100644 --- a/feint-compiler/src/compiler/compiler.rs +++ b/feint-compiler/src/compiler/compiler.rs @@ -168,7 +168,7 @@ impl Compiler { Inst::ScopeEnd => { break; } - Inst::AssignVar(n) if n == name => { + Inst::AssignVar(n, 0) if n == name => { info.cell_var_assignments.push(addr); } Inst::LoadVar(n, 0) if n == name => { diff --git a/feint-compiler/src/compiler/result.rs b/feint-compiler/src/compiler/result.rs index 63db871..a10bfbc 100644 --- a/feint-compiler/src/compiler/result.rs +++ b/feint-compiler/src/compiler/result.rs @@ -61,6 +61,10 @@ impl CompErr { Self::new(CompErrKind::CannotReassignSpecialIdent(name, start, end)) } + pub fn reassignment(msg: String, start: Location, end: Location) -> Self { + Self::new(CompErrKind::Reassignment(msg, start, end)) + } + pub fn main_must_be_func(start: Location, end: Location) -> Self { Self::new(CompErrKind::MainMustBeFunc(start, end)) } @@ -91,6 +95,7 @@ impl CompErr { ExpectedIdent(start, end) => (start, end), CannotAssignSpecialIdent(_, start, end) => (start, end), CannotReassignSpecialIdent(_, start, end) => (start, end), + Reassignment(_, start, end) => (start, end), MainMustBeFunc(start, end) => (start, end), GlobalNotFound(_, start, end) => (start, end), VarArgsMustBeLast(start, end) => (start, end), @@ -110,6 +115,7 @@ pub enum CompErrKind { ExpectedIdent(Location, Location), CannotAssignSpecialIdent(String, Location, Location), CannotReassignSpecialIdent(String, Location, Location), + Reassignment(String, Location, Location), MainMustBeFunc(Location, Location), GlobalNotFound(String, Location, Location), VarArgsMustBeLast(Location, Location), diff --git a/feint-compiler/src/compiler/visitor.rs b/feint-compiler/src/compiler/visitor.rs index 56a0bb1..2d14c05 100644 --- a/feint-compiler/src/compiler/visitor.rs +++ b/feint-compiler/src/compiler/visitor.rs @@ -223,7 +223,7 @@ impl CompilerVisitor { self.scope_tree.add_var(self.len(), &var_name, true); self.push(Inst::DeclareVar(var_name.clone())); self.push(Inst::LoadModule(name.clone())); - self.push(Inst::AssignVar(var_name.clone())); + self.push(Inst::AssignVar(var_name.clone(), 0)); } else { let var_name = name .split('.') @@ -232,7 +232,7 @@ impl CompilerVisitor { self.scope_tree.add_var(self.len(), var_name, true); self.push(Inst::DeclareVar(var_name.to_owned())); self.push(Inst::LoadModule(name.clone())); - self.push(Inst::AssignVar(var_name.to_owned())); + self.push(Inst::AssignVar(var_name.to_owned(), 0)); } Ok(()) } @@ -313,6 +313,9 @@ impl CompilerVisitor { Kind::Assignment(lhs_expr, value_expr) => { self.visit_assignment(*lhs_expr, *value_expr)? } + Kind::Reassignment(lhs_expr, value_expr) => { + self.visit_reassignment(*lhs_expr, *value_expr)? + } Kind::Block(block) => self.visit_block(block)?, Kind::Conditional(branches, default) => { self.visit_conditional(branches, default)? @@ -723,13 +726,45 @@ impl CompilerVisitor { } self.visit_expr(value_expr, Some(name.clone()))?; self.scope_tree.mark_assigned(self.scope_tree.pointer(), name.as_str()); - self.push(Inst::AssignVar(name)); + self.push(Inst::AssignVar(name, 0)); Ok(()) } else { Err(CompErr::expected_ident(lhs_expr.start, lhs_expr.end)) } } + fn visit_reassignment( + &mut self, + lhs_expr: ast::Expr, + value_expr: ast::Expr, + ) -> VisitResult { + if let Some(name) = lhs_expr.ident_name() { + if let Some(var) = self.scope_tree.find_var(&name, None) { + let depth = var.depth; + let offset = self.scope_depth - depth; + if self.is_module() && depth == 0 { + let msg = format!( + "Cannot reassign {name} using <- in global scope. Use = instead." + ); + Err(CompErr::reassignment(msg, lhs_expr.start, lhs_expr.end)) + } else if self.is_func() && offset == 0 { + let msg = format!( + "Cannot reassign var {name} using <- in same scope. Use = instead." + ); + Err(CompErr::reassignment(msg, lhs_expr.start, lhs_expr.end)) + } else { + self.visit_expr(value_expr, Some(name.clone()))?; + self.push(Inst::AssignVar(name, offset)); + Ok(()) + } + } else { + Err(CompErr::name_not_found(name, lhs_expr.start, lhs_expr.end)) + } + } else { + Err(CompErr::expected_ident(lhs_expr.start, lhs_expr.end)) + } + } + fn visit_compare_op( &mut self, expr_a: ast::Expr, diff --git a/feint-compiler/src/parser/parser.rs b/feint-compiler/src/parser/parser.rs index d72bf5e..43c7477 100644 --- a/feint-compiler/src/parser/parser.rs +++ b/feint-compiler/src/parser/parser.rs @@ -666,6 +666,12 @@ impl> Parser { ast::Expr::new_assignment(lhs, value, start, end) } } + Token::Feed => { + log::trace!("BINOP: reassignment"); + let value = self.expr(infix_prec)?; + let end = value.end; + ast::Expr::new_reassignment(lhs, value, start, end) + } // Call Token::LParen => { log::trace!("BINOP: call {lhs:?}"); diff --git a/feint-compiler/src/parser/precedence.rs b/feint-compiler/src/parser/precedence.rs index 2d72ebb..0852e90 100644 --- a/feint-compiler/src/parser/precedence.rs +++ b/feint-compiler/src/parser/precedence.rs @@ -13,7 +13,7 @@ pub fn get_binary_precedence(token: &Token) -> u8 { /// Return true if the token represents a right-associate operator. pub fn is_right_associative(token: &Token) -> bool { // Exponentiation and assignment - matches!(token, Token::Caret | Token::Equal) + matches!(token, Token::Caret | Token::Equal | Token::Feed) } #[rustfmt::skip] @@ -30,6 +30,7 @@ pub fn get_operator_precedence(token: &Token) -> (u8, u8) { use Token::*; match token { Equal // a = b + | Feed // a <- b | MulEqual // a *= b | DivEqual // a /= b | PlusEqual // a -= b diff --git a/feint-compiler/src/scanner/scanner.rs b/feint-compiler/src/scanner/scanner.rs index 0d48481..c020db7 100644 --- a/feint-compiler/src/scanner/scanner.rs +++ b/feint-compiler/src/scanner/scanner.rs @@ -151,7 +151,7 @@ impl<'a, T: BufRead> Scanner<'a, T> { Some(('<', Some('='), _)) => { self.consume_char_and_return_token(LessThanOrEqual) } - Some(('<', Some('-'), _)) => self.consume_char_and_return_token(LoopFeed), + Some(('<', Some('-'), _)) => self.consume_char_and_return_token(Feed), Some(('<', _, _)) => LessThan, Some(('>', Some('='), _)) => { self.consume_char_and_return_token(GreaterThanOrEqual) diff --git a/feint-compiler/src/scanner/token.rs b/feint-compiler/src/scanner/token.rs index 2dd465c..3d37770 100644 --- a/feint-compiler/src/scanner/token.rs +++ b/feint-compiler/src/scanner/token.rs @@ -20,7 +20,7 @@ pub enum Token { Colon, // : DotDot, // .. Ellipsis, // ... - LoopFeed, // <- + Feed, // <- // Fundamental types ----------------------------------------------- At, // @ (used to represent the singleton Always) @@ -128,7 +128,7 @@ impl Token { Self::Colon => ":", Self::DotDot => "..", Self::Ellipsis => "...", - Self::LoopFeed => "<-", + Self::Feed => "<-", // Fundamental types ----------------------------------------------- Self::Int(_) => "Int", diff --git a/feint-driver/src/driver.rs b/feint-driver/src/driver.rs index 80e398b..92e699a 100644 --- a/feint-driver/src/driver.rs +++ b/feint-driver/src/driver.rs @@ -174,7 +174,7 @@ impl Driver { if let Some(Inst::Pop) = last_inst { let print_flags = PrintFlags::NL | PrintFlags::REPR | PrintFlags::NO_NIL; code.push_inst(Inst::DeclareVar("_".to_owned())); - code.push_inst(Inst::AssignVar("_".to_owned())); + code.push_inst(Inst::AssignVar("_".to_owned(), 0)); code.push_inst(Inst::Print(print_flags)); } else { let last_inst = match last_inst { @@ -636,6 +636,9 @@ impl Driver { CannotReassignSpecialIdent(name, ..) => { format!("cannot reassign special name: {name}") } + Reassignment(msg, ..) => { + msg.to_string() + } MainMustBeFunc(..) => { "$main must be a function".to_owned() } diff --git a/feint-vm/src/context.rs b/feint-vm/src/context.rs index 2a53854..9533197 100644 --- a/feint-vm/src/context.rs +++ b/feint-vm/src/context.rs @@ -110,11 +110,13 @@ impl ModuleExecutionContext { &mut self, name: &str, obj: ObjectRef, + offset: usize, ) -> Result { - let ns = self.current_mut(); + let depth = self.current_depth() - offset; + let ns = self.ns_stack.get_mut(depth).unwrap(); if ns.contains_key(name) { ns.insert(name.to_owned(), obj); - Ok(self.ns_stack.len() - 1) + Ok(depth) } else { let message = format!("Name not defined in current scope: {name}"); Err(RuntimeErr::name_err(message)) @@ -128,7 +130,7 @@ impl ModuleExecutionContext { obj: ObjectRef, ) -> Result { self.declare_var(name); - self.assign_var(name, obj) + self.assign_var(name, obj, 0) } /// Assign value to var--reach into the scope at depth and set the diff --git a/feint-vm/src/dis.rs b/feint-vm/src/dis.rs index 71c771b..6c8865a 100644 --- a/feint-vm/src/dis.rs +++ b/feint-vm/src/dis.rs @@ -69,9 +69,13 @@ impl Disassembler { None => self.align("LOAD_CONST", ""), }, DeclareVar(name) => self.align("DECLARE_VAR", name), - AssignVar(name) => self.align("ASSIGN_VAR", name), + AssignVar(name, offset) => { + let offset_prefix = if *offset == 0 { "" } else { "-" }; + self.align("ASSIGN_VAR", format!("{name} @ {offset_prefix}{offset}")) + } LoadVar(name, offset) => { - self.align("LOAD_VAR", format!("{name} @ -{offset}")) + let offset_prefix = if *offset == 0 { "" } else { "-" }; + self.align("LOAD_VAR", format!("{name} @ {offset_prefix}{offset}")) } LoadGlobal(name) => self.align("LOAD_GLOBAL", name), LoadBuiltin(name) => self.align("LOAD_BUILTIN", name), diff --git a/feint-vm/src/vm.rs b/feint-vm/src/vm.rs index ffcd14d..3657eee 100644 --- a/feint-vm/src/vm.rs +++ b/feint-vm/src/vm.rs @@ -181,9 +181,9 @@ impl VM { self.ctx.declare_var(name.as_str()); } } - AssignVar(name) => { + AssignVar(name, offset) => { let obj = self.pop_obj()?; - let depth = self.ctx.assign_var(name, obj)?; + let depth = self.ctx.assign_var(name, obj, *offset)?; self.push_var(depth, name.clone())?; } LoadVar(name, offset) => { @@ -243,12 +243,12 @@ impl VM { let depth = if let Some(cell) = var.down_to_cell_mut() { // Wrap TOS in existing cell. cell.set_value(value.clone()); - self.ctx.assign_var(name, var_ref.clone())? + self.ctx.assign_var(name, var_ref.clone(), 0)? } else { // Create new cell to wrap TOS in. assert!(var.is_nil()); let cell_ref = new::cell_with_value(value.clone()); - self.ctx.assign_var(name, cell_ref)? + self.ctx.assign_var(name, cell_ref, 0)? }; // Push cell *value* to TOS. self.push(ValueStackKind::CellVar(value, depth, name.to_owned())); @@ -461,8 +461,11 @@ impl VM { if let AssignCell(_) = &code[ip + 1] { let closure_cell = new::cell_with_value(func_ref.clone()); - self.ctx - .assign_var(func.name(), closure_cell.clone())?; + self.ctx.assign_var( + func.name(), + closure_cell.clone(), + 0, + )?; capture_set .insert(func.name().to_owned(), closure_cell); } From 1dec943e55d867e79e322e63d2654c01f0b2f134 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Fri, 14 Apr 2023 00:14:15 -0700 Subject: [PATCH 23/31] Improve Mandlebrot example - Remove/update obsolete comment - Simplify initialization of vars in loop --- examples/mandlebrot.fi | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/examples/mandlebrot.fi b/examples/mandlebrot.fi index f50ea2e..b8244a8 100644 --- a/examples/mandlebrot.fi +++ b/examples/mandlebrot.fi @@ -1,14 +1,8 @@ # Ported from https://github.com/RustPython/RustPython/blob/main/benches/benchmarks/mandelbrot.py # (not sure if it's that's the original source). # -# Python runs this in ~200ms on my laptop. This version takes over -# a second using a release build. -# -# The most notable issue is that a hack has to be used to reassign outer -# vars--compute new value into temporary, reset var using `*= 0` hack, -# reassign var to temporary value using `+=` hack. Given the number of -# computations in the inner loop, this likely causes serious performance -# degradation. +# Python runs this in ~200ms on my laptop. This version runs in ~425ms +# using a release build. import std.args import std.system @@ -60,10 +54,7 @@ $main = (...) => x = 0.0 loop x < w -> - zr = 0.0 - zi = 0.0 - tr = 0.0 - ti = 0.0 + zr = zi = tr = ti = 0.0 cr = 2 * x / w - 1.5 ci = 2 * y / h - 1.0 i = 0 From aed98bae7d598394906e880f0035af17f5f0a6b6 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Fri, 14 Apr 2023 00:31:53 -0700 Subject: [PATCH 24/31] Update README --- README.md | 66 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index d8046da..5184c89 100644 --- a/README.md +++ b/README.md @@ -65,22 +65,26 @@ instructions for use with [neovim]. - Everything is an object of some type - Strong typing - Lexical scoping -- Everything is an expression or acts like an expression; every +- Everything is an expression or acts like an expression--every statement returns some value - Significant whitespace (by default, but maybe consider `{...}` blocks for certain special cases like passing functions) -- No this/self on methods but this/self is required to access attributes -- Disallow arbitrary attachment of attributes (???) -- Everything is immutable by default (???) - Implicit return of last evaluated expression (like Rust); this applies to *all* blocks/scopes -- Custom types can implement operators by defining methods such as `+` - Only booleans and `nil` can be used in boolean contexts by default; custom types can implement the `!!` operator +- Everything is immutable by default (idea: use `=` for `const` and `<-` + for `var`) +- No this/self in method definitions but this/self is required to access + attributes +- Disallow arbitrary attachment of attributes +- Custom types can implement operators by defining methods such as `+` +- No inheritance (???) ## Memory Management -TODO +The current implementation uses reference counting via Rust's `Arc`, but +memory can leak if there are cycles. ## Intrinsic Types @@ -100,21 +104,27 @@ TODO ## Vars Variables are defined without the use of any keywords, like Python or -Ruby. +Ruby. Vars can be reassigned within a given scope using `=`. Vars in +outer scopes can be reassigned using `<-`. ``` -a = true -b = nil - a = true block -> print(a) # outer a -a = true +b = true +block -> + # This creates a new `b` in the block. + b = false + +# After exiting the block, `b` is `true` + +c = 1 block -> - a = false - print(a) # block a -print(a) # outer a + # This reassigns the outer `c` + c <- 2 + +# After exiting the block, `c` is `2` ``` ## Type Hints @@ -133,8 +143,7 @@ f: Str = (s: Str, count: Int) => ## Format Strings -Similar to f-strings in Python. Sometimes called $-strings since they -use a `$` to mark strings as format strings. +Similar to $-strings in F# and f-strings in Python. ``` x = 1 @@ -154,7 +163,7 @@ block_val = block -> y = 2 x + y -block_val == 4 +assert(block_val == 4) ``` ## Scopes @@ -165,6 +174,8 @@ scope. Blocks are denoted by `->` and always return (so to speak) a value, which may be `nil`. +Function blocks are denoted by `=>` to resolve a parsing ambiguity. + ## Conditionals NOTE: By default, only booleans and `nil` can be used in boolean @@ -210,11 +221,13 @@ can be written more succinctly as: ``` x = "abc" + result = match x -> "a" -> 1 "ab" -> 2 "abc" -> 3 * -> 4 + print(result) # -> 3 ``` @@ -227,19 +240,21 @@ branch and no match is found, the `match` block will return `nil`. ## Pattern Matching Pattern matching can be done using the builtin `Always` singleton `@`. -`@` always evaluates as `true` can be compared for equality with +`@` always evaluates as `true`. It can be compared for equality with _anything_ and the comparison will _always_ be `true`. ``` r = match (1, 2) -> (1, @) -> true * -> false -print(r) # -> true + +assert(r == true) r = match (1, 2) -> (@, 2) -> true * -> false -print(r) # -> true + +assert(r == true) ``` ## Loops @@ -280,7 +295,8 @@ loop cond -> - Forward jumps support the jump-to-exit pattern - Backward jumps are disallowed (so no looping via goto) - Labels can only be placed at the beginning of a line or directly - after a scope start marker + after a scope start marker (although the latter isn't particularly + useful) - Labels are fenced by colons e.g. `:my_label:` (this differentiates labels from identifiers with type hints) - Labels can't be redefined in a scope @@ -320,7 +336,8 @@ my_func(() => nil) Functions can be defined with a var args parameter in the last position, which can be accessed in the body of the function as `$args`. `$args` -is always a tuple and will be empty if there are no var args. +is always defined, is always a tuple, and will be empty if there are no +var args. ``` # $main is special; $args is argv @@ -332,10 +349,7 @@ f = (x, y, ...) => print(x, y, $args) ### Closures Closures work pretty much like Python or JavaScript. One difference is -that names defined *after* a function won't be captured. Allowing this -seems a bit wonky in the first place and also adds a bit of complexity -for no apparent benefit (or, at least, I'm not sure what the benefit of -this capability is off the top of my head.) +that names defined *after* a function won't be captured. Here's an extremely artificial example: From 298fb5cd4c92709805735e2b564861718df4ee72 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Sat, 4 Feb 2023 22:13:10 -0800 Subject: [PATCH 25/31] WIP --- feint-builtins/src/builtins.rs | 613 +++++++++++++++++++++ feint-builtins/src/lib.rs | 3 + feint-builtins/src/modules/mod.rs | 42 -- feint-builtins/src/modules/std/mod.rs | 2 - feint-builtins/src/modules/std/proc.rs | 19 - feint-builtins/src/modules/std/std.rs | 90 +-- feint-builtins/src/tests/types.rs | 42 +- feint-builtins/src/types/always.rs | 25 +- feint-builtins/src/types/base.rs | 239 ++------ feint-builtins/src/types/bool.rs | 24 +- feint-builtins/src/types/bound_func.rs | 33 +- feint-builtins/src/types/cell.rs | 29 +- feint-builtins/src/types/class.rs | 76 ++- feint-builtins/src/types/closure.rs | 30 +- feint-builtins/src/types/code.rs | 7 +- feint-builtins/src/types/custom.rs | 136 +---- feint-builtins/src/types/err.rs | 143 +++-- feint-builtins/src/types/err_type.rs | 69 +-- feint-builtins/src/types/file.rs | 109 ++-- feint-builtins/src/types/float.rs | 76 ++- feint-builtins/src/types/func.rs | 46 +- feint-builtins/src/types/int.rs | 98 ++-- feint-builtins/src/types/intrinsic_func.rs | 46 +- feint-builtins/src/types/iterator.rs | 64 +-- feint-builtins/src/types/list.rs | 195 ++++--- feint-builtins/src/types/map.rs | 209 ++++--- feint-builtins/src/types/mod.rs | 3 +- feint-builtins/src/types/module.rs | 147 ++--- feint-builtins/src/types/new.rs | 362 ------------ feint-builtins/src/types/nil.rs | 22 +- feint-builtins/src/types/ns.rs | 14 + feint-builtins/src/types/prop.rs | 24 +- feint-builtins/src/types/seq.rs | 18 +- feint-builtins/src/types/str.rs | 224 ++++---- feint-builtins/src/types/tuple.rs | 123 ++--- feint-builtins/src/util.rs | 7 +- feint-cli/src/repl.rs | 5 +- feint-code-gen/src/lib.rs | 118 +--- feint-compiler/src/compiler/compiler.rs | 13 +- feint-compiler/src/compiler/visitor.rs | 23 +- feint-compiler/src/format.rs | 14 +- feint-driver/src/driver.rs | 26 +- feint-vm/src/context.rs | 5 +- feint-vm/src/tests.rs | 19 +- feint-vm/src/vm.rs | 81 +-- scripts/type.template | 4 +- 46 files changed, 1765 insertions(+), 1952 deletions(-) create mode 100644 feint-builtins/src/builtins.rs delete mode 100644 feint-builtins/src/modules/std/proc.rs delete mode 100644 feint-builtins/src/types/new.rs diff --git a/feint-builtins/src/builtins.rs b/feint-builtins/src/builtins.rs new file mode 100644 index 0000000..6c7d71d --- /dev/null +++ b/feint-builtins/src/builtins.rs @@ -0,0 +1,613 @@ +use std::sync::{Arc, RwLock}; + +use indexmap::IndexMap; +use num_bigint::BigInt; +use num_traits::{FromPrimitive, Num}; +use once_cell::sync::{Lazy, OnceCell}; + +use feint_code_gen::{meth, obj_ref, obj_ref_t, use_arg}; +use feint_util::string::format_doc; + +use crate::types::{self, ObjectRef, ObjectTrait, Params, TypeRef}; + +use crate::types::always::Always; +use crate::types::bool::Bool; +use crate::types::bound_func::BoundFunc; +use crate::types::cell::Cell; +use crate::types::class::Type; +use crate::types::closure::Closure; +use crate::types::code::Code; +use crate::types::custom::CustomObj; +use crate::types::err::ErrObj; +use crate::types::err_type::ErrKind; +use crate::types::file::File; +use crate::types::float::Float; +use crate::types::func::Func; +use crate::types::int::Int; +use crate::types::intrinsic_func::{IntrinsicFn, IntrinsicFunc}; +use crate::types::iterator::FIIterator; +use crate::types::list::List; +use crate::types::map::Map; +use crate::types::module::Module; +use crate::types::nil::Nil; +use crate::types::ns::Namespace; +use crate::types::prop::Prop; +use crate::types::str::Str; +use crate::types::tuple::Tuple; + +pub static BUILTINS: Lazy = Lazy::new(Builtins::default); + +pub struct Builtins { + // Modules --------------------------------------------------------- + pub modules: OnceCel, + + // Types ----------------------------------------------------------- + pub type_type: TypeRef, + pub always_type: TypeRef, + pub bool_type: TypeRef, + pub bound_func_type: TypeRef, + pub cell_type: TypeRef, + pub closure_type: TypeRef, + pub err_type: TypeRef, + pub err_type_type: TypeRef, + pub file_type: TypeRef, + pub float_type: TypeRef, + pub func_type: TypeRef, + pub int_type: TypeRef, + pub intrinsic_func_type: TypeRef, + pub iterator_type: TypeRef, + pub list_type: TypeRef, + pub map_type: TypeRef, + pub module_type: TypeRef, + pub nil_type: TypeRef, + pub prop_type: TypeRef, + pub str_type: TypeRef, + pub tuple_type: TypeRef, + + // Singletons ------------------------------------------------------ + pub nil: ObjectRef, + pub true_: ObjectRef, + pub false_: ObjectRef, + pub always: ObjectRef, + pub empty_str: ObjectRef, + pub newline: ObjectRef, + pub empty_tuple: ObjectRef, + pub ok_err: ObjectRef, +} + +unsafe impl Send for Builtins {} +unsafe impl Sync for Builtins {} + +fn new_type(name: &str) -> TypeRef { + obj_ref!(Type::new("std", name)) +} + +impl Default for Builtins { + fn default() -> Self { + let builtins = Self { + type_type: new_type("Type"), + always_type: new_type("Always"), + bool_type: new_type("Bool"), + bound_func_type: new_type("BoundFunc"), + cell_type: new_type("Cell"), + closure_type: new_type("Closure"), + err_type: new_type("Err"), + err_type_type: new_type("ErrType"), + file_type: new_type("File"), + float_type: new_type("Float"), + func_type: new_type("Func"), + int_type: new_type("Int"), + intrinsic_func_type: new_type("IntrinsicFunc"), + iterator_type: new_type("Iterator"), + list_type: new_type("List"), + map_type: new_type("Map"), + module_type: new_type("Module"), + nil_type: new_type("Nil"), + prop_type: new_type("Prop"), + str_type: new_type("Str"), + tuple_type: new_type("Tuple"), + }; + + builtins + } +} + +impl Builtins { + // Modules --------------------------------------------------------- + + pub fn modules(&self) -> obj_ref_t!(Map) { + self.modules + .get_or_init(|| obj_ref!(Map::new(self.map_type(), IndexMap::default()))) + .clone() + } + + /// Add module to `std.system.modules`. + pub fn add_module(&self, name: &str, module: ObjectRef) { + let modules = self.modules(); + let modules = modules.write().unwrap(); + let modules = modules.down_to_map().unwrap(); + modules.insert(name, module); + } + + /// Get module from `system.modules`. + /// + /// XXX: Panics if the module doesn't exist (since that shouldn't be + /// possible). + pub fn get_module(&self, name: &str) -> ObjectRef { + let modules = self.modules(); + let modules = modules.read().unwrap(); + let modules = modules.down_to_map().unwrap(); + if let Some(module) = modules.get(name) { + module.clone() + } else { + panic!("Module not registered: {name}"); + } + } + + /// Get module from `system.modules`. + /// + /// XXX: This will return `None` if the module doesn't exist. Generally, + /// this should only be used during bootstrap. In most cases, + /// `get_module` should be used instead. + pub fn maybe_get_module(&self, name: &str) -> Option { + let modules = self.modules(); + let modules = modules.read().unwrap(); + let modules = modules.down_to_map().unwrap(); + modules.get(name) + } + + // Types ----------------------------------------------------------- + + fn new_type(&self, name: &str) -> TypeRef { + obj_ref!(Type::new("std", name)) + } + + pub fn type_type(&self) -> TypeRef { + self.type_type.get_or_init(|| self.new_type("Type")).clone() + } + + pub fn always_type(&self) -> TypeRef { + self.always_type.get_or_init(|| self.new_type("Always")).clone() + } + + pub fn bool_type(&self) -> TypeRef { + self.bool_type.get_or_init(|| self.new_type("Bool")).clone() + } + + pub fn bound_func_type(&self) -> TypeRef { + self.bound_func_type.get_or_init(|| self.new_type("BoundFunc")).clone() + } + + pub fn cell_type(&self) -> TypeRef { + self.cell_type.get_or_init(|| self.new_type("Cell")).clone() + } + + pub fn closure_type(&self) -> TypeRef { + self.closure_type.get_or_init(|| self.new_type("Closure")).clone() + } + + pub fn err_type(&self) -> TypeRef { + self.err_type.get_or_init(|| self.new_type("Err")).clone() + // self.err_type.get_or_init(make_err_type).clone() + } + + pub fn err_type_type(&self) -> TypeRef { + self.err_type_type.get_or_init(|| self.new_type("ErrType")).clone() + } + + pub fn file_type(&self) -> TypeRef { + self.file_type.get_or_init(|| self.new_type("File")).clone() + // self.file_type.get_or_init(make_file_type).clone() + } + + pub fn float_type(&self) -> TypeRef { + self.float_type.get_or_init(|| self.new_type("Float")).clone() + // self.float_type.get_or_init(make_float_type).clone() + } + + pub fn func_type(&self) -> TypeRef { + self.func_type.get_or_init(|| self.new_type("Func")).clone() + } + + pub fn intrinsic_func_type(&self) -> TypeRef { + self.intrinsic_func_type.get_or_init(|| self.new_type("IntrinsicFunc")).clone() + } + + pub fn int_type(&self) -> TypeRef { + self.int_type.get_or_init(|| self.new_type("Int")).clone() + // self.int_type.get_or_init(make_int_type).clone() + } + + pub fn iterator_type(&self) -> TypeRef { + self.iterator_type.get_or_init(|| self.new_type("Iterator")).clone() + // self.iterator_type.get_or_init(make_iterator_type).clone() + } + + pub fn list_type(&self) -> TypeRef { + self.list_type.get_or_init(|| self.new_type("List")).clone() + // self.list_type.get_or_init(make_list_type).clone() + } + + pub fn map_type(&self) -> TypeRef { + self.map_type.get_or_init(|| self.new_type("Map")).clone() + // self.map_type.get_or_init(make_map_type).clone() + } + + pub fn module_type(&self) -> TypeRef { + self.module_type.get_or_init(|| self.new_type("Module")).clone() + // self.module_type.get_or_init(make_module_type).clone() + } + + pub fn nil_type(&self) -> TypeRef { + self.nil_type.get_or_init(|| self.new_type("Nil")).clone() + } + + pub fn prop_type(&self) -> TypeRef { + self.prop_type.get_or_init(|| self.new_type("Prop")).clone() + } + + pub fn str_type(&self) -> TypeRef { + self.str_type.get_or_init(|| self.new_type("Str")).clone() + // self.str_type.get_or_init(make_str_type).clone() + } + + pub fn tuple_type(&self) -> TypeRef { + self.tuple_type.get_or_init(|| self.new_type("Tuple")).clone() + // self.tuple_type.get_or_init(make_tuple_type).clone() + } + + // Singletons ------------------------------------------------------ + + pub fn nil(&self) -> ObjectRef { + self.nil.get_or_init(|| obj_ref!(Nil::new(self.nil_type()))).clone() + } + + pub fn bool(&self, val: bool) -> ObjectRef { + if val { + self.true_() + } else { + self.false_() + } + } + + pub fn true_(&self) -> ObjectRef { + self.true_.get_or_init(|| obj_ref!(Bool::new(self.bool_type(), true))).clone() + } + + pub fn false_(&self) -> ObjectRef { + self.false_.get_or_init(|| obj_ref!(Bool::new(self.bool_type(), false))).clone() + } + + pub fn always(&self) -> ObjectRef { + self.always.get_or_init(|| obj_ref!(Always::new(self.always_type()))).clone() + } + + pub fn empty_str(&self) -> ObjectRef { + self.empty_str + .get_or_init(|| obj_ref!(Str::new(self.str_type(), "".to_owned()))) + .clone() + } + + pub fn newline(&self) -> ObjectRef { + self.newline + .get_or_init(|| obj_ref!(Str::new(self.str_type(), "\n".to_owned()))) + .clone() + } + + pub fn empty_tuple(&self) -> ObjectRef { + self.empty_tuple + .get_or_init(|| obj_ref!(Tuple::new(self.tuple_type(), vec![]))) + .clone() + } + + pub fn ok_err(&self) -> ObjectRef { + self.ok_err + .get_or_init(|| { + obj_ref!(ErrObj::new( + self.err_type(), + ErrKind::Ok, + "".to_string(), + self.nil() + )) + }) + .clone() + } + + // Modules ------------------------------------------------------------- + + pub fn module( + &self, + name: &str, + path: &str, + doc: &str, + entries: &[(&str, ObjectRef)], + ) -> obj_ref_t!(types::module::Module) { + obj_ref!(Module::with_entries( + self.module_type(), + entries, + name.to_owned(), + path.to_owned(), + Code::default(), + Some(doc.to_owned()) + )) + } + + // Functions ----------------------------------------------------------- + + pub fn intrinsic_func( + &self, + module_name: &str, + name: &str, + this_type: Option, + params: &[&str], + doc: &str, + func: IntrinsicFn, + ) -> ObjectRef { + let params = params.iter().map(|n| n.to_string()).collect(); + obj_ref!(IntrinsicFunc::new( + self.intrinsic_func_type(), + module_name.to_owned(), + name.to_owned(), + this_type, + params, + format_doc(doc), + func + )) + } + + pub fn func>( + &self, + module_name: S, + func_name: S, + params: Params, + code: Code, + ) -> ObjectRef { + obj_ref!(Func::new( + self.func_type(), + module_name.into(), + func_name.into(), + params, + code + )) + } + + pub fn bound_func(&self, func: ObjectRef, this: ObjectRef) -> ObjectRef { + obj_ref!(BoundFunc::new(self.bound_func_type(), func, this)) + } + + pub fn closure(&self, func: ObjectRef, captured: ObjectRef) -> ObjectRef { + obj_ref!(Closure::new(self.closure_type(), func, captured)) + } + + pub fn cell(&self) -> ObjectRef { + obj_ref!(Cell::new(self.cell_type())) + } + + pub fn cell_with_value(&self, value: ObjectRef) -> ObjectRef { + obj_ref!(Cell::with_value(self.cell_type(), value)) + } + + pub fn argv_tuple(&self, argv: &[String]) -> ObjectRef { + obj_ref!(Tuple::new( + self.tuple_type(), + argv.iter().map(|a| self.str(a)).collect() + )) + } + + // Numbers --------------------------------------------------------- + + pub fn float(&self, value: f64) -> ObjectRef { + obj_ref!(Float::new(self.float_type(), value)) + } + + pub fn float_from_string>(&self, val: S) -> ObjectRef { + let val = val.into(); + if let Ok(val) = val.parse::() { + self.float(val) + } else { + self.type_err("Could not convert string to Float", self.str(val)) + } + } + + pub fn int>(&self, val: I) -> ObjectRef { + let val = val.into(); + obj_ref!(Int::new(self.int_type(), val)) + } + + pub fn int_from_string>(&self, val: S) -> ObjectRef { + let val = val.into(); + if let Ok(val) = BigInt::from_str_radix(val.as_ref(), 10) { + self.int(val) + } else if let Ok(val) = val.parse::() { + self.int(BigInt::from_f64(val).unwrap()) + } else { + self.type_err("Could not convert string to Int", self.str(val)) + } + } + + // Strings --------------------------------------------------------- + + pub fn str>(&self, val: S) -> ObjectRef { + let val = val.into(); + if val.is_empty() { + self.empty_str() + } else if val == "\n" { + self.newline() + } else { + obj_ref!(Str::new(self.str_type(), val)) + } + } + + // Collections ----------------------------------------------------- + + pub fn iterator(&self, wrapped: Vec) -> ObjectRef { + obj_ref!(FIIterator::new(self.iterator_type(), wrapped)) + } + + pub fn list(&self, items: Vec) -> ObjectRef { + obj_ref!(List::new(self.list_type(), items.to_vec())) + } + + pub fn map(&self, map: IndexMap) -> ObjectRef { + obj_ref!(Map::new(self.map_type(), map)) + } + + pub fn empty_map(&self) -> ObjectRef { + obj_ref!(Map::new(self.map_type(), IndexMap::default())) + } + + pub fn map_from_keys_and_vals( + &self, + keys: Vec, + vals: Vec, + ) -> ObjectRef { + assert_eq!(keys.len(), vals.len()); + self.map(IndexMap::from_iter(keys.into_iter().zip(vals))) + } + + pub fn tuple(&self, items: Vec) -> ObjectRef { + if items.is_empty() { + self.empty_tuple() + } else { + obj_ref!(Tuple::new(self.tuple_type(), items)) + } + } + + // Miscellaneous --------------------------------------------------- + + pub fn file>(&self, file_name: S) -> ObjectRef { + obj_ref!(File::new(self.file_type(), file_name.into())) + } + + pub fn prop(&self, getter: ObjectRef) -> ObjectRef { + obj_ref!(Prop::new(self.prop_type(), getter)) + } + + // Errors ---------------------------------------------------------- + + pub fn err>( + &self, + kind: ErrKind, + msg: S, + obj: ObjectRef, + ) -> ObjectRef { + obj_ref!(ErrObj::new(self.err_type(), kind, msg.into(), obj)) + } + + pub fn err_with_responds_to_bool>( + &self, + kind: ErrKind, + msg: S, + obj: ObjectRef, + ) -> ObjectRef { + obj_ref!(ErrObj::with_responds_to_bool(self.err_type(), kind, msg.into(), obj)) + } + + pub fn arg_err>(&self, msg: S, obj: ObjectRef) -> ObjectRef { + self.err(ErrKind::Arg, msg, obj) + } + + pub fn attr_err>(&self, msg: S, obj: ObjectRef) -> ObjectRef { + self.err(ErrKind::Attr, msg, obj) + } + + pub fn attr_not_found_err>( + &self, + msg: S, + obj: ObjectRef, + ) -> ObjectRef { + self.err(ErrKind::AttrNotFound, msg, obj) + } + + pub fn file_not_found_err>( + &self, + msg: S, + obj: ObjectRef, + ) -> ObjectRef { + self.err(ErrKind::FileNotFound, msg, obj) + } + + pub fn file_unreadable_err>( + &self, + msg: S, + obj: ObjectRef, + ) -> ObjectRef { + self.err(ErrKind::FileUnreadable, msg, obj) + } + + pub fn index_out_of_bounds_err(&self, index: usize, obj: ObjectRef) -> ObjectRef { + self.err(ErrKind::IndexOutOfBounds, index.to_string(), obj) + } + + pub fn not_callable_err(&self, obj: ObjectRef) -> ObjectRef { + self.err(ErrKind::NotCallable, format!("{}", obj.read().unwrap()), obj) + } + + pub fn string_err>(&self, msg: S, obj: ObjectRef) -> ObjectRef { + self.err(ErrKind::String, msg, obj) + } + + pub fn type_err>(&self, msg: S, obj: ObjectRef) -> ObjectRef { + self.err(ErrKind::Type, msg, obj) + } + + // Custom type constructor --------------------------------------------- + + // pub fn custom_type(&self, module: ObjectRef, name: &str) -> ObjectRef { + // let class_ref = obj_ref!(CustomType::new(module.clone(), name.to_owned())); + // + // { + // let mut class = class_ref.write().unwrap(); + // let ns = class.ns_mut(); + // ns.insert( + // "new", + // self.intrinsic_func( + // module.read().unwrap().down_to_mod().unwrap().name(), + // name, + // Some(class_ref.clone()), + // &["attrs"], + // "Create a new custom type. + // + // # Args + // + // - class: TypeRef + // - type_obj: ObjectRef + // - attributes: Map + // + // ", + // |this, args| { + // let attrs_arg = args.get(0).unwrap(); + // let attrs_arg = attrs_arg.read().unwrap(); + // let attrs = attrs_arg.down_to_map().unwrap(); + // + // let mut ns = Namespace::default(); + // ns.extend_from_map(attrs); + // + // // XXX: Cloning the inner object is wonky and breaks + // // identity testing. + // let type_obj = this.read().unwrap(); + // let type_obj = if type_obj.is_type_object() { + // // Called via custom type. + // let type_obj = type_obj.down_to_custom_type().unwrap(); + // obj_ref!(type_obj.clone()) + // } else { + // // Called via custom instance. + // // XXX: This branch isn't reachable because the + // // VM will panic due to the identity test + // // issue noted above. + // let type_obj = type_obj.type_obj(); + // let type_obj = type_obj.read().unwrap(); + // let type_obj = type_obj.down_to_custom_type().unwrap(); + // obj_ref!(type_obj.clone()) + // }; + // + // let instance = CustomObj::new(type_obj, ns); + // obj_ref!(instance) + // }, + // ), + // ); + // } + // + // class_ref + // } +} diff --git a/feint-builtins/src/lib.rs b/feint-builtins/src/lib.rs index ebe10d1..98c8cf5 100644 --- a/feint-builtins/src/lib.rs +++ b/feint-builtins/src/lib.rs @@ -1,6 +1,9 @@ #[macro_use] extern crate bitflags; +pub use builtins::BUILTINS; + +pub mod builtins; pub mod modules; pub mod types; diff --git a/feint-builtins/src/modules/mod.rs b/feint-builtins/src/modules/mod.rs index cfd7f04..a7d66b6 100644 --- a/feint-builtins/src/modules/mod.rs +++ b/feint-builtins/src/modules/mod.rs @@ -2,56 +2,14 @@ use ::std::borrow::Cow; use ::std::collections::HashMap; use ::std::io::Read; use ::std::path::Path; -use ::std::sync::{Arc, RwLock}; use flate2::read::GzDecoder; use once_cell::sync::Lazy; use tar::Archive as TarArchive; -use feint_code_gen::{obj_ref, obj_ref_t}; - -use crate::types::map::Map; -use crate::types::{ObjectRef, ObjectTrait}; - pub mod std; pub use self::std::STD; -/// This mirrors `system.modules`. It provides a way to access -/// modules in Rust code (e.g., in the VM). -pub static MODULES: Lazy = Lazy::new(|| obj_ref!(Map::default())); - -/// Add module to `std.system.modules`. -pub fn add_module(name: &str, module: ObjectRef) { - let modules = MODULES.write().unwrap(); - let modules = modules.down_to_map().unwrap(); - modules.insert(name, module); -} - -/// Get module from `system.modules`. -/// -/// XXX: Panics if the module doesn't exist (since that shouldn't be -/// possible). -pub fn get_module(name: &str) -> ObjectRef { - let modules = MODULES.read().unwrap(); - let modules = modules.down_to_map().unwrap(); - if let Some(module) = modules.get(name) { - module.clone() - } else { - panic!("Module not registered: {name}"); - } -} - -/// Get module from `system.modules`. -/// -/// XXX: This will return `None` if the module doesn't exist. Generally, -/// this should only be used during bootstrap. In most cases, -/// `get_module` should be used instead. -pub fn maybe_get_module(name: &str) -> Option { - let modules = MODULES.read().unwrap(); - let modules = modules.down_to_map().unwrap(); - modules.get(name) -} - /// At build time, a compressed archive is created containing the /// std .fi module files (see `build.rs`). /// diff --git a/feint-builtins/src/modules/std/mod.rs b/feint-builtins/src/modules/std/mod.rs index a882de4..c8ef84e 100644 --- a/feint-builtins/src/modules/std/mod.rs +++ b/feint-builtins/src/modules/std/mod.rs @@ -1,5 +1,3 @@ pub use self::std::STD; -pub use proc::PROC; -mod proc; mod std; diff --git a/feint-builtins/src/modules/std/proc.rs b/feint-builtins/src/modules/std/proc.rs deleted file mode 100644 index d398d41..0000000 --- a/feint-builtins/src/modules/std/proc.rs +++ /dev/null @@ -1,19 +0,0 @@ -//use std::process::Command; -use std::sync::{Arc, RwLock}; - -use once_cell::sync::Lazy; - -use feint_code_gen::obj_ref_t; - -use crate::types::{new, Module}; - -pub static PROC: Lazy = Lazy::new(|| { - new::module( - "std.proc", - "", - "Proc module", - &[ - // TODO: - ], - ) -}); diff --git a/feint-builtins/src/modules/std/std.rs b/feint-builtins/src/modules/std/std.rs index 0eec819..8b9dc5b 100644 --- a/feint-builtins/src/modules/std/std.rs +++ b/feint-builtins/src/modules/std/std.rs @@ -5,57 +5,57 @@ use once_cell::sync::Lazy; use feint_code_gen::{obj_ref_t, use_arg, use_arg_str}; -use crate::types::{self, new}; +use crate::{types, BUILTINS}; pub static STD: Lazy = Lazy::new(|| { - new::module( + BUILTINS.module( "std", "", "std module (builtins)", &[ - ("Type", types::class::TYPE_TYPE.clone()), - ("Always", types::always::ALWAYS_TYPE.clone()), - ("Bool", types::bool::BOOL_TYPE.clone()), - ("BoundFunc", types::bound_func::BOUND_FUNC_TYPE.clone()), - ("IntrinsicFunc", types::intrinsic_func::INTRINSIC_FUNC_TYPE.clone()), - ("Closure", types::closure::CLOSURE_TYPE.clone()), - ("Err", types::err::ERR_TYPE.clone()), - ("ErrType", types::err_type::ERR_TYPE_TYPE.clone()), - ("File", types::file::FILE_TYPE.clone()), - ("Func", types::func::FUNC_TYPE.clone()), - ("Float", types::float::FLOAT_TYPE.clone()), - ("Int", types::int::INT_TYPE.clone()), - ("Iterator", types::iterator::ITERATOR_TYPE.clone()), - ("List", types::list::LIST_TYPE.clone()), - ("Map", types::map::MAP_TYPE.clone()), - ("Module", types::module::MODULE_TYPE.clone()), - ("Nil", types::nil::NIL_TYPE.clone()), - ("Str", types::str::STR_TYPE.clone()), - ("Tuple", types::tuple::TUPLE_TYPE.clone()), - ( - "new_type", - new::intrinsic_func( - "std", - "new_type", - None, - &["module", "name"], - "Make a new custom type - - # Args - - - module: Module - - name: Str - - ", - |_, args| { - let module = args[0].clone(); - let name_arg = use_arg!(args, 1); - let name = use_arg_str!(new_type, name, name_arg); - let class = new::custom_type(module, name); - class - }, - ), - ), + ("Type", BUILTINS.type_type()), + ("Always", BUILTINS.always_type()), + ("Bool", BUILTINS.bool_type()), + ("BoundFunc", BUILTINS.bound_func_type()), + ("IntrinsicFunc", BUILTINS.intrinsic_func_type()), + ("Closure", BUILTINS.closure_type()), + ("Err", BUILTINS.err_type()), + ("ErrType", BUILTINS.err_type_type()), + ("File", BUILTINS.file_type()), + ("Func", BUILTINS.func_type()), + ("Float", BUILTINS.float_type()), + ("Int", BUILTINS.int_type()), + ("Iterator", BUILTINS.iterator_type()), + ("List", BUILTINS.list_type()), + ("Map", BUILTINS.map_type()), + ("Module", BUILTINS.module_type()), + ("Nil", BUILTINS.nil_type()), + ("Str", BUILTINS.str_type()), + ("Tuple", BUILTINS.tuple_type()), + // ( + // "new_type", + // BUILTINS.intrinsic_func( + // "std", + // "new_type", + // None, + // &["module", "name"], + // "Make a new custom type + // + // # Args + // + // - module: Module + // - name: Str + // + // ", + // |_, args| { + // let module = args[0].clone(); + // let name_arg = use_arg!(args, 1); + // let name = use_arg_str!(new_type, name, name_arg); + // let class = BUILTINS.custom_type(module, name); + // class + // }, + // ), + // ), ], ) }); diff --git a/feint-builtins/src/tests/types.rs b/feint-builtins/src/tests/types.rs index 26e425e..4f0183a 100644 --- a/feint-builtins/src/tests/types.rs +++ b/feint-builtins/src/tests/types.rs @@ -1,4 +1,5 @@ -use crate::types::{new, ObjectRef}; +use crate::types::{ObjectRef, ObjectTrait}; +use crate::BUILTINS; fn check_ok>(obj: ObjectRef, msg: S) { assert!(!obj.read().unwrap().is_err(), "{}", msg.into()) @@ -64,9 +65,9 @@ mod float { #[test] fn test_float() { - let float1 = new::float(0.0); - let float2 = new::float(0.0); - let float3 = new::float(1.0); + let float1 = BUILTINS.float(0.0); + let float2 = BUILTINS.float(0.0); + let float3 = BUILTINS.float(1.0); check_type_is(float1.clone(), float2.clone()); check_type_is(float2.clone(), float3.clone()); @@ -84,8 +85,8 @@ mod float { #[test] fn test_compare_to_int() { - let float = new::float(1.0); - let int = new::int(1); + let float = BUILTINS.float(1.0); + let int = BUILTINS.int(1); check_eq(float.clone(), int.clone()); check_eq(int.clone(), float.clone()); } @@ -96,7 +97,7 @@ mod list { #[test] fn test_push_exists() { - let obj_ref = new::list(vec![]); + let obj_ref = BUILTINS.list(vec![]); let list = obj_ref.read().unwrap(); let push = list.get_attr("push", obj_ref.clone()); check_ok(push.clone(), "list.push() is not OK"); @@ -110,7 +111,7 @@ mod custom { use super::*; fn instance(type_obj: ObjectRef, attrs: &[(&str, ObjectRef)]) -> ObjectRef { - let attrs = new::map(IndexMap::from_iter( + let attrs = BUILTINS.map(IndexMap::from_iter( attrs.into_iter().map(|(n, v)| (n.to_string(), v.clone())), )); let new = type_obj.read().unwrap().get_attr("new", type_obj.clone()); @@ -122,14 +123,14 @@ mod custom { #[test] fn test_custom() { - let mod1 = new::module("test1", "", "test module 1", &[]); - let t1 = new::custom_type(mod1, "Custom1"); - let t1_obj1 = instance(t1.clone(), &[("value", new::nil())]); - let t1_obj2 = instance(t1.clone(), &[("value", new::nil())]); - let t1_obj3 = instance(t1.clone(), &[("value", new::nil())]); - - let mod2 = new::module("test2", "", "test module 2", &[]); - let t2 = new::custom_type(mod2, "Custom2"); + let mod1 = BUILTINS.module("test1", "", "test module 1", &[]); + let t1 = BUILTINS.custom_type(mod1, "Custom1"); + let t1_obj1 = instance(t1.clone(), &[("value", BUILTINS.nil())]); + let t1_obj2 = instance(t1.clone(), &[("value", BUILTINS.nil())]); + let t1_obj3 = instance(t1.clone(), &[("value", BUILTINS.nil())]); + + let mod2 = BUILTINS.module("test2", "", "test module 2", &[]); + let t2 = BUILTINS.custom_type(mod2, "Custom2"); let t2_obj1 = instance(t2.clone(), &[]); check_attr(t1.clone(), "$id"); @@ -137,11 +138,14 @@ mod custom { check_attr(t1_obj1.clone(), "$id"); check_attr(t1_obj1.clone(), "$type"); - let result = - t1_obj3.write().unwrap().set_attr("value", new::int(1), t1_obj3.clone()); + let result = t1_obj3.write().unwrap().set_attr( + "value", + BUILTINS.int(1), + t1_obj3.clone(), + ); check_ok(result, "Could not set `value` on t1_obj3"); check_attr(t1_obj3.clone(), "value"); - check_attr_eq(t1_obj3.clone(), "value", new::int(1)); + check_attr_eq(t1_obj3.clone(), "value", BUILTINS.int(1)); // An object should be equal to itself. check_eq(t1_obj1.clone(), t1_obj1.clone()); diff --git a/feint-builtins/src/types/always.rs b/feint-builtins/src/types/always.rs index bc38d50..f96dd50 100644 --- a/feint-builtins/src/types/always.rs +++ b/feint-builtins/src/types/always.rs @@ -1,41 +1,28 @@ use std::any::Any; use std::fmt; -use std::sync::{Arc, RwLock}; - -use once_cell::sync::Lazy; use feint_code_gen::*; -use super::new; - -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; +use super::base::{ObjectRef, ObjectTrait, TypeRef}; use super::ns::Namespace; -// AlwaysType Type ----------------------------------------------------- - -type_and_impls!(AlwaysType, Always); - -pub static ALWAYS_TYPE: Lazy = - Lazy::new(|| obj_ref!(AlwaysType::new())); - -// Always Object ------------------------------------------------------- +// Always -------------------------------------------------------------- pub struct Always { + class: TypeRef, ns: Namespace, } standard_object_impls!(Always); impl Always { - #[allow(clippy::new_without_default)] - pub fn new() -> Self { - Self { ns: Namespace::default() } + pub fn new(class: TypeRef) -> Self { + Self { class, ns: Namespace::default() } } } impl ObjectTrait for Always { - object_trait_header!(ALWAYS_TYPE); + object_trait_header!(); fn is_equal(&self, _rhs: &dyn ObjectTrait) -> bool { true diff --git a/feint-builtins/src/types/base.rs b/feint-builtins/src/types/base.rs index 66ef95d..b6e8231 100644 --- a/feint-builtins/src/types/base.rs +++ b/feint-builtins/src/types/base.rs @@ -8,62 +8,37 @@ use num_traits::ToPrimitive; use feint_code_gen::*; -use crate::modules::std::STD; +use crate::BUILTINS; use super::func_trait::FuncTrait; -use super::new; use super::ns::Namespace; -use super::always::{Always, AlwaysType}; -use super::bool::{Bool, BoolType}; -use super::bound_func::{BoundFunc, BoundFuncType}; -use super::cell::{Cell, CellType}; -use super::class::{Type, TypeType}; -use super::closure::{Closure, ClosureType}; -use super::custom::{CustomObj, CustomType}; -use super::err::{ErrObj, ErrType}; -use super::err_type::{ErrTypeObj, ErrTypeType}; -use super::file::{File, FileType}; -use super::float::{Float, FloatType}; -use super::func::{Func, FuncType}; -use super::int::{Int, IntType}; -use super::intrinsic_func::{IntrinsicFunc, IntrinsicFuncType}; -use super::iterator::{FIIterator, IteratorType}; -use super::list::{List, ListType}; -use super::map::{Map, MapType}; -use super::module::{Module, ModuleType}; -use super::nil::{Nil, NilType}; -use super::prop::{Prop, PropType}; -use super::str::{Str, StrType}; -use super::tuple::{Tuple, TupleType}; - -pub type TypeRef = obj_ref_t!(dyn TypeTrait); +use super::always::Always; +use super::bool::Bool; +use super::bound_func::BoundFunc; +use super::cell::Cell; +use super::class::Type; +use super::closure::Closure; +use super::custom::CustomObj; +use super::err::ErrObj; +use super::err_type::ErrTypeObj; +use super::file::File; +use super::float::Float; +use super::func::Func; +use super::int::Int; +use super::intrinsic_func::IntrinsicFunc; +use super::iterator::FIIterator; +use super::list::List; +use super::map::Map; +use super::module::Module; +use super::nil::Nil; +use super::prop::Prop; +use super::str::Str; +use super::tuple::Tuple; + +pub type TypeRef = obj_ref_t!(Type); pub type ObjectRef = obj_ref_t!(dyn ObjectTrait); -// Type Trait ---------------------------------------------------------- - -/// Types in the system are backed by an implementation of `TypeTrait`. -/// Each type implementation will be instantiated exactly once (i.e., -/// types are singletons). Example: `IntType`. -pub trait TypeTrait { - fn name(&self) -> &str; - fn full_name(&self) -> &str; - fn ns(&self) -> &Namespace; - - fn module(&self) -> ObjectRef { - STD.clone() - } - - fn id(&self) -> usize { - let p = self as *const Self; - p as *const () as usize - } - - fn is(&self, other: &dyn TypeTrait) -> bool { - self.id() == other.id() - } -} - // Object Trait -------------------------------------------------------- /// Create associated function to check is object ref is a specific impl @@ -141,9 +116,6 @@ pub trait ObjectTrait { fn ns(&self) -> &Namespace; fn ns_mut(&mut self) -> &mut Namespace; - /// Cast object to type, if possible. - fn as_type(&self) -> Option<&dyn TypeTrait>; - fn id(&self) -> usize { let p = self as *const Self; p as *const () as usize @@ -151,7 +123,7 @@ pub trait ObjectTrait { fn id_obj(&self) -> ObjectRef { // TODO: Cache? - new::int(self.id()) + BUILTINS.int(self.id()) } /// XXX: This resolves to `std` unless overridden. @@ -175,6 +147,10 @@ pub trait ObjectTrait { /// TODO: There's probably a more elegant way to do this, but it /// might require a bit of re-architecting. fn get_attr(&self, name: &str, this: ObjectRef) -> ObjectRef { + self.base_get_attr(name, this) + } + + fn base_get_attr(&self, name: &str, this: ObjectRef) -> ObjectRef { // Special attributes that *cannot* be overridden -------------- if name == "$id" { return self.id_obj(); @@ -198,8 +174,8 @@ pub trait ObjectTrait { names.extend(obj_ns.iter().map(|(n, _)| n).cloned()); names.sort(); names.dedup(); - let items = names.iter().map(new::str).collect(); - return new::tuple(items); + let items = names.iter().map(|n| BUILTINS.str(n)).collect(); + return BUILTINS.tuple(items); } // if name == "$dis" { @@ -232,7 +208,7 @@ pub trait ObjectTrait { // } else { // eprintln!("Cannot disassemble object: {}", &*this.read().unwrap()); // } - // return new::nil(); + // return BUILTINS.nil(); // } // Instance attributes ----------------------------------------- @@ -262,9 +238,9 @@ pub trait ObjectTrait { if name == "ok" { let this = this.read().unwrap(); return if let Some(err) = this.down_to_err() { - new::bool(!err.retrieve_bool_val()) + BUILTINS.bool(!err.retrieve_bool_val()) } else { - new::bool(true) + BUILTINS.bool(true) }; } @@ -277,13 +253,13 @@ pub trait ObjectTrait { // that responds to bool is returned. if name == "err" { return if let Some(err) = this.read().unwrap().down_to_err() { - new::err_with_responds_to_bool( + BUILTINS.err_with_responds_to_bool( err.kind.clone(), err.message.as_str(), this.clone(), ) } else { - new::ok_err() + BUILTINS.ok_err() }; } @@ -291,7 +267,7 @@ pub trait ObjectTrait { return if self.is_str() { this.clone() } else { - new::str(this.read().unwrap().to_string()) + BUILTINS.str(this.read().unwrap().to_string()) }; } @@ -317,14 +293,14 @@ pub trait ObjectTrait { } fn attr_not_found(&self, name: &str, obj: ObjectRef) -> ObjectRef { - new::attr_not_found_err(name, obj) + BUILTINS.attr_not_found_err(name, obj) } // Items (accessed by index) --------------------------------------- fn get_item(&self, index: usize, this: ObjectRef) -> ObjectRef { // TODO: The default should be a "does not support" indexing err - new::index_out_of_bounds_err(index, this) + BUILTINS.index_out_of_bounds_err(index, this) } fn set_item( @@ -334,37 +310,15 @@ pub trait ObjectTrait { _value: ObjectRef, ) -> ObjectRef { // TODO: The default should be a "does not support" indexing err - new::index_out_of_bounds_err(index, this) + BUILTINS.index_out_of_bounds_err(index, this) } fn index_out_of_bounds(&self, index: usize, this: ObjectRef) -> ObjectRef { - new::index_out_of_bounds_err(index, this) + BUILTINS.index_out_of_bounds_err(index, this) } // Type checkers --------------------------------------------------- - make_type_checker!(is_type_type, TypeType); - make_type_checker!(is_always_type, AlwaysType); - make_type_checker!(is_bool_type, BoolType); - make_type_checker!(is_bound_func_type, BoundFuncType); - make_type_checker!(is_intrinsic_func_type, IntrinsicFuncType); - make_type_checker!(is_cell_type, CellType); - make_type_checker!(is_closure_type, ClosureType); - make_type_checker!(is_err_type, ErrType); - make_type_checker!(is_err_type_type, ErrTypeType); - make_type_checker!(is_file_type, FileType); - make_type_checker!(is_float_type, FloatType); - make_type_checker!(is_func_type, FuncType); - make_type_checker!(is_int_type, IntType); - make_type_checker!(is_iterator_type, IteratorType); - make_type_checker!(is_list_type, ListType); - make_type_checker!(is_map_type, MapType); - make_type_checker!(is_mod_type, ModuleType); - make_type_checker!(is_nil_type, NilType); - make_type_checker!(is_prop_type, PropType); - make_type_checker!(is_str_type, StrType); - make_type_checker!(is_tuple_type, TupleType); - make_type_checker!(is_type, Type); make_type_checker!(is_always, Always); make_type_checker!(is_bool, Bool); @@ -387,11 +341,6 @@ pub trait ObjectTrait { make_type_checker!(is_str, Str); make_type_checker!(is_tuple, Tuple); - /// Is this object a type object? - fn is_type_object(&self) -> bool { - self.type_obj().read().unwrap().is_type_type() - } - fn is_immutable(&self) -> bool { !(self.is_cell() || self.is_file() || self.is_list() || self.is_map()) } @@ -404,29 +353,6 @@ pub trait ObjectTrait { // // These downcast object refs to their concrete types. - make_down_to!(down_to_type_type, TypeType); - make_down_to!(down_to_always_type, AlwaysType); - make_down_to!(down_to_bool_type, BoolType); - make_down_to!(down_to_bound_func_type, BoundFuncType); - make_down_to!(down_to_intrinsic_func_type, IntrinsicFuncType); - make_down_to!(down_to_cell_type, CellType); - make_down_to!(down_to_closure_type, ClosureType); - make_down_to!(down_to_custom_type, CustomType); - make_down_to!(down_to_err_type, ErrType); - make_down_to!(down_to_err_type_type, ErrTypeType); - make_down_to!(down_to_file_type, FileType); - make_down_to!(down_to_float_type, FloatType); - make_down_to!(down_to_func_type, FuncType); - make_down_to!(down_to_list_type, ListType); - make_down_to!(down_to_int_type, IntType); - make_down_to!(down_to_iterator_type, IteratorType); - make_down_to!(down_to_map_type, MapType); - make_down_to!(down_to_mod_type, ModuleType); - make_down_to!(down_to_nil_type, NilType); - make_down_to!(down_to_prop_type, PropType); - make_down_to!(down_to_str_type, StrType); - make_down_to!(down_to_tuple_type, TupleType); - make_down_to!(down_to_type, Type); make_down_to!(down_to_always, Always); make_down_to!(down_to_bool, Bool); @@ -546,22 +472,6 @@ pub trait ObjectTrait { // Display ------------------------------------------------------------- -macro_rules! write_type_instance { - ( $f:ident, $t:ident, $($A:ty),+ ) => { $( - if let Some(t) = $t.as_any().downcast_ref::<$A>() { - return write!($f, "", t.full_name()); - } - )+ }; -} - -macro_rules! debug_type_instance { - ( $f:ident, $t:ident, $($A:ty),+ ) => { $( - if let Some(t) = $t.as_any().downcast_ref::<$A>() { - return write!($f, "", t.full_name(), ObjectTrait::id(t)); - } - )+ }; -} - macro_rules! write_instance { ( $f:ident, $i:ident, $($A:ty),+ ) => { $( if let Some(i) = $i.as_any().downcast_ref::<$A>() { @@ -578,50 +488,11 @@ macro_rules! debug_instance { )+ }; } -impl fmt::Display for dyn TypeTrait { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "", self.full_name()) - } -} - -impl fmt::Debug for dyn TypeTrait { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "", self.full_name(), self.id()) - } -} - impl fmt::Display for dyn ObjectTrait { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write_type_instance!( - f, - self, - TypeType, - AlwaysType, - BoolType, - BoundFuncType, - IntrinsicFuncType, - CellType, - ClosureType, - CustomType, - ErrType, - ErrTypeType, - FileType, - FloatType, - FuncType, - IntType, - IteratorType, - ListType, - MapType, - ModuleType, - NilType, - PropType, - StrType, - TupleType - ); write_instance!( f, self, - Type, Always, Bool, BoundFunc, @@ -650,32 +521,6 @@ impl fmt::Display for dyn ObjectTrait { impl fmt::Debug for dyn ObjectTrait { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - debug_type_instance!( - f, - self, - TypeType, - AlwaysType, - BoolType, - BoundFuncType, - IntrinsicFuncType, - CellType, - ClosureType, - CustomType, - ErrType, - ErrTypeType, - FileType, - FloatType, - FuncType, - IntType, - IteratorType, - ListType, - MapType, - ModuleType, - NilType, - PropType, - StrType, - TupleType - ); debug_instance!( f, self, diff --git a/feint-builtins/src/types/bool.rs b/feint-builtins/src/types/bool.rs index 74df938..8d86a94 100644 --- a/feint-builtins/src/types/bool.rs +++ b/feint-builtins/src/types/bool.rs @@ -1,27 +1,15 @@ use std::any::Any; use std::fmt; -use std::sync::{Arc, RwLock}; - -use once_cell::sync::Lazy; use feint_code_gen::*; -use super::new; - -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; +use super::base::{ObjectRef, ObjectTrait, TypeRef}; use super::ns::Namespace; -// Bool Type ----------------------------------------------------------- - -type_and_impls!(BoolType, Bool); - -pub static BOOL_TYPE: Lazy = - Lazy::new(|| obj_ref!(BoolType::new())); - -// Bool Object --------------------------------------------------------- +// Bool ---------------------------------------------------------------- pub struct Bool { + class: TypeRef, ns: Namespace, value: bool, } @@ -29,8 +17,8 @@ pub struct Bool { standard_object_impls!(Bool); impl Bool { - pub fn new(value: bool) -> Self { - Self { ns: Namespace::default(), value } + pub fn new(class: TypeRef, value: bool) -> Self { + Self { class, ns: Namespace::default(), value } } pub fn value(&self) -> &bool { @@ -39,7 +27,7 @@ impl Bool { } impl ObjectTrait for Bool { - object_trait_header!(BOOL_TYPE); + object_trait_header!(); // Unary operations ----------------------------------------------- diff --git a/feint-builtins/src/types/bound_func.rs b/feint-builtins/src/types/bound_func.rs index 6fb8b46..2367875 100644 --- a/feint-builtins/src/types/bound_func.rs +++ b/feint-builtins/src/types/bound_func.rs @@ -2,28 +2,20 @@ use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use once_cell::sync::Lazy; - use feint_code_gen::*; -use super::new; +use crate::BUILTINS; + +use super::base::{ObjectRef, ObjectTrait, TypeRef}; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; use super::func_trait::FuncTrait; use super::ns::Namespace; use super::Params; -// Bound Function Type ------------------------------------------------- - -type_and_impls!(BoundFuncType, BoundFunc); - -pub static BOUND_FUNC_TYPE: Lazy = - Lazy::new(|| obj_ref!(BoundFuncType::new())); - -// BoundFunc Object ---------------------------------------------------------- +// BoundFunc ---------------------------------------------------------- pub struct BoundFunc { + class: TypeRef, ns: Namespace, module_name: String, func: ObjectRef, @@ -35,7 +27,7 @@ pub struct BoundFunc { standard_object_impls!(BoundFunc); impl BoundFunc { - pub fn new(func_ref: ObjectRef, this: ObjectRef) -> Self { + pub fn new(class: TypeRef, func_ref: ObjectRef, this: ObjectRef) -> Self { let (module_name, name, doc, params, params_tuple, arity, has_var_args) = { let func_guard = func_ref.read().unwrap(); @@ -62,14 +54,15 @@ impl BoundFunc { }; Self { + class, ns: Namespace::with_entries(&[ - ("$module_name", new::str(&module_name)), - ("$full_name", new::str(format!("{module_name}.{name}"))), - ("$name", new::str(&name)), + ("$module_name", BUILTINS.str(&module_name)), + ("$full_name", BUILTINS.str(format!("{module_name}.{name}"))), + ("$name", BUILTINS.str(&name)), ("$params", params_tuple), ("$doc", doc), - ("$arity", new::int(arity)), - ("$has_var_args", new::bool(has_var_args)), + ("$arity", BUILTINS.int(arity)), + ("$has_var_args", BUILTINS.bool(has_var_args)), ]), module_name, func: func_ref, @@ -111,7 +104,7 @@ impl FuncTrait for BoundFunc { } impl ObjectTrait for BoundFunc { - object_trait_header!(BOUND_FUNC_TYPE); + object_trait_header!(); fn module(&self) -> ObjectRef { self.func().read().unwrap().module() diff --git a/feint-builtins/src/types/cell.rs b/feint-builtins/src/types/cell.rs index 8e045a0..d60e745 100644 --- a/feint-builtins/src/types/cell.rs +++ b/feint-builtins/src/types/cell.rs @@ -2,26 +2,18 @@ use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use once_cell::sync::Lazy; - use feint_code_gen::*; -use super::new; - -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; -use super::ns::Namespace; +use crate::BUILTINS; -// Cell Type ----------------------------------------------------------- +use super::base::{ObjectRef, ObjectTrait, TypeRef}; -type_and_impls!(CellType, Cell); - -pub static CELL_TYPE: Lazy = - Lazy::new(|| obj_ref!(CellType::new())); +use super::ns::Namespace; -// Cell Object --------------------------------------------------------- +// Cell --------------------------------------------------------- pub struct Cell { + class: TypeRef, ns: Namespace, value: ObjectRef, } @@ -29,13 +21,12 @@ pub struct Cell { standard_object_impls!(Cell); impl Cell { - #[allow(clippy::new_without_default)] - pub fn new() -> Self { - Self { ns: Namespace::default(), value: new::nil() } + pub fn new(class: TypeRef) -> Self { + Self { class, ns: Namespace::default(), value: BUILTINS.nil() } } - pub fn with_value(value: ObjectRef) -> Self { - let mut cell = Self::new(); + pub fn with_value(class: TypeRef, value: ObjectRef) -> Self { + let mut cell = Self::new(class); cell.set_value(value); cell } @@ -50,7 +41,7 @@ impl Cell { } impl ObjectTrait for Cell { - object_trait_header!(CELL_TYPE); + object_trait_header!(); fn bool_val(&self) -> Option { Some(false) diff --git a/feint-builtins/src/types/class.rs b/feint-builtins/src/types/class.rs index 44a92c2..6bf8ee6 100644 --- a/feint-builtins/src/types/class.rs +++ b/feint-builtins/src/types/class.rs @@ -3,52 +3,84 @@ //! latter is a Rust keyword. use std::any::Any; use std::fmt; -use std::sync::{Arc, RwLock}; - -use once_cell::sync::Lazy; use feint_code_gen::*; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::new; -use super::ns::Namespace; - -// Type Type ----------------------------------------------------------- +use crate::BUILTINS; -type_and_impls!(TypeType, Type); - -pub static TYPE_TYPE: Lazy = - Lazy::new(|| obj_ref!(TypeType::new())); - -// Type Object --------------------------------------------------------- +use super::base::{ObjectRef, ObjectTrait, TypeRef}; +use super::ns::Namespace; pub struct Type { + module_name: String, + name: String, ns: Namespace, } standard_object_impls!(Type); impl Type { - #[allow(clippy::new_without_default)] - pub fn new() -> Self { - Self { ns: Namespace::default() } + pub fn new(module_name: &str, name: &str) -> Self { + Self { + module_name: module_name.to_owned(), + name: name.to_owned(), + ns: Namespace::default(), + } + } + + pub fn module_name(&self) -> &String { + &self.module_name + } + + pub fn name(&self) -> &String { + &self.name } } impl ObjectTrait for Type { - object_trait_header!(TYPE_TYPE); -} + fn as_any(&self) -> &dyn Any { + self + } -// Display ------------------------------------------------------------- + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn class(&self) -> TypeRef { + BUILTINS.type_type() + } + + fn type_obj(&self) -> ObjectRef { + BUILTINS.type_type() + } + + fn ns(&self) -> &Namespace { + &self.ns + } + + fn ns_mut(&mut self) -> &mut Namespace { + &mut self.ns + } + + fn get_attr(&self, name: &str, this: ObjectRef) -> ObjectRef { + // TODO: Don't recreate attrs on every access + match name { + "$module" => BUILTINS.str(&self.module_name), + "$name" => BUILTINS.str(&self.name), + "$full_name" => BUILTINS.str(format!("{}.{}", self.module_name, self.name)), + _ => self.base_get_attr(name, this), + } + } +} impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} @ {}", self.type_obj().read().unwrap(), self.id()) + write!(f, "", self.name) } } impl fmt::Debug for Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{self}") + write!(f, "", self.module_name, self.name, self.id()) } } diff --git a/feint-builtins/src/types/closure.rs b/feint-builtins/src/types/closure.rs index 3c1bf36..cde43b9 100644 --- a/feint-builtins/src/types/closure.rs +++ b/feint-builtins/src/types/closure.rs @@ -2,26 +2,21 @@ use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use once_cell::sync::Lazy; - use feint_code_gen::*; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; -use super::func_trait::FuncTrait; -use super::ns::Namespace; -use super::{new, Params}; - -// Closure Type -------------------------------------------------------- +use crate::BUILTINS; -type_and_impls!(ClosureType, Closure); +use super::base::{ObjectRef, ObjectTrait, TypeRef}; -pub static CLOSURE_TYPE: Lazy = - Lazy::new(|| obj_ref!(ClosureType::new())); +use super::func_trait::FuncTrait; +use super::ns::Namespace; +use super::Params; -// Closure Object ------------------------------------------------------ +// Closure ------------------------------------------------------------- pub struct Closure { + class: TypeRef, + ns: Namespace, module_name: String, name: String, @@ -34,15 +29,16 @@ pub struct Closure { standard_object_impls!(Closure); impl Closure { - pub fn new(func_ref: ObjectRef, captured: ObjectRef) -> Self { + pub fn new(class: TypeRef, func_ref: ObjectRef, captured: ObjectRef) -> Self { let func = func_ref.read().unwrap(); let func = func.down_to_func().unwrap(); Self { + class, ns: Namespace::with_entries(&[ ("$params", func.get_params()), ("$doc", func.get_doc()), - ("$arity", new::int(func.arity())), - ("$has_var_args", new::bool(func.has_var_args())), + ("$arity", BUILTINS.int(func.arity())), + ("$has_var_args", BUILTINS.bool(func.has_var_args())), ]), module_name: func.module_name().to_owned(), name: func.name().to_owned(), @@ -91,7 +87,7 @@ impl FuncTrait for Closure { } impl ObjectTrait for Closure { - object_trait_header!(CLOSURE_TYPE); + object_trait_header!(); } // Display ------------------------------------------------------------- diff --git a/feint-builtins/src/types/code.rs b/feint-builtins/src/types/code.rs index 34cd4e0..455f0ff 100644 --- a/feint-builtins/src/types/code.rs +++ b/feint-builtins/src/types/code.rs @@ -5,7 +5,8 @@ use feint_util::op::{BinaryOperator, CompareOperator, InplaceOperator, UnaryOper use feint_util::source::Location; use feint_util::string::format_doc; -use crate::types::{new, FuncTrait, ObjectRef}; +use crate::types::{FuncTrait, ObjectRef}; +use crate::BUILTINS; type FreeVarEntry = ( usize, // address @@ -106,11 +107,11 @@ impl Code { if let Some(obj_ref) = self.get_const(0) { let obj = obj_ref.read().unwrap(); if let Some(doc) = obj.get_str_val() { - return new::str(format_doc(doc)); + return BUILTINS.str(format_doc(doc)); } } } - new::nil() + BUILTINS.nil() } // Instructions ---------------------------------------------------- diff --git a/feint-builtins/src/types/custom.rs b/feint-builtins/src/types/custom.rs index 1e48b64..87be9bb 100644 --- a/feint-builtins/src/types/custom.rs +++ b/feint-builtins/src/types/custom.rs @@ -2,142 +2,31 @@ use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use super::new; use feint_code_gen::*; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; -use super::ns::Namespace; - -// Custom Type --------------------------------------------------------- +use crate::BUILTINS; -/// TODO: This shouldn't need to be cloneable -#[derive(Clone)] -pub struct CustomType { - ns: Namespace, - module: ObjectRef, - name: String, - full_name: String, -} - -impl CustomType { - pub fn new(module_ref: ObjectRef, name: String) -> Self { - let module = module_ref.read().unwrap(); - let module = module.down_to_mod().unwrap(); - let full_name = format!("{}.{name}", module.name()); - let type_ref = Self { - ns: Namespace::with_entries(&[ - // Class Attributes - ("$module_name", new::str(module.name())), - ("$full_name", new::str(&full_name)), - ("$name", new::str(&name)), - ]), - module: module_ref.clone(), - name, - full_name, - }; - type_ref - } -} - -standard_object_impls!(CustomType); - -impl TypeTrait for CustomType { - fn name(&self) -> &str { - self.name.as_str() - } - - fn full_name(&self) -> &str { - self.full_name.as_str() - } +use super::base::{ObjectRef, ObjectTrait, TypeRef}; - fn ns(&self) -> &Namespace { - &self.ns - } - - fn module(&self) -> ObjectRef { - self.module.clone() - } -} - -/// NOTE: This is customized so the module is correct. -impl ObjectTrait for CustomType { - fn as_any(&self) -> &dyn Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } - - fn class(&self) -> TypeRef { - TYPE_TYPE.clone() - } - - fn type_obj(&self) -> ObjectRef { - TYPE_TYPE.clone() - } - - fn ns(&self) -> &Namespace { - &self.ns - } - - fn ns_mut(&mut self) -> &mut Namespace { - &mut self.ns - } - - fn as_type(&self) -> Option<&dyn TypeTrait> { - Some(self) - } - - fn module(&self) -> ObjectRef { - self.module.clone() - } -} +use super::ns::Namespace; -// Custom Object ------------------------------------------------------- +// Custom Type --------------------------------------------------------- pub struct CustomObj { - type_obj: obj_ref_t!(CustomType), + class: TypeRef, ns: Namespace, } standard_object_impls!(CustomObj); impl CustomObj { - pub fn new(type_obj: obj_ref_t!(CustomType), attrs: Namespace) -> Self { - Self { type_obj, ns: attrs } + pub fn new(class: TypeRef, attrs: Namespace) -> Self { + Self { class, ns: attrs } } } impl ObjectTrait for CustomObj { - fn as_any(&self) -> &dyn Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } - - fn class(&self) -> TypeRef { - self.type_obj.clone() - } - - fn type_obj(&self) -> ObjectRef { - self.type_obj.clone() - } - - fn ns(&self) -> &Namespace { - &self.ns - } - - fn ns_mut(&mut self) -> &mut Namespace { - &mut self.ns - } - - fn as_type(&self) -> Option<&dyn TypeTrait> { - None - } + object_trait_header!(); fn set_attr( &mut self, @@ -146,7 +35,7 @@ impl ObjectTrait for CustomObj { _this: ObjectRef, ) -> ObjectRef { self.ns.set(name, value); - new::nil() + BUILTINS.nil() } fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { @@ -158,9 +47,10 @@ impl ObjectTrait for CustomObj { impl fmt::Display for CustomObj { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let class = self.class(); - let class = class.read().unwrap(); - write!(f, "<{} object @ {}>", class.full_name(), self.id()) + // let class = self.class(); + // let class = class.read().unwrap(); + // write!(f, "<{} object @ {}>", class.full_name(), self.id()) + write!(f, "", self.id()) } } diff --git a/feint-builtins/src/types/err.rs b/feint-builtins/src/types/err.rs index 253f704..710be52 100644 --- a/feint-builtins/src/types/err.rs +++ b/feint-builtins/src/types/err.rs @@ -17,87 +17,82 @@ use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use once_cell::sync::Lazy; - use feint_code_gen::*; use crate::util::check_args; +use crate::BUILTINS; + +use super::base::{ObjectRef, ObjectTrait, TypeRef}; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; use super::err_type::ErrKind; -use super::new; use super::ns::Namespace; -// Err Type ------------------------------------------------------------ - -type_and_impls!(ErrType, Err); - -pub static ERR_TYPE: Lazy = Lazy::new(|| { - let type_ref = obj_ref!(ErrType::new()); - let mut type_obj = type_ref.write().unwrap(); - - type_obj.add_attrs(&[ - // Class Methods ----------------------------------------------- - meth!("new", type_ref, &["type", "msg"], "", |_, args| { - let name = "Err.new()"; - - let result = check_args(name, &args, false, 2, Some(2)); - if let Err(err) = result { - return err; - } - - let type_arg = use_arg!(args, 0); - let msg_arg = use_arg!(args, 1); - - let err_type = if let Some(err_type) = type_arg.down_to_err_type_obj() { - err_type - } else { - let arg_err_msg = format!("{name} expected type to be an ErrType"); - // NOTE: This is problematic because user code won't be - // able to tell if the arg error was the result of - // creating an arg err explicitly or the result of - // an internal error. Note that this applies to - // *any* user-constructible error. - // - // TODO: Figure out a solution for this, perhaps an err - // type that is *not* user-constructible or a - // nested err type? - return new::arg_err(arg_err_msg, new::nil()); - }; - - let kind = err_type.kind().clone(); - - let msg = if let Some(msg) = msg_arg.get_str_val() { - msg - } else { - let arg_err_msg = format!("{name} expected message to be a Str"); - return new::arg_err(arg_err_msg, new::nil()); - }; - - new::err(kind, msg, new::nil()) - }), - // Instance Attributes ----------------------------------------- - prop!("type", type_ref, "", |this, _| { - let this = this.read().unwrap(); - let this = this.down_to_err().unwrap(); - this.kind.get_obj().unwrap() - }), - prop!("message", type_ref, "", |this, _| { - let this = this.read().unwrap(); - let this = this.down_to_err().unwrap(); - new::str(&this.message) - }), - ]); - - type_ref.clone() -}); - -// Error Object -------------------------------------------------------- +// pub fn make_err_type() -> obj_ref_t!(ErrType) { +// let type_ref = obj_ref!(ErrType::new()); +// let mut type_obj = type_ref.write().unwrap(); +// +// type_obj.add_attrs(&[ +// // Class Methods ----------------------------------------------- +// meth!("new", type_ref, &["type", "msg"], "", |_, args| { +// let name = "Err.new()"; +// +// let result = check_args(name, &args, false, 2, Some(2)); +// if let Err(err) = result { +// return err; +// } +// +// let type_arg = use_arg!(args, 0); +// let msg_arg = use_arg!(args, 1); +// +// let err_type = if let Some(err_type) = type_arg.down_to_err_type_obj() { +// err_type +// } else { +// let arg_err_msg = format!("{name} expected type to be an ErrType"); +// // NOTE: This is problematic because user code won't be +// // able to tell if the arg error was the result of +// // creating an arg err explicitly or the result of +// // an internal error. Note that this applies to +// // *any* user-constructible error. +// // +// // TODO: Figure out a solution for this, perhaps an err +// // type that is *not* user-constructible or a +// // nested err type? +// return BUILTINS.arg_err(arg_err_msg, BUILTINS.nil()); +// }; +// +// let kind = err_type.kind().clone(); +// +// let msg = if let Some(msg) = msg_arg.get_str_val() { +// msg +// } else { +// let arg_err_msg = format!("{name} expected message to be a Str"); +// return BUILTINS.arg_err(arg_err_msg, BUILTINS.nil()); +// }; +// +// BUILTINS.err(kind, msg, BUILTINS.nil()) +// }), +// // Instance Attributes ----------------------------------------- +// prop!("type", type_ref, "", |this, _| { +// let this = this.read().unwrap(); +// let this = this.down_to_err().unwrap(); +// this.kind.get_obj().unwrap() +// }), +// prop!("message", type_ref, "", |this, _| { +// let this = this.read().unwrap(); +// let this = this.down_to_err().unwrap(); +// BUILTINS.str(&this.message) +// }), +// ]); +// +// type_ref.clone() +// } + +// Error -------------------------------------------------------- // NOTE: This is named `ErrObj` instead of `Err` to avoid conflict with // Rust's `Err`. pub struct ErrObj { + class: TypeRef, ns: Namespace, pub kind: ErrKind, pub message: String, @@ -109,9 +104,10 @@ pub struct ErrObj { standard_object_impls!(ErrObj); impl ErrObj { - pub fn new(kind: ErrKind, message: String, obj: ObjectRef) -> Self { + pub fn new(class: TypeRef, kind: ErrKind, message: String, obj: ObjectRef) -> Self { let bool_val = kind != ErrKind::Ok; Self { + class, ns: Namespace::default(), kind, message, @@ -122,11 +118,12 @@ impl ErrObj { } pub fn with_responds_to_bool( + class: TypeRef, kind: ErrKind, message: String, obj: ObjectRef, ) -> Self { - let mut instance = Self::new(kind, message, obj); + let mut instance = Self::new(class, kind, message, obj); instance.responds_to_bool = true; instance } @@ -137,7 +134,7 @@ impl ErrObj { } impl ObjectTrait for ErrObj { - object_trait_header!(ERR_TYPE); + object_trait_header!(); fn bool_val(&self) -> Option { if self.responds_to_bool { diff --git a/feint-builtins/src/types/err_type.rs b/feint-builtins/src/types/err_type.rs index 65e363a..5a6f20f 100644 --- a/feint-builtins/src/types/err_type.rs +++ b/feint-builtins/src/types/err_type.rs @@ -7,11 +7,11 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use super::new; +use crate::BUILTINS; use feint_code_gen::*; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; +use super::base::{ObjectRef, ObjectTrait, TypeRef}; + use super::ns::Namespace; #[derive(Clone, Debug, PartialEq)] @@ -65,39 +65,40 @@ impl ErrKind { } pub fn get_obj(&self) -> Option { - let err_type_type = ERR_TYPE_TYPE.read().unwrap(); - err_type_type.ns.get(self.name()) + // let err_type_type = ERR_TYPE_TYPE.read().unwrap(); + // err_type_type.ns.get(self.name()) + None } } -// ErrType Type -------------------------------------------------------- - -type_and_impls!(ErrTypeType, ErrType); - -pub static ERR_TYPE_TYPE: Lazy = Lazy::new(|| { - let type_ref = obj_ref!(ErrTypeType::new()); - let mut type_obj = type_ref.write().unwrap(); - - // Types as class attributes - for kind in ERR_KINDS.iter() { - type_obj.add_attr(kind.name(), obj_ref!(ErrTypeObj::new(kind.clone()))); - } - - type_obj.add_attrs(&[ - // Instance Attributes ----------------------------------------- - prop!("name", type_ref, "", |this, _| { - let this = this.read().unwrap(); - let this = this.as_any().downcast_ref::().unwrap(); - new::str(this.name()) - }), - ]); - - type_ref.clone() -}); - -// ErrType Object ------------------------------------------------------ +// pub static ERR_TYPE_TYPE: Lazy = Lazy::new(|| { +// let type_ref = obj_ref!(ErrTypeType::new()); +// let mut type_obj = type_ref.write().unwrap(); +// +// // Types as class attributes +// for kind in ERR_KINDS.iter() { +// type_obj.add_attr( +// kind.name(), +// obj_ref!(ErrTypeObj::new(type_ref.clone(), kind.clone())), +// ); +// } +// +// type_obj.add_attrs(&[ +// // Instance Attributes ----------------------------------------- +// prop!("name", type_ref, "", |this, _| { +// let this = this.read().unwrap(); +// let this = this.as_any().downcast_ref::().unwrap(); +// BUILTINS.str(this.name()) +// }), +// ]); +// +// type_ref.clone() +// }); + +// ErrType ------------------------------------------------------ pub struct ErrTypeObj { + class: TypeRef, ns: Namespace, kind: ErrKind, } @@ -105,8 +106,8 @@ pub struct ErrTypeObj { standard_object_impls!(ErrTypeObj); impl ErrTypeObj { - pub fn new(kind: ErrKind) -> Self { - Self { ns: Namespace::default(), kind } + pub fn new(class: TypeRef, kind: ErrKind) -> Self { + Self { class, ns: Namespace::default(), kind } } pub fn kind(&self) -> &ErrKind { @@ -119,7 +120,7 @@ impl ErrTypeObj { } impl ObjectTrait for ErrTypeObj { - object_trait_header!(ERR_TYPE_TYPE); + object_trait_header!(); fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { if self.is(rhs) || rhs.is_always() { diff --git a/feint-builtins/src/types/file.rs b/feint-builtins/src/types/file.rs index be5634f..3091805 100644 --- a/feint-builtins/src/types/file.rs +++ b/feint-builtins/src/types/file.rs @@ -5,58 +5,56 @@ use std::io::{BufRead, BufReader}; use std::path::{Path, PathBuf}; use std::sync::{Arc, RwLock}; -use once_cell::sync::{Lazy, OnceCell}; +use once_cell::sync::OnceCell; -use super::new; use feint_code_gen::*; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; +use crate::BUILTINS; + +use super::base::{ObjectRef, ObjectTrait, TypeRef}; + use super::ns::Namespace; -// File Type ------------------------------------------------------------ - -type_and_impls!(FileType, File); - -pub static FILE_TYPE: Lazy = Lazy::new(|| { - let type_ref = obj_ref!(FileType::new()); - let mut type_obj = type_ref.write().unwrap(); - - type_obj.add_attrs(&[ - // Class Methods - meth!("new", type_ref, &["file_name"], "", |_, args| { - let arg = use_arg!(args, 0); - if let Some(file_name) = arg.get_str_val() { - let path = Path::new(file_name); - if path.is_file() { - new::file(file_name) - } else { - new::file_not_found_err(file_name, new::nil()) - } - } else { - let message = format!("File.new(file_name) expected string; got {arg}"); - new::arg_err(message, new::nil()) - } - }), - // Instance Attributes - prop!("text", type_ref, "", |this, _| { - let this = this.read().unwrap(); - let this = this.down_to_file().unwrap(); - this.text() - }), - prop!("lines", type_ref, "", |this, _| { - let this = this.read().unwrap(); - let this = &mut this.down_to_file().unwrap(); - this.lines() - }), - ]); - - type_ref.clone() -}); - -// File Object ---------------------------------------------------------- +// pub fn make_file_type() -> obj_ref_t!(FileType) { +// let type_ref = obj_ref!(FileType::new()); +// let mut type_obj = type_ref.write().unwrap(); +// +// type_obj.add_attrs(&[ +// // Class Methods +// meth!("new", type_ref, &["file_name"], "", |_, args| { +// let arg = use_arg!(args, 0); +// if let Some(file_name) = arg.get_str_val() { +// let path = Path::new(file_name); +// if path.is_file() { +// BUILTINS.file(file_name) +// } else { +// BUILTINS.file_not_found_err(file_name, BUILTINS.nil()) +// } +// } else { +// let message = format!("File.new(file_name) expected string; got {arg}"); +// BUILTINS.arg_err(message, BUILTINS.nil()) +// } +// }), +// // Instance Attributes +// prop!("text", type_ref, "", |this, _| { +// let this = this.read().unwrap(); +// let this = this.down_to_file().unwrap(); +// this.text() +// }), +// prop!("lines", type_ref, "", |this, _| { +// let this = this.read().unwrap(); +// let this = &mut this.down_to_file().unwrap(); +// this.lines() +// }), +// ]); +// +// type_ref.clone() +// } + +// File ---------------------------------------------------------- pub struct File { + class: TypeRef, ns: Namespace, file_name: String, path: PathBuf, @@ -67,11 +65,12 @@ pub struct File { standard_object_impls!(File); impl File { - pub fn new(file_name: String) -> Self { + pub fn new(class: TypeRef, file_name: String) -> Self { let path = fs::canonicalize(&file_name); let path = path.map_or_else(|_| Path::new(&file_name).to_path_buf(), |p| p); - let name_obj = new::str(file_name.as_str()); + let name_obj = BUILTINS.str(file_name.as_str()); Self { + class, ns: Namespace::with_entries(&[("name", name_obj)]), file_name, path, @@ -83,8 +82,10 @@ impl File { fn text(&self) -> ObjectRef { let result = self.text.get_or_try_init(|| { fs::read_to_string(&self.file_name) - .map(new::str) - .map_err(|err| new::file_unreadable_err(err.to_string(), new::nil())) + .map(|contents| BUILTINS.str(contents)) + .map_err(|err| { + BUILTINS.file_unreadable_err(err.to_string(), BUILTINS.nil()) + }) }); match result { Ok(text) => text.clone(), @@ -100,11 +101,13 @@ impl File { let lines = reader .lines() // TODO: Handle lines that can't be read - .map(|line| new::str(line.unwrap())) + .map(|line| BUILTINS.str(line.unwrap())) .collect(); - new::tuple(lines) + BUILTINS.tuple(lines) + }) + .map_err(|err| { + BUILTINS.file_unreadable_err(err.to_string(), BUILTINS.nil()) }) - .map_err(|err| new::file_unreadable_err(err.to_string(), new::nil())) }); match result { Ok(lines) => lines.clone(), @@ -114,7 +117,7 @@ impl File { } impl ObjectTrait for File { - object_trait_header!(FILE_TYPE); + object_trait_header!(); fn bool_val(&self) -> Option { Some(false) diff --git a/feint-builtins/src/types/float.rs b/feint-builtins/src/types/float.rs index 744197c..139647a 100644 --- a/feint-builtins/src/types/float.rs +++ b/feint-builtins/src/types/float.rs @@ -3,47 +3,42 @@ use std::fmt; use std::sync::{Arc, RwLock}; use num_traits::ToPrimitive; -use once_cell::sync::Lazy; use feint_code_gen::*; -use super::new; use super::util::{eq_int_float, float_gt_int, float_lt_int}; +use crate::BUILTINS; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; -use super::ns::Namespace; - -// Float Type ---------------------------------------------------------- - -type_and_impls!(FloatType, Float); - -pub static FLOAT_TYPE: Lazy = Lazy::new(|| { - let type_ref = obj_ref!(FloatType::new()); - let mut type_obj = type_ref.write().unwrap(); +use super::base::{ObjectRef, ObjectTrait, TypeRef}; - type_obj.add_attrs(&[ - // Class Methods ----------------------------------------------- - meth!("new", type_ref, &["value"], "", |this, args| { - let arg = use_arg!(args, 0); - let float = if let Some(val) = arg.get_float_val() { - new::float(*val) - } else if let Some(val) = arg.get_int_val() { - new::float(val.to_f64().unwrap()) - } else if let Some(val) = arg.get_str_val() { - new::float_from_string(val) - } else { - let msg = format!("Float.new() expected string or float; got {arg}"); - new::type_err(msg, this) - }; - float - }), - ]); - - type_ref.clone() -}); +use super::ns::Namespace; -// Float Object -------------------------------------------------------- +// pub fn make_float_type() -> obj_ref_t!(FloatType) { +// let type_ref = obj_ref!(FloatType::new()); +// let mut type_obj = type_ref.write().unwrap(); +// +// type_obj.add_attrs(&[ +// // Class Methods ----------------------------------------------- +// meth!("new", type_ref, &["value"], "", |this, args| { +// let arg = use_arg!(args, 0); +// let float = if let Some(val) = arg.get_float_val() { +// BUILTINS.float(*val) +// } else if let Some(val) = arg.get_int_val() { +// BUILTINS.float(val.to_f64().unwrap()) +// } else if let Some(val) = arg.get_str_val() { +// BUILTINS.float_from_string(val) +// } else { +// let msg = format!("Float.new() expected string or float; got {arg}"); +// BUILTINS.type_err(msg, this) +// }; +// float +// }), +// ]); +// +// type_ref.clone() +// } + +// Float -------------------------------------------------------- macro_rules! make_op { ( $meth:ident, $op:tt, $message:literal, $trunc:literal ) => { @@ -59,13 +54,14 @@ macro_rules! make_op { if $trunc { value = value.trunc(); } - let value = new::float(value); + let value = BUILTINS.float(value); Some(value) } }; } pub struct Float { + class: TypeRef, ns: Namespace, value: f64, } @@ -73,8 +69,8 @@ pub struct Float { standard_object_impls!(Float); impl Float { - pub fn new(value: f64) -> Self { - Self { ns: Namespace::default(), value } + pub fn new(class: TypeRef, value: f64) -> Self { + Self { class, ns: Namespace::default(), value } } pub fn value(&self) -> &f64 { @@ -83,10 +79,10 @@ impl Float { } impl ObjectTrait for Float { - object_trait_header!(FLOAT_TYPE); + object_trait_header!(); fn negate(&self) -> Option { - Some(new::float(-*self.value())) + Some(BUILTINS.float(-*self.value())) } fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { @@ -130,7 +126,7 @@ impl ObjectTrait for Float { return None; }; let value = self.value().powf(exp); - let value = new::float(value); + let value = BUILTINS.float(value); Some(value) } diff --git a/feint-builtins/src/types/func.rs b/feint-builtins/src/types/func.rs index 48b4e4c..88e88a8 100644 --- a/feint-builtins/src/types/func.rs +++ b/feint-builtins/src/types/func.rs @@ -2,29 +2,23 @@ use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use once_cell::sync::{Lazy, OnceCell}; +use once_cell::sync::OnceCell; use feint_code_gen::*; -use crate::modules::get_module; +use crate::BUILTINS; + +use super::base::{ObjectRef, ObjectTrait, TypeRef}; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; use super::code::Code; use super::func_trait::FuncTrait; use super::ns::Namespace; -use super::{new, Params}; - -// Function Type ------------------------------------------------------- - -type_and_impls!(FuncType, Func); - -pub static FUNC_TYPE: Lazy = - Lazy::new(|| obj_ref!(FuncType::new())); +use super::Params; -// Func Object ---------------------------------------------------------- +// Func ---------------------------------------------------------- pub struct Func { + class: TypeRef, ns: Namespace, module_name: String, module: OnceCell, @@ -36,15 +30,23 @@ pub struct Func { standard_object_impls!(Func); impl Func { - pub fn new(module_name: String, name: String, params: Params, code: Code) -> Self { - let params_tuple = new::tuple(params.iter().map(new::str).collect()); + pub fn new( + class: TypeRef, + module_name: String, + name: String, + params: Params, + code: Code, + ) -> Self { + let params_tuple = + BUILTINS.tuple(params.iter().map(|p| BUILTINS.str(p)).collect()); let mut instance = Self { + class, ns: Namespace::with_entries(&[ // Instance Attributes - ("$module_name", new::str(&module_name)), - ("$full_name", new::str(format!("{module_name}.{name}"))), - ("$name", new::str(&name)), + ("$module_name", BUILTINS.str(&module_name)), + ("$full_name", BUILTINS.str(format!("{module_name}.{name}"))), + ("$name", BUILTINS.str(&name)), ("$params", params_tuple), ("$doc", code.get_doc()), ]), @@ -57,8 +59,8 @@ impl Func { let arity = (&instance as &dyn FuncTrait).arity(); let has_var_args = (&instance as &dyn FuncTrait).has_var_args(); - instance.ns_mut().insert("$arity", new::int(arity)); - instance.ns_mut().insert("$has_var_args", new::bool(has_var_args)); + instance.ns_mut().insert("$arity", BUILTINS.int(arity)); + instance.ns_mut().insert("$has_var_args", BUILTINS.bool(has_var_args)); instance } @@ -103,10 +105,10 @@ impl FuncTrait for Func { } impl ObjectTrait for Func { - object_trait_header!(FUNC_TYPE); + object_trait_header!(); fn module(&self) -> ObjectRef { - self.module.get_or_init(|| get_module(&self.module_name)).clone() + self.module.get_or_init(|| BUILTINS.get_module(&self.module_name)).clone() } fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { diff --git a/feint-builtins/src/types/int.rs b/feint-builtins/src/types/int.rs index fb67dba..fef8e13 100644 --- a/feint-builtins/src/types/int.rs +++ b/feint-builtins/src/types/int.rs @@ -5,52 +5,49 @@ use std::sync::{Arc, RwLock}; use num_bigint::BigInt; use num_traits::{FromPrimitive, ToPrimitive}; -use once_cell::sync::Lazy; - use feint_code_gen::*; -use super::new; use super::util::{eq_int_float, int_gt_float, int_lt_float}; +use crate::BUILTINS; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; -use super::ns::Namespace; +use super::base::{ObjectRef, ObjectTrait, TypeRef}; -// Int Type ------------------------------------------------------------ - -static DOC: &str = " -Intrinsic Int type -"; - -type_and_impls!(IntType, Int); - -pub static INT_TYPE: Lazy = Lazy::new(|| { - let type_ref = obj_ref!(IntType::new()); - let mut type_obj = type_ref.write().unwrap(); - - type_obj.add_attrs(&[ - ("$doc", new::str(DOC)), - // Class Methods ----------------------------------------------- - meth!("new", type_ref, &["value"], "", |this, args| { - let arg = use_arg!(args, 0); - let int = if let Some(val) = arg.get_int_val() { - new::int(val.clone()) - } else if let Some(val) = arg.get_float_val() { - new::int(BigInt::from_f64(*val).unwrap()) - } else if let Some(val) = arg.get_str_val() { - new::int_from_string(val) - } else { - let msg = format!("Int.new() expected number or string; got {arg}"); - new::type_err(msg, this) - }; - int - }), - ]); - - type_ref.clone() -}); +use super::ns::Namespace; -// Int Object ---------------------------------------------------------- +// pub fn make_int_type() -> obj_ref_t!(IntType) { +// eprintln!("make_int_type 1"); +// let type_ref = obj_ref!(IntType::new()); +// +// eprintln!("make_int_type 2"); +// { +// eprintln!("make_int_type 2.a"); +// let mut type_obj = type_ref.write().unwrap(); +// +// eprintln!("make_int_type 2.b"); +// type_obj.add_attrs(&[ +// // Class Methods ----------------------------------------------- +// meth!("new", type_ref, &["value"], "", |this, args| { +// let arg = use_arg!(args, 0); +// let int = if let Some(val) = arg.get_int_val() { +// BUILTINS.int(val.clone()) +// } else if let Some(val) = arg.get_float_val() { +// BUILTINS.int(BigInt::from_f64(*val).unwrap()) +// } else if let Some(val) = arg.get_str_val() { +// BUILTINS.int_from_string(val) +// } else { +// let msg = format!("Int.new() expected number or string; got {arg}"); +// BUILTINS.type_err(msg, this) +// }; +// int +// }), +// ]); +// } +// +// eprintln!("make_int_type 3"); +// type_ref +// } + +// Int ---------------------------------------------------------- macro_rules! make_op { ( $meth:ident, $op:tt ) => { @@ -58,11 +55,11 @@ macro_rules! make_op { if let Some(rhs) = rhs.down_to_int() { // XXX: Return Int let value = self.value() $op rhs.value(); - Some(new::int(value)) + Some(BUILTINS.int(value)) } else if let Some(rhs) = rhs.down_to_float() { // XXX: Return Float let value = self.value().to_f64().unwrap() $op rhs.value(); - Some(new::float(value)) + Some(BUILTINS.float(value)) } else { None } @@ -71,6 +68,7 @@ macro_rules! make_op { } pub struct Int { + class: TypeRef, ns: Namespace, value: BigInt, } @@ -78,8 +76,8 @@ pub struct Int { standard_object_impls!(Int); impl Int { - pub fn new(value: BigInt) -> Self { - Self { ns: Namespace::default(), value } + pub fn new(class: TypeRef, value: BigInt) -> Self { + Self { class, ns: Namespace::default(), value } } pub fn value(&self) -> &BigInt { @@ -101,10 +99,10 @@ impl Int { } impl ObjectTrait for Int { - object_trait_header!(INT_TYPE); + object_trait_header!(); fn negate(&self) -> Option { - Some(new::int(-self.value.clone())) + Some(BUILTINS.int(-self.value.clone())) } fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { @@ -145,14 +143,14 @@ impl ObjectTrait for Int { let base = self.value(); let exp = rhs.value().to_u32().unwrap(); let value = base.pow(exp); - let value = new::int(value); + let value = BUILTINS.int(value); Some(value) } else if let Some(rhs) = rhs.down_to_float() { // XXX: Return Float let base = self.value().to_f64().unwrap(); let exp = *rhs.value(); let value = base.powf(exp); - let value = new::float(value); + let value = BUILTINS.float(value); Some(value) } else { None @@ -167,7 +165,7 @@ impl ObjectTrait for Int { // Int division *always* returns a Float fn div(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(value) = self.div_f64(rhs) { - Some(new::float(value)) + Some(BUILTINS.float(value)) } else { None } @@ -177,7 +175,7 @@ impl ObjectTrait for Int { fn floor_div(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(value) = self.div_f64(rhs) { let value = BigInt::from_f64(value).unwrap(); - Some(new::int(value)) + Some(BUILTINS.int(value)) } else { None } diff --git a/feint-builtins/src/types/intrinsic_func.rs b/feint-builtins/src/types/intrinsic_func.rs index 13a8f5f..189f295 100644 --- a/feint-builtins/src/types/intrinsic_func.rs +++ b/feint-builtins/src/types/intrinsic_func.rs @@ -3,30 +3,24 @@ use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use once_cell::sync::{Lazy, OnceCell}; +use once_cell::sync::OnceCell; use feint_code_gen::*; -use crate::modules::get_module; +use crate::BUILTINS; + +use super::base::{ObjectRef, ObjectTrait, TypeRef}; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; use super::func_trait::FuncTrait; use super::ns::Namespace; -use super::{new, Args, CallResult, Params}; +use super::{Args, CallResult, Params}; pub type IntrinsicFn = fn(ObjectRef, Args) -> CallResult; -// Intrinsic Function Type --------------------------------------------- - -type_and_impls!(IntrinsicFuncType, IntrinsicFunc); - -pub static INTRINSIC_FUNC_TYPE: Lazy = - Lazy::new(|| obj_ref!(IntrinsicFuncType::new())); - -// IntrinsicFunc Object ------------------------------------------------ +// IntrinsicFunc ------------------------------------------------ pub struct IntrinsicFunc { + class: TypeRef, ns: Namespace, module_name: String, module: OnceCell, @@ -40,23 +34,27 @@ standard_object_impls!(IntrinsicFunc); impl IntrinsicFunc { pub fn new( + class: TypeRef, module_name: String, name: String, this_type: Option, params: Params, - doc: ObjectRef, + doc: String, func: IntrinsicFn, ) -> Self { - let params_tuple = new::tuple(params.iter().map(new::str).collect()); + // let params_tuple = + // BUILTINS.tuple(params.iter().map(|p| BUILTINS.str(p)).collect()); + eprintln!("new"); let mut instance = Self { + class, ns: Namespace::with_entries(&[ // Instance Attributes - ("$module_name", new::str(module_name.as_str())), - ("$full_name", new::str(format!("{module_name}.{name}"))), - ("$name", new::str(name.as_str())), - ("$params", params_tuple), - ("$doc", doc), + // ("$module_name", BUILTINS.str(module_name.as_str())), + // ("$full_name", BUILTINS.str(format!("{module_name}.{name}"))), + // ("$name", BUILTINS.str(name.as_str())), + // ("$params", params_tuple), + // ("$doc", doc), ]), module_name, module: OnceCell::default(), @@ -68,8 +66,8 @@ impl IntrinsicFunc { let arity = (&instance as &dyn FuncTrait).arity(); let has_var_args = (&instance as &dyn FuncTrait).has_var_args(); - instance.ns_mut().insert("$arity", new::int(arity)); - instance.ns_mut().insert("$has_var_args", new::bool(has_var_args)); + // instance.ns_mut().insert("$arity", BUILTINS.int(arity)); + // instance.ns_mut().insert("$has_var_args", BUILTINS.bool(has_var_args)); instance } @@ -106,10 +104,10 @@ impl FuncTrait for IntrinsicFunc { } impl ObjectTrait for IntrinsicFunc { - object_trait_header!(INTRINSIC_FUNC_TYPE); + object_trait_header!(); fn module(&self) -> ObjectRef { - self.module.get_or_init(|| get_module(&self.module_name)).clone() + self.module.get_or_init(|| BUILTINS.get_module(&self.module_name)).clone() } } diff --git a/feint-builtins/src/types/iterator.rs b/feint-builtins/src/types/iterator.rs index 228bf0c..7528ecb 100644 --- a/feint-builtins/src/types/iterator.rs +++ b/feint-builtins/src/types/iterator.rs @@ -2,43 +2,39 @@ use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use once_cell::sync::Lazy; - -use super::new; use feint_code_gen::*; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; -use super::ns::Namespace; - -// IteratorType Type --------------------------------------------------- +use crate::BUILTINS; -type_and_impls!(IteratorType, Iterator); +use super::base::{ObjectRef, ObjectTrait, TypeRef}; -pub static ITERATOR_TYPE: Lazy = Lazy::new(|| { - let type_ref = obj_ref!(IteratorType::new()); - let mut type_obj = type_ref.write().unwrap(); - - type_obj.add_attrs(&[ - // Instance Methods -------------------------------------------- - meth!("next", type_ref, &[], "", |this, _| { - let mut this = this.write().unwrap(); - let this = this.down_to_iterator_mut().unwrap(); - this.next() - }), - meth!("peek", type_ref, &[], "", |this, _| { - let this = this.write().unwrap(); - let this = this.down_to_iterator().unwrap(); - this.peek() - }), - ]); - - type_ref.clone() -}); +use super::ns::Namespace; -// Iterator Object ----------------------------------------------------- +// pub fn make_iterator_type() -> obj_ref_t!(IteratorType) { +// let type_ref = obj_ref!(IteratorType::new()); +// let mut type_obj = type_ref.write().unwrap(); +// +// type_obj.add_attrs(&[ +// // Instance Methods -------------------------------------------- +// meth!("next", type_ref, &[], "", |this, _| { +// let mut this = this.write().unwrap(); +// let this = this.down_to_iterator_mut().unwrap(); +// this.next() +// }), +// meth!("peek", type_ref, &[], "", |this, _| { +// let this = this.write().unwrap(); +// let this = this.down_to_iterator().unwrap(); +// this.peek() +// }), +// ]); +// +// type_ref.clone() +// } + +// Iterator ----------------------------------------------------- pub struct FIIterator { + class: TypeRef, ns: Namespace, wrapped: Vec, current: usize, @@ -47,8 +43,8 @@ pub struct FIIterator { standard_object_impls!(FIIterator); impl FIIterator { - pub fn new(wrapped: Vec) -> Self { - Self { ns: Namespace::default(), wrapped, current: 0 } + pub fn new(class: TypeRef, wrapped: Vec) -> Self { + Self { class, ns: Namespace::default(), wrapped, current: 0 } } fn next(&mut self) -> ObjectRef { @@ -69,7 +65,7 @@ impl FIIterator { fn get_or_nil(&self, index: usize) -> ObjectRef { if index >= self.len() { - new::nil() + BUILTINS.nil() } else { self.wrapped[index].clone() } @@ -77,7 +73,7 @@ impl FIIterator { } impl ObjectTrait for FIIterator { - object_trait_header!(ITERATOR_TYPE); + object_trait_header!(); } // Display ------------------------------------------------------------- diff --git a/feint-builtins/src/types/list.rs b/feint-builtins/src/types/list.rs index 87198f2..59d2b8a 100644 --- a/feint-builtins/src/types/list.rs +++ b/feint-builtins/src/types/list.rs @@ -2,110 +2,105 @@ use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use once_cell::sync::Lazy; - use feint_code_gen::*; -use super::new; +use crate::BUILTINS; + +use super::base::{ObjectRef, ObjectTrait, TypeRef}; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; use super::ns::Namespace; use super::seq; -// List Type ----------------------------------------------------------- - -type_and_impls!(ListType, List); - -pub static LIST_TYPE: Lazy = Lazy::new(|| { - let type_ref = obj_ref!(ListType::new()); - let mut type_obj = type_ref.write().unwrap(); - - type_obj.add_attrs(&[ - // Instance Attributes ----------------------------------------- - prop!("length", type_ref, "", |this, _| { - let this = this.read().unwrap(); - let this = this.down_to_list().unwrap(); - new::int(this.len()) - }), - prop!("is_empty", type_ref, "", |this, _| { - let this = this.read().unwrap(); - let this = this.down_to_list().unwrap(); - new::bool(this.len() == 0) - }), - prop!("sum", type_ref, "", |this, _| { - let this = this.read().unwrap(); - let this = this.down_to_list().unwrap(); - let items = &this.items.read().unwrap(); - seq::sum(items) - }), - // Instance Methods -------------------------------------------- - meth!( - "extend", - type_ref, - &["items"], - "Push items and return this.", - |this, args| { - let return_val = this.clone(); - let this = this.read().unwrap(); - let this = this.down_to_list().unwrap(); - if let Some(err) = this.extend(args[0].clone()) { - return err; - } - return_val - } - ), - meth!("get", type_ref, &["index"], "", |this, args| { - let this = this.read().unwrap(); - let this = this.down_to_list().unwrap(); - let index = use_arg_usize!(get, index, args, 0); - let result = match this.get(index) { - Some(obj) => obj, - None => new::nil(), - }; - result - }), - meth!("has", type_ref, &["member"], "", |this, args| { - let this = this.read().unwrap(); - let this = this.down_to_list().unwrap(); - let items = &this.items.read().unwrap(); - seq::has(items, &args) - }), - meth!("iter", type_ref, &[], "", |this_ref, _| { - let this = this_ref.read().unwrap(); - let this = this.down_to_list().unwrap(); - let items = this.items.read().unwrap(); - new::iterator(items.clone()) - }), - meth!("join", type_ref, &["sep"], "", |this, args| { - let this = this.read().unwrap(); - let this = this.down_to_list().unwrap(); - let items = &this.items.read().unwrap(); - seq::join(items, &args) - }), - meth!("pop", type_ref, &[], "", |this, _| { - let this = this.read().unwrap(); - let this = this.down_to_list().unwrap(); - match this.pop() { - Some(obj) => obj, - None => new::nil(), - } - }), - meth!("push", type_ref, &["item"], "Push item and return it.", |this, args| { - let this = this.read().unwrap(); - let this = this.down_to_list().unwrap(); - let arg = args[0].clone(); - this.push(arg.clone()); - arg - }), - ]); - - type_ref.clone() -}); - -// List Object --------------------------------------------------------- +// pub fn make_list_type() -> obj_ref_t!(ListType) { +// let type_ref = obj_ref!(ListType::new()); +// let mut type_obj = type_ref.write().unwrap(); +// +// type_obj.add_attrs(&[ +// // Instance Attributes ----------------------------------------- +// prop!("length", type_ref, "", |this, _| { +// let this = this.read().unwrap(); +// let this = this.down_to_list().unwrap(); +// BUILTINS.int(this.len()) +// }), +// prop!("is_empty", type_ref, "", |this, _| { +// let this = this.read().unwrap(); +// let this = this.down_to_list().unwrap(); +// BUILTINS.bool(this.len() == 0) +// }), +// prop!("sum", type_ref, "", |this, _| { +// let this = this.read().unwrap(); +// let this = this.down_to_list().unwrap(); +// let items = &this.items.read().unwrap(); +// seq::sum(items) +// }), +// // Instance Methods -------------------------------------------- +// meth!( +// "extend", +// type_ref, +// &["items"], +// "Push items and return this.", +// |this, args| { +// let return_val = this.clone(); +// let this = this.read().unwrap(); +// let this = this.down_to_list().unwrap(); +// if let Some(err) = this.extend(args[0].clone()) { +// return err; +// } +// return_val +// } +// ), +// meth!("get", type_ref, &["index"], "", |this, args| { +// let this = this.read().unwrap(); +// let this = this.down_to_list().unwrap(); +// let index = use_arg_usize!(get, index, args, 0); +// let result = match this.get(index) { +// Some(obj) => obj, +// None => BUILTINS.nil(), +// }; +// result +// }), +// meth!("has", type_ref, &["member"], "", |this, args| { +// let this = this.read().unwrap(); +// let this = this.down_to_list().unwrap(); +// let items = &this.items.read().unwrap(); +// seq::has(items, &args) +// }), +// meth!("iter", type_ref, &[], "", |this_ref, _| { +// let this = this_ref.read().unwrap(); +// let this = this.down_to_list().unwrap(); +// let items = this.items.read().unwrap(); +// BUILTINS.iterator(items.clone()) +// }), +// meth!("join", type_ref, &["sep"], "", |this, args| { +// let this = this.read().unwrap(); +// let this = this.down_to_list().unwrap(); +// let items = &this.items.read().unwrap(); +// seq::join(items, &args) +// }), +// meth!("pop", type_ref, &[], "", |this, _| { +// let this = this.read().unwrap(); +// let this = this.down_to_list().unwrap(); +// match this.pop() { +// Some(obj) => obj, +// None => BUILTINS.nil(), +// } +// }), +// meth!("push", type_ref, &["item"], "Push item and return it.", |this, args| { +// let this = this.read().unwrap(); +// let this = this.down_to_list().unwrap(); +// let arg = args[0].clone(); +// this.push(arg.clone()); +// arg +// }), +// ]); +// +// type_ref.clone() +// } + +// List --------------------------------------------------------- pub struct List { + class: TypeRef, ns: Namespace, items: RwLock>, } @@ -113,8 +108,8 @@ pub struct List { standard_object_impls!(List); impl List { - pub fn new(items: Vec) -> Self { - Self { ns: Namespace::default(), items: RwLock::new(items) } + pub fn new(class: TypeRef, items: Vec) -> Self { + Self { class, ns: Namespace::default(), items: RwLock::new(items) } } fn len(&self) -> usize { @@ -145,7 +140,7 @@ impl List { "List.extend() expected List or Tuple; got {}", obj.class().read().unwrap() ); - return Some(new::type_err(msg, obj_ref.clone())); + return Some(BUILTINS.type_err(msg, obj_ref.clone())); } None } @@ -170,7 +165,7 @@ impl List { } impl ObjectTrait for List { - object_trait_header!(LIST_TYPE); + object_trait_header!(); fn get_item(&self, index: usize, this: ObjectRef) -> ObjectRef { if let Some(item) = self.get(index) { diff --git a/feint-builtins/src/types/map.rs b/feint-builtins/src/types/map.rs index a4a7c08..9f9b9cb 100644 --- a/feint-builtins/src/types/map.rs +++ b/feint-builtins/src/types/map.rs @@ -3,129 +3,120 @@ use std::fmt; use std::sync::{Arc, RwLock}; use indexmap::IndexMap; -use once_cell::sync::Lazy; -use super::new; use feint_code_gen::*; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; -use super::ns::Namespace; +use crate::BUILTINS; -// Map Type ------------------------------------------------------------ - -type_and_impls!(MapType, Map); - -pub static MAP_TYPE: Lazy = Lazy::new(|| { - let type_ref = obj_ref!(MapType::new()); - let mut type_obj = type_ref.write().unwrap(); - - type_obj.add_attrs(&[ - // Instance Attributes ----------------------------------------- - prop!("length", type_ref, "", |this, _| { - let this = this.read().unwrap(); - let this = this.down_to_map().unwrap(); - new::int(this.len()) - }), - prop!("is_empty", type_ref, "", |this, _| { - let this = this.read().unwrap(); - let this = this.down_to_map().unwrap(); - new::bool(this.is_empty()) - }), - // Instance Methods -------------------------------------------- - meth!( - "add", - type_ref, - &["key", "val"], - "Add entry to Map. - - # Args - - - key: Str - - value: Any - - ", - |this, args| { - let this = this.read().unwrap(); - let this = this.down_to_map().unwrap(); - let arg = use_arg!(args, 0); - let key = use_arg_str!(get, key, arg); - let val = args[1].clone(); - this.insert(key, val); - new::nil() - } - ), - meth!( - "get", - type_ref, - &["key"], - "Get value for key from Map. - - # Args - - - key: Key - - # Returns - - - Any: If key is present - - nil: If key is not present - - > NOTE: There's no way to distinguish between a key that isn't present - > versus a key that has `nil` as its value. To avoid ambiguity, don't - > store `nil` values. - - ", - |this, args| { - let this = this.read().unwrap(); - let this = this.down_to_map().unwrap(); - let arg = use_arg!(args, 0); - let key = use_arg_str!(get, key, arg); - match this.get(key) { - Some(obj) => obj, - None => new::nil(), - } - } - ), - meth!("has", type_ref, &["member"], "", |this, args| { - let this = this.read().unwrap(); - let this = this.down_to_map().unwrap(); - let arg = use_arg!(args, 0); - let key = use_arg_str!(get, key, arg); - let result = this.contains_key(key); - new::bool(result) - }), - meth!("iter", type_ref, &[], "", |this_ref, _| { - let this = this_ref.read().unwrap(); - let this = this.down_to_map().unwrap(); - let mut items = vec![]; - for (name, val) in this.entries.read().unwrap().iter() { - items.push(new::tuple(vec![new::str(name), val.clone()])) - } - new::iterator(items) - }), - ]); +use super::base::{ObjectRef, ObjectTrait, TypeRef}; - type_ref.clone() -}); +use super::ns::Namespace; -// Map Object ---------------------------------------------------------- +// pub fn make_map_type() -> obj_ref_t!(MapType) { +// let type_ref = obj_ref!(MapType::new()); +// let mut type_obj = type_ref.write().unwrap(); +// +// type_obj.add_attrs(&[ +// // Instance Attributes ----------------------------------------- +// prop!("length", type_ref, "", |this, _| { +// let this = this.read().unwrap(); +// let this = this.down_to_map().unwrap(); +// BUILTINS.int(this.len()) +// }), +// prop!("is_empty", type_ref, "", |this, _| { +// let this = this.read().unwrap(); +// let this = this.down_to_map().unwrap(); +// BUILTINS.bool(this.is_empty()) +// }), +// // Instance Methods -------------------------------------------- +// meth!( +// "add", +// type_ref, +// &["key", "val"], +// "Add entry to Map. +// +// # Args +// +// - key: Str +// - value: Any +// +// ", +// |this, args| { +// let this = this.read().unwrap(); +// let this = this.down_to_map().unwrap(); +// let arg = use_arg!(args, 0); +// let key = use_arg_str!(get, key, arg); +// let val = args[1].clone(); +// this.insert(key, val); +// BUILTINS.nil() +// } +// ), +// meth!( +// "get", +// type_ref, +// &["key"], +// "Get value for key from Map. +// +// # Args +// +// - key: Key +// +// # Returns +// +// - Any: If key is present +// - nil: If key is not present +// +// > NOTE: There's no way to distinguish between a key that isn't present +// > versus a key that has `nil` as its value. To avoid ambiguity, don't +// > store `nil` values. +// +// ", +// |this, args| { +// let this = this.read().unwrap(); +// let this = this.down_to_map().unwrap(); +// let arg = use_arg!(args, 0); +// let key = use_arg_str!(get, key, arg); +// match this.get(key) { +// Some(obj) => obj, +// None => BUILTINS.nil(), +// } +// } +// ), +// meth!("has", type_ref, &["member"], "", |this, args| { +// let this = this.read().unwrap(); +// let this = this.down_to_map().unwrap(); +// let arg = use_arg!(args, 0); +// let key = use_arg_str!(get, key, arg); +// let result = this.contains_key(key); +// BUILTINS.bool(result) +// }), +// meth!("iter", type_ref, &[], "", |this_ref, _| { +// let this = this_ref.read().unwrap(); +// let this = this.down_to_map().unwrap(); +// let mut items = vec![]; +// for (name, val) in this.entries.read().unwrap().iter() { +// items.push(BUILTINS.tuple(vec![BUILTINS.str(name), val.clone()])) +// } +// BUILTINS.iterator(items) +// }), +// ]); +// +// type_ref.clone() +// } + +// Map ---------------------------------------------------------- pub struct Map { + class: TypeRef, ns: Namespace, entries: RwLock>, } standard_object_impls!(Map); -impl Default for Map { - fn default() -> Self { - Self { ns: Namespace::default(), entries: RwLock::new(IndexMap::default()) } - } -} - impl Map { - pub fn new(entries: IndexMap) -> Self { - Self { ns: Namespace::default(), entries: RwLock::new(entries) } + pub fn new(class: TypeRef, entries: IndexMap) -> Self { + Self { class, ns: Namespace::default(), entries: RwLock::new(entries) } } pub fn len(&self) -> usize { @@ -163,7 +154,7 @@ impl Map { } impl ObjectTrait for Map { - object_trait_header!(MAP_TYPE); + object_trait_header!(); fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { if self.is(rhs) || rhs.is_always() { diff --git a/feint-builtins/src/types/mod.rs b/feint-builtins/src/types/mod.rs index 14b17c1..a049dc8 100644 --- a/feint-builtins/src/types/mod.rs +++ b/feint-builtins/src/types/mod.rs @@ -1,4 +1,4 @@ -pub use base::{ObjectRef, ObjectTrait}; +pub use base::{ObjectRef, ObjectTrait, TypeRef}; pub use func::Func; pub use func_trait::FuncTrait; pub use intrinsic_func::IntrinsicFunc; @@ -17,7 +17,6 @@ mod func_trait; pub(crate) mod ns; pub mod code; -pub mod new; // Intrinsic Types pub mod always; diff --git a/feint-builtins/src/types/module.rs b/feint-builtins/src/types/module.rs index 3110b44..4021918 100644 --- a/feint-builtins/src/types/module.rs +++ b/feint-builtins/src/types/module.rs @@ -2,76 +2,72 @@ use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use once_cell::sync::Lazy; - use feint_code_gen::*; use crate::util::check_args; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; +use super::base::{ObjectRef, ObjectTrait, TypeRef}; + use super::code::Code; use super::map::Map; -use super::new; use super::ns::Namespace; - -// Module Type --------------------------------------------------------- - -type_and_impls!(ModuleType, Module); - -pub static MODULE_TYPE: Lazy = Lazy::new(|| { - let type_ref = obj_ref!(ModuleType::new()); - let mut type_obj = type_ref.write().unwrap(); - - type_obj.add_attrs(&[meth!( - "new", - type_ref, - &["name", "path", "doc", "attrs"], - "Create a new Module - - # Args - - - name: Str - - path: Str - - doc: Str - - attrs: Map - - # Returns - - Module", - |_, args| { - if let Err(err) = check_args("new", &args, false, 4, Some(4)) { - return err; - }; - - let name_arg = use_arg!(args, 0); - let path_arg = use_arg!(args, 1); - let doc_arg = use_arg!(args, 2); - let attrs_arg = use_arg!(args, 3); - - let name = use_arg_str!(new, name, name_arg); - let path = use_arg_str!(new, path, path_arg); - let doc = use_arg_str!(new, doc, doc_arg); - let attrs = use_arg_map!(new, attrs, attrs_arg); - - let module = Module::with_map_entries( - attrs, - name.to_owned(), - path.to_owned(), - Code::default(), - Some(doc.to_owned()), - ); - - obj_ref!(module) - } - )]); - - type_ref.clone() -}); - -// Module Object ------------------------------------------------------- +use crate::BUILTINS; + +// pub fn make_module_type() -> obj_ref_t!(ModuleType) { +// let type_ref = obj_ref!(ModuleType::new()); +// let mut type_obj = type_ref.write().unwrap(); +// +// type_obj.add_attrs(&[meth!( +// "new", +// type_ref, +// &["name", "path", "doc", "attrs"], +// "Create a new Module +// +// # Args +// +// - name: Str +// - path: Str +// - doc: Str +// - attrs: Map +// +// # Returns +// +// Module", +// |_, args| { +// if let Err(err) = check_args("new", &args, false, 4, Some(4)) { +// return err; +// }; +// +// let name_arg = use_arg!(args, 0); +// let path_arg = use_arg!(args, 1); +// let doc_arg = use_arg!(args, 2); +// let attrs_arg = use_arg!(args, 3); +// +// let name = use_arg_str!(new, name, name_arg); +// let path = use_arg_str!(new, path, path_arg); +// let doc = use_arg_str!(new, doc, doc_arg); +// let attrs = use_arg_map!(new, attrs, attrs_arg); +// +// let module = Module::with_map_entries( +// BUILTINS.module_type(), +// attrs, +// name.to_owned(), +// path.to_owned(), +// Code::default(), +// Some(doc.to_owned()), +// ); +// +// obj_ref!(module) +// } +// )]); +// +// type_ref.clone() +// } + +// Module ------------------------------------------------------- pub struct Module { + class: TypeRef, ns: Namespace, name: String, path: String, @@ -85,36 +81,47 @@ impl Module { /// modules and for special cases such as the REPL module. /// Modules implemented in FeInt will have their `$doc` /// attribute initialized from their module level docstring. - pub fn new(name: String, path: String, code: Code, doc: Option) -> Self { + pub fn new( + class: TypeRef, + name: String, + path: String, + code: Code, + doc: Option, + ) -> Self { let ns = Namespace::with_entries(&[ - ("$full_name", new::str(name.as_str())), - ("$name", new::str(name.as_str())), - ("$path", new::str(path.as_str())), - ("$doc", if let Some(doc) = doc { new::str(doc) } else { code.get_doc() }), + // ("$full_name", BUILTINS.str(name.as_str())), + // ("$name", BUILTINS.str(name.as_str())), + // ("$path", BUILTINS.str(path.as_str())), + // ( + // "$doc", + // if let Some(doc) = doc { BUILTINS.str(doc) } else { code.get_doc() }, + // ), ]); - Self { ns, path, name, code } + Self { class, ns, path, name, code } } pub fn with_entries( + class: TypeRef, entries: &[(&str, ObjectRef)], name: String, path: String, code: Code, doc: Option, ) -> Self { - let mut module = Self::new(name, path, code, doc); + let mut module = Self::new(class, name, path, code, doc); module.ns.extend(entries); module } pub fn with_map_entries( + class: TypeRef, map: &Map, name: String, path: String, code: Code, doc: Option, ) -> Self { - let mut module = Self::new(name, path, code, doc); + let mut module = Self::new(class, name, path, code, doc); module.ns.extend_from_map(map); module } @@ -164,7 +171,7 @@ impl Module { } impl ObjectTrait for Module { - object_trait_header!(MODULE_TYPE); + object_trait_header!(); } // Display ------------------------------------------------------------- diff --git a/feint-builtins/src/types/new.rs b/feint-builtins/src/types/new.rs deleted file mode 100644 index 4b28f5e..0000000 --- a/feint-builtins/src/types/new.rs +++ /dev/null @@ -1,362 +0,0 @@ -//! Type Constructors. -//! -//! These constructors simplify the creation of system objects. -use std::sync::{Arc, RwLock}; - -use num_bigint::BigInt; -use num_traits::{FromPrimitive, Num, Signed, ToPrimitive}; - -use indexmap::IndexMap; -use once_cell::sync::Lazy; - -use feint_code_gen::{obj_ref, obj_ref_t}; -use feint_util::string::format_doc; - -use super::base::{ObjectRef, ObjectTrait}; -use super::Params; - -use super::always::Always; -use super::bool::Bool; -use super::bound_func::BoundFunc; -use super::cell::Cell; -use super::closure::Closure; -use super::code::Code; -use super::custom::{CustomObj, CustomType}; -use super::err::ErrObj; -use super::err_type::ErrKind; -use super::file::File; -use super::float::Float; -use super::func::Func; -use super::int::Int; -use super::intrinsic_func::{IntrinsicFn, IntrinsicFunc}; -use super::iterator::FIIterator; -use super::list::List; -use super::map::Map; -use super::module::Module; -use super::nil::Nil; -use super::ns::Namespace; -use super::prop::Prop; -use super::str::Str; -use super::tuple::Tuple; - -// Global singletons --------------------------------------------------- - -static NIL: Lazy = Lazy::new(|| obj_ref!(Nil::new())); -static TRUE: Lazy = Lazy::new(|| obj_ref!(Bool::new(true))); -static FALSE: Lazy = Lazy::new(|| obj_ref!(Bool::new(false))); -static ALWAYS: Lazy = Lazy::new(|| obj_ref!(Always::new())); - -static EMPTY_STR: Lazy = - Lazy::new(|| obj_ref!(Str::new("".to_owned()))); - -static NEWLINE: Lazy = - Lazy::new(|| obj_ref!(Str::new("\n".to_owned()))); - -static EMPTY_TUPLE: Lazy = - Lazy::new(|| obj_ref!(Tuple::new(vec![]))); - -static SHARED_INT_MAX: usize = 256; -static SHARED_INT_MAX_BIGINT: Lazy = Lazy::new(|| BigInt::from(SHARED_INT_MAX)); -static SHARED_INTS: Lazy> = Lazy::new(|| { - (0..=SHARED_INT_MAX).map(|i| obj_ref!(Int::new(BigInt::from(i)))).collect() -}); - -#[inline] -pub fn nil() -> ObjectRef { - NIL.clone() -} - -#[inline] -pub fn bool(val: bool) -> ObjectRef { - if val { - TRUE.clone() - } else { - FALSE.clone() - } -} - -#[inline] -pub fn always() -> ObjectRef { - ALWAYS.clone() -} - -#[inline] -pub fn empty_str() -> ObjectRef { - EMPTY_STR.clone() -} - -#[inline] -pub fn newline() -> ObjectRef { - NEWLINE.clone() -} - -#[inline] -pub fn empty_tuple() -> ObjectRef { - EMPTY_TUPLE.clone() -} - -// Modules ------------------------------------------------------------- - -pub fn module( - name: &str, - path: &str, - doc: &str, - entries: &[(&str, ObjectRef)], -) -> obj_ref_t!(Module) { - obj_ref!(Module::with_entries( - entries, - name.to_owned(), - path.to_owned(), - Code::default(), - Some(doc.to_owned()) - )) -} - -// Function------------------------------------------------------------- - -pub fn intrinsic_func( - module_name: &str, - name: &str, - this_type: Option, - params: &[&str], - doc: &str, - func: IntrinsicFn, -) -> ObjectRef { - let params = params.iter().map(|n| n.to_string()).collect(); - let doc = format_doc(doc); - obj_ref!(IntrinsicFunc::new( - module_name.to_owned(), - name.to_owned(), - this_type, - params, - str(doc), - func - )) -} - -pub fn func>( - module_name: S, - func_name: S, - params: Params, - code: Code, -) -> ObjectRef { - obj_ref!(Func::new(module_name.into(), func_name.into(), params, code)) -} - -pub fn bound_func(func: ObjectRef, this: ObjectRef) -> ObjectRef { - obj_ref!(BoundFunc::new(func, this)) -} - -pub fn closure(func: ObjectRef, captured: ObjectRef) -> ObjectRef { - obj_ref!(Closure::new(func, captured)) -} - -pub fn cell() -> ObjectRef { - obj_ref!(Cell::new()) -} - -pub fn cell_with_value(value: ObjectRef) -> ObjectRef { - obj_ref!(Cell::with_value(value)) -} - -pub fn argv_tuple(argv: &[String]) -> ObjectRef { - obj_ref!(Tuple::new(argv.iter().map(str).collect())) -} - -// Errors -------------------------------------------------------------- - -pub fn err>(kind: ErrKind, msg: S, obj: ObjectRef) -> ObjectRef { - obj_ref!(ErrObj::new(kind, msg.into(), obj)) -} - -pub fn err_with_responds_to_bool>( - kind: ErrKind, - msg: S, - obj: ObjectRef, -) -> ObjectRef { - obj_ref!(ErrObj::with_responds_to_bool(kind, msg.into(), obj)) -} - -pub fn arg_err>(msg: S, obj: ObjectRef) -> ObjectRef { - err(ErrKind::Arg, msg, obj) -} - -pub fn attr_err>(msg: S, obj: ObjectRef) -> ObjectRef { - err(ErrKind::Attr, msg, obj) -} - -pub fn attr_not_found_err>(msg: S, obj: ObjectRef) -> ObjectRef { - err(ErrKind::AttrNotFound, msg, obj) -} - -pub fn file_not_found_err>(msg: S, obj: ObjectRef) -> ObjectRef { - err(ErrKind::FileNotFound, msg, obj) -} - -pub fn file_unreadable_err>(msg: S, obj: ObjectRef) -> ObjectRef { - err(ErrKind::FileUnreadable, msg, obj) -} - -pub fn index_out_of_bounds_err(index: usize, obj: ObjectRef) -> ObjectRef { - err(ErrKind::IndexOutOfBounds, index.to_string(), obj) -} - -pub fn not_callable_err(obj: ObjectRef) -> ObjectRef { - err(ErrKind::NotCallable, format!("{}", obj.read().unwrap()), obj) -} - -pub fn string_err>(msg: S, obj: ObjectRef) -> ObjectRef { - err(ErrKind::String, msg, obj) -} - -pub fn type_err>(msg: S, obj: ObjectRef) -> ObjectRef { - err(ErrKind::Type, msg, obj) -} - -static OK_ERR: Lazy = Lazy::new(|| { - obj_ref!(ErrObj::with_responds_to_bool(ErrKind::Ok, "".to_string(), nil())) -}); - -pub fn ok_err() -> ObjectRef { - OK_ERR.clone() -} - -// END Errors ---------------------------------------------------------- - -pub fn file>(file_name: S) -> ObjectRef { - obj_ref!(File::new(file_name.into())) -} - -pub fn float(value: f64) -> ObjectRef { - obj_ref!(Float::new(value)) -} - -pub fn float_from_string>(val: S) -> ObjectRef { - let val = val.into(); - if let Ok(val) = val.parse::() { - float(val) - } else { - type_err("Could not convert string to Float", str(val)) - } -} - -pub fn int>(value: I) -> ObjectRef { - let value = value.into(); - if value.is_positive() && &value <= Lazy::force(&SHARED_INT_MAX_BIGINT) { - let index = value.to_usize().unwrap(); - SHARED_INTS[index].clone() - } else { - obj_ref!(Int::new(value)) - } -} - -pub fn int_from_string>(val: S) -> ObjectRef { - let val = val.into(); - if let Ok(val) = BigInt::from_str_radix(val.as_ref(), 10) { - int(val) - } else if let Ok(val) = val.parse::() { - int(BigInt::from_f64(val).unwrap()) - } else { - type_err("Could not convert string to Int", str(val)) - } -} - -pub fn iterator(wrapped: Vec) -> ObjectRef { - obj_ref!(FIIterator::new(wrapped)) -} - -pub fn list(items: Vec) -> ObjectRef { - obj_ref!(List::new(items.to_vec())) -} - -pub fn map(map: IndexMap) -> ObjectRef { - obj_ref!(Map::new(map)) -} - -pub fn map_from_keys_and_vals(keys: Vec, vals: Vec) -> ObjectRef { - assert_eq!(keys.len(), vals.len()); - obj_ref!(Map::new(IndexMap::from_iter(keys.into_iter().zip(vals)))) -} - -pub fn prop(getter: ObjectRef) -> ObjectRef { - obj_ref!(Prop::new(getter)) -} - -pub fn str>(val: S) -> ObjectRef { - let val = val.into(); - if val.is_empty() { - EMPTY_STR.clone() - } else if val == "\n" { - NEWLINE.clone() - } else { - obj_ref!(Str::new(val)) - } -} - -pub fn tuple(items: Vec) -> ObjectRef { - if items.is_empty() { - EMPTY_TUPLE.clone() - } else { - obj_ref!(Tuple::new(items)) - } -} - -// Custom type constructor --------------------------------------------- - -pub fn custom_type(module: ObjectRef, name: &str) -> ObjectRef { - let class_ref = obj_ref!(CustomType::new(module.clone(), name.to_owned())); - - { - let mut class = class_ref.write().unwrap(); - let ns = class.ns_mut(); - ns.insert( - "new", - intrinsic_func( - module.read().unwrap().down_to_mod().unwrap().name(), - name, - Some(class_ref.clone()), - &["attrs"], - "Create a new custom type. - - # Args - - - class: TypeRef - - type_obj: ObjectRef - - attributes: Map - - ", - |this, args| { - let attrs_arg = args.get(0).unwrap(); - let attrs_arg = attrs_arg.read().unwrap(); - let attrs = attrs_arg.down_to_map().unwrap(); - - let mut ns = Namespace::default(); - ns.extend_from_map(attrs); - - // XXX: Cloning the inner object is wonky and breaks - // identity testing. - let type_obj = this.read().unwrap(); - let type_obj = if type_obj.is_type_object() { - // Called via custom type. - let type_obj = type_obj.down_to_custom_type().unwrap(); - obj_ref!(type_obj.clone()) - } else { - // Called via custom instance. - // XXX: This branch isn't reachable because the - // VM will panic due to the identity test - // issue noted above. - let type_obj = type_obj.type_obj(); - let type_obj = type_obj.read().unwrap(); - let type_obj = type_obj.down_to_custom_type().unwrap(); - obj_ref!(type_obj.clone()) - }; - - let instance = CustomObj::new(type_obj, ns); - obj_ref!(instance) - }, - ), - ); - } - - class_ref -} diff --git a/feint-builtins/src/types/nil.rs b/feint-builtins/src/types/nil.rs index b65a7b4..4be3529 100644 --- a/feint-builtins/src/types/nil.rs +++ b/feint-builtins/src/types/nil.rs @@ -2,24 +2,16 @@ use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use once_cell::sync::Lazy; - -use super::new; use feint_code_gen::*; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; -use super::ns::Namespace; - -// Nil Type ------------------------------------------------------------ +use super::base::{ObjectRef, ObjectTrait, TypeRef}; -type_and_impls!(NilType, Nil); - -pub static NIL_TYPE: Lazy = Lazy::new(|| obj_ref!(NilType::new())); +use super::ns::Namespace; -// Nil Object ---------------------------------------------------------- +// Nil ---------------------------------------------------------- pub struct Nil { + class: TypeRef, ns: Namespace, } @@ -27,13 +19,13 @@ standard_object_impls!(Nil); impl Nil { #[allow(clippy::new_without_default)] - pub fn new() -> Self { - Self { ns: Namespace::default() } + pub fn new(class: TypeRef) -> Self { + Self { class, ns: Namespace::default() } } } impl ObjectTrait for Nil { - object_trait_header!(NIL_TYPE); + object_trait_header!(); fn bool_val(&self) -> Option { Some(false) diff --git a/feint-builtins/src/types/ns.rs b/feint-builtins/src/types/ns.rs index b0cb98d..5f590e4 100644 --- a/feint-builtins/src/types/ns.rs +++ b/feint-builtins/src/types/ns.rs @@ -62,6 +62,20 @@ impl Namespace { self.objects.insert(name.into(), obj); } + pub fn get_or_insert ObjectRef>( + &mut self, + name: &str, + mut make_obj: F, + ) -> ObjectRef { + if let Some(obj) = self.get(name) { + obj.clone() + } else { + let obj = make_obj(); + self.insert(name, obj.clone()); + obj + } + } + /// Set an object's value. This will only succeed if the object /// already exists in the namespace. pub fn set(&mut self, name: &str, obj: ObjectRef) -> bool { diff --git a/feint-builtins/src/types/prop.rs b/feint-builtins/src/types/prop.rs index c73e215..90430ed 100644 --- a/feint-builtins/src/types/prop.rs +++ b/feint-builtins/src/types/prop.rs @@ -4,25 +4,17 @@ use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use once_cell::sync::Lazy; - use feint_code_gen::*; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; -use super::new; -use super::ns::Namespace; - -// Prop Type ----------------------------------------------------------- +use super::base::{ObjectRef, ObjectTrait, TypeRef}; -type_and_impls!(PropType, Prop); - -pub static PROP_TYPE: Lazy = - Lazy::new(|| obj_ref!(PropType::new())); +use super::ns::Namespace; -// Prop Object --------------------------------------------------------- +// Prop --------------------------------------------------------- pub struct Prop { + class: TypeRef, + ns: Namespace, getter: ObjectRef, } @@ -30,8 +22,8 @@ pub struct Prop { standard_object_impls!(Prop); impl Prop { - pub fn new(getter: ObjectRef) -> Self { - Self { ns: Namespace::default(), getter } + pub fn new(class: TypeRef, getter: ObjectRef) -> Self { + Self { class, ns: Namespace::default(), getter } } pub fn getter(&self) -> ObjectRef { @@ -40,7 +32,7 @@ impl Prop { } impl ObjectTrait for Prop { - object_trait_header!(PROP_TYPE); + object_trait_header!(); } // Display ------------------------------------------------------------- diff --git a/feint-builtins/src/types/seq.rs b/feint-builtins/src/types/seq.rs index 20157a8..641be0a 100644 --- a/feint-builtins/src/types/seq.rs +++ b/feint-builtins/src/types/seq.rs @@ -4,25 +4,27 @@ use num_bigint::BigInt; use feint_code_gen::{use_arg, use_arg_str}; +use crate::BUILTINS; + use super::base::ObjectRef; -use super::{new, Args}; +use super::Args; pub fn has(items: &[ObjectRef], args: &Args) -> ObjectRef { if items.is_empty() { - return new::bool(false); + return BUILTINS.bool(false); } let member = use_arg!(args, 0); for item in items.iter() { if member.is_equal(&*item.read().unwrap()) { - return new::bool(true); + return BUILTINS.bool(true); } } - new::bool(false) + BUILTINS.bool(false) } pub fn join(items: &[ObjectRef], args: &Args) -> ObjectRef { if items.is_empty() { - return new::empty_str(); + return BUILTINS.empty_str(); } let n_items = items.len(); @@ -43,11 +45,11 @@ pub fn join(items: &[ObjectRef], args: &Args) -> ObjectRef { } } - new::str(string) + BUILTINS.str(string) } pub fn sum(items: &[ObjectRef]) -> ObjectRef { - let mut sum = new::int(BigInt::from(0)); + let mut sum = BUILTINS.int(BigInt::from(0)); for item in items.iter() { sum = { let a = sum.read().unwrap(); @@ -55,7 +57,7 @@ pub fn sum(items: &[ObjectRef]) -> ObjectRef { if let Some(new_sum) = (*a).add(&*b) { new_sum } else { - return new::type_err("Could not add object to sum", item.clone()); + return BUILTINS.type_err("Could not add object to sum", item.clone()); } } } diff --git a/feint-builtins/src/types/str.rs b/feint-builtins/src/types/str.rs index 58736b0..524b32d 100644 --- a/feint-builtins/src/types/str.rs +++ b/feint-builtins/src/types/str.rs @@ -3,121 +3,116 @@ use std::fmt; use std::str::EscapeDefault; use std::sync::{Arc, RwLock}; -use once_cell::sync::Lazy; - use feint_code_gen::*; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; -use super::new; -use super::ns::Namespace; +use super::base::{ObjectRef, ObjectTrait, TypeRef}; -// Str Type ------------------------------------------------------------ - -type_and_impls!(StrType, Str); - -pub static STR_TYPE: Lazy = Lazy::new(|| { - let type_ref = obj_ref!(StrType::new()); - let mut type_obj = type_ref.write().unwrap(); - - type_obj.add_attrs(&[ - // Class Methods ----------------------------------------------- - meth!("new", type_ref, &["value"], "", |_, args| { - let arg = use_arg!(args, 0); - if arg.is_str() { - args[0].clone() - } else { - new::str(arg.to_string()) - } - }), - // Instance Attributes ----------------------------------------- - prop!("length", type_ref, "", |this, _| { - let this = this.read().unwrap(); - let value = this.get_str_val().unwrap(); - new::int(value.len()) - }), - // Instance Methods -------------------------------------------- - meth!("starts_with", type_ref, &["prefix"], "", |this, args| { - let this = this.read().unwrap(); - let value = this.get_str_val().unwrap(); - let arg = use_arg!(args, 0); - let prefix = use_arg_str!(starts_with, prefix, arg); - new::bool(value.starts_with(prefix)) - }), - meth!("ends_with", type_ref, &["suffix"], "", |this, args| { - let this = this.read().unwrap(); - let value = this.get_str_val().unwrap(); - let arg = use_arg!(args, 0); - let suffix = use_arg_str!(ends_with, suffix, arg); - new::bool(value.ends_with(suffix)) - }), - meth!("upper", type_ref, &[], "", |this, _| { - let this = this.read().unwrap(); - let value = this.get_str_val().unwrap(); - new::str(value.to_uppercase()) - }), - meth!("lower", type_ref, &[], "", |this, _| { - let this = this.read().unwrap(); - let value = this.get_str_val().unwrap(); - new::str(value.to_lowercase()) - }), - // meth!( - // "render", - // type_ref, - // &["context"], - // "Render string as template - // - // Templates may contain `{{ name }}` vars which will be replaced with the - // values provided in the context map. - // - // # Args - // - // - context: Map A map containing values to be rendered into the - // template. - // - // ", - // |this, args| { - // let context = args[0].clone(); - // let result = render_template(this.clone(), context)?; - // Ok(result) - // } - // ), - meth!("repeat", type_ref, &["count"], "", |this, args| { - let this = this.read().unwrap(); - let value = this.get_str_val().unwrap(); - let count = use_arg_usize!(get, index, args, 0); - new::str(value.repeat(count)) - }), - meth!("replace", type_ref, &["old", "new"], "", |this, args| { - let this = this.read().unwrap(); - let value = this.get_str_val().unwrap(); - let arg1 = use_arg!(args, 0); - let arg2 = use_arg!(args, 1); - let old = use_arg_str!(replace, old, arg1); - let new = use_arg_str!(replace, new, arg2); - let result = value.replace(old, new); - new::str(result) - }), - meth!("remove_prefix", type_ref, &["prefix"], "", |this_ref, args| { - let this = this_ref.read().unwrap(); - let val = this.get_str_val().unwrap(); - let arg = use_arg!(args, 0); - let prefix = use_arg_str!(starts_with, prefix, arg); - if let Some(new_val) = val.strip_prefix(prefix) { - new::str(new_val) - } else { - drop(this); - this_ref - } - }), - ]); - - type_ref.clone() -}); - -// Str Object ---------------------------------------------------------- +use super::ns::Namespace; +use crate::BUILTINS; + +// pub(crate) fn make_str_type() -> obj_ref_t!(StrType) { +// let type_ref = obj_ref!(StrType::new()); +// let mut type_obj = type_ref.write().unwrap(); +// +// type_obj.add_attrs(&[ +// // Class Methods ----------------------------------------------- +// meth!("new", type_ref, &["value"], "", |_, args| { +// let arg = use_arg!(args, 0); +// if arg.is_str() { +// args[0].clone() +// } else { +// BUILTINS.str(arg.to_string()) +// } +// }), +// // Instance Attributes ----------------------------------------- +// prop!("length", type_ref, "", |this, _| { +// let this = this.read().unwrap(); +// let value = this.get_str_val().unwrap(); +// BUILTINS.int(value.len()) +// }), +// // Instance Methods -------------------------------------------- +// meth!("starts_with", type_ref, &["prefix"], "", |this, args| { +// let this = this.read().unwrap(); +// let value = this.get_str_val().unwrap(); +// let arg = use_arg!(args, 0); +// let prefix = use_arg_str!(starts_with, prefix, arg); +// BUILTINS.bool(value.starts_with(prefix)) +// }), +// meth!("ends_with", type_ref, &["suffix"], "", |this, args| { +// let this = this.read().unwrap(); +// let value = this.get_str_val().unwrap(); +// let arg = use_arg!(args, 0); +// let suffix = use_arg_str!(ends_with, suffix, arg); +// BUILTINS.bool(value.ends_with(suffix)) +// }), +// meth!("upper", type_ref, &[], "", |this, _| { +// let this = this.read().unwrap(); +// let value = this.get_str_val().unwrap(); +// BUILTINS.str(value.to_uppercase()) +// }), +// meth!("lower", type_ref, &[], "", |this, _| { +// let this = this.read().unwrap(); +// let value = this.get_str_val().unwrap(); +// BUILTINS.str(value.to_lowercase()) +// }), +// // meth!( +// // "render", +// // type_ref, +// // &["context"], +// // "Render string as template +// // +// // Templates may contain `{{ name }}` vars which will be replaced with the +// // values provided in the context map. +// // +// // # Args +// // +// // - context: Map A map containing values to be rendered into the +// // template. +// // +// // ", +// // |this, args| { +// // let context = args[0].clone(); +// // let result = render_template(this.clone(), context)?; +// // Ok(result) +// // } +// // ), +// meth!("repeat", type_ref, &["count"], "", |this, args| { +// let this = this.read().unwrap(); +// let value = this.get_str_val().unwrap(); +// let count = use_arg_usize!(get, index, args, 0); +// BUILTINS.str(value.repeat(count)) +// }), +// meth!("replace", type_ref, &["old", "new"], "", |this, args| { +// let this = this.read().unwrap(); +// let value = this.get_str_val().unwrap(); +// let arg1 = use_arg!(args, 0); +// let arg2 = use_arg!(args, 1); +// let old = use_arg_str!(replace, old, arg1); +// let new = use_arg_str!(replace, new, arg2); +// let result = value.replace(old, new); +// BUILTINS.str(result) +// }), +// meth!("remove_prefix", type_ref, &["prefix"], "", |this_ref, args| { +// let this = this_ref.read().unwrap(); +// let val = this.get_str_val().unwrap(); +// let arg = use_arg!(args, 0); +// let prefix = use_arg_str!(starts_with, prefix, arg); +// if let Some(new_val) = val.strip_prefix(prefix) { +// BUILTINS.str(new_val) +// } else { +// drop(this); +// this_ref +// } +// }), +// ]); +// +// type_ref.clone() +// } + +// Str ---------------------------------------------------------- pub struct Str { + class: TypeRef, ns: Namespace, value: String, } @@ -125,11 +120,12 @@ pub struct Str { standard_object_impls!(Str); impl Str { - pub fn new(value: String) -> Self { + pub fn new(class: TypeRef, value: String) -> Self { Self { + class, ns: Namespace::with_entries(&[ // Instance Attributes - ("len", new::int(value.len())), + ("length", BUILTINS.int(value.len())), ]), value, } @@ -147,7 +143,7 @@ impl Str { } impl ObjectTrait for Str { - object_trait_header!(STR_TYPE); + object_trait_header!(); fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { if self.is(rhs) || rhs.is_always() { @@ -166,7 +162,7 @@ impl ObjectTrait for Str { let mut value = String::with_capacity(a.len() + b.len()); value.push_str(a); value.push_str(b); - let value = new::str(value); + let value = BUILTINS.str(value); Some(value) } else { None diff --git a/feint-builtins/src/types/tuple.rs b/feint-builtins/src/types/tuple.rs index 8297b67..5402eba 100644 --- a/feint-builtins/src/types/tuple.rs +++ b/feint-builtins/src/types/tuple.rs @@ -3,75 +3,70 @@ use std::fmt; use std::slice::Iter; use std::sync::{Arc, RwLock}; -use once_cell::sync::Lazy; - use feint_code_gen::*; -use super::new; +use crate::BUILTINS; + +use super::base::{ObjectRef, ObjectTrait, TypeRef}; -use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; use super::ns::Namespace; use super::seq; -// Tuple Type ---------------------------------------------------------- - -type_and_impls!(TupleType, Tuple); - -pub static TUPLE_TYPE: Lazy = Lazy::new(|| { - let type_ref = obj_ref!(TupleType::new()); - let mut type_obj = type_ref.write().unwrap(); - - type_obj.add_attrs(&[ - // Instance Attributes ----------------------------------------- - prop!("length", type_ref, "", |this, _| { - let this = this.read().unwrap(); - let this = this.down_to_tuple().unwrap(); - new::int(this.len()) - }), - prop!("is_empty", type_ref, "", |this, _| { - let this = this.read().unwrap(); - let this = this.down_to_tuple().unwrap(); - new::bool(this.len() == 0) - }), - prop!("sum", type_ref, "", |this, _| { - let this = this.read().unwrap(); - let this = this.down_to_tuple().unwrap(); - seq::sum(&this.items) - }), - // Instance Methods -------------------------------------------- - meth!("get", type_ref, &["index"], "", |this, args| { - let this = this.read().unwrap(); - let this = this.down_to_tuple().unwrap(); - let index = use_arg_usize!(get, index, args, 0); - match this.get(index) { - Some(obj) => obj, - None => new::nil(), - } - }), - meth!("has", type_ref, &["member"], "", |this, args| { - let this = this.read().unwrap(); - let this = this.down_to_tuple().unwrap(); - seq::has(&this.items, &args) - }), - meth!("iter", type_ref, &[], "", |this_ref, _| { - let this = this_ref.read().unwrap(); - let this = this.down_to_tuple().unwrap(); - new::iterator(this.items.clone()) - }), - meth!("join", type_ref, &["sep"], "", |this, args| { - let this = this.read().unwrap(); - let this = this.down_to_tuple().unwrap(); - seq::join(&this.items, &args) - }), - ]); - - type_ref.clone() -}); - -// Tuple Object -------------------------------------------------------- +// pub fn make_tuple_type() -> obj_ref_t!(TupleType) { +// let type_ref = obj_ref!(TupleType::new()); +// let mut type_obj = type_ref.write().unwrap(); +// +// type_obj.add_attrs(&[ +// // Instance Attributes ----------------------------------------- +// prop!("length", type_ref, "", |this, _| { +// let this = this.read().unwrap(); +// let this = this.down_to_tuple().unwrap(); +// BUILTINS.int(this.len()) +// }), +// prop!("is_empty", type_ref, "", |this, _| { +// let this = this.read().unwrap(); +// let this = this.down_to_tuple().unwrap(); +// BUILTINS.bool(this.len() == 0) +// }), +// prop!("sum", type_ref, "", |this, _| { +// let this = this.read().unwrap(); +// let this = this.down_to_tuple().unwrap(); +// seq::sum(&this.items) +// }), +// // Instance Methods -------------------------------------------- +// meth!("get", type_ref, &["index"], "", |this, args| { +// let this = this.read().unwrap(); +// let this = this.down_to_tuple().unwrap(); +// let index = use_arg_usize!(get, index, args, 0); +// match this.get(index) { +// Some(obj) => obj, +// None => BUILTINS.nil(), +// } +// }), +// meth!("has", type_ref, &["member"], "", |this, args| { +// let this = this.read().unwrap(); +// let this = this.down_to_tuple().unwrap(); +// seq::has(&this.items, &args) +// }), +// meth!("iter", type_ref, &[], "", |this_ref, _| { +// let this = this_ref.read().unwrap(); +// let this = this.down_to_tuple().unwrap(); +// BUILTINS.iterator(this.items.clone()) +// }), +// meth!("join", type_ref, &["sep"], "", |this, args| { +// let this = this.read().unwrap(); +// let this = this.down_to_tuple().unwrap(); +// seq::join(&this.items, &args) +// }), +// ]); +// +// type_ref.clone() +// } + +// Tuple -------------------------------------------------------- pub struct Tuple { + class: TypeRef, ns: Namespace, items: Vec, } @@ -79,8 +74,8 @@ pub struct Tuple { standard_object_impls!(Tuple); impl Tuple { - pub fn new(items: Vec) -> Self { - Self { ns: Namespace::default(), items } + pub fn new(class: TypeRef, items: Vec) -> Self { + Self { class, ns: Namespace::default(), items } } pub(crate) fn iter(&self) -> Iter<'_, ObjectRef> { @@ -101,7 +96,7 @@ impl Tuple { } impl ObjectTrait for Tuple { - object_trait_header!(TUPLE_TYPE); + object_trait_header!(); fn get_item(&self, index: usize, this: ObjectRef) -> ObjectRef { if let Some(item) = self.items.get(index) { diff --git a/feint-builtins/src/util.rs b/feint-builtins/src/util.rs index c55bef6..7039e9a 100644 --- a/feint-builtins/src/util.rs +++ b/feint-builtins/src/util.rs @@ -1,4 +1,5 @@ -use crate::types::{new, Args, ObjectRef}; +use crate::types::{Args, ObjectRef}; +use crate::BUILTINS; /// Check args and return info. /// @@ -50,7 +51,7 @@ pub(crate) fn check_args( n_args = n_args - 1 + n_var_args; (n_var_args, var_args_ref.clone()) } else { - (0, new::empty_tuple()) + (0, BUILTINS.empty_tuple()) }; // NOTE: Slightly hacky, but it's extremely unlikely anyone would @@ -65,7 +66,7 @@ pub(crate) fn check_args( } else { format!("{name} expected {min} to {max} args; got {n_args}") }; - return Err(new::arg_err(msg, new::nil())); + return Err(BUILTINS.arg_err(msg, BUILTINS.nil())); } Ok((n_args, n_var_args, var_args)) diff --git a/feint-cli/src/repl.rs b/feint-cli/src/repl.rs index 78a8480..7aebfac 100644 --- a/feint-cli/src/repl.rs +++ b/feint-cli/src/repl.rs @@ -4,7 +4,8 @@ use std::path::PathBuf; use rustyline::config::Configurer; use rustyline::error::ReadlineError; -use feint_builtins::types::{new, ObjectRef, ObjectTrait}; +use feint_builtins::types::{ObjectRef, ObjectTrait}; +use feint_builtins::BUILTINS; use feint_compiler::{CompErrKind, ParseErrKind, ScanErrKind}; use feint_driver::result::{DriverErr, DriverErrKind, DriverOptResult, DriverResult}; use feint_driver::Driver; @@ -18,7 +19,7 @@ pub struct Repl { impl Repl { pub fn new(history_path: Option, mut driver: Driver) -> Self { - let module = new::module("$repl", "", "FeInt REPL module", &[]); + let module = BUILTINS.module("$repl", "", "FeInt REPL module", &[]); driver.add_module("$repl", module.clone()); let mut reader = diff --git a/feint-code-gen/src/lib.rs b/feint-code-gen/src/lib.rs index 53ee467..18b8c46 100644 --- a/feint-code-gen/src/lib.rs +++ b/feint-code-gen/src/lib.rs @@ -12,100 +12,6 @@ macro_rules! obj_ref { }; } -/// Generate an intrinsic type definition. This includes the type's -/// struct and impl as well as the TypeTrait, Send, and Sync impls. -/// -/// Args: -/// -/// $type_name: ident -/// The type name. E.g., `NilType` -/// -/// $name: ident -/// The type's object name. E.g., `Nil` -#[macro_export] -macro_rules! type_and_impls { - ( $type_name:ident, $name:ident ) => { - pub struct $type_name { - ns: Namespace, - } - - unsafe impl Send for $type_name {} - unsafe impl Sync for $type_name {} - - impl $type_name { - #[allow(clippy::new_without_default)] - pub fn new() -> Self { - let name = new::str(stringify!($name)); - let full_name = new::str(concat!("std.", stringify!($name))); - Self { - ns: Namespace::with_entries(&[ - ("$name", name), - ("$full_name", full_name), - ]), - } - } - - pub fn with_attrs(attrs: &[(&str, ObjectRef)]) -> Self { - let mut type_obj = Self::new(); - type_obj.ns.extend(attrs); - type_obj - } - - pub fn add_attr(&mut self, name: &str, val: ObjectRef) { - self.ns.insert(name, val); - } - - pub fn add_attrs(&mut self, attrs: &[(&str, ObjectRef)]) { - self.ns.extend(attrs); - } - } - - impl TypeTrait for $type_name { - fn name(&self) -> &str { - stringify!($name) - } - - fn full_name(&self) -> &str { - concat!("std.", stringify!($name)) - } - - fn ns(&self) -> &Namespace { - &self.ns - } - } - - impl ObjectTrait for $type_name { - fn as_any(&self) -> &dyn Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } - - fn class(&self) -> TypeRef { - TYPE_TYPE.clone() - } - - fn type_obj(&self) -> ObjectRef { - TYPE_TYPE.clone() - } - - fn ns(&self) -> &Namespace { - &self.ns - } - - fn ns_mut(&mut self) -> &mut Namespace { - &mut self.ns - } - - fn as_type(&self) -> Option<&dyn TypeTrait> { - Some(self) - } - } - }; -} - /// Generate standard obj impls. #[macro_export] macro_rules! standard_object_impls { @@ -124,7 +30,7 @@ macro_rules! standard_object_impls { /// The singleton type instance. E.g. `NIL_TYPE`. #[macro_export] macro_rules! object_trait_header { - ( $class:ident ) => { + () => { fn as_any(&self) -> &dyn Any { self } @@ -134,11 +40,11 @@ macro_rules! object_trait_header { } fn class(&self) -> TypeRef { - $class.clone() + self.class.clone() } fn type_obj(&self) -> ObjectRef { - $class.clone() + self.class.clone() } fn ns(&self) -> &Namespace { @@ -148,10 +54,6 @@ macro_rules! object_trait_header { fn ns_mut(&mut self) -> &mut Namespace { &mut self.ns } - - fn as_type(&self) -> Option<&dyn TypeTrait> { - None - } }; } @@ -190,7 +92,7 @@ macro_rules! meth { ( $name:literal, $this_type:expr, $params:expr, $doc:literal, $func:expr ) => { ( $name, - new::intrinsic_func( + BUILTINS.intrinsic_func( "std", $name, Some($this_type.clone()), @@ -209,7 +111,7 @@ macro_rules! prop { ( $name:literal, $this_type:expr, $doc:literal, $func:expr ) => { ( $name, - new::prop(new::intrinsic_func( + BUILTINS.prop(BUILTINS.intrinsic_func( "std", $name, Some($this_type.clone()), @@ -235,7 +137,7 @@ macro_rules! use_arg { } else { let msg = format!("{}() didn't receive enough args", stringify!($func_name)); - return new::arg_err(msg, new::nil()); + return BUILTINS.arg_err(msg, BUILTINS.nil()); } }}; } @@ -254,7 +156,7 @@ macro_rules! use_arg_str { stringify!($func_name), stringify!($arg_name) ); - return new::arg_err(msg, new::nil()); + return BUILTINS.arg_err(msg, BUILTINS.nil()); } }}; } @@ -270,7 +172,7 @@ macro_rules! use_arg_map { stringify!($func_name), stringify!($arg_name) ); - return new::arg_err(msg, new::nil()); + return BUILTINS.arg_err(msg, BUILTINS.nil()); } }}; } @@ -288,12 +190,12 @@ macro_rules! use_arg_usize { stringify!($func_name), stringify!($arg_name) ); - return new::arg_err(msg, new::nil()); + return BUILTINS.arg_err(msg, BUILTINS.nil()); } } else { let msg = format!("{}() didn't receive enough args", stringify!($func_name)); - return new::arg_err(msg, new::nil()); + return BUILTINS.arg_err(msg, BUILTINS.nil()); } }}; } diff --git a/feint-compiler/src/compiler/compiler.rs b/feint-compiler/src/compiler/compiler.rs index 732ab1a..04cdfe7 100644 --- a/feint-compiler/src/compiler/compiler.rs +++ b/feint-compiler/src/compiler/compiler.rs @@ -3,7 +3,8 @@ use std::collections::HashSet; use feint_builtins::modules::STD; use feint_builtins::types::code::{Code, Inst}; -use feint_builtins::types::{new, Module}; +use feint_builtins::types::Module; +use feint_builtins::BUILTINS; use feint_util::stack::Stack; use crate::ast; @@ -56,7 +57,13 @@ impl Compiler { ast_module: ast::Module, ) -> CompResult { let code = self.compile_module_to_code(name, ast_module)?; - Ok(Module::new(name.to_owned(), file_name.to_owned(), code, None)) + Ok(Module::new( + BUILTINS.module_type(), + name.to_owned(), + file_name.to_owned(), + code, + None, + )) } /// Compile AST module node to code object. @@ -263,7 +270,7 @@ impl Compiler { // END Inner Functions ----------------------------------------- - let func = new::func(module_name, func_name, params, visitor.code); + let func = BUILTINS.func(module_name, func_name, params, visitor.code); let parent_visitor = &mut self.visitor_stack.peek_mut().unwrap().0; let const_index = parent_visitor.code.add_const(func); diff --git a/feint-compiler/src/compiler/visitor.rs b/feint-compiler/src/compiler/visitor.rs index 2d14c05..8a51daa 100644 --- a/feint-compiler/src/compiler/visitor.rs +++ b/feint-compiler/src/compiler/visitor.rs @@ -3,9 +3,10 @@ use std::collections::HashSet; use std::fmt; use std::fmt::Formatter; +use feint_builtins::builtins::BUILTINS; use feint_builtins::modules::STD; use feint_builtins::types::code::{Code, Inst, PrintFlags}; -use feint_builtins::types::{new, ObjectRef}; +use feint_builtins::types::ObjectRef; use feint_util::op::{ BinaryOperator, CompareOperator, InplaceOperator, ShortCircuitCompareOperator, UnaryOperator, @@ -381,10 +382,10 @@ impl CompilerVisitor { Kind::Always => self.push_always(), Kind::Ellipsis => self.push_nil(), Kind::Int(value) => { - self.add_const(new::int(value)); + self.add_const(BUILTINS.int(value)); } Kind::Float(value) => { - self.add_const(new::float(value)); + self.add_const(BUILTINS.float(value)); } Kind::String(value) => { if value.is_empty() { @@ -392,7 +393,7 @@ impl CompilerVisitor { } else if value == "\n" { self.push_newline(); } else { - self.add_const(new::str(value)); + self.add_const(BUILTINS.str(value)); } } } @@ -903,31 +904,31 @@ impl CompilerVisitor { // Global constants ------------------------------------------------ fn push_nil(&mut self) { - self.add_const(new::nil()); + self.add_const(BUILTINS.nil()); } fn push_true(&mut self) { - self.add_const(new::bool(true)); + self.add_const(BUILTINS.bool(true)); } fn push_false(&mut self) { - self.add_const(new::bool(false)); + self.add_const(BUILTINS.bool(false)); } fn push_always(&mut self) { - self.add_const(new::always()); + self.add_const(BUILTINS.always()); } fn push_empty_str(&mut self) { - self.add_const(new::empty_str()); + self.add_const(BUILTINS.empty_str()); } fn push_newline(&mut self) { - self.add_const(new::newline()); + self.add_const(BUILTINS.newline()); } fn push_empty_tuple(&mut self) { - self.add_const(new::empty_tuple()); + self.add_const(BUILTINS.empty_tuple()); } // Code unit constants --------------------------------------------- diff --git a/feint-compiler/src/format.rs b/feint-compiler/src/format.rs index 5215da1..88ba20e 100644 --- a/feint-compiler/src/format.rs +++ b/feint-compiler/src/format.rs @@ -1,4 +1,5 @@ -use feint_builtins::types::{new, ObjectRef}; +use feint_builtins::types::ObjectRef; +use feint_builtins::BUILTINS; use feint_util::source::source_from_text; use crate::scanner::{ScanTokensResult, Scanner, Token, TokenWithLocation as TWL}; @@ -120,7 +121,8 @@ pub fn render_template(template_ref: ObjectRef, context_ref: ObjectRef) -> Objec let context = if let Some(context) = context.down_to_map() { context } else { - return new::string_err("Expected context to be a map", template_ref.clone()); + return BUILTINS + .string_err("Expected context to be a map", template_ref.clone()); }; let scan_result = scan_format_string(template.as_str(), Some(("{{", "}}"))); @@ -129,7 +131,7 @@ pub fn render_template(template_ref: ObjectRef, context_ref: ObjectRef) -> Objec Ok(tokens) => tokens, Err(err) => { let msg = format!("Could not parse template: {err:?}"); - return new::string_err(msg, template_ref.clone()); + return BUILTINS.string_err(msg, template_ref.clone()); } }; @@ -148,7 +150,7 @@ pub fn render_template(template_ref: ObjectRef, context_ref: ObjectRef) -> Objec output.push_str(val.as_str()); } else { let msg = format!("Name not found in context: {name}"); - return new::string_err(msg, template_ref.clone()); + return BUILTINS.string_err(msg, template_ref.clone()); } } _ => { @@ -157,11 +159,11 @@ pub fn render_template(template_ref: ObjectRef, context_ref: ObjectRef) -> Objec let msg = format!( "Template is contains an invalid expression: {tokens:?}" ); - return new::string_err(msg, template_ref.clone()); + return BUILTINS.string_err(msg, template_ref.clone()); } }, } } - new::str(output) + BUILTINS.str(output) } diff --git a/feint-driver/src/driver.rs b/feint-driver/src/driver.rs index 92e699a..a42552a 100644 --- a/feint-driver/src/driver.rs +++ b/feint-driver/src/driver.rs @@ -5,14 +5,12 @@ use std::io::BufRead; use std::path::Path; use std::sync::{Arc, RwLock}; -use feint_builtins::modules::{ - add_module, maybe_get_module, std as stdlib, MODULES, STD, STD_FI_MODULES, -}; +use feint_builtins::modules::{STD, STD_FI_MODULES}; use feint_builtins::types::{ - self, code::{Inst, PrintFlags}, - new, Module, ObjectRef, ObjectTrait, + Module, ObjectRef, ObjectTrait, }; +use feint_builtins::BUILTINS; use feint_code_gen::obj_ref; use feint_compiler::{ ast, CompErr, CompErrKind, Compiler, ParseErr, ParseErrKind, Parser, ScanErr, @@ -92,15 +90,13 @@ impl Driver { // it's used early (i.e., during import). { let mut system = system_ref.write().unwrap(); - system.ns_mut().insert("modules", MODULES.clone()); - system.ns_mut().insert("argv", new::argv_tuple(&self.argv)); + system.ns_mut().insert("modules", BUILTINS.modules()); + system.ns_mut().insert("argv", BUILTINS.argv_tuple(&self.argv)); } - self.add_module("std.proc", stdlib::PROC.clone()); - - self.extend_intrinsic_type(types::list::LIST_TYPE.clone(), "std.list")?; - self.extend_intrinsic_type(types::map::MAP_TYPE.clone(), "std.map")?; - self.extend_intrinsic_type(types::tuple::TUPLE_TYPE.clone(), "std.tuple")?; + self.extend_intrinsic_type(BUILTINS.list_type(), "std.list")?; + self.extend_intrinsic_type(BUILTINS.map_type(), "std.map")?; + self.extend_intrinsic_type(BUILTINS.tuple_type(), "std.tuple")?; Ok(()) } @@ -287,7 +283,7 @@ impl Driver { if result.is_ok() && is_main { if let Some(main) = module.get_main() { let main = main.read().unwrap(); - let args = self.argv.iter().map(new::str).collect(); + let args = self.argv.iter().map(|a| BUILTINS.str(a)).collect(); if let Some(main) = main.down_to_func() { result = self .vm @@ -401,12 +397,12 @@ impl Driver { /// Add a module to both `MODULES` and `system.modules`. pub fn add_module(&mut self, name: &str, module: ObjectRef) { - add_module(name, module.clone()); + BUILTINS.add_module(name, module.clone()); } /// Get module from `MODULES` (the `system.modules` mirror). fn get_module(&mut self, name: &str) -> Result { - if let Some(module) = maybe_get_module(name) { + if let Some(module) = BUILTINS.maybe_get_module(name) { Ok(module) } else { Err(DriverErr::new(DriverErrKind::ModuleNotFound(name.to_owned()))) diff --git a/feint-vm/src/context.rs b/feint-vm/src/context.rs index 9533197..76fc658 100644 --- a/feint-vm/src/context.rs +++ b/feint-vm/src/context.rs @@ -2,7 +2,8 @@ use indexmap::IndexMap; use feint_builtins::modules::STD; -use feint_builtins::types::{new, ObjectRef, ObjectTrait}; +use feint_builtins::types::{ObjectRef, ObjectTrait}; +use feint_builtins::BUILTINS; use crate::RuntimeObjResult; @@ -96,7 +97,7 @@ impl ModuleExecutionContext { /// the var in the current namespace and sets its initial value to /// nil. pub(super) fn declare_var(&mut self, name: &str) { - let initial = new::nil(); + let initial = BUILTINS.nil(); let ns = self.current_mut(); ns.insert(name.to_owned(), initial); } diff --git a/feint-vm/src/tests.rs b/feint-vm/src/tests.rs index b740ba5..3812138 100644 --- a/feint-vm/src/tests.rs +++ b/feint-vm/src/tests.rs @@ -1,5 +1,8 @@ -use feint_builtins::types::code::{Code, Inst}; -use feint_builtins::types::{new, Module}; +use feint_builtins::types::{ + code::{Code, Inst}, + Module, +}; +use feint_builtins::BUILTINS; use feint_util::op::BinaryOperator; use crate::*; @@ -11,9 +14,15 @@ fn execute_simple_program() { Inst::LoadConst(1), Inst::BinaryOp(BinaryOperator::Add), ]); - code.add_const(new::int(1)); - code.add_const(new::int(2)); - let module = Module::new("test".to_owned(), "test".to_owned(), code, None); + code.add_const(BUILTINS.int(1)); + code.add_const(BUILTINS.int(2)); + let module = Module::new( + BUILTINS.module_type(), + "test".to_owned(), + "test".to_owned(), + code, + None, + ); let mut vm = VM::default(); assert!(matches!(vm.execute_module(&module, 0), Ok(()))); assert!(matches!(vm.state, VMState::Idle(Some(_)))); diff --git a/feint-vm/src/vm.rs b/feint-vm/src/vm.rs index 3657eee..07ab0da 100644 --- a/feint-vm/src/vm.rs +++ b/feint-vm/src/vm.rs @@ -12,11 +12,11 @@ use ctrlc; use indexmap::IndexMap; use num_traits::ToPrimitive; -use feint_builtins::modules::get_module; use feint_builtins::types::code::{Code, Inst, PrintFlags}; use feint_builtins::types::{ - new, Args, Func, FuncTrait, IntrinsicFunc, Module, ObjectRef, ThisOpt, + Args, Func, FuncTrait, IntrinsicFunc, Module, ObjectRef, ThisOpt, }; +use feint_builtins::BUILTINS; use feint_util::op::{BinaryOperator, CompareOperator, InplaceOperator, UnaryOperator}; use feint_util::source::Location; use feint_util::stack::Stack; @@ -172,7 +172,7 @@ impl VM { } // Modules LoadModule(name) => { - let module = get_module(name.as_str()); + let module = BUILTINS.get_module(name.as_str()); self.push_temp(module); } // Vars @@ -247,7 +247,7 @@ impl VM { } else { // Create new cell to wrap TOS in. assert!(var.is_nil()); - let cell_ref = new::cell_with_value(value.clone()); + let cell_ref = BUILTINS.cell_with_value(value.clone()); self.ctx.assign_var(name, cell_ref, 0)? }; // Push cell *value* to TOS. @@ -293,7 +293,7 @@ impl VM { } } JumpPushNil(addr, forward, scope_exit_count) => { - self.push_temp(new::nil()); + self.push_temp(BUILTINS.nil()); self.exit_scopes(*scope_exit_count); if *forward { jump_ip = Some(ip + *addr); @@ -385,17 +385,17 @@ impl VM { let obj = obj.read().unwrap(); string.push_str(obj.to_string().as_str()); } - let string_obj = new::str(string); + let string_obj = BUILTINS.str(string); self.push_temp(string_obj); } MakeTuple(n) => { let objects = self.pop_n_obj(*n)?; - let tuple = new::tuple(objects); + let tuple = BUILTINS.tuple(objects); self.push_temp(tuple); } MakeList(n) => { let objects = self.pop_n_obj(*n)?; - let list = new::list(objects); + let list = BUILTINS.list(objects); self.push_temp(list); } MakeMap(n) => { @@ -411,7 +411,7 @@ impl VM { vals.push(obj.clone()); } } - let map = new::map_from_keys_and_vals(keys, vals); + let map = BUILTINS.map_from_keys_and_vals(keys, vals); self.push_temp(map); } CaptureSet(names) => { @@ -425,7 +425,7 @@ impl VM { capture_set.insert(name.to_owned(), var_ref.clone()); } else { assert!(var.is_nil()); - capture_set.insert(name.to_owned(), new::cell()); + capture_set.insert(name.to_owned(), BUILTINS.cell()); } } else if let Some(frame) = self.call_stack.peek() { // Capture cell does not exist. @@ -442,7 +442,7 @@ impl VM { } } } - self.push_temp(new::map(capture_set)); + self.push_temp(BUILTINS.map(capture_set)); } MakeFunc => { let capture_set_ref = self.pop_obj()?; @@ -460,7 +460,7 @@ impl VM { if func_captured && ip + 1 < len_chunk { if let AssignCell(_) = &code[ip + 1] { let closure_cell = - new::cell_with_value(func_ref.clone()); + BUILTINS.cell_with_value(func_ref.clone()); self.ctx.assign_var( func.name(), closure_cell.clone(), @@ -471,10 +471,9 @@ impl VM { } } - self.push_temp(new::closure( - func_ref.clone(), - capture_set_ref.clone(), - )); + self.push_temp( + BUILTINS.closure(func_ref.clone(), capture_set_ref.clone()), + ); } } // VM control @@ -617,21 +616,21 @@ impl VM { if let Some(result) = a.negate() { result } else { - new::type_err(format!("- not defined for {a}"), a_ref.clone()) + BUILTINS.type_err(format!("- not defined for {a}"), a_ref.clone()) } } AsBool => { if let Some(result) = a.bool_val() { - new::bool(result) + BUILTINS.bool(result) } else { - new::type_err(format!("!! not defined for {a}"), a_ref.clone()) + BUILTINS.type_err(format!("!! not defined for {a}"), a_ref.clone()) } } Not => { if let Some(result) = a.not() { - new::bool(result) + BUILTINS.bool(result) } else { - new::type_err(format!("! not defined for {a}"), a_ref.clone()) + BUILTINS.type_err(format!("! not defined for {a}"), a_ref.clone()) } } }; @@ -683,7 +682,7 @@ impl VM { // XXX: This can happen for a construct like `1.()`, // but that should probably be a syntax error // that's caught early. - new::attr_err( + BUILTINS.attr_err( format!("Not an attribute name or index: {b:?}"), a_ref.clone(), ) @@ -696,7 +695,7 @@ impl VM { // TODO: Check whether `a` is a type or an instance. - new::bound_func(obj_ref.clone(), a_ref.clone()) + BUILTINS.bound_func(obj_ref.clone(), a_ref.clone()) } else if let Some(prop) = obj.down_to_prop() { // If `b` in `a.b` is a property, bind `b`'s getter // to `a` then call the bound getter. @@ -705,8 +704,8 @@ impl VM { // and return the property itself when `a` is // a type. - let func = new::bound_func(prop.getter(), a_ref.clone()); - if a.is_type_object() { + let func = BUILTINS.bound_func(prop.getter(), a_ref.clone()); + if a.is_type() { func } else { return self.call(func, vec![]); @@ -723,10 +722,10 @@ impl VM { if let Some(result) = result { self.push_temp(result); } else { - self.push_temp(new::type_err( - format!("Operation not defined for {b}"), - a_ref.clone(), - )); + self.push_temp( + BUILTINS + .type_err(format!("Operation not defined for {b}"), a_ref.clone()), + ); } Ok(()) @@ -754,7 +753,7 @@ impl VM { GreaterThan => a.greater_than(b).unwrap_or(false), GreaterThanOrEqual => a.greater_than(b).unwrap_or(false) || a.is_equal(b), }; - self.push_temp(new::bool(result)); + self.push_temp(BUILTINS.bool(result)); Ok(()) } @@ -778,10 +777,10 @@ impl VM { let result = if let Some(result) = result { result } else { - self.push_temp(new::type_err( - format!("Operation not defined for {b}"), - a_ref.clone(), - )); + self.push_temp( + BUILTINS + .type_err(format!("Operation not defined for {b}"), a_ref.clone()), + ); return Ok(()); }; @@ -906,7 +905,7 @@ impl VM { return this.clone(); } } - new::nil() + BUILTINS.nil() } // Function calls -------------------------------------------------- @@ -960,11 +959,11 @@ impl VM { ); self.call_closure(func_ref.clone(), this_opt, args) } else { - self.push_temp(new::not_callable_err(callable_ref.clone())); + self.push_temp(BUILTINS.not_callable_err(callable_ref.clone())); Ok(()) } } else { - self.push_temp(new::not_callable_err(callable_ref.clone())); + self.push_temp(BUILTINS.not_callable_err(callable_ref.clone())); Ok(()) } } @@ -999,7 +998,7 @@ impl VM { // better to track which params are captured. See related // note in push_var(). for (name, arg) in func.arg_names().iter().zip(args) { - let cell = new::cell_with_value(arg); + let cell = BUILTINS.cell_with_value(arg); self.ctx.declare_and_assign_var(name, cell)?; } match self.execute_func(func, 0) { @@ -1044,7 +1043,7 @@ impl VM { self.check_arity(name, arity, n_args, this_opt)?; let mut args = args.clone(); let var_args_items = args.split_off(var_args_index); - let var_args = new::tuple(var_args_items); + let var_args = BUILTINS.tuple(var_args_items); args.push(var_args); Ok(args) } else { @@ -1068,7 +1067,9 @@ impl VM { || "".to_owned(), |this_ref| { let this_obj = this_ref.read().unwrap(); - format!("{}.", this_obj.class().read().unwrap().full_name()) + // TODO: + // format!("{}.", this_obj.class().read().unwrap().full_name()) + format!("{this_obj}") } ), name diff --git a/scripts/type.template b/scripts/type.template index 8be9038..305acda 100644 --- a/scripts/type.template +++ b/scripts/type.template @@ -10,12 +10,12 @@ use super::gen; use super::new; use super::base::{ObjectRef, ObjectTrait, TypeRef, TypeTrait}; -use super::class::TYPE_TYPE; + use super::ns::Namespace; // {{ type_name }} Type {{ type_rule }} -gen::type_and_impls!({{ type_name }}, {{ obj_name }}); + pub static {{ singleton_type_name }}: Lazy = Lazy::new(|| gen::obj_ref!({{ type_name }}::new())); From fcf9b92162a6deefee3b8eb52e27921945dfcb70 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Sat, 4 Feb 2023 23:36:38 -0800 Subject: [PATCH 26/31] WIP --- feint-builtins/src/builtins.rs | 223 +++++++++++++++++------------- feint-builtins/src/types/base.rs | 22 ++- feint-builtins/src/types/class.rs | 32 +++-- feint-builtins/src/types/str.rs | 2 +- feint-code-gen/src/lib.rs | 4 - feint-vm/src/vm.rs | 6 +- 6 files changed, 160 insertions(+), 129 deletions(-) diff --git a/feint-builtins/src/builtins.rs b/feint-builtins/src/builtins.rs index 6c7d71d..71f0498 100644 --- a/feint-builtins/src/builtins.rs +++ b/feint-builtins/src/builtins.rs @@ -39,7 +39,7 @@ pub static BUILTINS: Lazy = Lazy::new(Builtins::default); pub struct Builtins { // Modules --------------------------------------------------------- - pub modules: OnceCel, + pub modules: obj_ref_t!(Map), // Types ----------------------------------------------------------- pub type_type: TypeRef, @@ -78,37 +78,87 @@ pub struct Builtins { unsafe impl Send for Builtins {} unsafe impl Sync for Builtins {} -fn new_type(name: &str) -> TypeRef { - obj_ref!(Type::new("std", name)) -} - impl Default for Builtins { fn default() -> Self { - let builtins = Self { - type_type: new_type("Type"), - always_type: new_type("Always"), - bool_type: new_type("Bool"), - bound_func_type: new_type("BoundFunc"), - cell_type: new_type("Cell"), - closure_type: new_type("Closure"), - err_type: new_type("Err"), - err_type_type: new_type("ErrType"), - file_type: new_type("File"), - float_type: new_type("Float"), - func_type: new_type("Func"), - int_type: new_type("Int"), - intrinsic_func_type: new_type("IntrinsicFunc"), - iterator_type: new_type("Iterator"), - list_type: new_type("List"), - map_type: new_type("Map"), - module_type: new_type("Module"), - nil_type: new_type("Nil"), - prop_type: new_type("Prop"), - str_type: new_type("Str"), - tuple_type: new_type("Tuple"), + fn new_type(name: &str) -> TypeRef { + obj_ref!(Type::new("std", name)) + } + + let type_type = new_type("Type"); + let always_type = new_type("Always"); + let bool_type = new_type("Bool"); + let bound_func_type = new_type("BoundFunc"); + let cell_type = new_type("Cell"); + let closure_type = new_type("Closure"); + let err_type = new_type("Err"); + let err_type_type = new_type("ErrType"); + let file_type = new_type("File"); + let float_type = new_type("Float"); + let func_type = new_type("Func"); + let int_type = new_type("Int"); + let intrinsic_func_type = new_type("IntrinsicFunc"); + let iterator_type = new_type("Iterator"); + let list_type = new_type("List"); + let map_type = new_type("Map"); + let module_type = new_type("Module"); + let nil_type = new_type("Nil"); + let prop_type = new_type("Prop"); + let str_type = new_type("Str"); + let tuple_type = new_type("Tuple"); + + let modules = obj_ref!(Map::new(map_type.clone(), IndexMap::default())); + + let nil = obj_ref!(Nil::new(nil_type.clone())); + let true_ = obj_ref!(Bool::new(bool_type.clone(), true)); + let false_ = obj_ref!(Bool::new(bool_type.clone(), false)); + let always = obj_ref!(Always::new(always_type.clone())); + let empty_str = obj_ref!(Str::new(str_type.clone(), "".to_owned())); + let newline = obj_ref!(Str::new(str_type.clone(), "\n".to_owned())); + let empty_tuple = obj_ref!(Tuple::new(tuple_type.clone(), vec![])); + let ok_err = obj_ref!(ErrObj::new( + err_type.clone(), + ErrKind::Ok, + "".to_string(), + nil.clone() + )); + + let instance = Self { + // Modules --------------------------------------------------------- + modules, + // Types ----------------------------------------------------------- + type_type, + always_type, + bool_type, + bound_func_type, + cell_type, + closure_type, + err_type, + err_type_type, + file_type, + float_type, + func_type, + int_type, + intrinsic_func_type, + iterator_type, + list_type, + map_type, + module_type, + nil_type, + prop_type, + str_type, + tuple_type, + // Singletons ------------------------------------------------------ + nil, + true_, + false_, + always, + empty_str, + newline, + empty_tuple, + ok_err, }; - builtins + instance } } @@ -116,9 +166,24 @@ impl Builtins { // Modules --------------------------------------------------------- pub fn modules(&self) -> obj_ref_t!(Map) { - self.modules - .get_or_init(|| obj_ref!(Map::new(self.map_type(), IndexMap::default()))) - .clone() + self.modules.clone() + } + + pub fn module( + &self, + name: &str, + path: &str, + doc: &str, + entries: &[(&str, ObjectRef)], + ) -> obj_ref_t!(types::module::Module) { + obj_ref!(Module::with_entries( + self.module_type(), + entries, + name.to_owned(), + path.to_owned(), + Code::default(), + Some(doc.to_owned()) + )) } /// Add module to `std.system.modules`. @@ -158,108 +223,104 @@ impl Builtins { // Types ----------------------------------------------------------- - fn new_type(&self, name: &str) -> TypeRef { - obj_ref!(Type::new("std", name)) - } - pub fn type_type(&self) -> TypeRef { - self.type_type.get_or_init(|| self.new_type("Type")).clone() + self.type_type.clone() } pub fn always_type(&self) -> TypeRef { - self.always_type.get_or_init(|| self.new_type("Always")).clone() + self.always_type.clone() } pub fn bool_type(&self) -> TypeRef { - self.bool_type.get_or_init(|| self.new_type("Bool")).clone() + self.bool_type.clone() } pub fn bound_func_type(&self) -> TypeRef { - self.bound_func_type.get_or_init(|| self.new_type("BoundFunc")).clone() + self.bound_func_type.clone() } pub fn cell_type(&self) -> TypeRef { - self.cell_type.get_or_init(|| self.new_type("Cell")).clone() + self.cell_type.clone() } pub fn closure_type(&self) -> TypeRef { - self.closure_type.get_or_init(|| self.new_type("Closure")).clone() + self.closure_type.clone() } pub fn err_type(&self) -> TypeRef { - self.err_type.get_or_init(|| self.new_type("Err")).clone() + self.err_type.clone() // self.err_type.get_or_init(make_err_type).clone() } pub fn err_type_type(&self) -> TypeRef { - self.err_type_type.get_or_init(|| self.new_type("ErrType")).clone() + self.err_type_type.clone() } pub fn file_type(&self) -> TypeRef { - self.file_type.get_or_init(|| self.new_type("File")).clone() + self.file_type.clone() // self.file_type.get_or_init(make_file_type).clone() } pub fn float_type(&self) -> TypeRef { - self.float_type.get_or_init(|| self.new_type("Float")).clone() + self.float_type.clone() // self.float_type.get_or_init(make_float_type).clone() } pub fn func_type(&self) -> TypeRef { - self.func_type.get_or_init(|| self.new_type("Func")).clone() + self.func_type.clone() } pub fn intrinsic_func_type(&self) -> TypeRef { - self.intrinsic_func_type.get_or_init(|| self.new_type("IntrinsicFunc")).clone() + self.intrinsic_func_type.clone() } pub fn int_type(&self) -> TypeRef { - self.int_type.get_or_init(|| self.new_type("Int")).clone() + self.int_type.clone() // self.int_type.get_or_init(make_int_type).clone() } pub fn iterator_type(&self) -> TypeRef { - self.iterator_type.get_or_init(|| self.new_type("Iterator")).clone() + self.iterator_type.clone() // self.iterator_type.get_or_init(make_iterator_type).clone() } pub fn list_type(&self) -> TypeRef { - self.list_type.get_or_init(|| self.new_type("List")).clone() + self.list_type.clone() // self.list_type.get_or_init(make_list_type).clone() } pub fn map_type(&self) -> TypeRef { - self.map_type.get_or_init(|| self.new_type("Map")).clone() + self.map_type.clone() // self.map_type.get_or_init(make_map_type).clone() } pub fn module_type(&self) -> TypeRef { - self.module_type.get_or_init(|| self.new_type("Module")).clone() + self.module_type.clone() // self.module_type.get_or_init(make_module_type).clone() } pub fn nil_type(&self) -> TypeRef { - self.nil_type.get_or_init(|| self.new_type("Nil")).clone() + self.nil_type.clone() } pub fn prop_type(&self) -> TypeRef { - self.prop_type.get_or_init(|| self.new_type("Prop")).clone() + self.prop_type.clone() } pub fn str_type(&self) -> TypeRef { - self.str_type.get_or_init(|| self.new_type("Str")).clone() + self.str_type.clone() // self.str_type.get_or_init(make_str_type).clone() } pub fn tuple_type(&self) -> TypeRef { - self.tuple_type.get_or_init(|| self.new_type("Tuple")).clone() + self.tuple_type.clone() // self.tuple_type.get_or_init(make_tuple_type).clone() } // Singletons ------------------------------------------------------ pub fn nil(&self) -> ObjectRef { - self.nil.get_or_init(|| obj_ref!(Nil::new(self.nil_type()))).clone() + self.nil.clone() } pub fn bool(&self, val: bool) -> ObjectRef { @@ -271,65 +332,31 @@ impl Builtins { } pub fn true_(&self) -> ObjectRef { - self.true_.get_or_init(|| obj_ref!(Bool::new(self.bool_type(), true))).clone() + self.true_.clone() } pub fn false_(&self) -> ObjectRef { - self.false_.get_or_init(|| obj_ref!(Bool::new(self.bool_type(), false))).clone() + self.false_.clone() } pub fn always(&self) -> ObjectRef { - self.always.get_or_init(|| obj_ref!(Always::new(self.always_type()))).clone() + self.always.clone() } pub fn empty_str(&self) -> ObjectRef { - self.empty_str - .get_or_init(|| obj_ref!(Str::new(self.str_type(), "".to_owned()))) - .clone() + self.empty_str.clone() } pub fn newline(&self) -> ObjectRef { - self.newline - .get_or_init(|| obj_ref!(Str::new(self.str_type(), "\n".to_owned()))) - .clone() + self.newline.clone() } pub fn empty_tuple(&self) -> ObjectRef { - self.empty_tuple - .get_or_init(|| obj_ref!(Tuple::new(self.tuple_type(), vec![]))) - .clone() + self.empty_tuple.clone() } pub fn ok_err(&self) -> ObjectRef { - self.ok_err - .get_or_init(|| { - obj_ref!(ErrObj::new( - self.err_type(), - ErrKind::Ok, - "".to_string(), - self.nil() - )) - }) - .clone() - } - - // Modules ------------------------------------------------------------- - - pub fn module( - &self, - name: &str, - path: &str, - doc: &str, - entries: &[(&str, ObjectRef)], - ) -> obj_ref_t!(types::module::Module) { - obj_ref!(Module::with_entries( - self.module_type(), - entries, - name.to_owned(), - path.to_owned(), - Code::default(), - Some(doc.to_owned()) - )) + self.ok_err.clone() } // Functions ----------------------------------------------------------- diff --git a/feint-builtins/src/types/base.rs b/feint-builtins/src/types/base.rs index b6e8231..bcbc7d6 100644 --- a/feint-builtins/src/types/base.rs +++ b/feint-builtins/src/types/base.rs @@ -103,15 +103,8 @@ macro_rules! make_bin_op { pub trait ObjectTrait { fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; - - /// Get an instance's type as a type. This is needed to retrieve - /// type level attributes. fn class(&self) -> TypeRef; - /// Get an instance's type as an object. This is needed so the type - /// can be used in object contexts. - fn type_obj(&self) -> ObjectRef; - /// Each object has a namespace that holds its attributes. fn ns(&self) -> &Namespace; fn ns_mut(&mut self) -> &mut Namespace; @@ -156,12 +149,12 @@ pub trait ObjectTrait { return self.id_obj(); } - if name == "$module" { - return self.module(); - } + // if name == "$module" { + // return self.module(); + // } if name == "$type" { - return self.type_obj(); + return self.class(); } if name == "$names" { @@ -178,6 +171,7 @@ pub trait ObjectTrait { return BUILTINS.tuple(items); } + // TODO: Convert to builtin function // if name == "$dis" { // // User functions, bound functions wrapping user functions, // // and closures wrapping user functions can be disassembled. @@ -219,7 +213,7 @@ pub trait ObjectTrait { return obj; } - if let Some(obj) = self.type_obj().read().unwrap().ns().get(name) { + if let Some(obj) = self.class().read().unwrap().ns().get(name) { return obj; } @@ -445,9 +439,9 @@ pub trait ObjectTrait { if self.is(rhs) { return true; } - let t = self.type_obj(); + let t = self.class(); let t = t.read().unwrap(); - let u = rhs.type_obj(); + let u = rhs.class(); let u = u.read().unwrap(); t.is(&*u) && self.is_equal(rhs) } diff --git a/feint-builtins/src/types/class.rs b/feint-builtins/src/types/class.rs index 6bf8ee6..2e805e6 100644 --- a/feint-builtins/src/types/class.rs +++ b/feint-builtins/src/types/class.rs @@ -3,6 +3,9 @@ //! latter is a Rust keyword. use std::any::Any; use std::fmt; +use std::sync::{Arc, RwLock}; + +use once_cell::sync::{Lazy, OnceCell}; use feint_code_gen::*; @@ -11,10 +14,15 @@ use crate::BUILTINS; use super::base::{ObjectRef, ObjectTrait, TypeRef}; use super::ns::Namespace; +pub static TYPE_TYPE: Lazy = Lazy::new(|| obj_ref!(Type::new("std", "Type"))); + pub struct Type { module_name: String, name: String, ns: Namespace, + module_attr: OnceCell, + name_attr: OnceCell, + full_name_attr: OnceCell, } standard_object_impls!(Type); @@ -25,6 +33,9 @@ impl Type { module_name: module_name.to_owned(), name: name.to_owned(), ns: Namespace::default(), + module_attr: OnceCell::default(), + name_attr: OnceCell::default(), + full_name_attr: OnceCell::default(), } } @@ -47,11 +58,7 @@ impl ObjectTrait for Type { } fn class(&self) -> TypeRef { - BUILTINS.type_type() - } - - fn type_obj(&self) -> ObjectRef { - BUILTINS.type_type() + TYPE_TYPE.clone() } fn ns(&self) -> &Namespace { @@ -63,11 +70,18 @@ impl ObjectTrait for Type { } fn get_attr(&self, name: &str, this: ObjectRef) -> ObjectRef { - // TODO: Don't recreate attrs on every access match name { - "$module" => BUILTINS.str(&self.module_name), - "$name" => BUILTINS.str(&self.name), - "$full_name" => BUILTINS.str(format!("{}.{}", self.module_name, self.name)), + "$module" => self + .module_attr + .get_or_init(|| BUILTINS.get_module(self.module_name())) + .clone(), + "$name" => self.name_attr.get_or_init(|| BUILTINS.str(self.name())).clone(), + "$full_name" => self + .full_name_attr + .get_or_init(|| { + BUILTINS.str(format!("{}.{}", self.module_name(), self.name())) + }) + .clone(), _ => self.base_get_attr(name, this), } } diff --git a/feint-builtins/src/types/str.rs b/feint-builtins/src/types/str.rs index 524b32d..6c4f3e4 100644 --- a/feint-builtins/src/types/str.rs +++ b/feint-builtins/src/types/str.rs @@ -125,7 +125,7 @@ impl Str { class, ns: Namespace::with_entries(&[ // Instance Attributes - ("length", BUILTINS.int(value.len())), + // ("length", BUILTINS.int(value.len())), ]), value, } diff --git a/feint-code-gen/src/lib.rs b/feint-code-gen/src/lib.rs index 18b8c46..cc39371 100644 --- a/feint-code-gen/src/lib.rs +++ b/feint-code-gen/src/lib.rs @@ -43,10 +43,6 @@ macro_rules! object_trait_header { self.class.clone() } - fn type_obj(&self) -> ObjectRef { - self.class.clone() - } - fn ns(&self) -> &Namespace { &self.ns } diff --git a/feint-vm/src/vm.rs b/feint-vm/src/vm.rs index 07ab0da..e753084 100644 --- a/feint-vm/src/vm.rs +++ b/feint-vm/src/vm.rs @@ -14,7 +14,7 @@ use num_traits::ToPrimitive; use feint_builtins::types::code::{Code, Inst, PrintFlags}; use feint_builtins::types::{ - Args, Func, FuncTrait, IntrinsicFunc, Module, ObjectRef, ThisOpt, + Args, Func, FuncTrait, IntrinsicFunc, Module, ObjectRef, ObjectTrait, ThisOpt, }; use feint_builtins::BUILTINS; use feint_util::op::{BinaryOperator, CompareOperator, InplaceOperator, UnaryOperator}; @@ -109,7 +109,7 @@ impl VM { } pub fn execute_func(&mut self, func: &Func, start: usize) -> RuntimeResult { - let module = func.module(); + let module = (func as &dyn FuncTrait).module(); let module = module.read().unwrap(); let module = module.down_to_mod().unwrap(); self.execute_code(module, func.code(), start) @@ -935,7 +935,7 @@ impl VM { let expected_type = &*expected_type.read().unwrap(); let this = bound_func.this(); let this = this.read().unwrap(); - let this_type = this.type_obj(); + let this_type = this.class(); let this_type = this_type.read().unwrap(); // class method || instance method // XXX: Not sure this is the best way to distinguish From 4332b9d511181f8d6143e7758b5bf51629bc8227 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Sun, 5 Feb 2023 00:54:02 -0800 Subject: [PATCH 27/31] WIP --- feint-builtins/src/builtins.rs | 640 --------------------- feint-builtins/src/lib.rs | 4 +- feint-builtins/src/modules/mod.rs | 42 ++ feint-builtins/src/modules/std/std.rs | 42 +- feint-builtins/src/new.rs | 358 ++++++++++++ feint-builtins/src/tests/types.rs | 22 +- feint-builtins/src/types/always.rs | 18 +- feint-builtins/src/types/base.rs | 28 +- feint-builtins/src/types/bool.rs | 18 +- feint-builtins/src/types/bound_func.rs | 27 +- feint-builtins/src/types/cell.rs | 22 +- feint-builtins/src/types/class.rs | 76 +-- feint-builtins/src/types/closure.rs | 22 +- feint-builtins/src/types/code.rs | 6 +- feint-builtins/src/types/custom.rs | 42 +- feint-builtins/src/types/err.rs | 31 +- feint-builtins/src/types/err_type.rs | 22 +- feint-builtins/src/types/file.rs | 39 +- feint-builtins/src/types/float.rs | 35 +- feint-builtins/src/types/func.rs | 39 +- feint-builtins/src/types/int.rs | 43 +- feint-builtins/src/types/intrinsic_func.rs | 39 +- feint-builtins/src/types/iterator.rs | 22 +- feint-builtins/src/types/list.rs | 30 +- feint-builtins/src/types/map.rs | 20 +- feint-builtins/src/types/module.rs | 36 +- feint-builtins/src/types/nil.rs | 18 +- feint-builtins/src/types/prop.rs | 18 +- feint-builtins/src/types/seq.rs | 16 +- feint-builtins/src/types/str.rs | 37 +- feint-builtins/src/types/tuple.rs | 23 +- feint-builtins/src/util.rs | 6 +- feint-cli/src/repl.rs | 4 +- feint-code-gen/src/lib.rs | 28 +- feint-compiler/src/compiler/compiler.rs | 12 +- feint-compiler/src/compiler/visitor.rs | 22 +- feint-compiler/src/format.rs | 13 +- feint-driver/src/driver.rs | 23 +- feint-vm/src/context.rs | 4 +- feint-vm/src/vm.rs | 74 +-- 40 files changed, 882 insertions(+), 1139 deletions(-) delete mode 100644 feint-builtins/src/builtins.rs create mode 100644 feint-builtins/src/new.rs diff --git a/feint-builtins/src/builtins.rs b/feint-builtins/src/builtins.rs deleted file mode 100644 index 71f0498..0000000 --- a/feint-builtins/src/builtins.rs +++ /dev/null @@ -1,640 +0,0 @@ -use std::sync::{Arc, RwLock}; - -use indexmap::IndexMap; -use num_bigint::BigInt; -use num_traits::{FromPrimitive, Num}; -use once_cell::sync::{Lazy, OnceCell}; - -use feint_code_gen::{meth, obj_ref, obj_ref_t, use_arg}; -use feint_util::string::format_doc; - -use crate::types::{self, ObjectRef, ObjectTrait, Params, TypeRef}; - -use crate::types::always::Always; -use crate::types::bool::Bool; -use crate::types::bound_func::BoundFunc; -use crate::types::cell::Cell; -use crate::types::class::Type; -use crate::types::closure::Closure; -use crate::types::code::Code; -use crate::types::custom::CustomObj; -use crate::types::err::ErrObj; -use crate::types::err_type::ErrKind; -use crate::types::file::File; -use crate::types::float::Float; -use crate::types::func::Func; -use crate::types::int::Int; -use crate::types::intrinsic_func::{IntrinsicFn, IntrinsicFunc}; -use crate::types::iterator::FIIterator; -use crate::types::list::List; -use crate::types::map::Map; -use crate::types::module::Module; -use crate::types::nil::Nil; -use crate::types::ns::Namespace; -use crate::types::prop::Prop; -use crate::types::str::Str; -use crate::types::tuple::Tuple; - -pub static BUILTINS: Lazy = Lazy::new(Builtins::default); - -pub struct Builtins { - // Modules --------------------------------------------------------- - pub modules: obj_ref_t!(Map), - - // Types ----------------------------------------------------------- - pub type_type: TypeRef, - pub always_type: TypeRef, - pub bool_type: TypeRef, - pub bound_func_type: TypeRef, - pub cell_type: TypeRef, - pub closure_type: TypeRef, - pub err_type: TypeRef, - pub err_type_type: TypeRef, - pub file_type: TypeRef, - pub float_type: TypeRef, - pub func_type: TypeRef, - pub int_type: TypeRef, - pub intrinsic_func_type: TypeRef, - pub iterator_type: TypeRef, - pub list_type: TypeRef, - pub map_type: TypeRef, - pub module_type: TypeRef, - pub nil_type: TypeRef, - pub prop_type: TypeRef, - pub str_type: TypeRef, - pub tuple_type: TypeRef, - - // Singletons ------------------------------------------------------ - pub nil: ObjectRef, - pub true_: ObjectRef, - pub false_: ObjectRef, - pub always: ObjectRef, - pub empty_str: ObjectRef, - pub newline: ObjectRef, - pub empty_tuple: ObjectRef, - pub ok_err: ObjectRef, -} - -unsafe impl Send for Builtins {} -unsafe impl Sync for Builtins {} - -impl Default for Builtins { - fn default() -> Self { - fn new_type(name: &str) -> TypeRef { - obj_ref!(Type::new("std", name)) - } - - let type_type = new_type("Type"); - let always_type = new_type("Always"); - let bool_type = new_type("Bool"); - let bound_func_type = new_type("BoundFunc"); - let cell_type = new_type("Cell"); - let closure_type = new_type("Closure"); - let err_type = new_type("Err"); - let err_type_type = new_type("ErrType"); - let file_type = new_type("File"); - let float_type = new_type("Float"); - let func_type = new_type("Func"); - let int_type = new_type("Int"); - let intrinsic_func_type = new_type("IntrinsicFunc"); - let iterator_type = new_type("Iterator"); - let list_type = new_type("List"); - let map_type = new_type("Map"); - let module_type = new_type("Module"); - let nil_type = new_type("Nil"); - let prop_type = new_type("Prop"); - let str_type = new_type("Str"); - let tuple_type = new_type("Tuple"); - - let modules = obj_ref!(Map::new(map_type.clone(), IndexMap::default())); - - let nil = obj_ref!(Nil::new(nil_type.clone())); - let true_ = obj_ref!(Bool::new(bool_type.clone(), true)); - let false_ = obj_ref!(Bool::new(bool_type.clone(), false)); - let always = obj_ref!(Always::new(always_type.clone())); - let empty_str = obj_ref!(Str::new(str_type.clone(), "".to_owned())); - let newline = obj_ref!(Str::new(str_type.clone(), "\n".to_owned())); - let empty_tuple = obj_ref!(Tuple::new(tuple_type.clone(), vec![])); - let ok_err = obj_ref!(ErrObj::new( - err_type.clone(), - ErrKind::Ok, - "".to_string(), - nil.clone() - )); - - let instance = Self { - // Modules --------------------------------------------------------- - modules, - // Types ----------------------------------------------------------- - type_type, - always_type, - bool_type, - bound_func_type, - cell_type, - closure_type, - err_type, - err_type_type, - file_type, - float_type, - func_type, - int_type, - intrinsic_func_type, - iterator_type, - list_type, - map_type, - module_type, - nil_type, - prop_type, - str_type, - tuple_type, - // Singletons ------------------------------------------------------ - nil, - true_, - false_, - always, - empty_str, - newline, - empty_tuple, - ok_err, - }; - - instance - } -} - -impl Builtins { - // Modules --------------------------------------------------------- - - pub fn modules(&self) -> obj_ref_t!(Map) { - self.modules.clone() - } - - pub fn module( - &self, - name: &str, - path: &str, - doc: &str, - entries: &[(&str, ObjectRef)], - ) -> obj_ref_t!(types::module::Module) { - obj_ref!(Module::with_entries( - self.module_type(), - entries, - name.to_owned(), - path.to_owned(), - Code::default(), - Some(doc.to_owned()) - )) - } - - /// Add module to `std.system.modules`. - pub fn add_module(&self, name: &str, module: ObjectRef) { - let modules = self.modules(); - let modules = modules.write().unwrap(); - let modules = modules.down_to_map().unwrap(); - modules.insert(name, module); - } - - /// Get module from `system.modules`. - /// - /// XXX: Panics if the module doesn't exist (since that shouldn't be - /// possible). - pub fn get_module(&self, name: &str) -> ObjectRef { - let modules = self.modules(); - let modules = modules.read().unwrap(); - let modules = modules.down_to_map().unwrap(); - if let Some(module) = modules.get(name) { - module.clone() - } else { - panic!("Module not registered: {name}"); - } - } - - /// Get module from `system.modules`. - /// - /// XXX: This will return `None` if the module doesn't exist. Generally, - /// this should only be used during bootstrap. In most cases, - /// `get_module` should be used instead. - pub fn maybe_get_module(&self, name: &str) -> Option { - let modules = self.modules(); - let modules = modules.read().unwrap(); - let modules = modules.down_to_map().unwrap(); - modules.get(name) - } - - // Types ----------------------------------------------------------- - - pub fn type_type(&self) -> TypeRef { - self.type_type.clone() - } - - pub fn always_type(&self) -> TypeRef { - self.always_type.clone() - } - - pub fn bool_type(&self) -> TypeRef { - self.bool_type.clone() - } - - pub fn bound_func_type(&self) -> TypeRef { - self.bound_func_type.clone() - } - - pub fn cell_type(&self) -> TypeRef { - self.cell_type.clone() - } - - pub fn closure_type(&self) -> TypeRef { - self.closure_type.clone() - } - - pub fn err_type(&self) -> TypeRef { - self.err_type.clone() - // self.err_type.get_or_init(make_err_type).clone() - } - - pub fn err_type_type(&self) -> TypeRef { - self.err_type_type.clone() - } - - pub fn file_type(&self) -> TypeRef { - self.file_type.clone() - // self.file_type.get_or_init(make_file_type).clone() - } - - pub fn float_type(&self) -> TypeRef { - self.float_type.clone() - // self.float_type.get_or_init(make_float_type).clone() - } - - pub fn func_type(&self) -> TypeRef { - self.func_type.clone() - } - - pub fn intrinsic_func_type(&self) -> TypeRef { - self.intrinsic_func_type.clone() - } - - pub fn int_type(&self) -> TypeRef { - self.int_type.clone() - // self.int_type.get_or_init(make_int_type).clone() - } - - pub fn iterator_type(&self) -> TypeRef { - self.iterator_type.clone() - // self.iterator_type.get_or_init(make_iterator_type).clone() - } - - pub fn list_type(&self) -> TypeRef { - self.list_type.clone() - // self.list_type.get_or_init(make_list_type).clone() - } - - pub fn map_type(&self) -> TypeRef { - self.map_type.clone() - // self.map_type.get_or_init(make_map_type).clone() - } - - pub fn module_type(&self) -> TypeRef { - self.module_type.clone() - // self.module_type.get_or_init(make_module_type).clone() - } - - pub fn nil_type(&self) -> TypeRef { - self.nil_type.clone() - } - - pub fn prop_type(&self) -> TypeRef { - self.prop_type.clone() - } - - pub fn str_type(&self) -> TypeRef { - self.str_type.clone() - // self.str_type.get_or_init(make_str_type).clone() - } - - pub fn tuple_type(&self) -> TypeRef { - self.tuple_type.clone() - // self.tuple_type.get_or_init(make_tuple_type).clone() - } - - // Singletons ------------------------------------------------------ - - pub fn nil(&self) -> ObjectRef { - self.nil.clone() - } - - pub fn bool(&self, val: bool) -> ObjectRef { - if val { - self.true_() - } else { - self.false_() - } - } - - pub fn true_(&self) -> ObjectRef { - self.true_.clone() - } - - pub fn false_(&self) -> ObjectRef { - self.false_.clone() - } - - pub fn always(&self) -> ObjectRef { - self.always.clone() - } - - pub fn empty_str(&self) -> ObjectRef { - self.empty_str.clone() - } - - pub fn newline(&self) -> ObjectRef { - self.newline.clone() - } - - pub fn empty_tuple(&self) -> ObjectRef { - self.empty_tuple.clone() - } - - pub fn ok_err(&self) -> ObjectRef { - self.ok_err.clone() - } - - // Functions ----------------------------------------------------------- - - pub fn intrinsic_func( - &self, - module_name: &str, - name: &str, - this_type: Option, - params: &[&str], - doc: &str, - func: IntrinsicFn, - ) -> ObjectRef { - let params = params.iter().map(|n| n.to_string()).collect(); - obj_ref!(IntrinsicFunc::new( - self.intrinsic_func_type(), - module_name.to_owned(), - name.to_owned(), - this_type, - params, - format_doc(doc), - func - )) - } - - pub fn func>( - &self, - module_name: S, - func_name: S, - params: Params, - code: Code, - ) -> ObjectRef { - obj_ref!(Func::new( - self.func_type(), - module_name.into(), - func_name.into(), - params, - code - )) - } - - pub fn bound_func(&self, func: ObjectRef, this: ObjectRef) -> ObjectRef { - obj_ref!(BoundFunc::new(self.bound_func_type(), func, this)) - } - - pub fn closure(&self, func: ObjectRef, captured: ObjectRef) -> ObjectRef { - obj_ref!(Closure::new(self.closure_type(), func, captured)) - } - - pub fn cell(&self) -> ObjectRef { - obj_ref!(Cell::new(self.cell_type())) - } - - pub fn cell_with_value(&self, value: ObjectRef) -> ObjectRef { - obj_ref!(Cell::with_value(self.cell_type(), value)) - } - - pub fn argv_tuple(&self, argv: &[String]) -> ObjectRef { - obj_ref!(Tuple::new( - self.tuple_type(), - argv.iter().map(|a| self.str(a)).collect() - )) - } - - // Numbers --------------------------------------------------------- - - pub fn float(&self, value: f64) -> ObjectRef { - obj_ref!(Float::new(self.float_type(), value)) - } - - pub fn float_from_string>(&self, val: S) -> ObjectRef { - let val = val.into(); - if let Ok(val) = val.parse::() { - self.float(val) - } else { - self.type_err("Could not convert string to Float", self.str(val)) - } - } - - pub fn int>(&self, val: I) -> ObjectRef { - let val = val.into(); - obj_ref!(Int::new(self.int_type(), val)) - } - - pub fn int_from_string>(&self, val: S) -> ObjectRef { - let val = val.into(); - if let Ok(val) = BigInt::from_str_radix(val.as_ref(), 10) { - self.int(val) - } else if let Ok(val) = val.parse::() { - self.int(BigInt::from_f64(val).unwrap()) - } else { - self.type_err("Could not convert string to Int", self.str(val)) - } - } - - // Strings --------------------------------------------------------- - - pub fn str>(&self, val: S) -> ObjectRef { - let val = val.into(); - if val.is_empty() { - self.empty_str() - } else if val == "\n" { - self.newline() - } else { - obj_ref!(Str::new(self.str_type(), val)) - } - } - - // Collections ----------------------------------------------------- - - pub fn iterator(&self, wrapped: Vec) -> ObjectRef { - obj_ref!(FIIterator::new(self.iterator_type(), wrapped)) - } - - pub fn list(&self, items: Vec) -> ObjectRef { - obj_ref!(List::new(self.list_type(), items.to_vec())) - } - - pub fn map(&self, map: IndexMap) -> ObjectRef { - obj_ref!(Map::new(self.map_type(), map)) - } - - pub fn empty_map(&self) -> ObjectRef { - obj_ref!(Map::new(self.map_type(), IndexMap::default())) - } - - pub fn map_from_keys_and_vals( - &self, - keys: Vec, - vals: Vec, - ) -> ObjectRef { - assert_eq!(keys.len(), vals.len()); - self.map(IndexMap::from_iter(keys.into_iter().zip(vals))) - } - - pub fn tuple(&self, items: Vec) -> ObjectRef { - if items.is_empty() { - self.empty_tuple() - } else { - obj_ref!(Tuple::new(self.tuple_type(), items)) - } - } - - // Miscellaneous --------------------------------------------------- - - pub fn file>(&self, file_name: S) -> ObjectRef { - obj_ref!(File::new(self.file_type(), file_name.into())) - } - - pub fn prop(&self, getter: ObjectRef) -> ObjectRef { - obj_ref!(Prop::new(self.prop_type(), getter)) - } - - // Errors ---------------------------------------------------------- - - pub fn err>( - &self, - kind: ErrKind, - msg: S, - obj: ObjectRef, - ) -> ObjectRef { - obj_ref!(ErrObj::new(self.err_type(), kind, msg.into(), obj)) - } - - pub fn err_with_responds_to_bool>( - &self, - kind: ErrKind, - msg: S, - obj: ObjectRef, - ) -> ObjectRef { - obj_ref!(ErrObj::with_responds_to_bool(self.err_type(), kind, msg.into(), obj)) - } - - pub fn arg_err>(&self, msg: S, obj: ObjectRef) -> ObjectRef { - self.err(ErrKind::Arg, msg, obj) - } - - pub fn attr_err>(&self, msg: S, obj: ObjectRef) -> ObjectRef { - self.err(ErrKind::Attr, msg, obj) - } - - pub fn attr_not_found_err>( - &self, - msg: S, - obj: ObjectRef, - ) -> ObjectRef { - self.err(ErrKind::AttrNotFound, msg, obj) - } - - pub fn file_not_found_err>( - &self, - msg: S, - obj: ObjectRef, - ) -> ObjectRef { - self.err(ErrKind::FileNotFound, msg, obj) - } - - pub fn file_unreadable_err>( - &self, - msg: S, - obj: ObjectRef, - ) -> ObjectRef { - self.err(ErrKind::FileUnreadable, msg, obj) - } - - pub fn index_out_of_bounds_err(&self, index: usize, obj: ObjectRef) -> ObjectRef { - self.err(ErrKind::IndexOutOfBounds, index.to_string(), obj) - } - - pub fn not_callable_err(&self, obj: ObjectRef) -> ObjectRef { - self.err(ErrKind::NotCallable, format!("{}", obj.read().unwrap()), obj) - } - - pub fn string_err>(&self, msg: S, obj: ObjectRef) -> ObjectRef { - self.err(ErrKind::String, msg, obj) - } - - pub fn type_err>(&self, msg: S, obj: ObjectRef) -> ObjectRef { - self.err(ErrKind::Type, msg, obj) - } - - // Custom type constructor --------------------------------------------- - - // pub fn custom_type(&self, module: ObjectRef, name: &str) -> ObjectRef { - // let class_ref = obj_ref!(CustomType::new(module.clone(), name.to_owned())); - // - // { - // let mut class = class_ref.write().unwrap(); - // let ns = class.ns_mut(); - // ns.insert( - // "new", - // self.intrinsic_func( - // module.read().unwrap().down_to_mod().unwrap().name(), - // name, - // Some(class_ref.clone()), - // &["attrs"], - // "Create a new custom type. - // - // # Args - // - // - class: TypeRef - // - type_obj: ObjectRef - // - attributes: Map - // - // ", - // |this, args| { - // let attrs_arg = args.get(0).unwrap(); - // let attrs_arg = attrs_arg.read().unwrap(); - // let attrs = attrs_arg.down_to_map().unwrap(); - // - // let mut ns = Namespace::default(); - // ns.extend_from_map(attrs); - // - // // XXX: Cloning the inner object is wonky and breaks - // // identity testing. - // let type_obj = this.read().unwrap(); - // let type_obj = if type_obj.is_type_object() { - // // Called via custom type. - // let type_obj = type_obj.down_to_custom_type().unwrap(); - // obj_ref!(type_obj.clone()) - // } else { - // // Called via custom instance. - // // XXX: This branch isn't reachable because the - // // VM will panic due to the identity test - // // issue noted above. - // let type_obj = type_obj.type_obj(); - // let type_obj = type_obj.read().unwrap(); - // let type_obj = type_obj.down_to_custom_type().unwrap(); - // obj_ref!(type_obj.clone()) - // }; - // - // let instance = CustomObj::new(type_obj, ns); - // obj_ref!(instance) - // }, - // ), - // ); - // } - // - // class_ref - // } -} diff --git a/feint-builtins/src/lib.rs b/feint-builtins/src/lib.rs index 98c8cf5..20d2935 100644 --- a/feint-builtins/src/lib.rs +++ b/feint-builtins/src/lib.rs @@ -1,10 +1,8 @@ #[macro_use] extern crate bitflags; -pub use builtins::BUILTINS; - -pub mod builtins; pub mod modules; +pub mod new; pub mod types; mod util; diff --git a/feint-builtins/src/modules/mod.rs b/feint-builtins/src/modules/mod.rs index a7d66b6..cfd7f04 100644 --- a/feint-builtins/src/modules/mod.rs +++ b/feint-builtins/src/modules/mod.rs @@ -2,14 +2,56 @@ use ::std::borrow::Cow; use ::std::collections::HashMap; use ::std::io::Read; use ::std::path::Path; +use ::std::sync::{Arc, RwLock}; use flate2::read::GzDecoder; use once_cell::sync::Lazy; use tar::Archive as TarArchive; +use feint_code_gen::{obj_ref, obj_ref_t}; + +use crate::types::map::Map; +use crate::types::{ObjectRef, ObjectTrait}; + pub mod std; pub use self::std::STD; +/// This mirrors `system.modules`. It provides a way to access +/// modules in Rust code (e.g., in the VM). +pub static MODULES: Lazy = Lazy::new(|| obj_ref!(Map::default())); + +/// Add module to `std.system.modules`. +pub fn add_module(name: &str, module: ObjectRef) { + let modules = MODULES.write().unwrap(); + let modules = modules.down_to_map().unwrap(); + modules.insert(name, module); +} + +/// Get module from `system.modules`. +/// +/// XXX: Panics if the module doesn't exist (since that shouldn't be +/// possible). +pub fn get_module(name: &str) -> ObjectRef { + let modules = MODULES.read().unwrap(); + let modules = modules.down_to_map().unwrap(); + if let Some(module) = modules.get(name) { + module.clone() + } else { + panic!("Module not registered: {name}"); + } +} + +/// Get module from `system.modules`. +/// +/// XXX: This will return `None` if the module doesn't exist. Generally, +/// this should only be used during bootstrap. In most cases, +/// `get_module` should be used instead. +pub fn maybe_get_module(name: &str) -> Option { + let modules = MODULES.read().unwrap(); + let modules = modules.down_to_map().unwrap(); + modules.get(name) +} + /// At build time, a compressed archive is created containing the /// std .fi module files (see `build.rs`). /// diff --git a/feint-builtins/src/modules/std/std.rs b/feint-builtins/src/modules/std/std.rs index 8b9dc5b..ae9d2e8 100644 --- a/feint-builtins/src/modules/std/std.rs +++ b/feint-builtins/src/modules/std/std.rs @@ -5,33 +5,33 @@ use once_cell::sync::Lazy; use feint_code_gen::{obj_ref_t, use_arg, use_arg_str}; -use crate::{types, BUILTINS}; +use crate::{new, types}; pub static STD: Lazy = Lazy::new(|| { - BUILTINS.module( + new::module( "std", "", "std module (builtins)", &[ - ("Type", BUILTINS.type_type()), - ("Always", BUILTINS.always_type()), - ("Bool", BUILTINS.bool_type()), - ("BoundFunc", BUILTINS.bound_func_type()), - ("IntrinsicFunc", BUILTINS.intrinsic_func_type()), - ("Closure", BUILTINS.closure_type()), - ("Err", BUILTINS.err_type()), - ("ErrType", BUILTINS.err_type_type()), - ("File", BUILTINS.file_type()), - ("Func", BUILTINS.func_type()), - ("Float", BUILTINS.float_type()), - ("Int", BUILTINS.int_type()), - ("Iterator", BUILTINS.iterator_type()), - ("List", BUILTINS.list_type()), - ("Map", BUILTINS.map_type()), - ("Module", BUILTINS.module_type()), - ("Nil", BUILTINS.nil_type()), - ("Str", BUILTINS.str_type()), - ("Tuple", BUILTINS.tuple_type()), + ("Type", types::class::TYPE_TYPE.clone()), + ("Always", types::always::ALWAYS_TYPE.clone()), + ("Bool", types::bool::BOOL_TYPE.clone()), + ("BoundFunc", types::bound_func::BOUND_FUNC_TYPE.clone()), + ("IntrinsicFunc", types::intrinsic_func::INTRINSIC_FUNC_TYPE.clone()), + ("Closure", types::closure::CLOSURE_TYPE.clone()), + ("Err", types::err::ERR_TYPE.clone()), + ("ErrType", types::err_type::ERR_TYPE_TYPE.clone()), + ("File", types::file::FILE_TYPE.clone()), + ("Func", types::func::FUNC_TYPE.clone()), + ("Float", types::float::FLOAT_TYPE.clone()), + ("Int", types::int::INT_TYPE.clone()), + ("Iterator", types::iterator::ITERATOR_TYPE.clone()), + ("List", types::list::LIST_TYPE.clone()), + ("Map", types::map::MAP_TYPE.clone()), + ("Module", types::module::MODULE_TYPE.clone()), + ("Nil", types::nil::NIL_TYPE.clone()), + ("Str", types::str::STR_TYPE.clone()), + ("Tuple", types::tuple::TUPLE_TYPE.clone()), // ( // "new_type", // BUILTINS.intrinsic_func( diff --git a/feint-builtins/src/new.rs b/feint-builtins/src/new.rs new file mode 100644 index 0000000..3f65da9 --- /dev/null +++ b/feint-builtins/src/new.rs @@ -0,0 +1,358 @@ +//! Type Constructors. +//! +//! These constructors simplify the creation of system objects. +use std::sync::{Arc, RwLock}; + +use num_bigint::BigInt; +use num_traits::{FromPrimitive, Num, Signed, ToPrimitive}; + +use indexmap::IndexMap; +use once_cell::sync::Lazy; + +use feint_code_gen::{obj_ref, obj_ref_t}; +use feint_util::string::format_doc; + +use crate::types::{ObjectRef, ObjectTrait, Params}; + +use crate::types::always::Always; +use crate::types::bool::Bool; +use crate::types::bound_func::BoundFunc; +use crate::types::cell::Cell; +use crate::types::class::Type; +use crate::types::closure::Closure; +use crate::types::code::Code; +use crate::types::custom::CustomObj; +use crate::types::err::ErrObj; +use crate::types::err_type::ErrKind; +use crate::types::file::File; +use crate::types::float::Float; +use crate::types::func::Func; +use crate::types::int::Int; +use crate::types::intrinsic_func::{IntrinsicFn, IntrinsicFunc}; +use crate::types::iterator::FIIterator; +use crate::types::list::List; +use crate::types::map::Map; +use crate::types::module::Module; +use crate::types::nil::Nil; +use crate::types::ns::Namespace; +use crate::types::prop::Prop; +use crate::types::str::Str; +use crate::types::tuple::Tuple; + +// Global singletons --------------------------------------------------- + +static NIL: Lazy = Lazy::new(|| obj_ref!(Nil::new())); +static TRUE: Lazy = Lazy::new(|| obj_ref!(Bool::new(true))); +static FALSE: Lazy = Lazy::new(|| obj_ref!(Bool::new(false))); +static ALWAYS: Lazy = Lazy::new(|| obj_ref!(Always::new())); + +static EMPTY_STR: Lazy = + Lazy::new(|| obj_ref!(Str::new("".to_owned()))); + +static NEWLINE: Lazy = + Lazy::new(|| obj_ref!(Str::new("\n".to_owned()))); + +static EMPTY_TUPLE: Lazy = + Lazy::new(|| obj_ref!(Tuple::new(vec![]))); + +static SHARED_INT_MAX: usize = 256; +static SHARED_INT_MAX_BIGINT: Lazy = Lazy::new(|| BigInt::from(SHARED_INT_MAX)); +static SHARED_INTS: Lazy> = Lazy::new(|| { + (0..=SHARED_INT_MAX).map(|i| obj_ref!(Int::new(BigInt::from(i)))).collect() +}); + +#[inline] +pub fn nil() -> ObjectRef { + NIL.clone() +} + +#[inline] +pub fn bool(val: bool) -> ObjectRef { + if val { + TRUE.clone() + } else { + FALSE.clone() + } +} + +#[inline] +pub fn always() -> ObjectRef { + ALWAYS.clone() +} + +#[inline] +pub fn empty_str() -> ObjectRef { + EMPTY_STR.clone() +} + +#[inline] +pub fn newline() -> ObjectRef { + NEWLINE.clone() +} + +#[inline] +pub fn empty_tuple() -> ObjectRef { + EMPTY_TUPLE.clone() +} + +// Modules ------------------------------------------------------------- + +pub fn module( + name: &str, + path: &str, + doc: &str, + entries: &[(&str, ObjectRef)], +) -> obj_ref_t!(Module) { + obj_ref!(Module::with_entries( + entries, + name.to_owned(), + path.to_owned(), + Code::default(), + Some(doc.to_owned()) + )) +} + +// Function------------------------------------------------------------- + +pub fn intrinsic_func( + module_name: &str, + name: &str, + this_type: Option, + params: &[&str], + doc: &str, + func: IntrinsicFn, +) -> ObjectRef { + let params = params.iter().map(|n| n.to_string()).collect(); + let doc = format_doc(doc); + obj_ref!(IntrinsicFunc::new( + module_name.to_owned(), + name.to_owned(), + this_type, + params, + str(doc), + func + )) +} + +pub fn func>( + module_name: S, + func_name: S, + params: Params, + code: Code, +) -> ObjectRef { + obj_ref!(Func::new(module_name.into(), func_name.into(), params, code)) +} + +pub fn bound_func(func: ObjectRef, this: ObjectRef) -> ObjectRef { + obj_ref!(BoundFunc::new(func, this)) +} + +pub fn closure(func: ObjectRef, captured: ObjectRef) -> ObjectRef { + obj_ref!(Closure::new(func, captured)) +} + +pub fn cell() -> ObjectRef { + obj_ref!(Cell::new()) +} + +pub fn cell_with_value(value: ObjectRef) -> ObjectRef { + obj_ref!(Cell::with_value(value)) +} + +pub fn argv_tuple(argv: &[String]) -> ObjectRef { + obj_ref!(Tuple::new(argv.iter().map(str).collect())) +} + +// Errors -------------------------------------------------------------- + +pub fn err>(kind: ErrKind, msg: S, obj: ObjectRef) -> ObjectRef { + obj_ref!(ErrObj::new(kind, msg.into(), obj)) +} + +pub fn err_with_responds_to_bool>( + kind: ErrKind, + msg: S, + obj: ObjectRef, +) -> ObjectRef { + obj_ref!(ErrObj::with_responds_to_bool(kind, msg.into(), obj)) +} + +pub fn arg_err>(msg: S, obj: ObjectRef) -> ObjectRef { + err(ErrKind::Arg, msg, obj) +} + +pub fn attr_err>(msg: S, obj: ObjectRef) -> ObjectRef { + err(ErrKind::Attr, msg, obj) +} + +pub fn attr_not_found_err>(msg: S, obj: ObjectRef) -> ObjectRef { + err(ErrKind::AttrNotFound, msg, obj) +} + +pub fn file_not_found_err>(msg: S, obj: ObjectRef) -> ObjectRef { + err(ErrKind::FileNotFound, msg, obj) +} + +pub fn file_unreadable_err>(msg: S, obj: ObjectRef) -> ObjectRef { + err(ErrKind::FileUnreadable, msg, obj) +} + +pub fn index_out_of_bounds_err(index: usize, obj: ObjectRef) -> ObjectRef { + err(ErrKind::IndexOutOfBounds, index.to_string(), obj) +} + +pub fn not_callable_err(obj: ObjectRef) -> ObjectRef { + err(ErrKind::NotCallable, format!("{}", obj.read().unwrap()), obj) +} + +pub fn string_err>(msg: S, obj: ObjectRef) -> ObjectRef { + err(ErrKind::String, msg, obj) +} + +pub fn type_err>(msg: S, obj: ObjectRef) -> ObjectRef { + err(ErrKind::Type, msg, obj) +} + +static OK_ERR: Lazy = Lazy::new(|| { + obj_ref!(ErrObj::with_responds_to_bool(ErrKind::Ok, "".to_string(), nil())) +}); + +pub fn ok_err() -> ObjectRef { + OK_ERR.clone() +} + +// END Errors ---------------------------------------------------------- + +pub fn file>(file_name: S) -> ObjectRef { + obj_ref!(File::new(file_name.into())) +} + +pub fn float(value: f64) -> ObjectRef { + obj_ref!(Float::new(value)) +} + +pub fn float_from_string>(val: S) -> ObjectRef { + let val = val.into(); + if let Ok(val) = val.parse::() { + float(val) + } else { + type_err("Could not convert string to Float", str(val)) + } +} + +pub fn int>(value: I) -> ObjectRef { + let value = value.into(); + if value.is_positive() && &value <= Lazy::force(&SHARED_INT_MAX_BIGINT) { + let index = value.to_usize().unwrap(); + SHARED_INTS[index].clone() + } else { + obj_ref!(Int::new(value)) + } +} + +pub fn int_from_string>(val: S) -> ObjectRef { + let val = val.into(); + if let Ok(val) = BigInt::from_str_radix(val.as_ref(), 10) { + int(val) + } else if let Ok(val) = val.parse::() { + int(BigInt::from_f64(val).unwrap()) + } else { + type_err("Could not convert string to Int", str(val)) + } +} + +pub fn iterator(wrapped: Vec) -> ObjectRef { + obj_ref!(FIIterator::new(wrapped)) +} + +pub fn list(items: Vec) -> ObjectRef { + obj_ref!(List::new(items.to_vec())) +} + +pub fn map(map: IndexMap) -> ObjectRef { + obj_ref!(Map::new(map)) +} + +pub fn map_from_keys_and_vals(keys: Vec, vals: Vec) -> ObjectRef { + assert_eq!(keys.len(), vals.len()); + obj_ref!(Map::new(IndexMap::from_iter(keys.into_iter().zip(vals)))) +} + +pub fn prop(getter: ObjectRef) -> ObjectRef { + obj_ref!(Prop::new(getter)) +} + +pub fn str>(val: S) -> ObjectRef { + let val = val.into(); + if val.is_empty() { + EMPTY_STR.clone() + } else if val == "\n" { + NEWLINE.clone() + } else { + obj_ref!(Str::new(val)) + } +} + +pub fn tuple(items: Vec) -> ObjectRef { + if items.is_empty() { + EMPTY_TUPLE.clone() + } else { + obj_ref!(Tuple::new(items)) + } +} + +// Custom type constructor --------------------------------------------- + +pub fn custom_type(module_name: &str, name: &str) -> ObjectRef { + let class_ref = obj_ref!(Type::new(module_name, name)); + + { + let mut class = class_ref.write().unwrap(); + let ns = class.ns_mut(); + ns.insert( + "new", + intrinsic_func( + module_name, + name, + Some(class_ref.clone()), + &["attrs"], + "Create a new custom type. + + # Args + + - + - type_obj: ObjectRef + - attributes: Map + + ", + |this_ref, args| { + let attrs_arg = args.get(0).unwrap(); + let attrs_arg = attrs_arg.read().unwrap(); + let attrs = attrs_arg.down_to_map().unwrap(); + + let mut ns = Namespace::default(); + ns.extend_from_map(attrs); + + let type_obj = this_ref.read().unwrap(); + + let class = if type_obj.is_type() { + // Called via custom type. + this_ref.clone() + } else { + // Called via custom instance. + // XXX: This branch isn't reachable because the + // VM will panic due to the identity test + // issue noted above. + type_obj.class() + }; + + // let instance = CustomObj::new(class, ns); + // obj_ref!(instance) + nil() + }, + ), + ); + } + + class_ref +} diff --git a/feint-builtins/src/tests/types.rs b/feint-builtins/src/tests/types.rs index 4f0183a..f809318 100644 --- a/feint-builtins/src/tests/types.rs +++ b/feint-builtins/src/tests/types.rs @@ -1,5 +1,5 @@ +use crate::new; use crate::types::{ObjectRef, ObjectTrait}; -use crate::BUILTINS; fn check_ok>(obj: ObjectRef, msg: S) { assert!(!obj.read().unwrap().is_err(), "{}", msg.into()) @@ -65,9 +65,9 @@ mod float { #[test] fn test_float() { - let float1 = BUILTINS.float(0.0); - let float2 = BUILTINS.float(0.0); - let float3 = BUILTINS.float(1.0); + let float1 = new::float(0.0); + let float2 = new::float(0.0); + let float3 = new::float(1.0); check_type_is(float1.clone(), float2.clone()); check_type_is(float2.clone(), float3.clone()); @@ -85,8 +85,8 @@ mod float { #[test] fn test_compare_to_int() { - let float = BUILTINS.float(1.0); - let int = BUILTINS.int(1); + let float = new::float(1.0); + let int = new::int(1); check_eq(float.clone(), int.clone()); check_eq(int.clone(), float.clone()); } @@ -97,7 +97,7 @@ mod list { #[test] fn test_push_exists() { - let obj_ref = BUILTINS.list(vec![]); + let obj_ref = new::list(vec![]); let list = obj_ref.read().unwrap(); let push = list.get_attr("push", obj_ref.clone()); check_ok(push.clone(), "list.push() is not OK"); @@ -111,7 +111,7 @@ mod custom { use super::*; fn instance(type_obj: ObjectRef, attrs: &[(&str, ObjectRef)]) -> ObjectRef { - let attrs = BUILTINS.map(IndexMap::from_iter( + let attrs = new::map(IndexMap::from_iter( attrs.into_iter().map(|(n, v)| (n.to_string(), v.clone())), )); let new = type_obj.read().unwrap().get_attr("new", type_obj.clone()); @@ -123,9 +123,9 @@ mod custom { #[test] fn test_custom() { - let mod1 = BUILTINS.module("test1", "", "test module 1", &[]); - let t1 = BUILTINS.custom_type(mod1, "Custom1"); - let t1_obj1 = instance(t1.clone(), &[("value", BUILTINS.nil())]); + let mod1 = new::module("test1", "", "test module 1", &[]); + let t1 = new::custom_type(mod1, "Custom1"); + let t1_obj1 = instance(t1.clone(), &[("value", new::nil())]); let t1_obj2 = instance(t1.clone(), &[("value", BUILTINS.nil())]); let t1_obj3 = instance(t1.clone(), &[("value", BUILTINS.nil())]); diff --git a/feint-builtins/src/types/always.rs b/feint-builtins/src/types/always.rs index f96dd50..cc9bd6e 100644 --- a/feint-builtins/src/types/always.rs +++ b/feint-builtins/src/types/always.rs @@ -1,36 +1,38 @@ +//! Builtin `Always` type. use std::any::Any; use std::fmt; +use std::sync::{Arc, RwLock}; + +use once_cell::sync::Lazy; use feint_code_gen::*; -use super::base::{ObjectRef, ObjectTrait, TypeRef}; +use super::base::{ObjectTrait, TypeRef}; +use super::class::Type; use super::ns::Namespace; -// Always -------------------------------------------------------------- +std_type!(ALWAYS_TYPE, AlwaysType); pub struct Always { - class: TypeRef, ns: Namespace, } standard_object_impls!(Always); impl Always { - pub fn new(class: TypeRef) -> Self { - Self { class, ns: Namespace::default() } + pub fn new() -> Self { + Self { ns: Namespace::default() } } } impl ObjectTrait for Always { - object_trait_header!(); + object_trait_header!(ALWAYS_TYPE); fn is_equal(&self, _rhs: &dyn ObjectTrait) -> bool { true } } -// Display ------------------------------------------------------------- - impl fmt::Display for Always { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "@") diff --git a/feint-builtins/src/types/base.rs b/feint-builtins/src/types/base.rs index bcbc7d6..5340906 100644 --- a/feint-builtins/src/types/base.rs +++ b/feint-builtins/src/types/base.rs @@ -8,7 +8,7 @@ use num_traits::ToPrimitive; use feint_code_gen::*; -use crate::BUILTINS; +use crate::new; use super::func_trait::FuncTrait; use super::ns::Namespace; @@ -116,7 +116,7 @@ pub trait ObjectTrait { fn id_obj(&self) -> ObjectRef { // TODO: Cache? - BUILTINS.int(self.id()) + new::int(self.id()) } /// XXX: This resolves to `std` unless overridden. @@ -167,8 +167,8 @@ pub trait ObjectTrait { names.extend(obj_ns.iter().map(|(n, _)| n).cloned()); names.sort(); names.dedup(); - let items = names.iter().map(|n| BUILTINS.str(n)).collect(); - return BUILTINS.tuple(items); + let items = names.iter().map(new::str).collect(); + return new::tuple(items); } // TODO: Convert to builtin function @@ -202,7 +202,7 @@ pub trait ObjectTrait { // } else { // eprintln!("Cannot disassemble object: {}", &*this.read().unwrap()); // } - // return BUILTINS.nil(); + // return new::nil(); // } // Instance attributes ----------------------------------------- @@ -232,9 +232,9 @@ pub trait ObjectTrait { if name == "ok" { let this = this.read().unwrap(); return if let Some(err) = this.down_to_err() { - BUILTINS.bool(!err.retrieve_bool_val()) + new::bool(!err.retrieve_bool_val()) } else { - BUILTINS.bool(true) + new::bool(true) }; } @@ -247,13 +247,13 @@ pub trait ObjectTrait { // that responds to bool is returned. if name == "err" { return if let Some(err) = this.read().unwrap().down_to_err() { - BUILTINS.err_with_responds_to_bool( + new::err_with_responds_to_bool( err.kind.clone(), err.message.as_str(), this.clone(), ) } else { - BUILTINS.ok_err() + new::ok_err() }; } @@ -261,7 +261,7 @@ pub trait ObjectTrait { return if self.is_str() { this.clone() } else { - BUILTINS.str(this.read().unwrap().to_string()) + new::str(this.read().unwrap().to_string()) }; } @@ -287,14 +287,14 @@ pub trait ObjectTrait { } fn attr_not_found(&self, name: &str, obj: ObjectRef) -> ObjectRef { - BUILTINS.attr_not_found_err(name, obj) + new::attr_not_found_err(name, obj) } // Items (accessed by index) --------------------------------------- fn get_item(&self, index: usize, this: ObjectRef) -> ObjectRef { // TODO: The default should be a "does not support" indexing err - BUILTINS.index_out_of_bounds_err(index, this) + new::index_out_of_bounds_err(index, this) } fn set_item( @@ -304,11 +304,11 @@ pub trait ObjectTrait { _value: ObjectRef, ) -> ObjectRef { // TODO: The default should be a "does not support" indexing err - BUILTINS.index_out_of_bounds_err(index, this) + new::index_out_of_bounds_err(index, this) } fn index_out_of_bounds(&self, index: usize, this: ObjectRef) -> ObjectRef { - BUILTINS.index_out_of_bounds_err(index, this) + new::index_out_of_bounds_err(index, this) } // Type checkers --------------------------------------------------- diff --git a/feint-builtins/src/types/bool.rs b/feint-builtins/src/types/bool.rs index 8d86a94..2bf808c 100644 --- a/feint-builtins/src/types/bool.rs +++ b/feint-builtins/src/types/bool.rs @@ -1,15 +1,19 @@ +//! Builtin `Bool` type. use std::any::Any; use std::fmt; +use std::sync::{Arc, RwLock}; + +use once_cell::sync::Lazy; use feint_code_gen::*; -use super::base::{ObjectRef, ObjectTrait, TypeRef}; +use super::base::{ObjectTrait, TypeRef}; +use super::class::Type; use super::ns::Namespace; -// Bool ---------------------------------------------------------------- +std_type!(BOOL_TYPE, BoolType); pub struct Bool { - class: TypeRef, ns: Namespace, value: bool, } @@ -17,8 +21,8 @@ pub struct Bool { standard_object_impls!(Bool); impl Bool { - pub fn new(class: TypeRef, value: bool) -> Self { - Self { class, ns: Namespace::default(), value } + pub fn new(value: bool) -> Self { + Self { ns: Namespace::default(), value } } pub fn value(&self) -> &bool { @@ -27,7 +31,7 @@ impl Bool { } impl ObjectTrait for Bool { - object_trait_header!(); + object_trait_header!(BOOL_TYPE); // Unary operations ----------------------------------------------- @@ -64,8 +68,6 @@ impl ObjectTrait for Bool { } } -// Display ------------------------------------------------------------- - impl fmt::Display for Bool { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.value) diff --git a/feint-builtins/src/types/bound_func.rs b/feint-builtins/src/types/bound_func.rs index 2367875..e04afda 100644 --- a/feint-builtins/src/types/bound_func.rs +++ b/feint-builtins/src/types/bound_func.rs @@ -1,21 +1,23 @@ +//! Builtin `BoundFunc` type. use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; +use once_cell::sync::Lazy; + use feint_code_gen::*; -use crate::BUILTINS; +use crate::new; use super::base::{ObjectRef, ObjectTrait, TypeRef}; - +use super::class::Type; use super::func_trait::FuncTrait; use super::ns::Namespace; use super::Params; -// BoundFunc ---------------------------------------------------------- +std_type!(BOUND_FUNC_TYPE, BoundFuncType); pub struct BoundFunc { - class: TypeRef, ns: Namespace, module_name: String, func: ObjectRef, @@ -27,7 +29,7 @@ pub struct BoundFunc { standard_object_impls!(BoundFunc); impl BoundFunc { - pub fn new(class: TypeRef, func_ref: ObjectRef, this: ObjectRef) -> Self { + pub fn new(func_ref: ObjectRef, this: ObjectRef) -> Self { let (module_name, name, doc, params, params_tuple, arity, has_var_args) = { let func_guard = func_ref.read().unwrap(); @@ -54,15 +56,14 @@ impl BoundFunc { }; Self { - class, ns: Namespace::with_entries(&[ - ("$module_name", BUILTINS.str(&module_name)), - ("$full_name", BUILTINS.str(format!("{module_name}.{name}"))), - ("$name", BUILTINS.str(&name)), + ("$module_name", new::str(&module_name)), + ("$full_name", new::str(format!("{module_name}.{name}"))), + ("$name", new::str(&name)), ("$params", params_tuple), ("$doc", doc), - ("$arity", BUILTINS.int(arity)), - ("$has_var_args", BUILTINS.bool(has_var_args)), + ("$arity", new::int(arity)), + ("$has_var_args", new::bool(has_var_args)), ]), module_name, func: func_ref, @@ -104,15 +105,13 @@ impl FuncTrait for BoundFunc { } impl ObjectTrait for BoundFunc { - object_trait_header!(); + object_trait_header!(BOUND_FUNC_TYPE); fn module(&self) -> ObjectRef { self.func().read().unwrap().module() } } -// Display ------------------------------------------------------------- - impl fmt::Display for BoundFunc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.func.read().unwrap()) diff --git a/feint-builtins/src/types/cell.rs b/feint-builtins/src/types/cell.rs index d60e745..0f77d03 100644 --- a/feint-builtins/src/types/cell.rs +++ b/feint-builtins/src/types/cell.rs @@ -1,19 +1,21 @@ +//! Builtin `Cell` type. use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; +use once_cell::sync::Lazy; + use feint_code_gen::*; -use crate::BUILTINS; +use crate::new; use super::base::{ObjectRef, ObjectTrait, TypeRef}; - +use super::class::Type; use super::ns::Namespace; -// Cell --------------------------------------------------------- +std_type!(CELL_TYPE, CellType); pub struct Cell { - class: TypeRef, ns: Namespace, value: ObjectRef, } @@ -21,12 +23,12 @@ pub struct Cell { standard_object_impls!(Cell); impl Cell { - pub fn new(class: TypeRef) -> Self { - Self { class, ns: Namespace::default(), value: BUILTINS.nil() } + pub fn new() -> Self { + Self { ns: Namespace::default(), value: new::nil() } } - pub fn with_value(class: TypeRef, value: ObjectRef) -> Self { - let mut cell = Self::new(class); + pub fn with_value(value: ObjectRef) -> Self { + let mut cell = Self::new(); cell.set_value(value); cell } @@ -41,15 +43,13 @@ impl Cell { } impl ObjectTrait for Cell { - object_trait_header!(); + object_trait_header!(CELL_TYPE); fn bool_val(&self) -> Option { Some(false) } } -// Display ------------------------------------------------------------- - impl fmt::Display for Cell { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "&({:?})", &*self.value.read().unwrap()) diff --git a/feint-builtins/src/types/class.rs b/feint-builtins/src/types/class.rs index 2e805e6..0da09e4 100644 --- a/feint-builtins/src/types/class.rs +++ b/feint-builtins/src/types/class.rs @@ -1,41 +1,46 @@ -//! "Class" and "type" are used interchangeably and mean exactly the -//! same thing. Lower case "class" is used instead of "type" because the -//! latter is a Rust keyword. +//! Builtin `Type` type--the base of the type hierarchy. +//! +//! "Class" and "type" are synonymous and used interchangeably. Lower +//! case "class" is used instead of "type" because the latter is a Rust +//! keyword. use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use once_cell::sync::{Lazy, OnceCell}; +use once_cell::sync::Lazy; use feint_code_gen::*; -use crate::BUILTINS; +use crate::modules::get_module; +use crate::new; -use super::base::{ObjectRef, ObjectTrait, TypeRef}; +use super::base::{ObjectTrait, TypeRef}; use super::ns::Namespace; -pub static TYPE_TYPE: Lazy = Lazy::new(|| obj_ref!(Type::new("std", "Type"))); +std_type!(TYPE_TYPE, Type); pub struct Type { module_name: String, name: String, + full_name: String, ns: Namespace, - module_attr: OnceCell, - name_attr: OnceCell, - full_name_attr: OnceCell, } standard_object_impls!(Type); impl Type { pub fn new(module_name: &str, name: &str) -> Self { + let full_name = format!("{module_name}.{name}"); + let full_name_str = new::str(&full_name); Self { module_name: module_name.to_owned(), name: name.to_owned(), - ns: Namespace::default(), - module_attr: OnceCell::default(), - name_attr: OnceCell::default(), - full_name_attr: OnceCell::default(), + full_name, + ns: Namespace::with_entries(&[ + ("$module", get_module(module_name)), + ("$name", new::str(name)), + ("$full_name", full_name_str), + ]), } } @@ -46,45 +51,14 @@ impl Type { pub fn name(&self) -> &String { &self.name } -} - -impl ObjectTrait for Type { - fn as_any(&self) -> &dyn Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } - - fn class(&self) -> TypeRef { - TYPE_TYPE.clone() - } - - fn ns(&self) -> &Namespace { - &self.ns - } - fn ns_mut(&mut self) -> &mut Namespace { - &mut self.ns + pub fn full_name(&self) -> &String { + &self.full_name } +} - fn get_attr(&self, name: &str, this: ObjectRef) -> ObjectRef { - match name { - "$module" => self - .module_attr - .get_or_init(|| BUILTINS.get_module(self.module_name())) - .clone(), - "$name" => self.name_attr.get_or_init(|| BUILTINS.str(self.name())).clone(), - "$full_name" => self - .full_name_attr - .get_or_init(|| { - BUILTINS.str(format!("{}.{}", self.module_name(), self.name())) - }) - .clone(), - _ => self.base_get_attr(name, this), - } - } +impl ObjectTrait for Type { + object_trait_header!(TYPE_TYPE); } impl fmt::Display for Type { @@ -95,6 +69,6 @@ impl fmt::Display for Type { impl fmt::Debug for Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "", self.module_name, self.name, self.id()) + write!(f, "", self.full_name(), self.id()) } } diff --git a/feint-builtins/src/types/closure.rs b/feint-builtins/src/types/closure.rs index cde43b9..334fbbd 100644 --- a/feint-builtins/src/types/closure.rs +++ b/feint-builtins/src/types/closure.rs @@ -1,22 +1,23 @@ +//! Builtin `Closure` type. use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; +use once_cell::sync::Lazy; + use feint_code_gen::*; -use crate::BUILTINS; +use crate::new; use super::base::{ObjectRef, ObjectTrait, TypeRef}; - +use super::class::Type; use super::func_trait::FuncTrait; use super::ns::Namespace; use super::Params; -// Closure ------------------------------------------------------------- +std_type!(CLOSURE_TYPE, ClosureType); pub struct Closure { - class: TypeRef, - ns: Namespace, module_name: String, name: String, @@ -29,16 +30,15 @@ pub struct Closure { standard_object_impls!(Closure); impl Closure { - pub fn new(class: TypeRef, func_ref: ObjectRef, captured: ObjectRef) -> Self { + pub fn new(func_ref: ObjectRef, captured: ObjectRef) -> Self { let func = func_ref.read().unwrap(); let func = func.down_to_func().unwrap(); Self { - class, ns: Namespace::with_entries(&[ ("$params", func.get_params()), ("$doc", func.get_doc()), - ("$arity", BUILTINS.int(func.arity())), - ("$has_var_args", BUILTINS.bool(func.has_var_args())), + ("$arity", new::int(func.arity())), + ("$has_var_args", new::bool(func.has_var_args())), ]), module_name: func.module_name().to_owned(), name: func.name().to_owned(), @@ -87,11 +87,9 @@ impl FuncTrait for Closure { } impl ObjectTrait for Closure { - object_trait_header!(); + object_trait_header!(CLOSURE_TYPE); } -// Display ------------------------------------------------------------- - impl fmt::Display for Closure { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "[closure] {}", self.func.read().unwrap()) diff --git a/feint-builtins/src/types/code.rs b/feint-builtins/src/types/code.rs index 455f0ff..21104e4 100644 --- a/feint-builtins/src/types/code.rs +++ b/feint-builtins/src/types/code.rs @@ -5,8 +5,8 @@ use feint_util::op::{BinaryOperator, CompareOperator, InplaceOperator, UnaryOper use feint_util::source::Location; use feint_util::string::format_doc; +use crate::new; use crate::types::{FuncTrait, ObjectRef}; -use crate::BUILTINS; type FreeVarEntry = ( usize, // address @@ -107,11 +107,11 @@ impl Code { if let Some(obj_ref) = self.get_const(0) { let obj = obj_ref.read().unwrap(); if let Some(doc) = obj.get_str_val() { - return BUILTINS.str(format_doc(doc)); + return new::str(format_doc(doc)); } } } - BUILTINS.nil() + new::nil() } // Instructions ---------------------------------------------------- diff --git a/feint-builtins/src/types/custom.rs b/feint-builtins/src/types/custom.rs index 87be9bb..28f4631 100644 --- a/feint-builtins/src/types/custom.rs +++ b/feint-builtins/src/types/custom.rs @@ -1,17 +1,14 @@ +//! Builtin `CustomType` type. use std::any::Any; use std::fmt; -use std::sync::{Arc, RwLock}; use feint_code_gen::*; -use crate::BUILTINS; +use crate::new; use super::base::{ObjectRef, ObjectTrait, TypeRef}; - use super::ns::Namespace; -// Custom Type --------------------------------------------------------- - pub struct CustomObj { class: TypeRef, ns: Namespace, @@ -26,7 +23,25 @@ impl CustomObj { } impl ObjectTrait for CustomObj { - object_trait_header!(); + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn class(&self) -> TypeRef { + self.class.clone() + } + + fn ns(&self) -> &Namespace { + &self.ns + } + + fn ns_mut(&mut self) -> &mut Namespace { + &mut self.ns + } fn set_attr( &mut self, @@ -35,7 +50,7 @@ impl ObjectTrait for CustomObj { _this: ObjectRef, ) -> ObjectRef { self.ns.set(name, value); - BUILTINS.nil() + new::nil() } fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { @@ -43,19 +58,18 @@ impl ObjectTrait for CustomObj { } } -// Display ------------------------------------------------------------- - impl fmt::Display for CustomObj { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // let class = self.class(); - // let class = class.read().unwrap(); - // write!(f, "<{} object @ {}>", class.full_name(), self.id()) - write!(f, "", self.id()) + let class = self.class(); + let class = class.read().unwrap(); + write!(f, "<{} object>", class.name()) } } impl fmt::Debug for CustomObj { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{self}") + let class = self.class(); + let class = class.read().unwrap(); + write!(f, "<{} object @ {}>", class.full_name(), self.id()) } } diff --git a/feint-builtins/src/types/err.rs b/feint-builtins/src/types/err.rs index 710be52..f33a8a1 100644 --- a/feint-builtins/src/types/err.rs +++ b/feint-builtins/src/types/err.rs @@ -1,4 +1,4 @@ -//! # Error Type +//! Builtin `Error` type. //! //! The error type represents _recoverable_ runtime errors that can be //! checked in user code using this pattern: @@ -17,16 +17,20 @@ use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; +use once_cell::sync::Lazy; + use feint_code_gen::*; +use crate::new; use crate::util::check_args; -use crate::BUILTINS; use super::base::{ObjectRef, ObjectTrait, TypeRef}; - +use super::class::Type; use super::err_type::ErrKind; use super::ns::Namespace; +std_type!(ERR_TYPE, ErrType); + // pub fn make_err_type() -> obj_ref_t!(ErrType) { // let type_ref = obj_ref!(ErrType::new()); // let mut type_obj = type_ref.write().unwrap(); @@ -57,7 +61,7 @@ use super::ns::Namespace; // // TODO: Figure out a solution for this, perhaps an err // // type that is *not* user-constructible or a // // nested err type? -// return BUILTINS.arg_err(arg_err_msg, BUILTINS.nil()); +// return new::arg_err(arg_err_msg, new::nil()); // }; // // let kind = err_type.kind().clone(); @@ -66,10 +70,10 @@ use super::ns::Namespace; // msg // } else { // let arg_err_msg = format!("{name} expected message to be a Str"); -// return BUILTINS.arg_err(arg_err_msg, BUILTINS.nil()); +// return new::arg_err(arg_err_msg, new::nil()); // }; // -// BUILTINS.err(kind, msg, BUILTINS.nil()) +// new::err(kind, msg, new::nil()) // }), // // Instance Attributes ----------------------------------------- // prop!("type", type_ref, "", |this, _| { @@ -80,19 +84,16 @@ use super::ns::Namespace; // prop!("message", type_ref, "", |this, _| { // let this = this.read().unwrap(); // let this = this.down_to_err().unwrap(); -// BUILTINS.str(&this.message) +// new::str(&this.message) // }), // ]); // // type_ref.clone() // } -// Error -------------------------------------------------------- - // NOTE: This is named `ErrObj` instead of `Err` to avoid conflict with // Rust's `Err`. pub struct ErrObj { - class: TypeRef, ns: Namespace, pub kind: ErrKind, pub message: String, @@ -104,10 +105,9 @@ pub struct ErrObj { standard_object_impls!(ErrObj); impl ErrObj { - pub fn new(class: TypeRef, kind: ErrKind, message: String, obj: ObjectRef) -> Self { + pub fn new(kind: ErrKind, message: String, obj: ObjectRef) -> Self { let bool_val = kind != ErrKind::Ok; Self { - class, ns: Namespace::default(), kind, message, @@ -118,12 +118,11 @@ impl ErrObj { } pub fn with_responds_to_bool( - class: TypeRef, kind: ErrKind, message: String, obj: ObjectRef, ) -> Self { - let mut instance = Self::new(class, kind, message, obj); + let mut instance = Self::new(kind, message, obj); instance.responds_to_bool = true; instance } @@ -134,7 +133,7 @@ impl ErrObj { } impl ObjectTrait for ErrObj { - object_trait_header!(); + object_trait_header!(ERR_TYPE); fn bool_val(&self) -> Option { if self.responds_to_bool { @@ -162,8 +161,6 @@ impl ObjectTrait for ErrObj { } } -// Display ------------------------------------------------------------- - impl fmt::Display for ErrObj { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let kind = &self.kind; diff --git a/feint-builtins/src/types/err_type.rs b/feint-builtins/src/types/err_type.rs index 5a6f20f..f80db56 100644 --- a/feint-builtins/src/types/err_type.rs +++ b/feint-builtins/src/types/err_type.rs @@ -1,4 +1,4 @@ -//! Error Types +//! Builtin error types //! //! Builtin type used to tag builtin `Err` instances. use std::any::Any; @@ -7,11 +7,12 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use crate::BUILTINS; use feint_code_gen::*; -use super::base::{ObjectRef, ObjectTrait, TypeRef}; +use crate::new; +use super::base::{ObjectRef, ObjectTrait, TypeRef}; +use super::class::Type; use super::ns::Namespace; #[derive(Clone, Debug, PartialEq)] @@ -71,6 +72,8 @@ impl ErrKind { } } +std_type!(ERR_TYPE_TYPE, ErrTypeType); + // pub static ERR_TYPE_TYPE: Lazy = Lazy::new(|| { // let type_ref = obj_ref!(ErrTypeType::new()); // let mut type_obj = type_ref.write().unwrap(); @@ -88,17 +91,14 @@ impl ErrKind { // prop!("name", type_ref, "", |this, _| { // let this = this.read().unwrap(); // let this = this.as_any().downcast_ref::().unwrap(); -// BUILTINS.str(this.name()) +// new::str(this.name()) // }), // ]); // // type_ref.clone() // }); -// ErrType ------------------------------------------------------ - pub struct ErrTypeObj { - class: TypeRef, ns: Namespace, kind: ErrKind, } @@ -106,8 +106,8 @@ pub struct ErrTypeObj { standard_object_impls!(ErrTypeObj); impl ErrTypeObj { - pub fn new(class: TypeRef, kind: ErrKind) -> Self { - Self { class, ns: Namespace::default(), kind } + pub fn new(kind: ErrKind) -> Self { + Self { ns: Namespace::default(), kind } } pub fn kind(&self) -> &ErrKind { @@ -120,7 +120,7 @@ impl ErrTypeObj { } impl ObjectTrait for ErrTypeObj { - object_trait_header!(); + object_trait_header!(ERR_TYPE_TYPE); fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { if self.is(rhs) || rhs.is_always() { @@ -133,8 +133,6 @@ impl ObjectTrait for ErrTypeObj { } } -// Display ------------------------------------------------------------- - impl fmt::Display for ErrKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use ErrKind::*; diff --git a/feint-builtins/src/types/file.rs b/feint-builtins/src/types/file.rs index 3091805..6b7acda 100644 --- a/feint-builtins/src/types/file.rs +++ b/feint-builtins/src/types/file.rs @@ -1,3 +1,4 @@ +//! Builtin `File` type. use std::any::Any; use std::fmt; use std::fs; @@ -5,14 +6,14 @@ use std::io::{BufRead, BufReader}; use std::path::{Path, PathBuf}; use std::sync::{Arc, RwLock}; -use once_cell::sync::OnceCell; +use once_cell::sync::{Lazy, OnceCell}; use feint_code_gen::*; -use crate::BUILTINS; +use crate::new; use super::base::{ObjectRef, ObjectTrait, TypeRef}; - +use super::class::Type; use super::ns::Namespace; // pub fn make_file_type() -> obj_ref_t!(FileType) { @@ -26,13 +27,13 @@ use super::ns::Namespace; // if let Some(file_name) = arg.get_str_val() { // let path = Path::new(file_name); // if path.is_file() { -// BUILTINS.file(file_name) +// new::file(file_name) // } else { -// BUILTINS.file_not_found_err(file_name, BUILTINS.nil()) +// new::file_not_found_err(file_name, new::nil()) // } // } else { // let message = format!("File.new(file_name) expected string; got {arg}"); -// BUILTINS.arg_err(message, BUILTINS.nil()) +// new::arg_err(message, new::nil()) // } // }), // // Instance Attributes @@ -51,10 +52,9 @@ use super::ns::Namespace; // type_ref.clone() // } -// File ---------------------------------------------------------- +std_type!(FILE_TYPE, FileType); pub struct File { - class: TypeRef, ns: Namespace, file_name: String, path: PathBuf, @@ -65,12 +65,11 @@ pub struct File { standard_object_impls!(File); impl File { - pub fn new(class: TypeRef, file_name: String) -> Self { + pub fn new(file_name: String) -> Self { let path = fs::canonicalize(&file_name); let path = path.map_or_else(|_| Path::new(&file_name).to_path_buf(), |p| p); - let name_obj = BUILTINS.str(file_name.as_str()); + let name_obj = new::str(file_name.as_str()); Self { - class, ns: Namespace::with_entries(&[("name", name_obj)]), file_name, path, @@ -82,10 +81,8 @@ impl File { fn text(&self) -> ObjectRef { let result = self.text.get_or_try_init(|| { fs::read_to_string(&self.file_name) - .map(|contents| BUILTINS.str(contents)) - .map_err(|err| { - BUILTINS.file_unreadable_err(err.to_string(), BUILTINS.nil()) - }) + .map(new::str) + .map_err(|err| new::file_unreadable_err(err.to_string(), new::nil())) }); match result { Ok(text) => text.clone(), @@ -101,13 +98,11 @@ impl File { let lines = reader .lines() // TODO: Handle lines that can't be read - .map(|line| BUILTINS.str(line.unwrap())) + .map(|line| new::str(line.unwrap())) .collect(); - BUILTINS.tuple(lines) - }) - .map_err(|err| { - BUILTINS.file_unreadable_err(err.to_string(), BUILTINS.nil()) + new::tuple(lines) }) + .map_err(|err| new::file_unreadable_err(err.to_string(), new::nil())) }); match result { Ok(lines) => lines.clone(), @@ -117,15 +112,13 @@ impl File { } impl ObjectTrait for File { - object_trait_header!(); + object_trait_header!(FILE_TYPE); fn bool_val(&self) -> Option { Some(false) } } -// Display ------------------------------------------------------------- - impl fmt::Display for File { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "", &self.path.display()) diff --git a/feint-builtins/src/types/float.rs b/feint-builtins/src/types/float.rs index 139647a..b0182f6 100644 --- a/feint-builtins/src/types/float.rs +++ b/feint-builtins/src/types/float.rs @@ -1,17 +1,21 @@ +//! Builtin `Float` type. use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; use num_traits::ToPrimitive; +use once_cell::sync::Lazy; use feint_code_gen::*; -use super::util::{eq_int_float, float_gt_int, float_lt_int}; -use crate::BUILTINS; +use crate::new; use super::base::{ObjectRef, ObjectTrait, TypeRef}; - +use super::class::Type; use super::ns::Namespace; +use super::util::{eq_int_float, float_gt_int, float_lt_int}; + +std_type!(FLOAT_TYPE, FloatType); // pub fn make_float_type() -> obj_ref_t!(FloatType) { // let type_ref = obj_ref!(FloatType::new()); @@ -22,14 +26,14 @@ use super::ns::Namespace; // meth!("new", type_ref, &["value"], "", |this, args| { // let arg = use_arg!(args, 0); // let float = if let Some(val) = arg.get_float_val() { -// BUILTINS.float(*val) +// new::float(*val) // } else if let Some(val) = arg.get_int_val() { -// BUILTINS.float(val.to_f64().unwrap()) +// new::float(val.to_f64().unwrap()) // } else if let Some(val) = arg.get_str_val() { -// BUILTINS.float_from_string(val) +// new::float_from_string(val) // } else { // let msg = format!("Float.new() expected string or float; got {arg}"); -// BUILTINS.type_err(msg, this) +// new::type_err(msg, this) // }; // float // }), @@ -38,8 +42,6 @@ use super::ns::Namespace; // type_ref.clone() // } -// Float -------------------------------------------------------- - macro_rules! make_op { ( $meth:ident, $op:tt, $message:literal, $trunc:literal ) => { fn $meth(&self, rhs: &dyn ObjectTrait) -> Option { @@ -54,14 +56,13 @@ macro_rules! make_op { if $trunc { value = value.trunc(); } - let value = BUILTINS.float(value); + let value = new::float(value); Some(value) } }; } pub struct Float { - class: TypeRef, ns: Namespace, value: f64, } @@ -69,8 +70,8 @@ pub struct Float { standard_object_impls!(Float); impl Float { - pub fn new(class: TypeRef, value: f64) -> Self { - Self { class, ns: Namespace::default(), value } + pub fn new(value: f64) -> Self { + Self { ns: Namespace::default(), value } } pub fn value(&self) -> &f64 { @@ -79,10 +80,10 @@ impl Float { } impl ObjectTrait for Float { - object_trait_header!(); + object_trait_header!(FLOAT_TYPE); fn negate(&self) -> Option { - Some(BUILTINS.float(-*self.value())) + Some(new::float(-*self.value())) } fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { @@ -126,7 +127,7 @@ impl ObjectTrait for Float { return None; }; let value = self.value().powf(exp); - let value = BUILTINS.float(value); + let value = new::float(value); Some(value) } @@ -138,8 +139,6 @@ impl ObjectTrait for Float { make_op!(sub, -, "Could not subtract {} from Float", false); } -// Display ------------------------------------------------------------- - impl fmt::Display for Float { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.value().fract() == 0.0 { diff --git a/feint-builtins/src/types/func.rs b/feint-builtins/src/types/func.rs index 88e88a8..d5d6e91 100644 --- a/feint-builtins/src/types/func.rs +++ b/feint-builtins/src/types/func.rs @@ -1,24 +1,25 @@ +//! Builtin `Func` type. use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use once_cell::sync::OnceCell; +use once_cell::sync::{Lazy, OnceCell}; use feint_code_gen::*; -use crate::BUILTINS; +use crate::modules::get_module; +use crate::new; use super::base::{ObjectRef, ObjectTrait, TypeRef}; - +use super::class::Type; use super::code::Code; use super::func_trait::FuncTrait; use super::ns::Namespace; use super::Params; -// Func ---------------------------------------------------------- +std_type!(FUNC_TYPE, FuncType); pub struct Func { - class: TypeRef, ns: Namespace, module_name: String, module: OnceCell, @@ -30,23 +31,15 @@ pub struct Func { standard_object_impls!(Func); impl Func { - pub fn new( - class: TypeRef, - module_name: String, - name: String, - params: Params, - code: Code, - ) -> Self { - let params_tuple = - BUILTINS.tuple(params.iter().map(|p| BUILTINS.str(p)).collect()); + pub fn new(module_name: String, name: String, params: Params, code: Code) -> Self { + let params_tuple = new::tuple(params.iter().map(new::str).collect()); let mut instance = Self { - class, ns: Namespace::with_entries(&[ // Instance Attributes - ("$module_name", BUILTINS.str(&module_name)), - ("$full_name", BUILTINS.str(format!("{module_name}.{name}"))), - ("$name", BUILTINS.str(&name)), + ("$module_name", new::str(&module_name)), + ("$full_name", new::str(format!("{module_name}.{name}"))), + ("$name", new::str(&name)), ("$params", params_tuple), ("$doc", code.get_doc()), ]), @@ -59,8 +52,8 @@ impl Func { let arity = (&instance as &dyn FuncTrait).arity(); let has_var_args = (&instance as &dyn FuncTrait).has_var_args(); - instance.ns_mut().insert("$arity", BUILTINS.int(arity)); - instance.ns_mut().insert("$has_var_args", BUILTINS.bool(has_var_args)); + instance.ns_mut().insert("$arity", new::int(arity)); + instance.ns_mut().insert("$has_var_args", new::bool(has_var_args)); instance } @@ -105,10 +98,10 @@ impl FuncTrait for Func { } impl ObjectTrait for Func { - object_trait_header!(); + object_trait_header!(FUNC_TYPE); fn module(&self) -> ObjectRef { - self.module.get_or_init(|| BUILTINS.get_module(&self.module_name)).clone() + self.module.get_or_init(|| get_module(&self.module_name)).clone() } fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { @@ -122,8 +115,6 @@ impl ObjectTrait for Func { } } -// Display ------------------------------------------------------------- - impl fmt::Display for Func { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", FuncTrait::format_string(self, None)) diff --git a/feint-builtins/src/types/int.rs b/feint-builtins/src/types/int.rs index fef8e13..461fc87 100644 --- a/feint-builtins/src/types/int.rs +++ b/feint-builtins/src/types/int.rs @@ -1,18 +1,22 @@ +//! Builtin `Int` type. use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; use num_bigint::BigInt; use num_traits::{FromPrimitive, ToPrimitive}; +use once_cell::sync::Lazy; use feint_code_gen::*; -use super::util::{eq_int_float, int_gt_float, int_lt_float}; -use crate::BUILTINS; +use crate::new; use super::base::{ObjectRef, ObjectTrait, TypeRef}; - +use super::class::Type; use super::ns::Namespace; +use super::util::{eq_int_float, int_gt_float, int_lt_float}; + +std_type!(INT_TYPE, IntType); // pub fn make_int_type() -> obj_ref_t!(IntType) { // eprintln!("make_int_type 1"); @@ -29,14 +33,14 @@ use super::ns::Namespace; // meth!("new", type_ref, &["value"], "", |this, args| { // let arg = use_arg!(args, 0); // let int = if let Some(val) = arg.get_int_val() { -// BUILTINS.int(val.clone()) +// new::int(val.clone()) // } else if let Some(val) = arg.get_float_val() { -// BUILTINS.int(BigInt::from_f64(*val).unwrap()) +// new::int(BigInt::from_f64(*val).unwrap()) // } else if let Some(val) = arg.get_str_val() { -// BUILTINS.int_from_string(val) +// new::int_from_string(val) // } else { // let msg = format!("Int.new() expected number or string; got {arg}"); -// BUILTINS.type_err(msg, this) +// new::type_err(msg, this) // }; // int // }), @@ -47,19 +51,17 @@ use super::ns::Namespace; // type_ref // } -// Int ---------------------------------------------------------- - macro_rules! make_op { ( $meth:ident, $op:tt ) => { fn $meth(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(rhs) = rhs.down_to_int() { // XXX: Return Int let value = self.value() $op rhs.value(); - Some(BUILTINS.int(value)) + Some(new::int(value)) } else if let Some(rhs) = rhs.down_to_float() { // XXX: Return Float let value = self.value().to_f64().unwrap() $op rhs.value(); - Some(BUILTINS.float(value)) + Some(new::float(value)) } else { None } @@ -68,7 +70,6 @@ macro_rules! make_op { } pub struct Int { - class: TypeRef, ns: Namespace, value: BigInt, } @@ -76,8 +77,8 @@ pub struct Int { standard_object_impls!(Int); impl Int { - pub fn new(class: TypeRef, value: BigInt) -> Self { - Self { class, ns: Namespace::default(), value } + pub fn new(value: BigInt) -> Self { + Self { ns: Namespace::default(), value } } pub fn value(&self) -> &BigInt { @@ -99,10 +100,10 @@ impl Int { } impl ObjectTrait for Int { - object_trait_header!(); + object_trait_header!(INT_TYPE); fn negate(&self) -> Option { - Some(BUILTINS.int(-self.value.clone())) + Some(new::int(-self.value.clone())) } fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { @@ -143,14 +144,14 @@ impl ObjectTrait for Int { let base = self.value(); let exp = rhs.value().to_u32().unwrap(); let value = base.pow(exp); - let value = BUILTINS.int(value); + let value = new::int(value); Some(value) } else if let Some(rhs) = rhs.down_to_float() { // XXX: Return Float let base = self.value().to_f64().unwrap(); let exp = *rhs.value(); let value = base.powf(exp); - let value = BUILTINS.float(value); + let value = new::float(value); Some(value) } else { None @@ -165,7 +166,7 @@ impl ObjectTrait for Int { // Int division *always* returns a Float fn div(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(value) = self.div_f64(rhs) { - Some(BUILTINS.float(value)) + Some(new::float(value)) } else { None } @@ -175,15 +176,13 @@ impl ObjectTrait for Int { fn floor_div(&self, rhs: &dyn ObjectTrait) -> Option { if let Some(value) = self.div_f64(rhs) { let value = BigInt::from_f64(value).unwrap(); - Some(BUILTINS.int(value)) + Some(new::int(value)) } else { None } } } -// Display ------------------------------------------------------------- - impl fmt::Display for Int { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.value) diff --git a/feint-builtins/src/types/intrinsic_func.rs b/feint-builtins/src/types/intrinsic_func.rs index 189f295..5537b8f 100644 --- a/feint-builtins/src/types/intrinsic_func.rs +++ b/feint-builtins/src/types/intrinsic_func.rs @@ -1,26 +1,28 @@ -//! Intrinsic (implemented in Rust) function type. +//! Builtin `IntrinsicFunc` type. +//! +//! Used to implement builtin functions in Rust. use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use once_cell::sync::OnceCell; +use once_cell::sync::{Lazy, OnceCell}; +use crate::modules::get_module; use feint_code_gen::*; -use crate::BUILTINS; +use crate::new; use super::base::{ObjectRef, ObjectTrait, TypeRef}; - +use super::class::Type; use super::func_trait::FuncTrait; use super::ns::Namespace; use super::{Args, CallResult, Params}; pub type IntrinsicFn = fn(ObjectRef, Args) -> CallResult; -// IntrinsicFunc ------------------------------------------------ +std_type!(INTRINSIC_FUNC_TYPE, IntrinsicFuncType); pub struct IntrinsicFunc { - class: TypeRef, ns: Namespace, module_name: String, module: OnceCell, @@ -34,27 +36,24 @@ standard_object_impls!(IntrinsicFunc); impl IntrinsicFunc { pub fn new( - class: TypeRef, module_name: String, name: String, this_type: Option, params: Params, - doc: String, + doc: ObjectRef, func: IntrinsicFn, ) -> Self { - // let params_tuple = - // BUILTINS.tuple(params.iter().map(|p| BUILTINS.str(p)).collect()); + let params_tuple = new::tuple(params.iter().map(new::str).collect()); eprintln!("new"); let mut instance = Self { - class, ns: Namespace::with_entries(&[ // Instance Attributes - // ("$module_name", BUILTINS.str(module_name.as_str())), - // ("$full_name", BUILTINS.str(format!("{module_name}.{name}"))), - // ("$name", BUILTINS.str(name.as_str())), - // ("$params", params_tuple), - // ("$doc", doc), + ("$module_name", new::str(module_name.as_str())), + ("$full_name", new::str(format!("{module_name}.{name}"))), + ("$name", new::str(name.as_str())), + ("$params", params_tuple), + ("$doc", doc), ]), module_name, module: OnceCell::default(), @@ -66,8 +65,8 @@ impl IntrinsicFunc { let arity = (&instance as &dyn FuncTrait).arity(); let has_var_args = (&instance as &dyn FuncTrait).has_var_args(); - // instance.ns_mut().insert("$arity", BUILTINS.int(arity)); - // instance.ns_mut().insert("$has_var_args", BUILTINS.bool(has_var_args)); + instance.ns_mut().insert("$arity", new::int(arity)); + instance.ns_mut().insert("$has_var_args", new::bool(has_var_args)); instance } @@ -104,10 +103,10 @@ impl FuncTrait for IntrinsicFunc { } impl ObjectTrait for IntrinsicFunc { - object_trait_header!(); + object_trait_header!(INTRINSIC_FUNC_TYPE); fn module(&self) -> ObjectRef { - self.module.get_or_init(|| BUILTINS.get_module(&self.module_name)).clone() + self.module.get_or_init(|| get_module(&self.module_name)).clone() } } diff --git a/feint-builtins/src/types/iterator.rs b/feint-builtins/src/types/iterator.rs index 7528ecb..14d754f 100644 --- a/feint-builtins/src/types/iterator.rs +++ b/feint-builtins/src/types/iterator.rs @@ -1,15 +1,20 @@ +//! Builtin `Iterator` type. use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; +use once_cell::sync::Lazy; + use feint_code_gen::*; -use crate::BUILTINS; +use crate::new; use super::base::{ObjectRef, ObjectTrait, TypeRef}; - +use super::class::Type; use super::ns::Namespace; +std_type!(ITERATOR_TYPE, IteratorType); + // pub fn make_iterator_type() -> obj_ref_t!(IteratorType) { // let type_ref = obj_ref!(IteratorType::new()); // let mut type_obj = type_ref.write().unwrap(); @@ -31,10 +36,7 @@ use super::ns::Namespace; // type_ref.clone() // } -// Iterator ----------------------------------------------------- - pub struct FIIterator { - class: TypeRef, ns: Namespace, wrapped: Vec, current: usize, @@ -43,8 +45,8 @@ pub struct FIIterator { standard_object_impls!(FIIterator); impl FIIterator { - pub fn new(class: TypeRef, wrapped: Vec) -> Self { - Self { class, ns: Namespace::default(), wrapped, current: 0 } + pub fn new(wrapped: Vec) -> Self { + Self { ns: Namespace::default(), wrapped, current: 0 } } fn next(&mut self) -> ObjectRef { @@ -65,7 +67,7 @@ impl FIIterator { fn get_or_nil(&self, index: usize) -> ObjectRef { if index >= self.len() { - BUILTINS.nil() + new::nil() } else { self.wrapped[index].clone() } @@ -73,11 +75,9 @@ impl FIIterator { } impl ObjectTrait for FIIterator { - object_trait_header!(); + object_trait_header!(ITERATOR_TYPE); } -// Display ------------------------------------------------------------- - impl fmt::Display for FIIterator { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "") diff --git a/feint-builtins/src/types/list.rs b/feint-builtins/src/types/list.rs index 59d2b8a..03e204b 100644 --- a/feint-builtins/src/types/list.rs +++ b/feint-builtins/src/types/list.rs @@ -1,16 +1,21 @@ +//! Builtin `List` type. use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; +use once_cell::sync::Lazy; + use feint_code_gen::*; -use crate::BUILTINS; +use crate::new; use super::base::{ObjectRef, ObjectTrait, TypeRef}; - +use super::class::Type; use super::ns::Namespace; use super::seq; +std_type!(LIST_TYPE, ListType); + // pub fn make_list_type() -> obj_ref_t!(ListType) { // let type_ref = obj_ref!(ListType::new()); // let mut type_obj = type_ref.write().unwrap(); @@ -20,12 +25,12 @@ use super::seq; // prop!("length", type_ref, "", |this, _| { // let this = this.read().unwrap(); // let this = this.down_to_list().unwrap(); -// BUILTINS.int(this.len()) +// new::int(this.len()) // }), // prop!("is_empty", type_ref, "", |this, _| { // let this = this.read().unwrap(); // let this = this.down_to_list().unwrap(); -// BUILTINS.bool(this.len() == 0) +// new::bool(this.len() == 0) // }), // prop!("sum", type_ref, "", |this, _| { // let this = this.read().unwrap(); @@ -55,7 +60,7 @@ use super::seq; // let index = use_arg_usize!(get, index, args, 0); // let result = match this.get(index) { // Some(obj) => obj, -// None => BUILTINS.nil(), +// None => new::nil(), // }; // result // }), @@ -69,7 +74,7 @@ use super::seq; // let this = this_ref.read().unwrap(); // let this = this.down_to_list().unwrap(); // let items = this.items.read().unwrap(); -// BUILTINS.iterator(items.clone()) +// new::iterator(items.clone()) // }), // meth!("join", type_ref, &["sep"], "", |this, args| { // let this = this.read().unwrap(); @@ -82,7 +87,7 @@ use super::seq; // let this = this.down_to_list().unwrap(); // match this.pop() { // Some(obj) => obj, -// None => BUILTINS.nil(), +// None => new::nil(), // } // }), // meth!("push", type_ref, &["item"], "Push item and return it.", |this, args| { @@ -97,10 +102,9 @@ use super::seq; // type_ref.clone() // } -// List --------------------------------------------------------- +std_type!(NIL_TYPE, NilType); pub struct List { - class: TypeRef, ns: Namespace, items: RwLock>, } @@ -108,8 +112,8 @@ pub struct List { standard_object_impls!(List); impl List { - pub fn new(class: TypeRef, items: Vec) -> Self { - Self { class, ns: Namespace::default(), items: RwLock::new(items) } + pub fn new(items: Vec) -> Self { + Self { ns: Namespace::default(), items: RwLock::new(items) } } fn len(&self) -> usize { @@ -140,7 +144,7 @@ impl List { "List.extend() expected List or Tuple; got {}", obj.class().read().unwrap() ); - return Some(BUILTINS.type_err(msg, obj_ref.clone())); + return Some(new::type_err(msg, obj_ref.clone())); } None } @@ -165,7 +169,7 @@ impl List { } impl ObjectTrait for List { - object_trait_header!(); + object_trait_header!(LIST_TYPE); fn get_item(&self, index: usize, this: ObjectRef) -> ObjectRef { if let Some(item) = self.get(index) { diff --git a/feint-builtins/src/types/map.rs b/feint-builtins/src/types/map.rs index 9f9b9cb..f7f748d 100644 --- a/feint-builtins/src/types/map.rs +++ b/feint-builtins/src/types/map.rs @@ -3,15 +3,18 @@ use std::fmt; use std::sync::{Arc, RwLock}; use indexmap::IndexMap; +use once_cell::sync::Lazy; use feint_code_gen::*; -use crate::BUILTINS; +use crate::new; use super::base::{ObjectRef, ObjectTrait, TypeRef}; - +use super::class::Type; use super::ns::Namespace; +std_type!(MAP_TYPE, MapType); + // pub fn make_map_type() -> obj_ref_t!(MapType) { // let type_ref = obj_ref!(MapType::new()); // let mut type_obj = type_ref.write().unwrap(); @@ -107,16 +110,21 @@ use super::ns::Namespace; // Map ---------------------------------------------------------- pub struct Map { - class: TypeRef, ns: Namespace, entries: RwLock>, } standard_object_impls!(Map); +impl Default for Map { + fn default() -> Self { + Self { ns: Namespace::default(), entries: RwLock::new(IndexMap::default()) } + } +} + impl Map { - pub fn new(class: TypeRef, entries: IndexMap) -> Self { - Self { class, ns: Namespace::default(), entries: RwLock::new(entries) } + pub fn new(entries: IndexMap) -> Self { + Self { ns: Namespace::default(), entries: RwLock::new(entries) } } pub fn len(&self) -> usize { @@ -154,7 +162,7 @@ impl Map { } impl ObjectTrait for Map { - object_trait_header!(); + object_trait_header!(MAP_TYPE); fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { if self.is(rhs) || rhs.is_always() { diff --git a/feint-builtins/src/types/module.rs b/feint-builtins/src/types/module.rs index 4021918..6938c1f 100644 --- a/feint-builtins/src/types/module.rs +++ b/feint-builtins/src/types/module.rs @@ -2,16 +2,21 @@ use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; +use once_cell::sync::Lazy; + use feint_code_gen::*; use crate::util::check_args; use super::base::{ObjectRef, ObjectTrait, TypeRef}; +use super::class::Type; use super::code::Code; use super::map::Map; use super::ns::Namespace; -use crate::BUILTINS; +use crate::new; + +std_type!(MODULE_TYPE, ModuleType); // pub fn make_module_type() -> obj_ref_t!(ModuleType) { // let type_ref = obj_ref!(ModuleType::new()); @@ -49,7 +54,7 @@ use crate::BUILTINS; // let attrs = use_arg_map!(new, attrs, attrs_arg); // // let module = Module::with_map_entries( -// BUILTINS.module_type(), +// new::module_type(), // attrs, // name.to_owned(), // path.to_owned(), @@ -67,7 +72,6 @@ use crate::BUILTINS; // Module ------------------------------------------------------- pub struct Module { - class: TypeRef, ns: Namespace, name: String, path: String, @@ -81,47 +85,39 @@ impl Module { /// modules and for special cases such as the REPL module. /// Modules implemented in FeInt will have their `$doc` /// attribute initialized from their module level docstring. - pub fn new( - class: TypeRef, - name: String, - path: String, - code: Code, - doc: Option, - ) -> Self { + pub fn new(name: String, path: String, code: Code, doc: Option) -> Self { let ns = Namespace::with_entries(&[ - // ("$full_name", BUILTINS.str(name.as_str())), - // ("$name", BUILTINS.str(name.as_str())), - // ("$path", BUILTINS.str(path.as_str())), + // ("$full_name", new::str(name.as_str())), + // ("$name", new::str(name.as_str())), + // ("$path", new::str(path.as_str())), // ( // "$doc", - // if let Some(doc) = doc { BUILTINS.str(doc) } else { code.get_doc() }, + // if let Some(doc) = doc { new::str(doc) } else { code.get_doc() }, // ), ]); - Self { class, ns, path, name, code } + Self { ns, path, name, code } } pub fn with_entries( - class: TypeRef, entries: &[(&str, ObjectRef)], name: String, path: String, code: Code, doc: Option, ) -> Self { - let mut module = Self::new(class, name, path, code, doc); + let mut module = Self::new(name, path, code, doc); module.ns.extend(entries); module } pub fn with_map_entries( - class: TypeRef, map: &Map, name: String, path: String, code: Code, doc: Option, ) -> Self { - let mut module = Self::new(class, name, path, code, doc); + let mut module = Self::new(name, path, code, doc); module.ns.extend_from_map(map); module } @@ -171,7 +167,7 @@ impl Module { } impl ObjectTrait for Module { - object_trait_header!(); + object_trait_header!(MODULE_TYPE); } // Display ------------------------------------------------------------- diff --git a/feint-builtins/src/types/nil.rs b/feint-builtins/src/types/nil.rs index 4be3529..8b23e4b 100644 --- a/feint-builtins/src/types/nil.rs +++ b/feint-builtins/src/types/nil.rs @@ -1,17 +1,19 @@ +//! Builtin `Nil` type. use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; -use feint_code_gen::*; +use once_cell::sync::Lazy; -use super::base::{ObjectRef, ObjectTrait, TypeRef}; +use feint_code_gen::*; +use super::base::{ObjectTrait, TypeRef}; +use super::class::Type; use super::ns::Namespace; -// Nil ---------------------------------------------------------- +std_type!(NIL_TYPE, NilType); pub struct Nil { - class: TypeRef, ns: Namespace, } @@ -19,21 +21,19 @@ standard_object_impls!(Nil); impl Nil { #[allow(clippy::new_without_default)] - pub fn new(class: TypeRef) -> Self { - Self { class, ns: Namespace::default() } + pub fn new() -> Self { + Self { ns: Namespace::default() } } } impl ObjectTrait for Nil { - object_trait_header!(); + object_trait_header!(NIL_TYPE); fn bool_val(&self) -> Option { Some(false) } } -// Display ------------------------------------------------------------- - impl fmt::Display for Nil { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "nil") diff --git a/feint-builtins/src/types/prop.rs b/feint-builtins/src/types/prop.rs index 90430ed..c018098 100644 --- a/feint-builtins/src/types/prop.rs +++ b/feint-builtins/src/types/prop.rs @@ -1,20 +1,22 @@ +//! Builtiln `Prop` type. +//! //! The `Prop` type wraps a function that is called to compute the value //! of an attribute. use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; +use once_cell::sync::Lazy; + use feint_code_gen::*; use super::base::{ObjectRef, ObjectTrait, TypeRef}; - +use super::class::Type; use super::ns::Namespace; -// Prop --------------------------------------------------------- +std_type!(PROP_TYPE, NilType); pub struct Prop { - class: TypeRef, - ns: Namespace, getter: ObjectRef, } @@ -22,8 +24,8 @@ pub struct Prop { standard_object_impls!(Prop); impl Prop { - pub fn new(class: TypeRef, getter: ObjectRef) -> Self { - Self { class, ns: Namespace::default(), getter } + pub fn new(getter: ObjectRef) -> Self { + Self { ns: Namespace::default(), getter } } pub fn getter(&self) -> ObjectRef { @@ -32,11 +34,9 @@ impl Prop { } impl ObjectTrait for Prop { - object_trait_header!(); + object_trait_header!(PROP_TYPE); } -// Display ------------------------------------------------------------- - impl fmt::Display for Prop { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "", self.getter.read().unwrap()) diff --git a/feint-builtins/src/types/seq.rs b/feint-builtins/src/types/seq.rs index 641be0a..f33a710 100644 --- a/feint-builtins/src/types/seq.rs +++ b/feint-builtins/src/types/seq.rs @@ -4,27 +4,27 @@ use num_bigint::BigInt; use feint_code_gen::{use_arg, use_arg_str}; -use crate::BUILTINS; +use crate::new; use super::base::ObjectRef; use super::Args; pub fn has(items: &[ObjectRef], args: &Args) -> ObjectRef { if items.is_empty() { - return BUILTINS.bool(false); + return new::bool(false); } let member = use_arg!(args, 0); for item in items.iter() { if member.is_equal(&*item.read().unwrap()) { - return BUILTINS.bool(true); + return new::bool(true); } } - BUILTINS.bool(false) + new::bool(false) } pub fn join(items: &[ObjectRef], args: &Args) -> ObjectRef { if items.is_empty() { - return BUILTINS.empty_str(); + return new::empty_str(); } let n_items = items.len(); @@ -45,11 +45,11 @@ pub fn join(items: &[ObjectRef], args: &Args) -> ObjectRef { } } - BUILTINS.str(string) + new::str(string) } pub fn sum(items: &[ObjectRef]) -> ObjectRef { - let mut sum = BUILTINS.int(BigInt::from(0)); + let mut sum = new::int(BigInt::from(0)); for item in items.iter() { sum = { let a = sum.read().unwrap(); @@ -57,7 +57,7 @@ pub fn sum(items: &[ObjectRef]) -> ObjectRef { if let Some(new_sum) = (*a).add(&*b) { new_sum } else { - return BUILTINS.type_err("Could not add object to sum", item.clone()); + return new::type_err("Could not add object to sum", item.clone()); } } } diff --git a/feint-builtins/src/types/str.rs b/feint-builtins/src/types/str.rs index 6c4f3e4..17920eb 100644 --- a/feint-builtins/src/types/str.rs +++ b/feint-builtins/src/types/str.rs @@ -3,12 +3,17 @@ use std::fmt; use std::str::EscapeDefault; use std::sync::{Arc, RwLock}; +use once_cell::sync::Lazy; + use feint_code_gen::*; -use super::base::{ObjectRef, ObjectTrait, TypeRef}; +use crate::new; +use super::base::{ObjectRef, ObjectTrait, TypeRef}; +use super::class::Type; use super::ns::Namespace; -use crate::BUILTINS; + +std_type!(STR_TYPE, StrType); // pub(crate) fn make_str_type() -> obj_ref_t!(StrType) { // let type_ref = obj_ref!(StrType::new()); @@ -21,14 +26,14 @@ use crate::BUILTINS; // if arg.is_str() { // args[0].clone() // } else { -// BUILTINS.str(arg.to_string()) +// new::str(arg.to_string()) // } // }), // // Instance Attributes ----------------------------------------- // prop!("length", type_ref, "", |this, _| { // let this = this.read().unwrap(); // let value = this.get_str_val().unwrap(); -// BUILTINS.int(value.len()) +// new::int(value.len()) // }), // // Instance Methods -------------------------------------------- // meth!("starts_with", type_ref, &["prefix"], "", |this, args| { @@ -36,24 +41,24 @@ use crate::BUILTINS; // let value = this.get_str_val().unwrap(); // let arg = use_arg!(args, 0); // let prefix = use_arg_str!(starts_with, prefix, arg); -// BUILTINS.bool(value.starts_with(prefix)) +// new::bool(value.starts_with(prefix)) // }), // meth!("ends_with", type_ref, &["suffix"], "", |this, args| { // let this = this.read().unwrap(); // let value = this.get_str_val().unwrap(); // let arg = use_arg!(args, 0); // let suffix = use_arg_str!(ends_with, suffix, arg); -// BUILTINS.bool(value.ends_with(suffix)) +// new::bool(value.ends_with(suffix)) // }), // meth!("upper", type_ref, &[], "", |this, _| { // let this = this.read().unwrap(); // let value = this.get_str_val().unwrap(); -// BUILTINS.str(value.to_uppercase()) +// new::str(value.to_uppercase()) // }), // meth!("lower", type_ref, &[], "", |this, _| { // let this = this.read().unwrap(); // let value = this.get_str_val().unwrap(); -// BUILTINS.str(value.to_lowercase()) +// new::str(value.to_lowercase()) // }), // // meth!( // // "render", @@ -80,7 +85,7 @@ use crate::BUILTINS; // let this = this.read().unwrap(); // let value = this.get_str_val().unwrap(); // let count = use_arg_usize!(get, index, args, 0); -// BUILTINS.str(value.repeat(count)) +// new::str(value.repeat(count)) // }), // meth!("replace", type_ref, &["old", "new"], "", |this, args| { // let this = this.read().unwrap(); @@ -90,7 +95,7 @@ use crate::BUILTINS; // let old = use_arg_str!(replace, old, arg1); // let new = use_arg_str!(replace, new, arg2); // let result = value.replace(old, new); -// BUILTINS.str(result) +// new::str(result) // }), // meth!("remove_prefix", type_ref, &["prefix"], "", |this_ref, args| { // let this = this_ref.read().unwrap(); @@ -98,7 +103,7 @@ use crate::BUILTINS; // let arg = use_arg!(args, 0); // let prefix = use_arg_str!(starts_with, prefix, arg); // if let Some(new_val) = val.strip_prefix(prefix) { -// BUILTINS.str(new_val) +// new::str(new_val) // } else { // drop(this); // this_ref @@ -112,7 +117,6 @@ use crate::BUILTINS; // Str ---------------------------------------------------------- pub struct Str { - class: TypeRef, ns: Namespace, value: String, } @@ -120,12 +124,11 @@ pub struct Str { standard_object_impls!(Str); impl Str { - pub fn new(class: TypeRef, value: String) -> Self { + pub fn new(value: String) -> Self { Self { - class, ns: Namespace::with_entries(&[ // Instance Attributes - // ("length", BUILTINS.int(value.len())), + // ("length", new::int(value.len())), ]), value, } @@ -143,7 +146,7 @@ impl Str { } impl ObjectTrait for Str { - object_trait_header!(); + object_trait_header!(STR_TYPE); fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { if self.is(rhs) || rhs.is_always() { @@ -162,7 +165,7 @@ impl ObjectTrait for Str { let mut value = String::with_capacity(a.len() + b.len()); value.push_str(a); value.push_str(b); - let value = BUILTINS.str(value); + let value = new::str(value); Some(value) } else { None diff --git a/feint-builtins/src/types/tuple.rs b/feint-builtins/src/types/tuple.rs index 5402eba..0256492 100644 --- a/feint-builtins/src/types/tuple.rs +++ b/feint-builtins/src/types/tuple.rs @@ -3,15 +3,19 @@ use std::fmt; use std::slice::Iter; use std::sync::{Arc, RwLock}; +use once_cell::sync::Lazy; + use feint_code_gen::*; -use crate::BUILTINS; +use crate::new; use super::base::{ObjectRef, ObjectTrait, TypeRef}; - +use super::class::Type; use super::ns::Namespace; use super::seq; +std_type!(TUPLE_TYPE, TupleType); + // pub fn make_tuple_type() -> obj_ref_t!(TupleType) { // let type_ref = obj_ref!(TupleType::new()); // let mut type_obj = type_ref.write().unwrap(); @@ -21,12 +25,12 @@ use super::seq; // prop!("length", type_ref, "", |this, _| { // let this = this.read().unwrap(); // let this = this.down_to_tuple().unwrap(); -// BUILTINS.int(this.len()) +// new::int(this.len()) // }), // prop!("is_empty", type_ref, "", |this, _| { // let this = this.read().unwrap(); // let this = this.down_to_tuple().unwrap(); -// BUILTINS.bool(this.len() == 0) +// new::bool(this.len() == 0) // }), // prop!("sum", type_ref, "", |this, _| { // let this = this.read().unwrap(); @@ -40,7 +44,7 @@ use super::seq; // let index = use_arg_usize!(get, index, args, 0); // match this.get(index) { // Some(obj) => obj, -// None => BUILTINS.nil(), +// None => new::nil(), // } // }), // meth!("has", type_ref, &["member"], "", |this, args| { @@ -51,7 +55,7 @@ use super::seq; // meth!("iter", type_ref, &[], "", |this_ref, _| { // let this = this_ref.read().unwrap(); // let this = this.down_to_tuple().unwrap(); -// BUILTINS.iterator(this.items.clone()) +// new::iterator(this.items.clone()) // }), // meth!("join", type_ref, &["sep"], "", |this, args| { // let this = this.read().unwrap(); @@ -66,7 +70,6 @@ use super::seq; // Tuple -------------------------------------------------------- pub struct Tuple { - class: TypeRef, ns: Namespace, items: Vec, } @@ -74,8 +77,8 @@ pub struct Tuple { standard_object_impls!(Tuple); impl Tuple { - pub fn new(class: TypeRef, items: Vec) -> Self { - Self { class, ns: Namespace::default(), items } + pub fn new(items: Vec) -> Self { + Self { ns: Namespace::default(), items } } pub(crate) fn iter(&self) -> Iter<'_, ObjectRef> { @@ -96,7 +99,7 @@ impl Tuple { } impl ObjectTrait for Tuple { - object_trait_header!(); + object_trait_header!(TUPLE_TYPE); fn get_item(&self, index: usize, this: ObjectRef) -> ObjectRef { if let Some(item) = self.items.get(index) { diff --git a/feint-builtins/src/util.rs b/feint-builtins/src/util.rs index 7039e9a..964088b 100644 --- a/feint-builtins/src/util.rs +++ b/feint-builtins/src/util.rs @@ -1,5 +1,5 @@ +use crate::new; use crate::types::{Args, ObjectRef}; -use crate::BUILTINS; /// Check args and return info. /// @@ -51,7 +51,7 @@ pub(crate) fn check_args( n_args = n_args - 1 + n_var_args; (n_var_args, var_args_ref.clone()) } else { - (0, BUILTINS.empty_tuple()) + (0, new::empty_tuple()) }; // NOTE: Slightly hacky, but it's extremely unlikely anyone would @@ -66,7 +66,7 @@ pub(crate) fn check_args( } else { format!("{name} expected {min} to {max} args; got {n_args}") }; - return Err(BUILTINS.arg_err(msg, BUILTINS.nil())); + return Err(new::arg_err(msg, new::nil())); } Ok((n_args, n_var_args, var_args)) diff --git a/feint-cli/src/repl.rs b/feint-cli/src/repl.rs index 7aebfac..54fbf8f 100644 --- a/feint-cli/src/repl.rs +++ b/feint-cli/src/repl.rs @@ -4,8 +4,8 @@ use std::path::PathBuf; use rustyline::config::Configurer; use rustyline::error::ReadlineError; +use feint_builtins::new; use feint_builtins::types::{ObjectRef, ObjectTrait}; -use feint_builtins::BUILTINS; use feint_compiler::{CompErrKind, ParseErrKind, ScanErrKind}; use feint_driver::result::{DriverErr, DriverErrKind, DriverOptResult, DriverResult}; use feint_driver::Driver; @@ -19,7 +19,7 @@ pub struct Repl { impl Repl { pub fn new(history_path: Option, mut driver: Driver) -> Self { - let module = BUILTINS.module("$repl", "", "FeInt REPL module", &[]); + let module = new::module("$repl", "", "FeInt REPL module", &[]); driver.add_module("$repl", module.clone()); let mut reader = diff --git a/feint-code-gen/src/lib.rs b/feint-code-gen/src/lib.rs index cc39371..a7d29a6 100644 --- a/feint-code-gen/src/lib.rs +++ b/feint-code-gen/src/lib.rs @@ -12,10 +12,18 @@ macro_rules! obj_ref { }; } +#[macro_export] +macro_rules! std_type { + ( $name:ident, $class_name:ident ) => { + pub static $name: Lazy = + Lazy::new(|| obj_ref!(Type::new("std", stringify!($class_name)))); + }; +} + /// Generate standard obj impls. #[macro_export] macro_rules! standard_object_impls { - ( $name:ident ) => { + ( $name:ident ) => { unsafe impl Send for $name {} unsafe impl Sync for $name {} }; @@ -30,7 +38,7 @@ macro_rules! standard_object_impls { /// The singleton type instance. E.g. `NIL_TYPE`. #[macro_export] macro_rules! object_trait_header { - () => { + ( $class:ident ) => { fn as_any(&self) -> &dyn Any { self } @@ -40,7 +48,7 @@ macro_rules! object_trait_header { } fn class(&self) -> TypeRef { - self.class.clone() + $class.clone() } fn ns(&self) -> &Namespace { @@ -88,7 +96,7 @@ macro_rules! meth { ( $name:literal, $this_type:expr, $params:expr, $doc:literal, $func:expr ) => { ( $name, - BUILTINS.intrinsic_func( + new::intrinsic_func( "std", $name, Some($this_type.clone()), @@ -107,7 +115,7 @@ macro_rules! prop { ( $name:literal, $this_type:expr, $doc:literal, $func:expr ) => { ( $name, - BUILTINS.prop(BUILTINS.intrinsic_func( + new::prop(new::intrinsic_func( "std", $name, Some($this_type.clone()), @@ -133,7 +141,7 @@ macro_rules! use_arg { } else { let msg = format!("{}() didn't receive enough args", stringify!($func_name)); - return BUILTINS.arg_err(msg, BUILTINS.nil()); + return new::arg_err(msg, new::nil()); } }}; } @@ -152,7 +160,7 @@ macro_rules! use_arg_str { stringify!($func_name), stringify!($arg_name) ); - return BUILTINS.arg_err(msg, BUILTINS.nil()); + return new::arg_err(msg, new::nil()); } }}; } @@ -168,7 +176,7 @@ macro_rules! use_arg_map { stringify!($func_name), stringify!($arg_name) ); - return BUILTINS.arg_err(msg, BUILTINS.nil()); + return new::arg_err(msg, new::nil()); } }}; } @@ -186,12 +194,12 @@ macro_rules! use_arg_usize { stringify!($func_name), stringify!($arg_name) ); - return BUILTINS.arg_err(msg, BUILTINS.nil()); + return new::arg_err(msg, new::nil()); } } else { let msg = format!("{}() didn't receive enough args", stringify!($func_name)); - return BUILTINS.arg_err(msg, BUILTINS.nil()); + return new::arg_err(msg, new::nil()); } }}; } diff --git a/feint-compiler/src/compiler/compiler.rs b/feint-compiler/src/compiler/compiler.rs index 04cdfe7..cc891a7 100644 --- a/feint-compiler/src/compiler/compiler.rs +++ b/feint-compiler/src/compiler/compiler.rs @@ -2,9 +2,9 @@ use std::collections::HashSet; use feint_builtins::modules::STD; +use feint_builtins::new; use feint_builtins::types::code::{Code, Inst}; use feint_builtins::types::Module; -use feint_builtins::BUILTINS; use feint_util::stack::Stack; use crate::ast; @@ -57,13 +57,7 @@ impl Compiler { ast_module: ast::Module, ) -> CompResult { let code = self.compile_module_to_code(name, ast_module)?; - Ok(Module::new( - BUILTINS.module_type(), - name.to_owned(), - file_name.to_owned(), - code, - None, - )) + Ok(Module::new(name.to_owned(), file_name.to_owned(), code, None)) } /// Compile AST module node to code object. @@ -270,7 +264,7 @@ impl Compiler { // END Inner Functions ----------------------------------------- - let func = BUILTINS.func(module_name, func_name, params, visitor.code); + let func = new::func(module_name, func_name, params, visitor.code); let parent_visitor = &mut self.visitor_stack.peek_mut().unwrap().0; let const_index = parent_visitor.code.add_const(func); diff --git a/feint-compiler/src/compiler/visitor.rs b/feint-compiler/src/compiler/visitor.rs index 8a51daa..1d5bc81 100644 --- a/feint-compiler/src/compiler/visitor.rs +++ b/feint-compiler/src/compiler/visitor.rs @@ -3,8 +3,8 @@ use std::collections::HashSet; use std::fmt; use std::fmt::Formatter; -use feint_builtins::builtins::BUILTINS; use feint_builtins::modules::STD; +use feint_builtins::new; use feint_builtins::types::code::{Code, Inst, PrintFlags}; use feint_builtins::types::ObjectRef; use feint_util::op::{ @@ -382,10 +382,10 @@ impl CompilerVisitor { Kind::Always => self.push_always(), Kind::Ellipsis => self.push_nil(), Kind::Int(value) => { - self.add_const(BUILTINS.int(value)); + self.add_const(new::int(value)); } Kind::Float(value) => { - self.add_const(BUILTINS.float(value)); + self.add_const(new::float(value)); } Kind::String(value) => { if value.is_empty() { @@ -393,7 +393,7 @@ impl CompilerVisitor { } else if value == "\n" { self.push_newline(); } else { - self.add_const(BUILTINS.str(value)); + self.add_const(new::str(value)); } } } @@ -904,31 +904,31 @@ impl CompilerVisitor { // Global constants ------------------------------------------------ fn push_nil(&mut self) { - self.add_const(BUILTINS.nil()); + self.add_const(new::nil()); } fn push_true(&mut self) { - self.add_const(BUILTINS.bool(true)); + self.add_const(new::bool(true)); } fn push_false(&mut self) { - self.add_const(BUILTINS.bool(false)); + self.add_const(new::bool(false)); } fn push_always(&mut self) { - self.add_const(BUILTINS.always()); + self.add_const(new::always()); } fn push_empty_str(&mut self) { - self.add_const(BUILTINS.empty_str()); + self.add_const(new::empty_str()); } fn push_newline(&mut self) { - self.add_const(BUILTINS.newline()); + self.add_const(new::newline()); } fn push_empty_tuple(&mut self) { - self.add_const(BUILTINS.empty_tuple()); + self.add_const(new::empty_tuple()); } // Code unit constants --------------------------------------------- diff --git a/feint-compiler/src/format.rs b/feint-compiler/src/format.rs index 88ba20e..93205d0 100644 --- a/feint-compiler/src/format.rs +++ b/feint-compiler/src/format.rs @@ -1,5 +1,5 @@ +use feint_builtins::new; use feint_builtins::types::ObjectRef; -use feint_builtins::BUILTINS; use feint_util::source::source_from_text; use crate::scanner::{ScanTokensResult, Scanner, Token, TokenWithLocation as TWL}; @@ -121,8 +121,7 @@ pub fn render_template(template_ref: ObjectRef, context_ref: ObjectRef) -> Objec let context = if let Some(context) = context.down_to_map() { context } else { - return BUILTINS - .string_err("Expected context to be a map", template_ref.clone()); + return new::string_err("Expected context to be a map", template_ref.clone()); }; let scan_result = scan_format_string(template.as_str(), Some(("{{", "}}"))); @@ -131,7 +130,7 @@ pub fn render_template(template_ref: ObjectRef, context_ref: ObjectRef) -> Objec Ok(tokens) => tokens, Err(err) => { let msg = format!("Could not parse template: {err:?}"); - return BUILTINS.string_err(msg, template_ref.clone()); + return new::string_err(msg, template_ref.clone()); } }; @@ -150,7 +149,7 @@ pub fn render_template(template_ref: ObjectRef, context_ref: ObjectRef) -> Objec output.push_str(val.as_str()); } else { let msg = format!("Name not found in context: {name}"); - return BUILTINS.string_err(msg, template_ref.clone()); + return new::string_err(msg, template_ref.clone()); } } _ => { @@ -159,11 +158,11 @@ pub fn render_template(template_ref: ObjectRef, context_ref: ObjectRef) -> Objec let msg = format!( "Template is contains an invalid expression: {tokens:?}" ); - return BUILTINS.string_err(msg, template_ref.clone()); + return new::string_err(msg, template_ref.clone()); } }, } } - BUILTINS.str(output) + new::str(output) } diff --git a/feint-driver/src/driver.rs b/feint-driver/src/driver.rs index a42552a..25e7371 100644 --- a/feint-driver/src/driver.rs +++ b/feint-driver/src/driver.rs @@ -5,12 +5,15 @@ use std::io::BufRead; use std::path::Path; use std::sync::{Arc, RwLock}; -use feint_builtins::modules::{STD, STD_FI_MODULES}; +use feint_builtins::modules::{ + add_module, maybe_get_module, MODULES, STD, STD_FI_MODULES, +}; +use feint_builtins::new; use feint_builtins::types::{ + self, code::{Inst, PrintFlags}, Module, ObjectRef, ObjectTrait, }; -use feint_builtins::BUILTINS; use feint_code_gen::obj_ref; use feint_compiler::{ ast, CompErr, CompErrKind, Compiler, ParseErr, ParseErrKind, Parser, ScanErr, @@ -90,13 +93,13 @@ impl Driver { // it's used early (i.e., during import). { let mut system = system_ref.write().unwrap(); - system.ns_mut().insert("modules", BUILTINS.modules()); - system.ns_mut().insert("argv", BUILTINS.argv_tuple(&self.argv)); + system.ns_mut().insert("modules", MODULES.clone()); + system.ns_mut().insert("argv", new::argv_tuple(&self.argv)); } - self.extend_intrinsic_type(BUILTINS.list_type(), "std.list")?; - self.extend_intrinsic_type(BUILTINS.map_type(), "std.map")?; - self.extend_intrinsic_type(BUILTINS.tuple_type(), "std.tuple")?; + self.extend_intrinsic_type(types::list::LIST_TYPE.clone(), "std.list")?; + self.extend_intrinsic_type(types::map::MAP_TYPE.clone(), "std.map")?; + self.extend_intrinsic_type(types::tuple::TUPLE_TYPE.clone(), "std.tuple")?; Ok(()) } @@ -283,7 +286,7 @@ impl Driver { if result.is_ok() && is_main { if let Some(main) = module.get_main() { let main = main.read().unwrap(); - let args = self.argv.iter().map(|a| BUILTINS.str(a)).collect(); + let args = self.argv.iter().map(new::str).collect(); if let Some(main) = main.down_to_func() { result = self .vm @@ -397,12 +400,12 @@ impl Driver { /// Add a module to both `MODULES` and `system.modules`. pub fn add_module(&mut self, name: &str, module: ObjectRef) { - BUILTINS.add_module(name, module.clone()); + add_module(name, module.clone()); } /// Get module from `MODULES` (the `system.modules` mirror). fn get_module(&mut self, name: &str) -> Result { - if let Some(module) = BUILTINS.maybe_get_module(name) { + if let Some(module) = maybe_get_module(name) { Ok(module) } else { Err(DriverErr::new(DriverErrKind::ModuleNotFound(name.to_owned()))) diff --git a/feint-vm/src/context.rs b/feint-vm/src/context.rs index 76fc658..817ae41 100644 --- a/feint-vm/src/context.rs +++ b/feint-vm/src/context.rs @@ -2,8 +2,8 @@ use indexmap::IndexMap; use feint_builtins::modules::STD; +use feint_builtins::new; use feint_builtins::types::{ObjectRef, ObjectTrait}; -use feint_builtins::BUILTINS; use crate::RuntimeObjResult; @@ -97,7 +97,7 @@ impl ModuleExecutionContext { /// the var in the current namespace and sets its initial value to /// nil. pub(super) fn declare_var(&mut self, name: &str) { - let initial = BUILTINS.nil(); + let initial = new::nil(); let ns = self.current_mut(); ns.insert(name.to_owned(), initial); } diff --git a/feint-vm/src/vm.rs b/feint-vm/src/vm.rs index e753084..d4dfb3e 100644 --- a/feint-vm/src/vm.rs +++ b/feint-vm/src/vm.rs @@ -9,14 +9,15 @@ use std::sync::{ }; use ctrlc; +use feint_builtins::modules::get_module; use indexmap::IndexMap; use num_traits::ToPrimitive; +use feint_builtins::new; use feint_builtins::types::code::{Code, Inst, PrintFlags}; use feint_builtins::types::{ Args, Func, FuncTrait, IntrinsicFunc, Module, ObjectRef, ObjectTrait, ThisOpt, }; -use feint_builtins::BUILTINS; use feint_util::op::{BinaryOperator, CompareOperator, InplaceOperator, UnaryOperator}; use feint_util::source::Location; use feint_util::stack::Stack; @@ -172,7 +173,7 @@ impl VM { } // Modules LoadModule(name) => { - let module = BUILTINS.get_module(name.as_str()); + let module = get_module(name.as_str()); self.push_temp(module); } // Vars @@ -247,7 +248,7 @@ impl VM { } else { // Create new cell to wrap TOS in. assert!(var.is_nil()); - let cell_ref = BUILTINS.cell_with_value(value.clone()); + let cell_ref = new::cell_with_value(value.clone()); self.ctx.assign_var(name, cell_ref, 0)? }; // Push cell *value* to TOS. @@ -293,7 +294,7 @@ impl VM { } } JumpPushNil(addr, forward, scope_exit_count) => { - self.push_temp(BUILTINS.nil()); + self.push_temp(new::nil()); self.exit_scopes(*scope_exit_count); if *forward { jump_ip = Some(ip + *addr); @@ -385,17 +386,17 @@ impl VM { let obj = obj.read().unwrap(); string.push_str(obj.to_string().as_str()); } - let string_obj = BUILTINS.str(string); + let string_obj = new::str(string); self.push_temp(string_obj); } MakeTuple(n) => { let objects = self.pop_n_obj(*n)?; - let tuple = BUILTINS.tuple(objects); + let tuple = new::tuple(objects); self.push_temp(tuple); } MakeList(n) => { let objects = self.pop_n_obj(*n)?; - let list = BUILTINS.list(objects); + let list = new::list(objects); self.push_temp(list); } MakeMap(n) => { @@ -411,7 +412,7 @@ impl VM { vals.push(obj.clone()); } } - let map = BUILTINS.map_from_keys_and_vals(keys, vals); + let map = new::map_from_keys_and_vals(keys, vals); self.push_temp(map); } CaptureSet(names) => { @@ -425,7 +426,7 @@ impl VM { capture_set.insert(name.to_owned(), var_ref.clone()); } else { assert!(var.is_nil()); - capture_set.insert(name.to_owned(), BUILTINS.cell()); + capture_set.insert(name.to_owned(), new::cell()); } } else if let Some(frame) = self.call_stack.peek() { // Capture cell does not exist. @@ -442,7 +443,7 @@ impl VM { } } } - self.push_temp(BUILTINS.map(capture_set)); + self.push_temp(new::map(capture_set)); } MakeFunc => { let capture_set_ref = self.pop_obj()?; @@ -460,7 +461,7 @@ impl VM { if func_captured && ip + 1 < len_chunk { if let AssignCell(_) = &code[ip + 1] { let closure_cell = - BUILTINS.cell_with_value(func_ref.clone()); + new::cell_with_value(func_ref.clone()); self.ctx.assign_var( func.name(), closure_cell.clone(), @@ -471,9 +472,10 @@ impl VM { } } - self.push_temp( - BUILTINS.closure(func_ref.clone(), capture_set_ref.clone()), - ); + self.push_temp(new::closure( + func_ref.clone(), + capture_set_ref.clone(), + )); } } // VM control @@ -616,21 +618,21 @@ impl VM { if let Some(result) = a.negate() { result } else { - BUILTINS.type_err(format!("- not defined for {a}"), a_ref.clone()) + new::type_err(format!("- not defined for {a}"), a_ref.clone()) } } AsBool => { if let Some(result) = a.bool_val() { - BUILTINS.bool(result) + new::bool(result) } else { - BUILTINS.type_err(format!("!! not defined for {a}"), a_ref.clone()) + new::type_err(format!("!! not defined for {a}"), a_ref.clone()) } } Not => { if let Some(result) = a.not() { - BUILTINS.bool(result) + new::bool(result) } else { - BUILTINS.type_err(format!("! not defined for {a}"), a_ref.clone()) + new::type_err(format!("! not defined for {a}"), a_ref.clone()) } } }; @@ -682,7 +684,7 @@ impl VM { // XXX: This can happen for a construct like `1.()`, // but that should probably be a syntax error // that's caught early. - BUILTINS.attr_err( + new::attr_err( format!("Not an attribute name or index: {b:?}"), a_ref.clone(), ) @@ -695,7 +697,7 @@ impl VM { // TODO: Check whether `a` is a type or an instance. - BUILTINS.bound_func(obj_ref.clone(), a_ref.clone()) + new::bound_func(obj_ref.clone(), a_ref.clone()) } else if let Some(prop) = obj.down_to_prop() { // If `b` in `a.b` is a property, bind `b`'s getter // to `a` then call the bound getter. @@ -704,7 +706,7 @@ impl VM { // and return the property itself when `a` is // a type. - let func = BUILTINS.bound_func(prop.getter(), a_ref.clone()); + let func = new::bound_func(prop.getter(), a_ref.clone()); if a.is_type() { func } else { @@ -722,10 +724,10 @@ impl VM { if let Some(result) = result { self.push_temp(result); } else { - self.push_temp( - BUILTINS - .type_err(format!("Operation not defined for {b}"), a_ref.clone()), - ); + self.push_temp(new::type_err( + format!("Operation not defined for {b}"), + a_ref.clone(), + )); } Ok(()) @@ -753,7 +755,7 @@ impl VM { GreaterThan => a.greater_than(b).unwrap_or(false), GreaterThanOrEqual => a.greater_than(b).unwrap_or(false) || a.is_equal(b), }; - self.push_temp(BUILTINS.bool(result)); + self.push_temp(new::bool(result)); Ok(()) } @@ -777,10 +779,10 @@ impl VM { let result = if let Some(result) = result { result } else { - self.push_temp( - BUILTINS - .type_err(format!("Operation not defined for {b}"), a_ref.clone()), - ); + self.push_temp(new::type_err( + format!("Operation not defined for {b}"), + a_ref.clone(), + )); return Ok(()); }; @@ -905,7 +907,7 @@ impl VM { return this.clone(); } } - BUILTINS.nil() + new::nil() } // Function calls -------------------------------------------------- @@ -959,11 +961,11 @@ impl VM { ); self.call_closure(func_ref.clone(), this_opt, args) } else { - self.push_temp(BUILTINS.not_callable_err(callable_ref.clone())); + self.push_temp(new::not_callable_err(callable_ref.clone())); Ok(()) } } else { - self.push_temp(BUILTINS.not_callable_err(callable_ref.clone())); + self.push_temp(new::not_callable_err(callable_ref.clone())); Ok(()) } } @@ -998,7 +1000,7 @@ impl VM { // better to track which params are captured. See related // note in push_var(). for (name, arg) in func.arg_names().iter().zip(args) { - let cell = BUILTINS.cell_with_value(arg); + let cell = new::cell_with_value(arg); self.ctx.declare_and_assign_var(name, cell)?; } match self.execute_func(func, 0) { @@ -1043,7 +1045,7 @@ impl VM { self.check_arity(name, arity, n_args, this_opt)?; let mut args = args.clone(); let var_args_items = args.split_off(var_args_index); - let var_args = BUILTINS.tuple(var_args_items); + let var_args = new::tuple(var_args_items); args.push(var_args); Ok(args) } else { From ed2935aec13c4675f9ee864c10d0ee90562818e6 Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Sun, 5 Feb 2023 01:43:26 -0800 Subject: [PATCH 28/31] WIP --- feint-builtins/src/modules/std/std.rs | 52 +++--- feint-builtins/src/new.rs | 28 +-- feint-builtins/src/tests/types.rs | 79 ++++---- feint-builtins/src/types/always.rs | 2 +- feint-builtins/src/types/base.rs | 7 +- feint-builtins/src/types/bool.rs | 2 +- feint-builtins/src/types/bound_func.rs | 2 +- feint-builtins/src/types/cell.rs | 2 +- feint-builtins/src/types/class.rs | 3 +- feint-builtins/src/types/closure.rs | 2 +- feint-builtins/src/types/err.rs | 121 +++++++------ feint-builtins/src/types/err_type.rs | 54 +++--- feint-builtins/src/types/file.rs | 71 ++++---- feint-builtins/src/types/float.rs | 52 +++--- feint-builtins/src/types/int.rs | 59 +++--- feint-builtins/src/types/intrinsic_func.rs | 1 - feint-builtins/src/types/iterator.rs | 43 +++-- feint-builtins/src/types/list.rs | 179 +++++++++--------- feint-builtins/src/types/map.rs | 185 ++++++++++--------- feint-builtins/src/types/module.rs | 113 ++++++------ feint-builtins/src/types/str.rs | 199 ++++++++++----------- feint-builtins/src/types/tuple.rs | 103 ++++++----- feint-vm/src/tests.rs | 14 +- 23 files changed, 674 insertions(+), 699 deletions(-) diff --git a/feint-builtins/src/modules/std/std.rs b/feint-builtins/src/modules/std/std.rs index ae9d2e8..ed3a8b7 100644 --- a/feint-builtins/src/modules/std/std.rs +++ b/feint-builtins/src/modules/std/std.rs @@ -3,7 +3,7 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use feint_code_gen::{obj_ref_t, use_arg, use_arg_str}; +use feint_code_gen::obj_ref_t; use crate::{new, types}; @@ -32,30 +32,32 @@ pub static STD: Lazy = Lazy::new(|| { ("Nil", types::nil::NIL_TYPE.clone()), ("Str", types::str::STR_TYPE.clone()), ("Tuple", types::tuple::TUPLE_TYPE.clone()), - // ( - // "new_type", - // BUILTINS.intrinsic_func( - // "std", - // "new_type", - // None, - // &["module", "name"], - // "Make a new custom type - // - // # Args - // - // - module: Module - // - name: Str - // - // ", - // |_, args| { - // let module = args[0].clone(); - // let name_arg = use_arg!(args, 1); - // let name = use_arg_str!(new_type, name, name_arg); - // let class = BUILTINS.custom_type(module, name); - // class - // }, - // ), - // ), + ( + "new_type", + new::intrinsic_func( + "std", + "new_type", + None, + &["module", "name"], + "Make a new custom type + + # Args + + - module: Module + - name: Str + + ", + |_, _args| { + // TODO: + // let module = args[0].clone(); + // let name_arg = use_arg!(args, 1); + // let name = use_arg_str!(new_type, name, name_arg); + // let class = new::custom_type(module, name); + // class + new::nil() + }, + ), + ), ], ) }); diff --git a/feint-builtins/src/new.rs b/feint-builtins/src/new.rs index 3f65da9..d3dc36e 100644 --- a/feint-builtins/src/new.rs +++ b/feint-builtins/src/new.rs @@ -21,7 +21,7 @@ use crate::types::cell::Cell; use crate::types::class::Type; use crate::types::closure::Closure; use crate::types::code::Code; -use crate::types::custom::CustomObj; +// use crate::types::custom::CustomObj; use crate::types::err::ErrObj; use crate::types::err_type::ErrKind; use crate::types::file::File; @@ -325,7 +325,7 @@ pub fn custom_type(module_name: &str, name: &str) -> ObjectRef { - attributes: Map ", - |this_ref, args| { + |_this_ref, args| { let attrs_arg = args.get(0).unwrap(); let attrs_arg = attrs_arg.read().unwrap(); let attrs = attrs_arg.down_to_map().unwrap(); @@ -333,18 +333,18 @@ pub fn custom_type(module_name: &str, name: &str) -> ObjectRef { let mut ns = Namespace::default(); ns.extend_from_map(attrs); - let type_obj = this_ref.read().unwrap(); - - let class = if type_obj.is_type() { - // Called via custom type. - this_ref.clone() - } else { - // Called via custom instance. - // XXX: This branch isn't reachable because the - // VM will panic due to the identity test - // issue noted above. - type_obj.class() - }; + // TODO: + // let type_obj = this_ref.read().unwrap(); + // let class = if type_obj.is_type() { + // // Called via custom type. + // this_ref.clone() + // } else { + // // Called via custom instance. + // // XXX: This branch isn't reachable because the + // // VM will panic due to the identity test + // // issue noted above. + // type_obj.class() + // }; // let instance = CustomObj::new(class, ns); // obj_ref!(instance) diff --git a/feint-builtins/src/tests/types.rs b/feint-builtins/src/tests/types.rs index f809318..a23595e 100644 --- a/feint-builtins/src/tests/types.rs +++ b/feint-builtins/src/tests/types.rs @@ -121,45 +121,42 @@ mod custom { new(type_obj.clone(), vec![attrs]) } - #[test] - fn test_custom() { - let mod1 = new::module("test1", "", "test module 1", &[]); - let t1 = new::custom_type(mod1, "Custom1"); - let t1_obj1 = instance(t1.clone(), &[("value", new::nil())]); - let t1_obj2 = instance(t1.clone(), &[("value", BUILTINS.nil())]); - let t1_obj3 = instance(t1.clone(), &[("value", BUILTINS.nil())]); - - let mod2 = BUILTINS.module("test2", "", "test module 2", &[]); - let t2 = BUILTINS.custom_type(mod2, "Custom2"); - let t2_obj1 = instance(t2.clone(), &[]); - - check_attr(t1.clone(), "$id"); - check_attr(t1.clone(), "$type"); - check_attr(t1_obj1.clone(), "$id"); - check_attr(t1_obj1.clone(), "$type"); - - let result = t1_obj3.write().unwrap().set_attr( - "value", - BUILTINS.int(1), - t1_obj3.clone(), - ); - check_ok(result, "Could not set `value` on t1_obj3"); - check_attr(t1_obj3.clone(), "value"); - check_attr_eq(t1_obj3.clone(), "value", BUILTINS.int(1)); - - // An object should be equal to itself. - check_eq(t1_obj1.clone(), t1_obj1.clone()); - - // An object should be equal to an object of the SAME type with - // the same attributes. - check_eq(t1_obj1.clone(), t1_obj2.clone()); - - // An object should NOT be equal to an object of the SAME type with - // the DIFFERENT attributes. - check_ne(t1_obj1.clone(), t1_obj3.clone()); - - // An object should NOT be equal to an object of a DIFFERENT type, - // regardless of attributes. - check_ne(t1_obj1.clone(), t2_obj1.clone()); - } + // #[test] + // fn test_custom() { + // let mod1 = new::module("test1", "", "test module 1", &[]); + // let t1 = new::custom_type(mod1, "Custom1"); + // let t1_obj1 = instance(t1.clone(), &[("value", new::nil())]); + // let t1_obj2 = instance(t1.clone(), &[("value", new::nil())]); + // let t1_obj3 = instance(t1.clone(), &[("value", new::nil())]); + // + // let mod2 = new::module("test2", "", "test module 2", &[]); + // let t2 = new::custom_type(mod2, "Custom2"); + // let t2_obj1 = instance(t2.clone(), &[]); + // + // check_attr(t1.clone(), "$id"); + // check_attr(t1.clone(), "$type"); + // check_attr(t1_obj1.clone(), "$id"); + // check_attr(t1_obj1.clone(), "$type"); + // + // let result = + // t1_obj3.write().unwrap().set_attr("value", new::int(1), t1_obj3.clone()); + // check_ok(result, "Could not set `value` on t1_obj3"); + // check_attr(t1_obj3.clone(), "value"); + // check_attr_eq(t1_obj3.clone(), "value", new::int(1)); + // + // // An object should be equal to itself. + // check_eq(t1_obj1.clone(), t1_obj1.clone()); + // + // // An object should be equal to an object of the SAME type with + // // the same attributes. + // check_eq(t1_obj1.clone(), t1_obj2.clone()); + // + // // An object should NOT be equal to an object of the SAME type with + // // the DIFFERENT attributes. + // check_ne(t1_obj1.clone(), t1_obj3.clone()); + // + // // An object should NOT be equal to an object of a DIFFERENT type, + // // regardless of attributes. + // check_ne(t1_obj1.clone(), t2_obj1.clone()); + // } } diff --git a/feint-builtins/src/types/always.rs b/feint-builtins/src/types/always.rs index cc9bd6e..01b22db 100644 --- a/feint-builtins/src/types/always.rs +++ b/feint-builtins/src/types/always.rs @@ -11,7 +11,7 @@ use super::base::{ObjectTrait, TypeRef}; use super::class::Type; use super::ns::Namespace; -std_type!(ALWAYS_TYPE, AlwaysType); +std_type!(ALWAYS_TYPE, Always); pub struct Always { ns: Namespace, diff --git a/feint-builtins/src/types/base.rs b/feint-builtins/src/types/base.rs index 5340906..e0843d8 100644 --- a/feint-builtins/src/types/base.rs +++ b/feint-builtins/src/types/base.rs @@ -149,9 +149,9 @@ pub trait ObjectTrait { return self.id_obj(); } - // if name == "$module" { - // return self.module(); - // } + if name == "$module" { + return self.class().read().unwrap().module(); + } if name == "$type" { return self.class(); @@ -487,6 +487,7 @@ impl fmt::Display for dyn ObjectTrait { write_instance!( f, self, + Type, Always, Bool, BoundFunc, diff --git a/feint-builtins/src/types/bool.rs b/feint-builtins/src/types/bool.rs index 2bf808c..a6666ac 100644 --- a/feint-builtins/src/types/bool.rs +++ b/feint-builtins/src/types/bool.rs @@ -11,7 +11,7 @@ use super::base::{ObjectTrait, TypeRef}; use super::class::Type; use super::ns::Namespace; -std_type!(BOOL_TYPE, BoolType); +std_type!(BOOL_TYPE, Bool); pub struct Bool { ns: Namespace, diff --git a/feint-builtins/src/types/bound_func.rs b/feint-builtins/src/types/bound_func.rs index e04afda..50d6a53 100644 --- a/feint-builtins/src/types/bound_func.rs +++ b/feint-builtins/src/types/bound_func.rs @@ -15,7 +15,7 @@ use super::func_trait::FuncTrait; use super::ns::Namespace; use super::Params; -std_type!(BOUND_FUNC_TYPE, BoundFuncType); +std_type!(BOUND_FUNC_TYPE, BoundFunc); pub struct BoundFunc { ns: Namespace, diff --git a/feint-builtins/src/types/cell.rs b/feint-builtins/src/types/cell.rs index 0f77d03..d576648 100644 --- a/feint-builtins/src/types/cell.rs +++ b/feint-builtins/src/types/cell.rs @@ -13,7 +13,7 @@ use super::base::{ObjectRef, ObjectTrait, TypeRef}; use super::class::Type; use super::ns::Namespace; -std_type!(CELL_TYPE, CellType); +std_type!(CELL_TYPE, Cell); pub struct Cell { ns: Namespace, diff --git a/feint-builtins/src/types/class.rs b/feint-builtins/src/types/class.rs index 0da09e4..4d311fa 100644 --- a/feint-builtins/src/types/class.rs +++ b/feint-builtins/src/types/class.rs @@ -11,7 +11,6 @@ use once_cell::sync::Lazy; use feint_code_gen::*; -use crate::modules::get_module; use crate::new; use super::base::{ObjectTrait, TypeRef}; @@ -37,7 +36,7 @@ impl Type { name: name.to_owned(), full_name, ns: Namespace::with_entries(&[ - ("$module", get_module(module_name)), + ("$module_name", new::str(module_name)), ("$name", new::str(name)), ("$full_name", full_name_str), ]), diff --git a/feint-builtins/src/types/closure.rs b/feint-builtins/src/types/closure.rs index 334fbbd..ced8376 100644 --- a/feint-builtins/src/types/closure.rs +++ b/feint-builtins/src/types/closure.rs @@ -15,7 +15,7 @@ use super::func_trait::FuncTrait; use super::ns::Namespace; use super::Params; -std_type!(CLOSURE_TYPE, ClosureType); +std_type!(CLOSURE_TYPE, Closure); pub struct Closure { ns: Namespace, diff --git a/feint-builtins/src/types/err.rs b/feint-builtins/src/types/err.rs index f33a8a1..67d0dad 100644 --- a/feint-builtins/src/types/err.rs +++ b/feint-builtins/src/types/err.rs @@ -29,67 +29,66 @@ use super::class::Type; use super::err_type::ErrKind; use super::ns::Namespace; -std_type!(ERR_TYPE, ErrType); - -// pub fn make_err_type() -> obj_ref_t!(ErrType) { -// let type_ref = obj_ref!(ErrType::new()); -// let mut type_obj = type_ref.write().unwrap(); -// -// type_obj.add_attrs(&[ -// // Class Methods ----------------------------------------------- -// meth!("new", type_ref, &["type", "msg"], "", |_, args| { -// let name = "Err.new()"; -// -// let result = check_args(name, &args, false, 2, Some(2)); -// if let Err(err) = result { -// return err; -// } -// -// let type_arg = use_arg!(args, 0); -// let msg_arg = use_arg!(args, 1); -// -// let err_type = if let Some(err_type) = type_arg.down_to_err_type_obj() { -// err_type -// } else { -// let arg_err_msg = format!("{name} expected type to be an ErrType"); -// // NOTE: This is problematic because user code won't be -// // able to tell if the arg error was the result of -// // creating an arg err explicitly or the result of -// // an internal error. Note that this applies to -// // *any* user-constructible error. -// // -// // TODO: Figure out a solution for this, perhaps an err -// // type that is *not* user-constructible or a -// // nested err type? -// return new::arg_err(arg_err_msg, new::nil()); -// }; -// -// let kind = err_type.kind().clone(); -// -// let msg = if let Some(msg) = msg_arg.get_str_val() { -// msg -// } else { -// let arg_err_msg = format!("{name} expected message to be a Str"); -// return new::arg_err(arg_err_msg, new::nil()); -// }; -// -// new::err(kind, msg, new::nil()) -// }), -// // Instance Attributes ----------------------------------------- -// prop!("type", type_ref, "", |this, _| { -// let this = this.read().unwrap(); -// let this = this.down_to_err().unwrap(); -// this.kind.get_obj().unwrap() -// }), -// prop!("message", type_ref, "", |this, _| { -// let this = this.read().unwrap(); -// let this = this.down_to_err().unwrap(); -// new::str(&this.message) -// }), -// ]); -// -// type_ref.clone() -// } +pub static ERR_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(Type::new("std", "Err")); + + { + type_ref.write().unwrap().ns_mut().extend(&[ + // Class Methods ----------------------------------------------- + meth!("new", type_ref, &["type", "msg"], "", |_, args| { + let name = "Err.new()"; + + let result = check_args(name, &args, false, 2, Some(2)); + if let Err(err) = result { + return err; + } + + let type_arg = use_arg!(args, 0); + let msg_arg = use_arg!(args, 1); + + let err_type = if let Some(err_type) = type_arg.down_to_err_type_obj() { + err_type + } else { + let arg_err_msg = format!("{name} expected type to be an ErrType"); + // NOTE: This is problematic because user code won't be + // able to tell if the arg error was the result of + // creating an arg err explicitly or the result of + // an internal error. Note that this applies to + // *any* user-constructible error. + // + // TODO: Figure out a solution for this, perhaps an err + // type that is *not* user-constructible or a + // nested err type? + return new::arg_err(arg_err_msg, new::nil()); + }; + + let kind = err_type.kind().clone(); + + let msg = if let Some(msg) = msg_arg.get_str_val() { + msg + } else { + let arg_err_msg = format!("{name} expected message to be a Str"); + return new::arg_err(arg_err_msg, new::nil()); + }; + + new::err(kind, msg, new::nil()) + }), + // Instance Attributes ----------------------------------------- + prop!("type", type_ref, "", |this, _| { + let this = this.read().unwrap(); + let this = this.down_to_err().unwrap(); + this.kind.get_obj().unwrap() + }), + prop!("message", type_ref, "", |this, _| { + let this = this.read().unwrap(); + let this = this.down_to_err().unwrap(); + new::str(&this.message) + }), + ]); + } + + type_ref +}); // NOTE: This is named `ErrObj` instead of `Err` to avoid conflict with // Rust's `Err`. diff --git a/feint-builtins/src/types/err_type.rs b/feint-builtins/src/types/err_type.rs index f80db56..86bef92 100644 --- a/feint-builtins/src/types/err_type.rs +++ b/feint-builtins/src/types/err_type.rs @@ -66,37 +66,35 @@ impl ErrKind { } pub fn get_obj(&self) -> Option { - // let err_type_type = ERR_TYPE_TYPE.read().unwrap(); - // err_type_type.ns.get(self.name()) - None + let err_type_type = ERR_TYPE_TYPE.read().unwrap(); + err_type_type.ns().get(self.name()) } } -std_type!(ERR_TYPE_TYPE, ErrTypeType); - -// pub static ERR_TYPE_TYPE: Lazy = Lazy::new(|| { -// let type_ref = obj_ref!(ErrTypeType::new()); -// let mut type_obj = type_ref.write().unwrap(); -// -// // Types as class attributes -// for kind in ERR_KINDS.iter() { -// type_obj.add_attr( -// kind.name(), -// obj_ref!(ErrTypeObj::new(type_ref.clone(), kind.clone())), -// ); -// } -// -// type_obj.add_attrs(&[ -// // Instance Attributes ----------------------------------------- -// prop!("name", type_ref, "", |this, _| { -// let this = this.read().unwrap(); -// let this = this.as_any().downcast_ref::().unwrap(); -// new::str(this.name()) -// }), -// ]); -// -// type_ref.clone() -// }); +pub static ERR_TYPE_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(Type::new("std", "ErrType")); + + { + let mut type_obj = type_ref.write().unwrap(); + let ns = type_obj.ns_mut(); + + // Types as class attributes ----------------------------------- + for kind in ERR_KINDS.iter() { + ns.insert(kind.name(), obj_ref!(ErrTypeObj::new(kind.clone()))); + } + + ns.extend(&[ + // Instance Attributes ------------------------------------- + prop!("name", type_ref, "", |this, _| { + let this = this.read().unwrap(); + let this = this.as_any().downcast_ref::().unwrap(); + new::str(this.name()) + }), + ]); + } + + type_ref +}); pub struct ErrTypeObj { ns: Namespace, diff --git a/feint-builtins/src/types/file.rs b/feint-builtins/src/types/file.rs index 6b7acda..1acb792 100644 --- a/feint-builtins/src/types/file.rs +++ b/feint-builtins/src/types/file.rs @@ -16,43 +16,42 @@ use super::base::{ObjectRef, ObjectTrait, TypeRef}; use super::class::Type; use super::ns::Namespace; -// pub fn make_file_type() -> obj_ref_t!(FileType) { -// let type_ref = obj_ref!(FileType::new()); -// let mut type_obj = type_ref.write().unwrap(); -// -// type_obj.add_attrs(&[ -// // Class Methods -// meth!("new", type_ref, &["file_name"], "", |_, args| { -// let arg = use_arg!(args, 0); -// if let Some(file_name) = arg.get_str_val() { -// let path = Path::new(file_name); -// if path.is_file() { -// new::file(file_name) -// } else { -// new::file_not_found_err(file_name, new::nil()) -// } -// } else { -// let message = format!("File.new(file_name) expected string; got {arg}"); -// new::arg_err(message, new::nil()) -// } -// }), -// // Instance Attributes -// prop!("text", type_ref, "", |this, _| { -// let this = this.read().unwrap(); -// let this = this.down_to_file().unwrap(); -// this.text() -// }), -// prop!("lines", type_ref, "", |this, _| { -// let this = this.read().unwrap(); -// let this = &mut this.down_to_file().unwrap(); -// this.lines() -// }), -// ]); -// -// type_ref.clone() -// } +pub static FILE_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(Type::new("std", "File")); -std_type!(FILE_TYPE, FileType); + { + type_ref.write().unwrap().ns_mut().extend(&[ + // Class Methods ------------------------------------------- + meth!("new", type_ref, &["file_name"], "", |_, args| { + let arg = use_arg!(args, 0); + if let Some(file_name) = arg.get_str_val() { + let path = Path::new(file_name); + if path.is_file() { + new::file(file_name) + } else { + new::file_not_found_err(file_name, new::nil()) + } + } else { + let message = + format!("File.new(file_name) expected string; got {arg}"); + new::arg_err(message, new::nil()) + } + }), + // Instance Attributes ------------------------------------- + prop!("text", type_ref, "", |this, _| { + let this = this.read().unwrap(); + let this = this.down_to_file().unwrap(); + this.text() + }), + prop!("lines", type_ref, "", |this, _| { + let this = this.read().unwrap(); + let this = &mut this.down_to_file().unwrap(); + this.lines() + }), + ]); + } + type_ref +}); pub struct File { ns: Namespace, diff --git a/feint-builtins/src/types/float.rs b/feint-builtins/src/types/float.rs index b0182f6..69cfabf 100644 --- a/feint-builtins/src/types/float.rs +++ b/feint-builtins/src/types/float.rs @@ -15,32 +15,32 @@ use super::class::Type; use super::ns::Namespace; use super::util::{eq_int_float, float_gt_int, float_lt_int}; -std_type!(FLOAT_TYPE, FloatType); - -// pub fn make_float_type() -> obj_ref_t!(FloatType) { -// let type_ref = obj_ref!(FloatType::new()); -// let mut type_obj = type_ref.write().unwrap(); -// -// type_obj.add_attrs(&[ -// // Class Methods ----------------------------------------------- -// meth!("new", type_ref, &["value"], "", |this, args| { -// let arg = use_arg!(args, 0); -// let float = if let Some(val) = arg.get_float_val() { -// new::float(*val) -// } else if let Some(val) = arg.get_int_val() { -// new::float(val.to_f64().unwrap()) -// } else if let Some(val) = arg.get_str_val() { -// new::float_from_string(val) -// } else { -// let msg = format!("Float.new() expected string or float; got {arg}"); -// new::type_err(msg, this) -// }; -// float -// }), -// ]); -// -// type_ref.clone() -// } +pub static FLOAT_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(Type::new("std", "Float")); + + { + type_ref.write().unwrap().ns_mut().extend(&[ + // Class Methods ----------------------------------------------- + meth!("new", type_ref, &["value"], "", |this, args| { + let arg = use_arg!(args, 0); + let float = if let Some(val) = arg.get_float_val() { + new::float(*val) + } else if let Some(val) = arg.get_int_val() { + new::float(val.to_f64().unwrap()) + } else if let Some(val) = arg.get_str_val() { + new::float_from_string(val) + } else { + let msg = + format!("Float.new() expected string or float; got {arg}"); + new::type_err(msg, this) + }; + float + }), + ]); + } + + type_ref +}); macro_rules! make_op { ( $meth:ident, $op:tt, $message:literal, $trunc:literal ) => { diff --git a/feint-builtins/src/types/int.rs b/feint-builtins/src/types/int.rs index 461fc87..adad366 100644 --- a/feint-builtins/src/types/int.rs +++ b/feint-builtins/src/types/int.rs @@ -16,40 +16,31 @@ use super::class::Type; use super::ns::Namespace; use super::util::{eq_int_float, int_gt_float, int_lt_float}; -std_type!(INT_TYPE, IntType); - -// pub fn make_int_type() -> obj_ref_t!(IntType) { -// eprintln!("make_int_type 1"); -// let type_ref = obj_ref!(IntType::new()); -// -// eprintln!("make_int_type 2"); -// { -// eprintln!("make_int_type 2.a"); -// let mut type_obj = type_ref.write().unwrap(); -// -// eprintln!("make_int_type 2.b"); -// type_obj.add_attrs(&[ -// // Class Methods ----------------------------------------------- -// meth!("new", type_ref, &["value"], "", |this, args| { -// let arg = use_arg!(args, 0); -// let int = if let Some(val) = arg.get_int_val() { -// new::int(val.clone()) -// } else if let Some(val) = arg.get_float_val() { -// new::int(BigInt::from_f64(*val).unwrap()) -// } else if let Some(val) = arg.get_str_val() { -// new::int_from_string(val) -// } else { -// let msg = format!("Int.new() expected number or string; got {arg}"); -// new::type_err(msg, this) -// }; -// int -// }), -// ]); -// } -// -// eprintln!("make_int_type 3"); -// type_ref -// } +pub static INT_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(Type::new("std", "Int")); + + { + type_ref.write().unwrap().ns_mut().extend(&[ + // Class Methods ------------------------------------------- + meth!("new", type_ref, &["value"], "", |this, args| { + let arg = use_arg!(args, 0); + let int = if let Some(val) = arg.get_int_val() { + new::int(val.clone()) + } else if let Some(val) = arg.get_float_val() { + new::int(BigInt::from_f64(*val).unwrap()) + } else if let Some(val) = arg.get_str_val() { + new::int_from_string(val) + } else { + let msg = format!("Int.new() expected number or string; got {arg}"); + new::type_err(msg, this) + }; + int + }), + ]); + } + + type_ref +}); macro_rules! make_op { ( $meth:ident, $op:tt ) => { diff --git a/feint-builtins/src/types/intrinsic_func.rs b/feint-builtins/src/types/intrinsic_func.rs index 5537b8f..b0b84ae 100644 --- a/feint-builtins/src/types/intrinsic_func.rs +++ b/feint-builtins/src/types/intrinsic_func.rs @@ -44,7 +44,6 @@ impl IntrinsicFunc { func: IntrinsicFn, ) -> Self { let params_tuple = new::tuple(params.iter().map(new::str).collect()); - eprintln!("new"); let mut instance = Self { ns: Namespace::with_entries(&[ diff --git a/feint-builtins/src/types/iterator.rs b/feint-builtins/src/types/iterator.rs index 14d754f..177872b 100644 --- a/feint-builtins/src/types/iterator.rs +++ b/feint-builtins/src/types/iterator.rs @@ -13,28 +13,27 @@ use super::base::{ObjectRef, ObjectTrait, TypeRef}; use super::class::Type; use super::ns::Namespace; -std_type!(ITERATOR_TYPE, IteratorType); - -// pub fn make_iterator_type() -> obj_ref_t!(IteratorType) { -// let type_ref = obj_ref!(IteratorType::new()); -// let mut type_obj = type_ref.write().unwrap(); -// -// type_obj.add_attrs(&[ -// // Instance Methods -------------------------------------------- -// meth!("next", type_ref, &[], "", |this, _| { -// let mut this = this.write().unwrap(); -// let this = this.down_to_iterator_mut().unwrap(); -// this.next() -// }), -// meth!("peek", type_ref, &[], "", |this, _| { -// let this = this.write().unwrap(); -// let this = this.down_to_iterator().unwrap(); -// this.peek() -// }), -// ]); -// -// type_ref.clone() -// } +pub static ITERATOR_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(Type::new("std", "Iterator")); + + { + type_ref.write().unwrap().ns_mut().extend(&[ + // Instance Methods ---------------------------------------- + meth!("next", type_ref, &[], "", |this, _| { + let mut this = this.write().unwrap(); + let this = this.down_to_iterator_mut().unwrap(); + this.next() + }), + meth!("peek", type_ref, &[], "", |this, _| { + let this = this.write().unwrap(); + let this = this.down_to_iterator().unwrap(); + this.peek() + }), + ]); + } + + type_ref +}); pub struct FIIterator { ns: Namespace, diff --git a/feint-builtins/src/types/list.rs b/feint-builtins/src/types/list.rs index 03e204b..0ab5440 100644 --- a/feint-builtins/src/types/list.rs +++ b/feint-builtins/src/types/list.rs @@ -14,93 +14,98 @@ use super::class::Type; use super::ns::Namespace; use super::seq; -std_type!(LIST_TYPE, ListType); - -// pub fn make_list_type() -> obj_ref_t!(ListType) { -// let type_ref = obj_ref!(ListType::new()); -// let mut type_obj = type_ref.write().unwrap(); -// -// type_obj.add_attrs(&[ -// // Instance Attributes ----------------------------------------- -// prop!("length", type_ref, "", |this, _| { -// let this = this.read().unwrap(); -// let this = this.down_to_list().unwrap(); -// new::int(this.len()) -// }), -// prop!("is_empty", type_ref, "", |this, _| { -// let this = this.read().unwrap(); -// let this = this.down_to_list().unwrap(); -// new::bool(this.len() == 0) -// }), -// prop!("sum", type_ref, "", |this, _| { -// let this = this.read().unwrap(); -// let this = this.down_to_list().unwrap(); -// let items = &this.items.read().unwrap(); -// seq::sum(items) -// }), -// // Instance Methods -------------------------------------------- -// meth!( -// "extend", -// type_ref, -// &["items"], -// "Push items and return this.", -// |this, args| { -// let return_val = this.clone(); -// let this = this.read().unwrap(); -// let this = this.down_to_list().unwrap(); -// if let Some(err) = this.extend(args[0].clone()) { -// return err; -// } -// return_val -// } -// ), -// meth!("get", type_ref, &["index"], "", |this, args| { -// let this = this.read().unwrap(); -// let this = this.down_to_list().unwrap(); -// let index = use_arg_usize!(get, index, args, 0); -// let result = match this.get(index) { -// Some(obj) => obj, -// None => new::nil(), -// }; -// result -// }), -// meth!("has", type_ref, &["member"], "", |this, args| { -// let this = this.read().unwrap(); -// let this = this.down_to_list().unwrap(); -// let items = &this.items.read().unwrap(); -// seq::has(items, &args) -// }), -// meth!("iter", type_ref, &[], "", |this_ref, _| { -// let this = this_ref.read().unwrap(); -// let this = this.down_to_list().unwrap(); -// let items = this.items.read().unwrap(); -// new::iterator(items.clone()) -// }), -// meth!("join", type_ref, &["sep"], "", |this, args| { -// let this = this.read().unwrap(); -// let this = this.down_to_list().unwrap(); -// let items = &this.items.read().unwrap(); -// seq::join(items, &args) -// }), -// meth!("pop", type_ref, &[], "", |this, _| { -// let this = this.read().unwrap(); -// let this = this.down_to_list().unwrap(); -// match this.pop() { -// Some(obj) => obj, -// None => new::nil(), -// } -// }), -// meth!("push", type_ref, &["item"], "Push item and return it.", |this, args| { -// let this = this.read().unwrap(); -// let this = this.down_to_list().unwrap(); -// let arg = args[0].clone(); -// this.push(arg.clone()); -// arg -// }), -// ]); -// -// type_ref.clone() -// } +pub static LIST_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(Type::new("std", "List")); + + { + type_ref.write().unwrap().ns_mut().extend(&[ + // Instance Attributes ------------------------------------- + prop!("length", type_ref, "", |this, _| { + let this = this.read().unwrap(); + let this = this.down_to_list().unwrap(); + new::int(this.len()) + }), + prop!("is_empty", type_ref, "", |this, _| { + let this = this.read().unwrap(); + let this = this.down_to_list().unwrap(); + new::bool(this.len() == 0) + }), + prop!("sum", type_ref, "", |this, _| { + let this = this.read().unwrap(); + let this = this.down_to_list().unwrap(); + let items = &this.items.read().unwrap(); + seq::sum(items) + }), + // Instance Methods ---------------------------------------- + meth!( + "extend", + type_ref, + &["items"], + "Push items and return this.", + |this, args| { + let return_val = this.clone(); + let this = this.read().unwrap(); + let this = this.down_to_list().unwrap(); + if let Some(err) = this.extend(args[0].clone()) { + return err; + } + return_val + } + ), + meth!("get", type_ref, &["index"], "", |this, args| { + let this = this.read().unwrap(); + let this = this.down_to_list().unwrap(); + let index = use_arg_usize!(get, index, args, 0); + let result = match this.get(index) { + Some(obj) => obj, + None => new::nil(), + }; + result + }), + meth!("has", type_ref, &["member"], "", |this, args| { + let this = this.read().unwrap(); + let this = this.down_to_list().unwrap(); + let items = &this.items.read().unwrap(); + seq::has(items, &args) + }), + meth!("iter", type_ref, &[], "", |this_ref, _| { + let this = this_ref.read().unwrap(); + let this = this.down_to_list().unwrap(); + let items = this.items.read().unwrap(); + new::iterator(items.clone()) + }), + meth!("join", type_ref, &["sep"], "", |this, args| { + let this = this.read().unwrap(); + let this = this.down_to_list().unwrap(); + let items = &this.items.read().unwrap(); + seq::join(items, &args) + }), + meth!("pop", type_ref, &[], "", |this, _| { + let this = this.read().unwrap(); + let this = this.down_to_list().unwrap(); + match this.pop() { + Some(obj) => obj, + None => new::nil(), + } + }), + meth!( + "push", + type_ref, + &["item"], + "Push item and return it.", + |this, args| { + let this = this.read().unwrap(); + let this = this.down_to_list().unwrap(); + let arg = args[0].clone(); + this.push(arg.clone()); + arg + } + ), + ]); + } + + type_ref +}); std_type!(NIL_TYPE, NilType); diff --git a/feint-builtins/src/types/map.rs b/feint-builtins/src/types/map.rs index f7f748d..9421ffe 100644 --- a/feint-builtins/src/types/map.rs +++ b/feint-builtins/src/types/map.rs @@ -13,99 +13,98 @@ use super::base::{ObjectRef, ObjectTrait, TypeRef}; use super::class::Type; use super::ns::Namespace; -std_type!(MAP_TYPE, MapType); - -// pub fn make_map_type() -> obj_ref_t!(MapType) { -// let type_ref = obj_ref!(MapType::new()); -// let mut type_obj = type_ref.write().unwrap(); -// -// type_obj.add_attrs(&[ -// // Instance Attributes ----------------------------------------- -// prop!("length", type_ref, "", |this, _| { -// let this = this.read().unwrap(); -// let this = this.down_to_map().unwrap(); -// BUILTINS.int(this.len()) -// }), -// prop!("is_empty", type_ref, "", |this, _| { -// let this = this.read().unwrap(); -// let this = this.down_to_map().unwrap(); -// BUILTINS.bool(this.is_empty()) -// }), -// // Instance Methods -------------------------------------------- -// meth!( -// "add", -// type_ref, -// &["key", "val"], -// "Add entry to Map. -// -// # Args -// -// - key: Str -// - value: Any -// -// ", -// |this, args| { -// let this = this.read().unwrap(); -// let this = this.down_to_map().unwrap(); -// let arg = use_arg!(args, 0); -// let key = use_arg_str!(get, key, arg); -// let val = args[1].clone(); -// this.insert(key, val); -// BUILTINS.nil() -// } -// ), -// meth!( -// "get", -// type_ref, -// &["key"], -// "Get value for key from Map. -// -// # Args -// -// - key: Key -// -// # Returns -// -// - Any: If key is present -// - nil: If key is not present -// -// > NOTE: There's no way to distinguish between a key that isn't present -// > versus a key that has `nil` as its value. To avoid ambiguity, don't -// > store `nil` values. -// -// ", -// |this, args| { -// let this = this.read().unwrap(); -// let this = this.down_to_map().unwrap(); -// let arg = use_arg!(args, 0); -// let key = use_arg_str!(get, key, arg); -// match this.get(key) { -// Some(obj) => obj, -// None => BUILTINS.nil(), -// } -// } -// ), -// meth!("has", type_ref, &["member"], "", |this, args| { -// let this = this.read().unwrap(); -// let this = this.down_to_map().unwrap(); -// let arg = use_arg!(args, 0); -// let key = use_arg_str!(get, key, arg); -// let result = this.contains_key(key); -// BUILTINS.bool(result) -// }), -// meth!("iter", type_ref, &[], "", |this_ref, _| { -// let this = this_ref.read().unwrap(); -// let this = this.down_to_map().unwrap(); -// let mut items = vec![]; -// for (name, val) in this.entries.read().unwrap().iter() { -// items.push(BUILTINS.tuple(vec![BUILTINS.str(name), val.clone()])) -// } -// BUILTINS.iterator(items) -// }), -// ]); -// -// type_ref.clone() -// } +pub static MAP_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(Type::new("std", "Map")); + + { + type_ref.write().unwrap().ns_mut().extend(&[ + // Instance Attributes ------------------------------------- + prop!("length", type_ref, "", |this, _| { + let this = this.read().unwrap(); + let this = this.down_to_map().unwrap(); + new::int(this.len()) + }), + prop!("is_empty", type_ref, "", |this, _| { + let this = this.read().unwrap(); + let this = this.down_to_map().unwrap(); + new::bool(this.is_empty()) + }), + // Instance Methods ---------------------------------------- + meth!( + "add", + type_ref, + &["key", "val"], + "Add entry to Map. + + # Args + + - key: Str + - value: Any + + ", + |this, args| { + let this = this.read().unwrap(); + let this = this.down_to_map().unwrap(); + let arg = use_arg!(args, 0); + let key = use_arg_str!(get, key, arg); + let val = args[1].clone(); + this.insert(key, val); + new::nil() + } + ), + meth!( + "get", + type_ref, + &["key"], + "Get value for key from Map. + + # Args + + - key: Key + + # Returns + + - Any: If key is present + - nil: If key is not present + + > NOTE: There's no way to distinguish between a key that isn't present + > versus a key that has `nil` as its value. To avoid ambiguity, don't + > store `nil` values. + + ", + |this, args| { + let this = this.read().unwrap(); + let this = this.down_to_map().unwrap(); + let arg = use_arg!(args, 0); + let key = use_arg_str!(get, key, arg); + match this.get(key) { + Some(obj) => obj, + None => new::nil(), + } + } + ), + meth!("has", type_ref, &["member"], "", |this, args| { + let this = this.read().unwrap(); + let this = this.down_to_map().unwrap(); + let arg = use_arg!(args, 0); + let key = use_arg_str!(get, key, arg); + let result = this.contains_key(key); + new::bool(result) + }), + meth!("iter", type_ref, &[], "", |this_ref, _| { + let this = this_ref.read().unwrap(); + let this = this.down_to_map().unwrap(); + let mut items = vec![]; + for (name, val) in this.entries.read().unwrap().iter() { + items.push(new::tuple(vec![new::str(name), val.clone()])) + } + new::iterator(items) + }), + ]); + } + + type_ref +}); // Map ---------------------------------------------------------- diff --git a/feint-builtins/src/types/module.rs b/feint-builtins/src/types/module.rs index 6938c1f..6b4f5c2 100644 --- a/feint-builtins/src/types/module.rs +++ b/feint-builtins/src/types/module.rs @@ -16,58 +16,56 @@ use super::map::Map; use super::ns::Namespace; use crate::new; -std_type!(MODULE_TYPE, ModuleType); - -// pub fn make_module_type() -> obj_ref_t!(ModuleType) { -// let type_ref = obj_ref!(ModuleType::new()); -// let mut type_obj = type_ref.write().unwrap(); -// -// type_obj.add_attrs(&[meth!( -// "new", -// type_ref, -// &["name", "path", "doc", "attrs"], -// "Create a new Module -// -// # Args -// -// - name: Str -// - path: Str -// - doc: Str -// - attrs: Map -// -// # Returns -// -// Module", -// |_, args| { -// if let Err(err) = check_args("new", &args, false, 4, Some(4)) { -// return err; -// }; -// -// let name_arg = use_arg!(args, 0); -// let path_arg = use_arg!(args, 1); -// let doc_arg = use_arg!(args, 2); -// let attrs_arg = use_arg!(args, 3); -// -// let name = use_arg_str!(new, name, name_arg); -// let path = use_arg_str!(new, path, path_arg); -// let doc = use_arg_str!(new, doc, doc_arg); -// let attrs = use_arg_map!(new, attrs, attrs_arg); -// -// let module = Module::with_map_entries( -// new::module_type(), -// attrs, -// name.to_owned(), -// path.to_owned(), -// Code::default(), -// Some(doc.to_owned()), -// ); -// -// obj_ref!(module) -// } -// )]); -// -// type_ref.clone() -// } +pub static MODULE_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(Type::new("std", "Module")); + + { + type_ref.write().unwrap().ns_mut().extend(&[meth!( + "new", + type_ref, + &["name", "path", "doc", "attrs"], + "Create a new Module + + # Args + + - name: Str + - path: Str + - doc: Str + - attrs: Map + + # Returns + + Module", + |_, args| { + if let Err(err) = check_args("new", &args, false, 4, Some(4)) { + return err; + }; + + let name_arg = use_arg!(args, 0); + let path_arg = use_arg!(args, 1); + let doc_arg = use_arg!(args, 2); + let attrs_arg = use_arg!(args, 3); + + let name = use_arg_str!(new, name, name_arg); + let path = use_arg_str!(new, path, path_arg); + let doc = use_arg_str!(new, doc, doc_arg); + let attrs = use_arg_map!(new, attrs, attrs_arg); + + let module = Module::with_map_entries( + attrs, + name.to_owned(), + path.to_owned(), + Code::default(), + Some(doc.to_owned()), + ); + + obj_ref!(module) + } + )]); + } + + type_ref +}); // Module ------------------------------------------------------- @@ -87,13 +85,10 @@ impl Module { /// attribute initialized from their module level docstring. pub fn new(name: String, path: String, code: Code, doc: Option) -> Self { let ns = Namespace::with_entries(&[ - // ("$full_name", new::str(name.as_str())), - // ("$name", new::str(name.as_str())), - // ("$path", new::str(path.as_str())), - // ( - // "$doc", - // if let Some(doc) = doc { new::str(doc) } else { code.get_doc() }, - // ), + ("$full_name", new::str(name.as_str())), + ("$name", new::str(name.as_str())), + ("$path", new::str(path.as_str())), + ("$doc", if let Some(doc) = doc { new::str(doc) } else { code.get_doc() }), ]); Self { ns, path, name, code } } diff --git a/feint-builtins/src/types/str.rs b/feint-builtins/src/types/str.rs index 17920eb..4754b23 100644 --- a/feint-builtins/src/types/str.rs +++ b/feint-builtins/src/types/str.rs @@ -13,106 +13,105 @@ use super::base::{ObjectRef, ObjectTrait, TypeRef}; use super::class::Type; use super::ns::Namespace; -std_type!(STR_TYPE, StrType); - -// pub(crate) fn make_str_type() -> obj_ref_t!(StrType) { -// let type_ref = obj_ref!(StrType::new()); -// let mut type_obj = type_ref.write().unwrap(); -// -// type_obj.add_attrs(&[ -// // Class Methods ----------------------------------------------- -// meth!("new", type_ref, &["value"], "", |_, args| { -// let arg = use_arg!(args, 0); -// if arg.is_str() { -// args[0].clone() -// } else { -// new::str(arg.to_string()) -// } -// }), -// // Instance Attributes ----------------------------------------- -// prop!("length", type_ref, "", |this, _| { -// let this = this.read().unwrap(); -// let value = this.get_str_val().unwrap(); -// new::int(value.len()) -// }), -// // Instance Methods -------------------------------------------- -// meth!("starts_with", type_ref, &["prefix"], "", |this, args| { -// let this = this.read().unwrap(); -// let value = this.get_str_val().unwrap(); -// let arg = use_arg!(args, 0); -// let prefix = use_arg_str!(starts_with, prefix, arg); -// new::bool(value.starts_with(prefix)) -// }), -// meth!("ends_with", type_ref, &["suffix"], "", |this, args| { -// let this = this.read().unwrap(); -// let value = this.get_str_val().unwrap(); -// let arg = use_arg!(args, 0); -// let suffix = use_arg_str!(ends_with, suffix, arg); -// new::bool(value.ends_with(suffix)) -// }), -// meth!("upper", type_ref, &[], "", |this, _| { -// let this = this.read().unwrap(); -// let value = this.get_str_val().unwrap(); -// new::str(value.to_uppercase()) -// }), -// meth!("lower", type_ref, &[], "", |this, _| { -// let this = this.read().unwrap(); -// let value = this.get_str_val().unwrap(); -// new::str(value.to_lowercase()) -// }), -// // meth!( -// // "render", -// // type_ref, -// // &["context"], -// // "Render string as template -// // -// // Templates may contain `{{ name }}` vars which will be replaced with the -// // values provided in the context map. -// // -// // # Args -// // -// // - context: Map A map containing values to be rendered into the -// // template. -// // -// // ", -// // |this, args| { -// // let context = args[0].clone(); -// // let result = render_template(this.clone(), context)?; -// // Ok(result) -// // } -// // ), -// meth!("repeat", type_ref, &["count"], "", |this, args| { -// let this = this.read().unwrap(); -// let value = this.get_str_val().unwrap(); -// let count = use_arg_usize!(get, index, args, 0); -// new::str(value.repeat(count)) -// }), -// meth!("replace", type_ref, &["old", "new"], "", |this, args| { -// let this = this.read().unwrap(); -// let value = this.get_str_val().unwrap(); -// let arg1 = use_arg!(args, 0); -// let arg2 = use_arg!(args, 1); -// let old = use_arg_str!(replace, old, arg1); -// let new = use_arg_str!(replace, new, arg2); -// let result = value.replace(old, new); -// new::str(result) -// }), -// meth!("remove_prefix", type_ref, &["prefix"], "", |this_ref, args| { -// let this = this_ref.read().unwrap(); -// let val = this.get_str_val().unwrap(); -// let arg = use_arg!(args, 0); -// let prefix = use_arg_str!(starts_with, prefix, arg); -// if let Some(new_val) = val.strip_prefix(prefix) { -// new::str(new_val) -// } else { -// drop(this); -// this_ref -// } -// }), -// ]); -// -// type_ref.clone() -// } +pub static STR_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(Type::new("std", "Str")); + + { + type_ref.write().unwrap().ns_mut().extend(&[ + // Class Methods ----------------------------------------------- + meth!("new", type_ref, &["value"], "", |_, args| { + let arg = use_arg!(args, 0); + if arg.is_str() { + args[0].clone() + } else { + new::str(arg.to_string()) + } + }), + // Instance Attributes ------------------------------------- + prop!("length", type_ref, "", |this, _| { + let this = this.read().unwrap(); + let value = this.get_str_val().unwrap(); + new::int(value.len()) + }), + // Instance Methods ---------------------------------------- + meth!("starts_with", type_ref, &["prefix"], "", |this, args| { + let this = this.read().unwrap(); + let value = this.get_str_val().unwrap(); + let arg = use_arg!(args, 0); + let prefix = use_arg_str!(starts_with, prefix, arg); + new::bool(value.starts_with(prefix)) + }), + meth!("ends_with", type_ref, &["suffix"], "", |this, args| { + let this = this.read().unwrap(); + let value = this.get_str_val().unwrap(); + let arg = use_arg!(args, 0); + let suffix = use_arg_str!(ends_with, suffix, arg); + new::bool(value.ends_with(suffix)) + }), + meth!("upper", type_ref, &[], "", |this, _| { + let this = this.read().unwrap(); + let value = this.get_str_val().unwrap(); + new::str(value.to_uppercase()) + }), + meth!("lower", type_ref, &[], "", |this, _| { + let this = this.read().unwrap(); + let value = this.get_str_val().unwrap(); + new::str(value.to_lowercase()) + }), + // meth!( + // "render", + // type_ref, + // &["context"], + // "Render string as template + // + // Templates may contain `{{ name }}` vars which will be replaced with the + // values provided in the context map. + // + // # Args + // + // - context: Map A map containing values to be rendered into the + // template. + // + // ", + // |this, args| { + // let context = args[0].clone(); + // let result = render_template(this.clone(), context)?; + // Ok(result) + // } + // ), + meth!("repeat", type_ref, &["count"], "", |this, args| { + let this = this.read().unwrap(); + let value = this.get_str_val().unwrap(); + let count = use_arg_usize!(get, index, args, 0); + new::str(value.repeat(count)) + }), + meth!("replace", type_ref, &["old", "new"], "", |this, args| { + let this = this.read().unwrap(); + let value = this.get_str_val().unwrap(); + let arg1 = use_arg!(args, 0); + let arg2 = use_arg!(args, 1); + let old = use_arg_str!(replace, old, arg1); + let new = use_arg_str!(replace, new, arg2); + let result = value.replace(old, new); + new::str(result) + }), + meth!("remove_prefix", type_ref, &["prefix"], "", |this_ref, args| { + let this = this_ref.read().unwrap(); + let val = this.get_str_val().unwrap(); + let arg = use_arg!(args, 0); + let prefix = use_arg_str!(starts_with, prefix, arg); + if let Some(new_val) = val.strip_prefix(prefix) { + new::str(new_val) + } else { + drop(this); + this_ref + } + }), + ]); + } + + type_ref +}); // Str ---------------------------------------------------------- diff --git a/feint-builtins/src/types/tuple.rs b/feint-builtins/src/types/tuple.rs index 0256492..c21575f 100644 --- a/feint-builtins/src/types/tuple.rs +++ b/feint-builtins/src/types/tuple.rs @@ -14,58 +14,57 @@ use super::class::Type; use super::ns::Namespace; use super::seq; -std_type!(TUPLE_TYPE, TupleType); - -// pub fn make_tuple_type() -> obj_ref_t!(TupleType) { -// let type_ref = obj_ref!(TupleType::new()); -// let mut type_obj = type_ref.write().unwrap(); -// -// type_obj.add_attrs(&[ -// // Instance Attributes ----------------------------------------- -// prop!("length", type_ref, "", |this, _| { -// let this = this.read().unwrap(); -// let this = this.down_to_tuple().unwrap(); -// new::int(this.len()) -// }), -// prop!("is_empty", type_ref, "", |this, _| { -// let this = this.read().unwrap(); -// let this = this.down_to_tuple().unwrap(); -// new::bool(this.len() == 0) -// }), -// prop!("sum", type_ref, "", |this, _| { -// let this = this.read().unwrap(); -// let this = this.down_to_tuple().unwrap(); -// seq::sum(&this.items) -// }), -// // Instance Methods -------------------------------------------- -// meth!("get", type_ref, &["index"], "", |this, args| { -// let this = this.read().unwrap(); -// let this = this.down_to_tuple().unwrap(); -// let index = use_arg_usize!(get, index, args, 0); -// match this.get(index) { -// Some(obj) => obj, -// None => new::nil(), -// } -// }), -// meth!("has", type_ref, &["member"], "", |this, args| { -// let this = this.read().unwrap(); -// let this = this.down_to_tuple().unwrap(); -// seq::has(&this.items, &args) -// }), -// meth!("iter", type_ref, &[], "", |this_ref, _| { -// let this = this_ref.read().unwrap(); -// let this = this.down_to_tuple().unwrap(); -// new::iterator(this.items.clone()) -// }), -// meth!("join", type_ref, &["sep"], "", |this, args| { -// let this = this.read().unwrap(); -// let this = this.down_to_tuple().unwrap(); -// seq::join(&this.items, &args) -// }), -// ]); -// -// type_ref.clone() -// } +pub static TUPLE_TYPE: Lazy = Lazy::new(|| { + let type_ref = obj_ref!(Type::new("std", "Tuple")); + + { + type_ref.write().unwrap().ns_mut().extend(&[ + // Instance Attributes ------------------------------------- + prop!("length", type_ref, "", |this, _| { + let this = this.read().unwrap(); + let this = this.down_to_tuple().unwrap(); + new::int(this.len()) + }), + prop!("is_empty", type_ref, "", |this, _| { + let this = this.read().unwrap(); + let this = this.down_to_tuple().unwrap(); + new::bool(this.len() == 0) + }), + prop!("sum", type_ref, "", |this, _| { + let this = this.read().unwrap(); + let this = this.down_to_tuple().unwrap(); + seq::sum(&this.items) + }), + // Instance Methods ---------------------------------------- + meth!("get", type_ref, &["index"], "", |this, args| { + let this = this.read().unwrap(); + let this = this.down_to_tuple().unwrap(); + let index = use_arg_usize!(get, index, args, 0); + match this.get(index) { + Some(obj) => obj, + None => new::nil(), + } + }), + meth!("has", type_ref, &["member"], "", |this, args| { + let this = this.read().unwrap(); + let this = this.down_to_tuple().unwrap(); + seq::has(&this.items, &args) + }), + meth!("iter", type_ref, &[], "", |this_ref, _| { + let this = this_ref.read().unwrap(); + let this = this.down_to_tuple().unwrap(); + new::iterator(this.items.clone()) + }), + meth!("join", type_ref, &["sep"], "", |this, args| { + let this = this.read().unwrap(); + let this = this.down_to_tuple().unwrap(); + seq::join(&this.items, &args) + }), + ]); + } + + type_ref +}); // Tuple -------------------------------------------------------- diff --git a/feint-vm/src/tests.rs b/feint-vm/src/tests.rs index 3812138..328420f 100644 --- a/feint-vm/src/tests.rs +++ b/feint-vm/src/tests.rs @@ -1,8 +1,8 @@ +use feint_builtins::new; use feint_builtins::types::{ code::{Code, Inst}, Module, }; -use feint_builtins::BUILTINS; use feint_util::op::BinaryOperator; use crate::*; @@ -14,15 +14,9 @@ fn execute_simple_program() { Inst::LoadConst(1), Inst::BinaryOp(BinaryOperator::Add), ]); - code.add_const(BUILTINS.int(1)); - code.add_const(BUILTINS.int(2)); - let module = Module::new( - BUILTINS.module_type(), - "test".to_owned(), - "test".to_owned(), - code, - None, - ); + code.add_const(new::int(1)); + code.add_const(new::int(2)); + let module = Module::new("test".to_owned(), "test".to_owned(), code, None); let mut vm = VM::default(); assert!(matches!(vm.execute_module(&module, 0), Ok(()))); assert!(matches!(vm.state, VMState::Idle(Some(_)))); From 2654f2a06c8396b5ec9785a9ee1a39cd3ae6a8cb Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Sun, 5 Feb 2023 02:08:32 -0800 Subject: [PATCH 29/31] WIP --- feint-builtins/src/modules/mod.rs | 2 +- feint-builtins/src/modules/std/std.rs | 17 +++--- feint-builtins/src/new.rs | 29 +++------- feint-builtins/src/tests/types.rs | 79 ++++++++++++++------------- feint-builtins/src/types/base.rs | 14 +++-- feint-builtins/src/types/ns.rs | 6 ++ 6 files changed, 72 insertions(+), 75 deletions(-) diff --git a/feint-builtins/src/modules/mod.rs b/feint-builtins/src/modules/mod.rs index cfd7f04..ae8b25f 100644 --- a/feint-builtins/src/modules/mod.rs +++ b/feint-builtins/src/modules/mod.rs @@ -20,7 +20,7 @@ pub use self::std::STD; /// modules in Rust code (e.g., in the VM). pub static MODULES: Lazy = Lazy::new(|| obj_ref!(Map::default())); -/// Add module to `std.system.modules`. +/// Add module to `system.modules`. pub fn add_module(name: &str, module: ObjectRef) { let modules = MODULES.write().unwrap(); let modules = modules.down_to_map().unwrap(); diff --git a/feint-builtins/src/modules/std/std.rs b/feint-builtins/src/modules/std/std.rs index ed3a8b7..70017af 100644 --- a/feint-builtins/src/modules/std/std.rs +++ b/feint-builtins/src/modules/std/std.rs @@ -3,7 +3,7 @@ use std::sync::{Arc, RwLock}; use once_cell::sync::Lazy; -use feint_code_gen::obj_ref_t; +use feint_code_gen::{obj_ref_t, use_arg, use_arg_str}; use crate::{new, types}; @@ -47,14 +47,13 @@ pub static STD: Lazy = Lazy::new(|| { - name: Str ", - |_, _args| { - // TODO: - // let module = args[0].clone(); - // let name_arg = use_arg!(args, 1); - // let name = use_arg_str!(new_type, name, name_arg); - // let class = new::custom_type(module, name); - // class - new::nil() + |_, args| { + let module_arg = use_arg!(args, 0); + let module = use_arg_str!(new_type, module_name, module_arg); + let name_arg = use_arg!(args, 1); + let name = use_arg_str!(new_type, name, name_arg); + let class = new::custom_type(module, name); + class }, ), ), diff --git a/feint-builtins/src/new.rs b/feint-builtins/src/new.rs index d3dc36e..89c4805 100644 --- a/feint-builtins/src/new.rs +++ b/feint-builtins/src/new.rs @@ -21,7 +21,7 @@ use crate::types::cell::Cell; use crate::types::class::Type; use crate::types::closure::Closure; use crate::types::code::Code; -// use crate::types::custom::CustomObj; +use crate::types::custom::CustomObj; use crate::types::err::ErrObj; use crate::types::err_type::ErrKind; use crate::types::file::File; @@ -325,30 +325,15 @@ pub fn custom_type(module_name: &str, name: &str) -> ObjectRef { - attributes: Map ", - |_this_ref, args| { + |this_ref, args| { + let type_obj = this_ref.read().unwrap(); let attrs_arg = args.get(0).unwrap(); let attrs_arg = attrs_arg.read().unwrap(); let attrs = attrs_arg.down_to_map().unwrap(); - - let mut ns = Namespace::default(); - ns.extend_from_map(attrs); - - // TODO: - // let type_obj = this_ref.read().unwrap(); - // let class = if type_obj.is_type() { - // // Called via custom type. - // this_ref.clone() - // } else { - // // Called via custom instance. - // // XXX: This branch isn't reachable because the - // // VM will panic due to the identity test - // // issue noted above. - // type_obj.class() - // }; - - // let instance = CustomObj::new(class, ns); - // obj_ref!(instance) - nil() + obj_ref!(CustomObj::new( + type_obj.class(), + Namespace::from_map(attrs) + )) }, ), ); diff --git a/feint-builtins/src/tests/types.rs b/feint-builtins/src/tests/types.rs index a23595e..aec0296 100644 --- a/feint-builtins/src/tests/types.rs +++ b/feint-builtins/src/tests/types.rs @@ -106,6 +106,7 @@ mod list { } mod custom { + use crate::modules::add_module; use indexmap::IndexMap; use super::*; @@ -121,42 +122,44 @@ mod custom { new(type_obj.clone(), vec![attrs]) } - // #[test] - // fn test_custom() { - // let mod1 = new::module("test1", "", "test module 1", &[]); - // let t1 = new::custom_type(mod1, "Custom1"); - // let t1_obj1 = instance(t1.clone(), &[("value", new::nil())]); - // let t1_obj2 = instance(t1.clone(), &[("value", new::nil())]); - // let t1_obj3 = instance(t1.clone(), &[("value", new::nil())]); - // - // let mod2 = new::module("test2", "", "test module 2", &[]); - // let t2 = new::custom_type(mod2, "Custom2"); - // let t2_obj1 = instance(t2.clone(), &[]); - // - // check_attr(t1.clone(), "$id"); - // check_attr(t1.clone(), "$type"); - // check_attr(t1_obj1.clone(), "$id"); - // check_attr(t1_obj1.clone(), "$type"); - // - // let result = - // t1_obj3.write().unwrap().set_attr("value", new::int(1), t1_obj3.clone()); - // check_ok(result, "Could not set `value` on t1_obj3"); - // check_attr(t1_obj3.clone(), "value"); - // check_attr_eq(t1_obj3.clone(), "value", new::int(1)); - // - // // An object should be equal to itself. - // check_eq(t1_obj1.clone(), t1_obj1.clone()); - // - // // An object should be equal to an object of the SAME type with - // // the same attributes. - // check_eq(t1_obj1.clone(), t1_obj2.clone()); - // - // // An object should NOT be equal to an object of the SAME type with - // // the DIFFERENT attributes. - // check_ne(t1_obj1.clone(), t1_obj3.clone()); - // - // // An object should NOT be equal to an object of a DIFFERENT type, - // // regardless of attributes. - // check_ne(t1_obj1.clone(), t2_obj1.clone()); - // } + #[test] + fn test_custom() { + let mod1 = new::module("test1", "", "test module 1", &[]); + add_module("test1", mod1); + let t1 = new::custom_type("test1", "Custom1"); + let t1_obj1 = instance(t1.clone(), &[("value", new::nil())]); + let t1_obj2 = instance(t1.clone(), &[("value", new::nil())]); + let t1_obj3 = instance(t1.clone(), &[("value", new::nil())]); + + let mod2 = new::module("test2", "", "test module 2", &[]); + add_module("test2", mod2); + let t2 = new::custom_type("test2", "Custom2"); + let t2_obj1 = instance(t2.clone(), &[]); + + check_attr(t1.clone(), "$id"); + check_attr(t1.clone(), "$type"); + check_attr(t1_obj1.clone(), "$id"); + check_attr(t1_obj1.clone(), "$type"); + + let result = + t1_obj3.write().unwrap().set_attr("value", new::int(1), t1_obj3.clone()); + check_ok(result, "Could not set `value` on t1_obj3"); + check_attr(t1_obj3.clone(), "value"); + check_attr_eq(t1_obj3.clone(), "value", new::int(1)); + + // An object should be equal to itself. + check_eq(t1_obj1.clone(), t1_obj1.clone()); + + // An object should be equal to an object of the SAME type with + // the same attributes. + check_eq(t1_obj1.clone(), t1_obj2.clone()); + + // An object should NOT be equal to an object of the SAME type with + // the DIFFERENT attributes. + check_ne(t1_obj1.clone(), t1_obj3.clone()); + + // An object should NOT be equal to an object of a DIFFERENT type, + // regardless of attributes. + check_ne(t1_obj1.clone(), t2_obj1.clone()); + } } diff --git a/feint-builtins/src/types/base.rs b/feint-builtins/src/types/base.rs index e0843d8..fd43309 100644 --- a/feint-builtins/src/types/base.rs +++ b/feint-builtins/src/types/base.rs @@ -6,6 +6,7 @@ use std::sync::{Arc, RwLock}; use num_bigint::BigInt; use num_traits::ToPrimitive; +use crate::modules::get_module; use feint_code_gen::*; use crate::new; @@ -119,11 +120,14 @@ pub trait ObjectTrait { new::int(self.id()) } - /// XXX: This resolves to `std` unless overridden. fn module(&self) -> ObjectRef { - let class = self.class(); - let class = class.read().unwrap(); - class.module().clone() + if let Some(t) = self.down_to_type() { + get_module(t.module_name()) + } else { + let class = self.class(); + let class = class.read().unwrap(); + class.module() + } } // Attributes (accessed by name) ----------------------------------- @@ -150,7 +154,7 @@ pub trait ObjectTrait { } if name == "$module" { - return self.class().read().unwrap().module(); + return self.module(); } if name == "$type" { diff --git a/feint-builtins/src/types/ns.rs b/feint-builtins/src/types/ns.rs index 5f590e4..d5a197f 100644 --- a/feint-builtins/src/types/ns.rs +++ b/feint-builtins/src/types/ns.rs @@ -36,6 +36,12 @@ impl Namespace { ns } + pub fn from_map(map: &Map) -> Self { + let mut ns = Self::default(); + ns.extend_from_map(map); + ns + } + pub fn clear(&mut self) { self.objects.clear() } From 173d60f2893f945ccaf103b4df0922ff4b24cd3e Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Sun, 5 Feb 2023 02:42:48 -0800 Subject: [PATCH 30/31] WIP --- examples/conditionals.fi | 6 +++--- feint-builtins/src/types/base.rs | 12 ++++-------- feint-builtins/src/types/code.rs | 2 +- feint-builtins/src/types/tuple.rs | 2 -- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/examples/conditionals.fi b/examples/conditionals.fi index b271650..41c893c 100644 --- a/examples/conditionals.fi +++ b/examples/conditionals.fi @@ -41,11 +41,11 @@ else -> # Pattern matching obj = (1, 2) pat = @ -assert(obj == pat, $"{obj} != {pat}", true) +assert_eq(obj, pat, true) pat = (1, @) -assert(obj == pat, $"{obj} != {pat}", true) +assert_eq(obj, pat, true) pat = (@, 2) -assert(obj == pat, $"{obj} != {pat}", true) +assert_eq(obj, pat, true) # This just demonstrates that an if/else suite at the end of the file # doesn't cause an error. diff --git a/feint-builtins/src/types/base.rs b/feint-builtins/src/types/base.rs index fd43309..fb90fc2 100644 --- a/feint-builtins/src/types/base.rs +++ b/feint-builtins/src/types/base.rs @@ -6,9 +6,9 @@ use std::sync::{Arc, RwLock}; use num_bigint::BigInt; use num_traits::ToPrimitive; -use crate::modules::get_module; use feint_code_gen::*; +use crate::modules::get_module; use crate::new; use super::func_trait::FuncTrait; @@ -269,7 +269,7 @@ pub trait ObjectTrait { }; } - self.attr_not_found(name, this) + new::attr_not_found_err(name, this) } /// Set attribute. @@ -287,11 +287,7 @@ pub trait ObjectTrait { this: ObjectRef, ) -> ObjectRef { // TODO: The default should be a "does not support" attr access - self.attr_not_found(name, this) - } - - fn attr_not_found(&self, name: &str, obj: ObjectRef) -> ObjectRef { - new::attr_not_found_err(name, obj) + new::attr_not_found_err(name, this) } // Items (accessed by index) --------------------------------------- @@ -451,7 +447,7 @@ pub trait ObjectTrait { } fn is_equal(&self, rhs: &dyn ObjectTrait) -> bool { - self.is(rhs) || rhs.is_always() + self.is(rhs) || self.is_always() || rhs.is_always() } make_bin_op!(and, "&&", bool); diff --git a/feint-builtins/src/types/code.rs b/feint-builtins/src/types/code.rs index 21104e4..a086674 100644 --- a/feint-builtins/src/types/code.rs +++ b/feint-builtins/src/types/code.rs @@ -166,7 +166,7 @@ impl Code { for (index, other_ref) in self.iter_constants().enumerate() { let other = other_ref.read().unwrap(); let other_is_comparable = other.is_immutable() && !other.is_func(); - if is_comparable && other_is_comparable && other.is_equal(val) { + if is_comparable && other_is_comparable && other.is_type_equal(val) { return index; } } diff --git a/feint-builtins/src/types/tuple.rs b/feint-builtins/src/types/tuple.rs index c21575f..1e41d62 100644 --- a/feint-builtins/src/types/tuple.rs +++ b/feint-builtins/src/types/tuple.rs @@ -130,8 +130,6 @@ impl ObjectTrait for Tuple { } } -// Display ------------------------------------------------------------- - impl fmt::Display for Tuple { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let num_items = self.len(); From 7deaf4aa49a734fba00b599e97ed6407b24aed5a Mon Sep 17 00:00:00 2001 From: Wyatt Baldwin Date: Fri, 14 Apr 2023 00:32:25 -0700 Subject: [PATCH 31/31] WIP --- feint-compiler/src/scanner/scanner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feint-compiler/src/scanner/scanner.rs b/feint-compiler/src/scanner/scanner.rs index c020db7..30ab1b8 100644 --- a/feint-compiler/src/scanner/scanner.rs +++ b/feint-compiler/src/scanner/scanner.rs @@ -894,7 +894,7 @@ impl<'a, T: BufRead> Scanner<'a, T> { '\'' => string.push('\''), // This also seems to be a standard. - '\"' => string.push('\"'), + '"' => string.push('"'), // Any other escaped char resolves to the // original *escaped* version of itself.