diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a999c92..f0845fd5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,9 @@ name: CI on: push: - branches: [master] + branches: + - master + - rebis-dev tags: - "v**" pull_request: @@ -47,8 +49,7 @@ jobs: # FIXME(issue #2138): run wasm tests, failing to run since https://github.com/mthom/scryer-prolog/pull/2137 removed wasm-pack - { os: ubuntu-22.04, rust-version: nightly, target: 'wasm32-unknown-unknown', publish: true, args: '--no-default-features' , test-args: '--no-run --no-default-features', use_swap: true } # Cargo.toml rust-version - - { os: ubuntu-22.04, rust-version: "1.85", target: 'x86_64-unknown-linux-gnu'} - # rust versions + - { os: ubuntu-22.04, rust-version: "1.87", target: 'x86_64-unknown-linux-gnu'} - { os: ubuntu-22.04, rust-version: beta, target: 'x86_64-unknown-linux-gnu'} - { os: ubuntu-22.04, rust-version: nightly, target: 'x86_64-unknown-linux-gnu', miri: true, components: "miri"} defaults: diff --git a/Cargo.lock b/Cargo.lock index 52a3a76a..0feaf573 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1673,9 +1673,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -2011,9 +2011,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -2021,9 +2021,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", @@ -2712,6 +2712,7 @@ dependencies = [ "num-order", "ordered-float", "ouroboros", + "parking_lot", "phf", "pprof", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index e7b7f930..67f65bc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["prolog", "prolog-interpreter", "prolog-system"] categories = ["command-line-utilities"] build = "build/main.rs" # Remember to check CI -rust-version = "1.85" +rust-version = "1.87" [lib] crate-type = ["cdylib", "rlib"] @@ -79,6 +79,7 @@ ego-tree = "0.10.0" serde_json = "1.0.122" serde = "1.0.204" +parking_lot = "0.12.4" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] crossterm = { version = "0.28.1", optional = true } @@ -136,6 +137,7 @@ opt-level = 3 [profile.release] lto = true opt-level = 3 +debug = 2 [profile.wasm-dev] inherits = "dev" diff --git a/README.md b/README.md index f822034e..19a2700a 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ Extend Scryer Prolog to include the following, among other features: (`atom`, `var`, etc) with if/else ladders. (_in progress_) - [ ] Inlining all built-ins and system call instructions. - [x] Greatly reducing the number of instructions used to compile disjunctives. - - [ ] Storing short atoms to heap cells without writing them to the atom table. + - [x] Storing short atoms to heap cells without writing them to the atom table. - [ ] A compacting garbage collector satisfying the five properties of "[Precise Garbage Collection in Prolog](https://www.complang.tuwien.ac.at/ulrich/papers/PDF/2008-ciclops.pdf)." (_in progress_) - [ ] Mode declarations. diff --git a/build/instructions_template.rs b/build/instructions_template.rs index f6f9351e..1cf004b1 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -19,6 +19,7 @@ use to_syn_value_derive::ToDeriveInput; */ use std::any::*; +use std::rc::Rc; use std::str::FromStr; struct ArithmeticTerm; @@ -28,6 +29,7 @@ struct Death; struct HeapCellValue; struct IndexingLine; struct Level; +// struct Literal; struct NextOrFail; struct RegType; @@ -535,6 +537,16 @@ enum SystemClauseType { UnsetEnv, #[strum_discriminants(strum(props(Arity = "2", Name = "$shell")))] Shell, + #[strum_discriminants(strum(props(Arity = "8", Name = "$process_create")))] + ProcessCreate, + #[strum_discriminants(strum(props(Arity = "2", Name = "$process_id")))] + ProcessId, + #[strum_discriminants(strum(props(Arity = "3", Name = "$process_wait")))] + ProcessWait, + #[strum_discriminants(strum(props(Arity = "1", Name = "$process_kill")))] + ProcessKill, + #[strum_discriminants(strum(props(Arity = "1", Name = "$process_release")))] + ProcessRelease, #[strum_discriminants(strum(props(Arity = "1", Name = "$pid")))] Pid, #[strum_discriminants(strum(props(Arity = "4", Name = "$chars_base64")))] @@ -622,7 +634,7 @@ enum InstructionTemplate { #[strum_discriminants(strum(props(Arity = "2", Name = "get_list")))] GetList(Level, RegType), #[strum_discriminants(strum(props(Arity = "4", Name = "get_partial_string")))] - GetPartialString(Level, Atom, RegType, bool), + GetPartialString(Level, Rc, RegType), #[strum_discriminants(strum(props(Arity = "3", Name = "get_structure")))] GetStructure(Level, Atom, usize, RegType), #[strum_discriminants(strum(props(Arity = "2", Name = "get_variable")))] @@ -645,7 +657,7 @@ enum InstructionTemplate { #[strum_discriminants(strum(props(Arity = "2", Name = "put_list")))] PutList(Level, RegType), #[strum_discriminants(strum(props(Arity = "4", Name = "put_partial_string")))] - PutPartialString(Level, Atom, RegType, bool), + PutPartialString(Level, Rc, RegType), #[strum_discriminants(strum(props(Arity = "3", Name = "put_structure")))] PutStructure(Atom, usize, RegType), #[strum_discriminants(strum(props(Arity = "2", Name = "put_unsafe_value")))] @@ -875,6 +887,7 @@ fn generate_instruction_preface() -> TokenStream { use crate::arithmetic::*; use crate::atom_table::*; use crate::forms::*; + use crate::functor_macro::*; use crate::machine::heap::*; use crate::machine::machine_errors::MachineStub; use crate::machine::machine_indices::CodeIndex; @@ -885,6 +898,7 @@ fn generate_instruction_preface() -> TokenStream { use indexmap::IndexMap; use std::collections::VecDeque; + use std::rc::Rc; fn reg_type_into_functor(r: RegType) -> MachineStub { match r { @@ -896,9 +910,9 @@ fn generate_instruction_preface() -> TokenStream { impl Level { fn into_functor(self) -> MachineStub { match self { - Level::Root => functor!(atom!("level"), [atom(atom!("root"))]), - Level::Shallow => functor!(atom!("level"), [atom(atom!("shallow"))]), - Level::Deep => functor!(atom!("level"), [atom(atom!("deep"))]), + Level::Root => functor!(atom!("level"), [atom_as_cell((atom!("root")))]), + Level::Shallow => functor!(atom!("level"), [atom_as_cell((atom!("shallow")))]), + Level::Deep => functor!(atom!("level"), [atom_as_cell((atom!("deep")))]), } } } @@ -911,7 +925,7 @@ fn generate_instruction_preface() -> TokenStream { functor!(atom!("intermediate"), [fixnum(i)]) } ArithmeticTerm::Number(n) => { - vec![HeapCellValue::from((n, arena))] + functor!(atom!("number"), [number(n, arena)]) } } } @@ -996,7 +1010,7 @@ fn generate_instruction_preface() -> TokenStream { IndexingCodePtr, IndexingCodePtr, ), - SwitchOnConstant(IndexMap), + SwitchOnConstant(IndexMap), SwitchOnStructure(IndexMap<(Atom, usize), IndexingCodePtr, FxBuildHasher>), } @@ -1016,7 +1030,7 @@ fn generate_instruction_preface() -> TokenStream { IndexingCodePtr::External(o) => functor!(atom!("external"), [fixnum(o)]), IndexingCodePtr::Internal(o) => functor!(atom!("internal"), [fixnum(o)]), IndexingCodePtr::Fail => { - vec![atom_as_cell!(atom!("fail"))] + functor!(atom!("fail")) }, } } @@ -1030,80 +1044,43 @@ fn generate_instruction_preface() -> TokenStream { } impl IndexingInstruction { - pub fn to_functor(&self, mut h: usize) -> MachineStub { + pub fn to_functor(&self) -> MachineStub { match self { &IndexingInstruction::SwitchOnTerm(arg, vars, constants, lists, structures) => { functor!( atom!("switch_on_term"), [ fixnum(arg), - indexing_code_ptr(h, vars), - indexing_code_ptr(h, constants), - indexing_code_ptr(h, lists), - indexing_code_ptr(h, structures) + indexing_code_ptr(vars), + indexing_code_ptr(constants), + indexing_code_ptr(lists), + indexing_code_ptr(structures) ] ) } IndexingInstruction::SwitchOnConstant(constants) => { - let mut key_value_list_stub = vec![]; - let orig_h = h; - - h += 2; // skip the 2-cell "switch_on_constant" functor. - - for (c, ptr) in constants.iter() { - let key_value_pair = functor!( - atom!(":"), - [literal(*c), indexing_code_ptr(h + 3, *ptr)] - ); - - key_value_list_stub.push(list_loc_as_cell!(h + 1)); - key_value_list_stub.push(str_loc_as_cell!(h + 3)); - key_value_list_stub.push(heap_loc_as_cell!(h + 3 + key_value_pair.len())); - - h += key_value_pair.len() + 3; - key_value_list_stub.extend(key_value_pair.into_iter()); - } - - key_value_list_stub.push(empty_list_as_cell!()); - - functor!( - atom!("switch_on_constant"), - [str(orig_h, 0)], - [key_value_list_stub] + variadic_functor( + atom!("switch_on_constants"), + 1, + constants.iter().map(|(c, ptr)| { + functor!( + atom!(":"), + [cell((*c)), indexing_code_ptr((*ptr))] + ) + }), ) } IndexingInstruction::SwitchOnStructure(structures) => { - let mut key_value_list_stub = vec![]; - let orig_h = h; - - h += 2; // skip the 2-cell "switch_on_constant" functor. - - for ((name, arity), ptr) in structures.iter() { - let predicate_indicator_stub = functor!( - atom!("/"), - [atom(name), fixnum(*arity)] - ); - - let key_value_pair = functor!( - atom!(":"), - [str(h + 3, 0), indexing_code_ptr(h + 3, *ptr)], - [predicate_indicator_stub] - ); - - key_value_list_stub.push(list_loc_as_cell!(h + 1)); - key_value_list_stub.push(str_loc_as_cell!(h + 3)); - key_value_list_stub.push(heap_loc_as_cell!(h + 3 + key_value_pair.len())); - - h += key_value_pair.len() + 3; - key_value_list_stub.extend(key_value_pair.into_iter()); - } - - key_value_list_stub.push(empty_list_as_cell!()); - - functor!( + variadic_functor( atom!("switch_on_structure"), - [str(orig_h, 0)], - [key_value_list_stub] + 1, + structures.iter().map(|((name, arity), ptr)| { + functor!( + atom!(":"), + [functor((atom!("/")), [atom_as_cell(name), fixnum((*arity))]), + indexing_code_ptr((*ptr))] + ) + }), ) } } @@ -1133,18 +1110,16 @@ fn generate_instruction_preface() -> TokenStream { } fn arith_instr_unary_functor( - h: usize, name: Atom, arena: &mut Arena, at: &ArithmeticTerm, t: usize, ) -> MachineStub { let at_stub = at.into_functor(arena); - functor!(name, [str(h, 0), fixnum(t)], [at_stub]) + functor!(name, [functor(at_stub), fixnum(t)]) } fn arith_instr_bin_functor( - h: usize, name: Atom, arena: &mut Arena, at_1: &ArithmeticTerm, @@ -1154,11 +1129,9 @@ fn generate_instruction_preface() -> TokenStream { let at_1_stub = at_1.into_functor(arena); let at_2_stub = at_2.into_functor(arena); - functor!( - name, - [str(h, 0), str(h, 1), fixnum(t)], - [at_1_stub, at_2_stub] - ) + functor!(name, [functor(at_1_stub), + functor(at_2_stub), + fixnum(t)]) } pub type Code = Vec; @@ -1170,7 +1143,7 @@ fn generate_instruction_preface() -> TokenStream { match *self { Instruction::GetConstant(_, _, r) => vec![r], Instruction::GetList(_, r) => vec![r], - Instruction::GetPartialString(_, _, r, _) => vec![r], + Instruction::GetPartialString(_, _, r) => vec![r], Instruction::GetStructure(_, _, _, r) => vec![r], Instruction::GetVariable(r, t) => vec![r, temp_v!(t)], Instruction::GetValue(r, t) => vec![r, temp_v!(t)], @@ -1178,7 +1151,7 @@ fn generate_instruction_preface() -> TokenStream { Instruction::UnifyVariable(r) => vec![r], Instruction::PutConstant(_, _, r) => vec![r], Instruction::PutList(_, r) => vec![r], - Instruction::PutPartialString(_, _, r, _) => vec![r], + Instruction::PutPartialString(_, _, r) => vec![r], Instruction::PutStructure(_, _, r) => vec![r], Instruction::PutValue(r, t) => vec![r, temp_v!(t)], Instruction::PutVariable(r, t) => vec![r, temp_v!(t)], @@ -1242,7 +1215,6 @@ fn generate_instruction_preface() -> TokenStream { pub fn enqueue_functors( &self, - mut h: usize, arena: &mut Arena, functors: &mut Vec, ) { @@ -1251,33 +1223,30 @@ fn generate_instruction_preface() -> TokenStream { for indexing_instr in indexing_instrs { match indexing_instr { IndexingLine::Indexing(indexing_instr) => { - let section = indexing_instr.to_functor(h); - h += section.len(); + let section = indexing_instr.to_functor(); functors.push(section); } IndexingLine::IndexedChoice(indexed_choice_instrs) => { for indexed_choice_instr in indexed_choice_instrs { let section = indexed_choice_instr.to_functor(); - h += section.len(); functors.push(section); } } IndexingLine::DynamicIndexedChoice(indexed_choice_instrs) => { for indexed_choice_instr in indexed_choice_instrs { - let section = functor!(atom!("dynamic"), [fixnum(*indexed_choice_instr)]); - - h += section.len(); + let section = functor!(atom!("dynamic"), + [fixnum((*indexed_choice_instr))]); functors.push(section); } } } } } - instr => functors.push(instr.to_functor(h, arena)), + instr => functors.push(instr.to_functor(arena)), } } - fn to_functor(&self, h: usize, arena: &mut Arena) -> MachineStub { + fn to_functor(&self, arena: &mut Arena) -> MachineStub { match self { &Instruction::InstallVerifyAttr => { functor!(atom!("install_verify_attr")) @@ -1290,25 +1259,23 @@ fn generate_instruction_preface() -> TokenStream { (Death::Infinity, NextOrFail::Next(i)) => { functor!( atom!("dynamic_else"), - [fixnum(birth), atom(atom!("inf")), fixnum(i)] + [fixnum(birth), atom_as_cell((atom!("inf"))), fixnum(i)] ) } (Death::Infinity, NextOrFail::Fail(i)) => { - let next_functor = functor!(atom!("fail"), [fixnum(i)]); - functor!( atom!("dynamic_else"), - [fixnum(birth), atom(atom!("inf")), str(h, 0)], - [next_functor] + [fixnum(birth), + atom_as_cell((atom!("inf"))), + functor((atom!("fail")), [fixnum(i)])] ) } (Death::Finite(d), NextOrFail::Fail(i)) => { - let next_functor = functor!(atom!("fail"), [fixnum(i)]); - functor!( atom!("dynamic_else"), - [fixnum(birth), fixnum(d), str(h, 0)], - [next_functor] + [fixnum(birth), + fixnum(d), + functor((atom!("fail")), [fixnum(i)])] ) } (Death::Finite(d), NextOrFail::Next(i)) => { @@ -1321,25 +1288,23 @@ fn generate_instruction_preface() -> TokenStream { (Death::Infinity, NextOrFail::Next(i)) => { functor!( atom!("dynamic_internal_else"), - [fixnum(birth), atom(atom!("inf")), fixnum(i)] + [fixnum(birth), atom_as_cell((atom!("inf"))), fixnum(i)] ) } (Death::Infinity, NextOrFail::Fail(i)) => { - let next_functor = functor!(atom!("fail"), [fixnum(i)]); - functor!( atom!("dynamic_internal_else"), - [fixnum(birth), atom(atom!("inf")), str(h, 0)], - [next_functor] + [fixnum(birth), + atom_as_cell((atom!("inf"))), + functor((atom!("fail")), [fixnum(i)])] ) } (Death::Finite(d), NextOrFail::Fail(i)) => { - let next_functor = functor!(atom!("fail"), [fixnum(i)]); - functor!( atom!("dynamic_internal_else"), - [fixnum(birth), fixnum(d), str(h, 0)], - [next_functor] + [fixnum(birth), + fixnum(d), + functor((atom!("fail")), [fixnum(i)])] ) } (Death::Finite(d), NextOrFail::Next(i)) => { @@ -1367,157 +1332,154 @@ fn generate_instruction_preface() -> TokenStream { } &Instruction::Cut(r) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("cut"), [str(h, 0)], [rt_stub]) + functor!(atom!("cut"), [functor(rt_stub)]) } &Instruction::CutPrev(r) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("cut_prev"), [str(h, 0)], [rt_stub]) + functor!(atom!("cut_prev"), [functor(rt_stub)]) } &Instruction::GetLevel(r) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("get_level"), [str(h, 0)], [rt_stub]) + functor!(atom!("get_level"), [functor(rt_stub)]) } &Instruction::GetPrevLevel(r) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("get_prev_level"), [str(h, 0)], [rt_stub]) + functor!(atom!("get_prev_level"), [functor(rt_stub)]) } &Instruction::GetCutPoint(r) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("get_cut_point"), [str(h, 0)], [rt_stub]) + functor!(atom!("get_cut_point"), [functor(rt_stub)]) } &Instruction::NeckCut => { functor!(atom!("neck_cut")) } &Instruction::Add(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("add"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("add"), arena, at_1, at_2, t) } &Instruction::Sub(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("sub"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("sub"), arena, at_1, at_2, t) } &Instruction::Mul(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("mul"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("mul"), arena, at_1, at_2, t) } &Instruction::IntPow(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("int_pow"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("int_pow"), arena, at_1, at_2, t) } &Instruction::Pow(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("pow"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("pow"), arena, at_1, at_2, t) } &Instruction::IDiv(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("idiv"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("idiv"), arena, at_1, at_2, t) } &Instruction::Max(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("max"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("max"), arena, at_1, at_2, t) } &Instruction::Min(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("min"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("min"), arena, at_1, at_2, t) } &Instruction::IntFloorDiv(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("int_floor_div"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("int_floor_div"), arena, at_1, at_2, t) } &Instruction::RDiv(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("rdiv"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("rdiv"), arena, at_1, at_2, t) } &Instruction::Div(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("div"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("div"), arena, at_1, at_2, t) } &Instruction::Shl(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("shl"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("shl"), arena, at_1, at_2, t) } &Instruction::Shr(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("shr"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("shr"), arena, at_1, at_2, t) } &Instruction::Xor(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("xor"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("xor"), arena, at_1, at_2, t) } &Instruction::And(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("and"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("and"), arena, at_1, at_2, t) } &Instruction::Or(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("or"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("or"), arena, at_1, at_2, t) } &Instruction::Mod(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("mod"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("mod"), arena, at_1, at_2, t) } &Instruction::Rem(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("rem"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("rem"), arena, at_1, at_2, t) } &Instruction::ATan2(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("rem"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("rem"), arena, at_1, at_2, t) } &Instruction::Gcd(ref at_1, ref at_2, t) => { - arith_instr_bin_functor(h, atom!("gcd"), arena, at_1, at_2, t) + arith_instr_bin_functor(atom!("gcd"), arena, at_1, at_2, t) } &Instruction::Sign(ref at, t) => { - arith_instr_unary_functor(h, atom!("sign"), arena, at, t) + arith_instr_unary_functor(atom!("sign"), arena, at, t) } &Instruction::Cos(ref at, t) => { - arith_instr_unary_functor(h, atom!("cos"), arena, at, t) + arith_instr_unary_functor(atom!("cos"), arena, at, t) } &Instruction::Sin(ref at, t) => { - arith_instr_unary_functor(h, atom!("sin"), arena, at, t) + arith_instr_unary_functor(atom!("sin"), arena, at, t) } &Instruction::Tan(ref at, t) => { - arith_instr_unary_functor(h, atom!("tan"), arena, at, t) + arith_instr_unary_functor(atom!("tan"), arena, at, t) } &Instruction::Log(ref at, t) => { - arith_instr_unary_functor(h, atom!("log"), arena, at, t) + arith_instr_unary_functor(atom!("log"), arena, at, t) } &Instruction::Exp(ref at, t) => { - arith_instr_unary_functor(h, atom!("exp"), arena, at, t) + arith_instr_unary_functor(atom!("exp"), arena, at, t) } &Instruction::ACos(ref at, t) => { - arith_instr_unary_functor(h, atom!("acos"), arena, at, t) + arith_instr_unary_functor(atom!("acos"), arena, at, t) } &Instruction::ASin(ref at, t) => { - arith_instr_unary_functor(h, atom!("asin"), arena, at, t) + arith_instr_unary_functor(atom!("asin"), arena, at, t) } &Instruction::ATan(ref at, t) => { - arith_instr_unary_functor(h, atom!("atan"), arena, at, t) + arith_instr_unary_functor(atom!("atan"), arena, at, t) } &Instruction::Sqrt(ref at, t) => { - arith_instr_unary_functor(h, atom!("sqrt"), arena, at, t) + arith_instr_unary_functor(atom!("sqrt"), arena, at, t) } &Instruction::Abs(ref at, t) => { - arith_instr_unary_functor(h, atom!("abs"), arena, at, t) + arith_instr_unary_functor(atom!("abs"), arena, at, t) } &Instruction::Float(ref at, t) => { - arith_instr_unary_functor(h, atom!("float"), arena, at, t) + arith_instr_unary_functor(atom!("float"), arena, at, t) } &Instruction::Truncate(ref at, t) => { - arith_instr_unary_functor(h, atom!("truncate"), arena, at, t) + arith_instr_unary_functor(atom!("truncate"), arena, at, t) } &Instruction::Round(ref at, t) => { - arith_instr_unary_functor(h, atom!("round"), arena, at, t) + arith_instr_unary_functor(atom!("round"), arena, at, t) } &Instruction::Ceiling(ref at, t) => { - arith_instr_unary_functor(h, atom!("ceiling"), arena, at, t) + arith_instr_unary_functor(atom!("ceiling"), arena, at, t) } &Instruction::Floor(ref at, t) => { - arith_instr_unary_functor(h, atom!("floor"), arena, at, t) + arith_instr_unary_functor(atom!("floor"), arena, at, t) } &Instruction::FloatFractionalPart(ref at, t) => { - arith_instr_unary_functor(h, atom!("float_fractional_part"), arena, at, t) + arith_instr_unary_functor(atom!("float_fractional_part"), arena, at, t) } &Instruction::FloatIntegerPart(ref at, t) => { - arith_instr_unary_functor(h, atom!("float_integer_part"), arena, at, t) + arith_instr_unary_functor(atom!("float_integer_part"), arena, at, t) } &Instruction::Neg(ref at, t) => arith_instr_unary_functor( - h, atom!("-"), arena, at, t, ), &Instruction::Plus(ref at, t) => arith_instr_unary_functor( - h, atom!("+"), arena, at, t, ), &Instruction::BitwiseComplement(ref at, t) => arith_instr_unary_functor( - h, atom!("\\"), arena, at, @@ -1533,16 +1495,16 @@ fn generate_instruction_preface() -> TokenStream { functor!(atom!("allocate"), [fixnum(num_frames)]) } &Instruction::CallNamed(arity, name, ..) => { - functor!(atom!("call"), [atom(name), fixnum(arity)]) + functor!(atom!("call"), [atom_as_cell(name), fixnum(arity)]) } &Instruction::ExecuteNamed(arity, name, ..) => { - functor!(atom!("execute"), [atom(name), fixnum(arity)]) + functor!(atom!("execute"), [atom_as_cell(name), fixnum(arity)]) } &Instruction::DefaultCallNamed(arity, name, ..) => { - functor!(atom!("call_default"), [atom(name), fixnum(arity)]) + functor!(atom!("call_default"), [atom_as_cell(name), fixnum(arity)]) } &Instruction::DefaultExecuteNamed(arity, name, ..) => { - functor!(atom!("execute_default"), [atom(name), fixnum(arity)]) + functor!(atom!("execute_default"), [atom_as_cell(name), fixnum(arity)]) } &Instruction::CallN(arity) => { functor!(atom!("call_n"), [fixnum(arity)]) @@ -1585,7 +1547,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallSort | &Instruction::CallGetNumber(_) => { let (name, arity) = self.to_name_and_arity(); - functor!(atom!("call"), [atom(name), fixnum(arity)]) + functor!(atom!("call"), [atom_as_cell(name), fixnum(arity)]) } // &Instruction::ExecuteTermGreaterThan | @@ -1611,7 +1573,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteSort | &Instruction::ExecuteGetNumber(_) => { let (name, arity) = self.to_name_and_arity(); - functor!(atom!("execute"), [atom(name), fixnum(arity)]) + functor!(atom!("execute"), [atom_as_cell(name), fixnum(arity)]) } // &Instruction::DefaultCallTermGreaterThan | @@ -1637,7 +1599,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::DefaultCallSort | &Instruction::DefaultCallGetNumber(_) => { let (name, arity) = self.to_name_and_arity(); - functor!(atom!("call_default"), [atom(name), fixnum(arity)]) + functor!(atom!("call_default"), [atom_as_cell(name), fixnum(arity)]) } // &Instruction::DefaultExecuteTermGreaterThan | @@ -1663,7 +1625,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::DefaultExecuteSort | &Instruction::DefaultExecuteGetNumber(_) => { let (name, arity) = self.to_name_and_arity(); - functor!(atom!("execute_default"), [atom(name), fixnum(arity)]) + functor!(atom!("execute_default"), [atom_as_cell(name), fixnum(arity)]) } &Instruction::CallIsAtom(r) | &Instruction::CallIsAtomic(r) | @@ -1676,7 +1638,8 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallIsVar(r) => { let (name, arity) = self.to_name_and_arity(); let rt_stub = reg_type_into_functor(r); - functor!(atom!("call"), [atom(name), fixnum(arity), str(h, 0)], [rt_stub]) + + functor!(atom!("call"), [atom_as_cell(name), fixnum(arity), functor(rt_stub)]) } &Instruction::ExecuteIsAtom(r) | &Instruction::ExecuteIsAtomic(r) | @@ -1689,7 +1652,8 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteIsVar(r) => { let (name, arity) = self.to_name_and_arity(); let rt_stub = reg_type_into_functor(r); - functor!(atom!("execute"), [atom(name), fixnum(arity), str(h, 0)], [rt_stub]) + + functor!(atom!("execute"), [atom_as_cell(name), fixnum(arity), functor(rt_stub)]) } // &Instruction::CallAtomChars | @@ -1871,6 +1835,11 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallSetEnv | &Instruction::CallUnsetEnv | &Instruction::CallShell | + &Instruction::CallProcessCreate | + &Instruction::CallProcessId | + &Instruction::CallProcessWait | + &Instruction::CallProcessKill | + &Instruction::CallProcessRelease | &Instruction::CallPid | &Instruction::CallCharsBase64 | &Instruction::CallDevourWhitespace | @@ -1920,14 +1889,14 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallEd25519VerifyRaw | &Instruction::CallEd25519SeedToPublicKey => { let (name, arity) = self.to_name_and_arity(); - functor!(atom!("call"), [atom(name), fixnum(arity)]) + functor!(atom!("call"), [atom_as_cell(name), fixnum(arity)]) } // #[cfg(feature = "crypto-full")] &Instruction::CallCryptoDataEncrypt | &Instruction::CallCryptoDataDecrypt => { let (name, arity) = self.to_name_and_arity(); - functor!(atom!("call"), [atom(name), fixnum(arity)]) + functor!(atom!("call"), [atom_as_cell(name), fixnum(arity)]) } // &Instruction::ExecuteAtomChars | @@ -2109,6 +2078,11 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteSetEnv | &Instruction::ExecuteUnsetEnv | &Instruction::ExecuteShell | + &Instruction::ExecuteProcessCreate | + &Instruction::ExecuteProcessId | + &Instruction::ExecuteProcessWait | + &Instruction::ExecuteProcessKill | + &Instruction::ExecuteProcessRelease | &Instruction::ExecutePid | &Instruction::ExecuteCharsBase64 | &Instruction::ExecuteDevourWhitespace | @@ -2158,14 +2132,14 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteEd25519VerifyRaw | &Instruction::ExecuteEd25519SeedToPublicKey => { let (name, arity) = self.to_name_and_arity(); - functor!(atom!("execute"), [atom(name), fixnum(arity)]) + functor!(atom!("execute"), [atom_as_cell(name), fixnum(arity)]) } // #[cfg(feature = "crypto-full")] &Instruction::ExecuteCryptoDataEncrypt | &Instruction::ExecuteCryptoDataDecrypt => { let (name, arity) = self.to_name_and_arity(); - functor!(atom!("execute"), [atom(name), fixnum(arity)]) + functor!(atom!("execute"), [atom_as_cell(name), fixnum(arity)]) } // &Instruction::Deallocate => { @@ -2180,73 +2154,60 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::Proceed => { functor!(atom!("proceed")) } - &Instruction::GetConstant(lvl, c, r) => { + &Instruction::GetConstant(lvl, lit, r) => { let lvl_stub = lvl.into_functor(); let rt_stub = reg_type_into_functor(r); - functor!( - atom!("get_constant"), - [str(h, 0), cell(c), str(h, 1)], - [lvl_stub, rt_stub] - ) + functor!(atom!("get_constant"), [functor(lvl_stub), + cell(lit), + functor(rt_stub)]) } &Instruction::GetList(lvl, r) => { let lvl_stub = lvl.into_functor(); let rt_stub = reg_type_into_functor(r); - functor!( - atom!("get_list"), - [str(h, 0), str(h, 1)], - [lvl_stub, rt_stub] - ) + functor!(atom!("get_list"), [functor(lvl_stub), functor(rt_stub)]) } - &Instruction::GetPartialString(lvl, s, r, has_tail) => { + &Instruction::GetPartialString(lvl, ref s, r) => { let lvl_stub = lvl.into_functor(); let rt_stub = reg_type_into_functor(r); - functor!( - atom!("get_partial_string"), - [ - str(h, 0), - string(h, s), - str(h, 1), - boolean(has_tail) - ], - [lvl_stub, rt_stub] - ) + functor!(atom!("get_partial_string"), [functor(lvl_stub), + string((s.to_string())), + functor(rt_stub)]) } &Instruction::GetStructure(lvl, name, arity, r) => { let lvl_stub = lvl.into_functor(); let rt_stub = reg_type_into_functor(r); - functor!( - atom!("get_structure"), - [str(h, 0), atom(name), fixnum(arity), str(h, 1)], - [lvl_stub, rt_stub] - ) + functor!(atom!("get_structure"), [functor(lvl_stub), + atom_as_cell(name), + fixnum(arity), + functor(rt_stub)]) } &Instruction::GetValue(r, arg) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("get_value"), [str(h, 0), fixnum(arg)], [rt_stub]) + functor!(atom!("get_value"), [functor(rt_stub), + fixnum(arg)]) } &Instruction::GetVariable(r, arg) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("get_variable"), [str(h, 0), fixnum(arg)], [rt_stub]) + functor!(atom!("get_variable"), [functor(rt_stub), fixnum(arg)]) } &Instruction::UnifyConstant(c) => { functor!(atom!("unify_constant"), [cell(c)]) } &Instruction::UnifyLocalValue(r) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("unify_local_value"), [str(h, 0)], [rt_stub]) + functor!(atom!("unify_local_value"), [functor(rt_stub)]) } &Instruction::UnifyVariable(r) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("unify_variable"), [str(h, 0)], [rt_stub]) + functor!(atom!("unify_variable"), [functor(rt_stub)]) } &Instruction::UnifyValue(r) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("unify_value"), [str(h, 0)], [rt_stub]) + functor!(atom!("unify_value"), [functor(rt_stub)]) } &Instruction::UnifyVoid(vars) => { functor!(atom!("unify_void"), [fixnum(vars)]) @@ -2258,68 +2219,55 @@ fn generate_instruction_preface() -> TokenStream { let lvl_stub = lvl.into_functor(); let rt_stub = reg_type_into_functor(r); - functor!( - atom!("put_constant"), - [str(h, 0), cell(c), str(h, 1)], - [lvl_stub, rt_stub] - ) + functor!(atom!("put_constant"), [functor(rt_stub), cell(c), functor(lvl_stub)]) } &Instruction::PutList(lvl, r) => { let lvl_stub = lvl.into_functor(); let rt_stub = reg_type_into_functor(r); - functor!( - atom!("put_list"), - [str(h, 0), str(h, 1)], - [lvl_stub, rt_stub] - ) + functor!(atom!("put_list"), [functor(lvl_stub), functor(rt_stub)]) } - &Instruction::PutPartialString(lvl, s, r, has_tail) => { + &Instruction::PutPartialString(lvl, ref s, r) => { let lvl_stub = lvl.into_functor(); let rt_stub = reg_type_into_functor(r); - functor!( - atom!("put_partial_string"), - [ - str(h, 0), - string(h, s), - str(h, 1), - boolean(has_tail) - ], - [lvl_stub, rt_stub] - ) + functor!(atom!("put_partial_string"), [functor(lvl_stub), + string((s.to_string())), + functor(rt_stub)]) } &Instruction::PutStructure(name, arity, r) => { let rt_stub = reg_type_into_functor(r); - functor!( - atom!("put_structure"), - [atom(name), fixnum(arity), str(h, 0)], - [rt_stub] - ) + functor!(atom!("put_structure"), [atom_as_cell(name), + fixnum(arity), + functor(rt_stub)]) } &Instruction::PutValue(r, arg) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("put_value"), [str(h, 0), fixnum(arg)], [rt_stub]) + + functor!(atom!("put_value"), [functor(rt_stub), + fixnum(arg)]) } &Instruction::PutVariable(r, arg) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("put_variable"), [str(h, 0), fixnum(arg)], [rt_stub]) + + functor!(atom!("put_variable"), [functor(rt_stub), + fixnum(arg)]) } &Instruction::SetConstant(c) => { functor!(atom!("set_constant"), [cell(c)]) } &Instruction::SetLocalValue(r) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("set_local_value"), [str(h, 0)], [rt_stub]) + functor!(atom!("set_local_value"), [functor(rt_stub)]) } &Instruction::SetVariable(r) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("set_variable"), [str(h, 0)], [rt_stub]) + functor!(atom!("set_variable"), [functor(rt_stub)]) } &Instruction::SetValue(r) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("set_value"), [str(h, 0)], [rt_stub]) + functor!(atom!("set_value"), [functor(rt_stub)]) } &Instruction::SetVoid(vars) => { functor!(atom!("set_void"), [fixnum(vars)]) @@ -2770,7 +2718,7 @@ pub fn generate_instructions_rs() -> TokenStream { if ident == "Named" { clause_type_from_name_and_arity_arms.push(quote! { - (name, arity) => ClauseType::Named(arity, name, CodeIndex::default(arena)) + (name, arity) => ClauseType::Named(arity, name, CodeIndex::default(&mut arena.code_index_tbl)) }); clause_type_to_instr_arms.push(quote! { @@ -3198,12 +3146,6 @@ pub fn generate_instructions_rs() -> TokenStream { } } - pub fn is_inbuilt(name: Atom, arity: usize) -> bool { - matches!((name, arity), - #(#is_inbuilt_arms)|* - ) - } - pub fn name(&self) -> Atom { match self { #( @@ -3212,6 +3154,12 @@ pub fn generate_instructions_rs() -> TokenStream { } } + pub fn is_inbuilt(name: Atom, arity: usize) -> bool { + matches!((name, arity), + #(#is_inbuilt_arms)|* + ) + } + pub fn is_inlined(name: Atom, arity: usize) -> bool { matches!((name, arity), #(#is_inlined_arms)|* @@ -3334,14 +3282,14 @@ where let disc = match DiscriminantT::from_str(id.to_string().as_str()) { Ok(disc) => disc, Err(_) => { - panic!("can't generate discriminant {}", id); + panic!("can't generate discriminant {id}"); } }; match disc.get_str(key) { Some(prop) => prop, None => { - panic!("can't find property {} of discriminant {:?}", key, disc); + panic!("can't find property {key} of discriminant {disc:?}"); } } } @@ -3459,7 +3407,7 @@ impl InstructionData { (name, arity, CountableInference::HasDefault) } else { - panic!("type ID is: {}", id); + panic!("type ID is: {id}"); }; let v_ident = variant diff --git a/build/static_string_indexing.rs b/build/static_string_indexing.rs index c3d1701c..7309f1cf 100644 --- a/build/static_string_indexing.rs +++ b/build/static_string_indexing.rs @@ -84,6 +84,18 @@ impl<'ast> Visit<'ast> for StaticStrVisitor { } } +const INLINED_ATOM_MAX_LEN: usize = 6; + +fn static_string_index(string: &str, index: usize) -> u64 { + if !string.is_empty() && string.len() <= INLINED_ATOM_MAX_LEN && !string.contains('\u{0}') { + let mut string_buf: [u8; 8] = [0u8; 8]; + string_buf[..string.len()].copy_from_slice(string.as_bytes()); + (u64::from_le_bytes(string_buf) << 1) | 1 + } else { + (index << 1) as u64 + } +} + pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStream { use quote::*; @@ -114,14 +126,14 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea match file.read_to_string(&mut src) { Ok(_) => {} Err(e) => { - panic!("error reading file: {:?}", e); + panic!("error reading file: {e:?}"); } } let syntax = match syn::parse_file(&src) { Ok(s) => s, Err(e) => { - panic!("parse error: {} in file {:?}", e, path); + panic!("parse error: {e} in file {path:?}"); } }; Ok(syntax) @@ -149,11 +161,29 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea visitor.visit_file(&syntax) } - let indices = (0..visitor.static_strs.len()).map(|i| (i << 3) as u64); - let indices_iter = indices.clone(); + let mut static_str_keys = vec![]; + let mut static_strs = vec![]; + let mut static_str_indices = vec![]; - let static_strs_len = visitor.static_strs.len(); - let static_strs: &Vec<_> = &visitor.static_strs.into_iter().collect(); + let indices: Vec = visitor + .static_strs + .iter() + .map(|string| { + let index = static_string_index(string, static_strs.len()); + + static_str_keys.push(string); + + if index & 1 == 1 { + index + } else { + static_str_indices.push(index); + static_strs.push(string); + index + } + }) + .collect(); + + let static_strs_len = static_strs.len(); quote! { static STRINGS: [&str; #static_strs_len] = [ @@ -163,11 +193,12 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea ]; macro_rules! atom { - #((#static_strs) => { Atom { index: #indices_iter } };)* + #((#static_str_keys) => { Atom { index: #indices } };)* + ($name:literal) => {compile_error!(concat!("unknown static atom ", $name))}; } pub static STATIC_ATOMS_MAP: phf::Map<&'static str, Atom> = phf::phf_map! { - #(#static_strs => { Atom { index: #indices } },)* + #(#static_strs => { Atom { index: #static_str_indices } },)* }; } } diff --git a/src/allocator.rs b/src/allocator.rs index e961b975..b4529d82 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -14,7 +14,7 @@ pub(crate) trait Allocator { lvl: Level, context: GenContext, code: &mut CodeDeque, - ); + ) -> RegType; fn mark_non_var<'a, Target: CompilationTarget<'a>>( &mut self, diff --git a/src/arena.rs b/src/arena.rs index e080df55..ee9dda20 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -3,31 +3,27 @@ #[cfg(feature = "http")] use crate::http::{HttpListener, HttpResponse}; use crate::machine::loader::LiveLoadState; -use crate::machine::machine_indices::*; use crate::machine::streams::*; -use crate::raw_block::*; +use crate::offset_table::*; use crate::read::*; use crate::types::UntypedArenaPtr; use crate::parser::dashu::{Integer, Rational}; -use arcu::atomic::Arcu; -use arcu::epoch_counters::GlobalEpochCounterPool; -use arcu::rcu_ref::RcuRef; -use arcu::Rcu; use ordered_float::OrderedFloat; -use std::cell::UnsafeCell; use std::fmt; use std::fmt::Debug; use std::hash::{Hash, Hasher}; +use std::io::PipeReader; +use std::io::PipeWriter; use std::mem; use std::mem::ManuallyDrop; use std::net::TcpListener; use std::ops::{Deref, DerefMut}; +use std::process::Child; use std::ptr; use std::ptr::addr_of_mut; use std::ptr::NonNull; -use std::sync::RwLock; macro_rules! arena_alloc { ($e:expr, $arena:expr) => {{ @@ -38,8 +34,7 @@ macro_rules! arena_alloc { macro_rules! float_alloc { ($e:expr, $arena:expr) => {{ - let result = $e; - unsafe { $arena.f64_tbl.build_with(result).as_ptr() } + $arena.f64_tbl.build_with(OrderedFloat($e)) }}; } @@ -55,120 +50,6 @@ where payload_offset - header_offset } -use std::sync::Arc; -use std::sync::Mutex; -use std::sync::Weak; - -const F64_TABLE_INIT_SIZE: usize = 1 << 16; -const F64_TABLE_ALIGN: usize = 8; - -#[inline(always)] -fn global_f64table() -> &'static RwLock> { - static GLOBAL_ATOM_TABLE: RwLock> = RwLock::new(Weak::new()); - &GLOBAL_ATOM_TABLE -} - -impl RawBlockTraits for F64Table { - #[inline] - fn init_size() -> usize { - F64_TABLE_INIT_SIZE - } - - #[inline] - fn align() -> usize { - F64_TABLE_ALIGN - } -} - -#[derive(Debug)] -pub struct F64Table { - block: Arcu, GlobalEpochCounterPool>, - update: Mutex<()>, -} - -// TODO: Actually prove this, as it's probably unsound right now -unsafe impl Send for F64Table {} -unsafe impl Sync for F64Table {} - -#[inline(always)] -pub fn lookup_float( - offset: F64Offset, -) -> RcuRef, UnsafeCell>> { - let f64table = global_f64table() - .read() - .unwrap() - .upgrade() - .expect("We should only be looking up floats while there is a float table"); - - RcuRef::try_map(f64table.block.read(), |raw_block| unsafe { - raw_block - .base - .add(offset.0) - .cast_mut() - .cast::>>() - .as_ref() - }) - .expect("The offset should result in a non-null pointer") -} - -impl F64Table { - #[inline] - pub fn new() -> Arc { - let upgraded = global_f64table().read().unwrap().upgrade(); - // don't inline upgraded, otherwise temporary will be dropped too late in case of None - if let Some(atom_table) = upgraded { - atom_table - } else { - let mut guard = global_f64table().write().unwrap(); - // try to upgrade again in case we lost the race on the write lock - if let Some(atom_table) = guard.upgrade() { - atom_table - } else { - let atom_table = Arc::new(Self { - block: Arcu::new(RawBlock::new(), GlobalEpochCounterPool), - update: Mutex::new(()), - }); - *guard = Arc::downgrade(&atom_table); - atom_table - } - } - } - - #[allow(clippy::missing_safety_doc)] - pub unsafe fn build_with(&self, value: f64) -> F64Offset { - let update_guard = self.update.lock(); - - // we don't have an index table for lookups as AtomTable does so - // just get the epoch after we take the upgrade lock - let mut block_epoch = self.block.read(); - - let mut ptr; - - loop { - ptr = block_epoch.alloc(mem::size_of::()); - - if ptr.is_null() { - let new_block = block_epoch.grow_new().unwrap(); - self.block.replace(new_block); - block_epoch = self.block.read(); - } else { - break; - } - } - - ptr::write(ptr as *mut OrderedFloat, OrderedFloat(value)); - - let float = F64Offset(ptr as usize - block_epoch.base as usize); - - // atometable would have to update the index table at this point - - // expicit drop to ensure we don't accidentally drop it early - drop(update_guard); - - float - } -} - #[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq)] #[bits = 7] pub enum ArenaHeaderTag { @@ -193,11 +74,10 @@ pub enum ArenaHeaderTag { TcpListener = 0b1000000, HttpListener = 0b1000001, HttpResponse = 0b1000010, + PipeWriter = 0b1000011, Dropped = 0b1000100, - IndexPtrDynamicUndefined = 0b1000101, - IndexPtrDynamicIndex = 0b1000110, - IndexPtrIndex = 0b1000111, - IndexPtrUndefined = 0b1001000, + PipeReader = 0b1001001, + ChildProcess = 0b1001010, } #[bitfield] @@ -436,137 +316,6 @@ pub trait ArenaAllocated { } } -#[derive(Debug)] -pub struct F64Ptr(RcuRef, UnsafeCell>>); - -impl Clone for F64Ptr { - fn clone(&self) -> Self { - Self(RcuRef::clone(&self.0)) - } -} - -impl PartialEq for F64Ptr { - fn eq(&self, other: &F64Ptr) -> bool { - RcuRef::ptr_eq(&self.0, &other.0) || self.deref() == other.deref() - } -} - -impl Eq for F64Ptr {} - -impl PartialOrd for F64Ptr { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for F64Ptr { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - (**self).cmp(&**other) - } -} - -impl Hash for F64Ptr { - #[inline(always)] - fn hash(&self, hasher: &mut H) { - (self as &OrderedFloat).hash(hasher) - } -} - -impl fmt::Display for F64Ptr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self as &OrderedFloat) - } -} - -impl Deref for F64Ptr { - type Target = OrderedFloat; - - #[inline] - fn deref(&self) -> &Self::Target { - unsafe { self.0.get().as_ref().unwrap() } - } -} - -impl DerefMut for F64Ptr { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { &mut *self.0.get().as_mut().unwrap() } - } -} - -impl F64Ptr { - #[inline(always)] - pub fn from_offset(offset: F64Offset) -> Self { - Self(lookup_float(offset)) - } - - #[inline(always)] - pub fn as_offset(&self) -> F64Offset { - F64Offset(self.0.get() as usize - RcuRef::get_root(&self.0).base as usize) - } -} - -#[derive(Clone, Copy, Debug)] -pub struct F64Offset(usize); - -impl F64Offset { - #[inline(always)] - pub fn new(offset: usize) -> Self { - Self(offset) - } - - #[inline(always)] - pub fn from_ptr(ptr: F64Ptr) -> Self { - ptr.as_offset() - } - - #[inline(always)] - pub fn as_ptr(self) -> F64Ptr { - F64Ptr::from_offset(self) - } - - #[inline(always)] - pub fn to_u64(self) -> u64 { - self.0 as u64 - } -} - -impl PartialEq for F64Offset { - #[inline(always)] - fn eq(&self, other: &F64Offset) -> bool { - self.as_ptr() == other.as_ptr() - } -} - -impl Eq for F64Offset {} - -impl PartialOrd for F64Offset { - #[inline(always)] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for F64Offset { - #[inline(always)] - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.as_ptr().cmp(&other.as_ptr()) - } -} - -impl Hash for F64Offset { - #[inline(always)] - fn hash(&self, hasher: &mut H) { - self.as_ptr().hash(hasher) - } -} - -impl fmt::Display for F64Offset { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "F64Offset({})", self.0) - } -} - impl ArenaAllocated for Integer { type Payload = Self; #[inline] @@ -644,43 +393,16 @@ impl ArenaAllocated for HttpResponse { } } -impl ArenaAllocated for IndexPtr { - type Payload = Self; +impl ArenaAllocated for Child { + type Payload = ManuallyDrop; #[inline] fn tag() -> ArenaHeaderTag { - ArenaHeaderTag::IndexPtrUndefined + ArenaHeaderTag::ChildProcess } - - #[inline] - fn header_offset_from_payload() -> usize { - 0 - } - - /// # Safety - /// - the caller must guarantee that the pointee type of UntypedArenaPtr is T - /// - the pointer must be non-null - unsafe fn typed_ptr(ptr: UntypedArenaPtr) -> TypedArenaPtr { - TypedArenaPtr(NonNull::new_unchecked( - ptr.get_ptr().cast_mut().cast::(), - )) - } - - #[inline] - fn alloc(arena: &mut Arena, value: Self) -> TypedArenaPtr { - let slab = Box::new(IndexPtrSlab { - next: arena.base.take(), - index_ptr: value, - }); - - let (allocated_ptr, untyped_slab) = slab.to_untyped(); - arena.base = Some(untyped_slab); - allocated_ptr - } - - /// # Safety - /// - ptr points to an allocated slab of the correct kind - unsafe fn dealloc(ptr: NonNull>) { - drop(unsafe { Box::from_raw(ptr.as_ptr().cast::()) }); +} +impl AllocateInArena for Child { + fn arena_allocate(self, arena: &mut Arena) -> TypedArenaPtr { + Child::alloc(arena, ManuallyDrop::new(self)) } } @@ -691,13 +413,6 @@ pub struct AllocSlab { header: ArenaHeader, } -#[repr(C)] -#[derive(Debug)] -pub struct IndexPtrSlab { - next: Option, - index_ptr: IndexPtr, -} - const _: () = { if std::mem::align_of::() < std::mem::align_of::<*const ()>() { panic!("alignment of AllocSlab is too low"); @@ -706,34 +421,8 @@ const _: () = { if std::mem::offset_of!(AllocSlab, header) % std::mem::align_of::<*const ()>() != 0 { panic!("alignment of header not a multiple of pointers alignment"); } - - if std::mem::offset_of!(AllocSlab, header) != std::mem::offset_of!(IndexPtrSlab, index_ptr) { - panic!("IndexPtrSlab.index_ptr and AllocSlab.header are at different offsets"); - } }; -impl IndexPtrSlab { - #[inline] - pub fn to_untyped(self: Box) -> (TypedArenaPtr, UntypedArenaSlab) { - let raw_box = Box::into_raw(self); - - // safety: the pointer from Box::into_raw fullfills addr_of_mut's saftey requirements - let index_ptr_ptr = unsafe { ptr::addr_of_mut!((*raw_box).index_ptr) }; - let allocated_ptr = TypedArenaPtr( - // safety: the pointer points into a valid allocation so it is non null - unsafe { NonNull::new_unchecked(index_ptr_ptr) }, - ); - - let untyped_arena = UntypedArenaSlab { - // safety: pointer from Box::into_raw is never null - slab: unsafe { NonNull::new_unchecked(raw_box.cast::()) }, - tag: ::tag(), - }; - - (allocated_ptr, untyped_arena) - } -} - #[repr(C)] #[derive(Debug)] pub struct TypedAllocSlab { @@ -786,7 +475,8 @@ impl Drop for UntypedArenaSlab { #[derive(Debug)] pub struct Arena { base: Option, - pub f64_tbl: Arc, + pub f64_tbl: F64Table, + pub code_index_tbl: CodeIndexTable, } unsafe impl Send for Arena {} @@ -799,6 +489,7 @@ impl Arena { Arena { base: None, f64_tbl: F64Table::new(), + code_index_tbl: CodeIndexTable::new(), } } } @@ -874,11 +565,14 @@ unsafe fn drop_slab_in_place(value: NonNull, tag: ArenaHeaderTag) { ArenaHeaderTag::StandardErrorStream => { drop_typed_slab_in_place!(StandardErrorStream, value); } - ArenaHeaderTag::IndexPtrUndefined - | ArenaHeaderTag::IndexPtrDynamicUndefined - | ArenaHeaderTag::IndexPtrDynamicIndex - | ArenaHeaderTag::IndexPtrIndex => { - drop_typed_slab_in_place!(IndexPtr, value); + ArenaHeaderTag::PipeReader => { + drop_typed_slab_in_place!(PipeReader, value); + } + ArenaHeaderTag::PipeWriter => { + drop_typed_slab_in_place!(PipeWriter, value); + } + ArenaHeaderTag::ChildProcess => { + drop_typed_slab_in_place!(Child, value); } ArenaHeaderTag::NullStream => { unreachable!("NullStream is never arena allocated!"); @@ -904,35 +598,34 @@ const_assert!(mem::size_of::>() == 8); #[cfg(test)] mod tests { - use std::ops::Deref; - use crate::arena::*; use crate::atom_table::*; use crate::machine::mock_wam::*; - use crate::machine::partial_string::*; + use crate::types::*; use crate::parser::dashu::{Integer, Rational}; use ordered_float::OrderedFloat; #[test] fn float_ptr_cast() { - let wam = MockWAM::new(); + let mut wam = MockWAM::new(); let f = 0f64; let fp = float_alloc!(f, wam.machine_st.arena); - let mut cell = HeapCellValue::from(fp.clone()); + let mut cell = HeapCellValue::from(fp); - assert_eq!(cell.get_tag(), HeapCellValueTag::F64); + assert_eq!(cell.get_tag(), HeapCellValueTag::F64Offset); assert!(!cell.get_mark_bit()); - assert_eq!(fp.deref(), &OrderedFloat(f)); + assert_eq!(wam.machine_st.arena.f64_tbl.get_entry(fp), OrderedFloat(f)); cell.set_mark_bit(true); assert!(cell.get_mark_bit()); read_heap_cell!(cell, - (HeapCellValueTag::F64, ptr) => { - assert_eq!(OrderedFloat(*ptr), OrderedFloat(f)) + (HeapCellValueTag::F64Offset, offset) => { + let fp = wam.machine_st.arena.f64_tbl.get_entry(offset); + assert_eq!(fp, OrderedFloat(0f64)) } _ => { unreachable!() } ); @@ -943,12 +636,12 @@ mod tests { let mut wam = MockWAM::new(); #[cfg(target_pointer_width = "32")] let const_value = HeapCellValue::from(ConsPtr::build_with( - 0x0000_0431 as *const _, + std::ptr::without_provenance(0x0000_0431), ConsPtrMaskTag::Cons, )); #[cfg(target_pointer_width = "64")] let const_value = HeapCellValue::from(ConsPtr::build_with( - 0x0000_5555_ff00_0431 as *const _, + std::ptr::without_provenance(0x0000_5555_ff00_0431), ConsPtrMaskTag::Cons, )); @@ -992,7 +685,7 @@ mod tests { assert!(!big_int_ptr.as_ptr().is_null()); - let cell = HeapCellValue::from(Literal::Integer(big_int_ptr)); + let cell = HeapCellValue::from(big_int_ptr); assert_eq!(cell.get_tag(), HeapCellValueTag::Cons); let untyped_arena_ptr = match cell.to_untyped_arena_ptr() { @@ -1098,31 +791,6 @@ mod tests { _ => { unreachable!() } ); - // complete string - - let pstr_var_cell = - put_partial_string(&mut wam.machine_st.heap, "ronan", &wam.machine_st.atom_tbl); - let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; - - assert_eq!(pstr_cell.get_tag(), HeapCellValueTag::PStr); - - match pstr_cell.to_pstr() { - Some(pstr) => { - assert_eq!(&*pstr.as_str_from(0), "ronan"); - } - None => { - unreachable!(); - } - } - - read_heap_cell!(pstr_cell, - (HeapCellValueTag::PStr, pstr_atom) => { - let pstr = PartialString::from(pstr_atom); - assert_eq!(&*pstr.as_str_from(0), "ronan"); - } - _ => { unreachable!() } - ); - // fixnum let fixnum_cell = fixnum_as_cell!(Fixnum::build_with(3)); @@ -1141,7 +809,9 @@ mod tests { _ => { unreachable!() } ); - let fixnum_b_cell = fixnum_as_cell!(Fixnum::build_with(1 << 54)); + let fixnum_b_cell = fixnum_as_cell!( + Fixnum::build_with_checked(1i64 << 54).expect("1 << 54 fits in Fixnum") + ); assert_eq!(fixnum_b_cell.get_tag(), HeapCellValueTag::Fixnum); @@ -1150,41 +820,29 @@ mod tests { None => unreachable!(), } - if Fixnum::build_with_checked(1 << 56).is_ok() { - unreachable!() - } + Fixnum::build_with_checked(1i64 << 56).expect_err("1 << 56 is too large for fixnum"); - if Fixnum::build_with_checked(i64::MAX).is_ok() { - unreachable!() - } + Fixnum::build_with_checked(i64::MAX).expect_err("i64::MAX is too large for Fixnum"); + Fixnum::build_with_checked(i64::MIN).expect_err("i64::MIN is too small for Fixnum"); + assert_eq!( + Fixnum::build_with_checked(-1i64) + .expect("-1 fits in fixnum") + .get_num(), + -1 + ); - if Fixnum::build_with_checked(i64::MIN).is_ok() { - unreachable!() - } + Fixnum::build_with_checked((1i64 << 55) - 1) + .expect("(1 << 55) - 1 is the largest value that fits in Fixnum"); - match Fixnum::build_with_checked(-1) { - Ok(n) => assert_eq!(n.get_num(), -1), - _ => unreachable!(), - } + Fixnum::build_with_checked(-(1i64 << 55)) + .expect("-(1 << 55) is the smallest value that fits in fixnum"); + Fixnum::build_with_checked(-(1i64 << 55) - 1) + .expect_err("-(1<<55) - 1 is too small for Fixnum"); - match Fixnum::build_with_checked((1 << 55) - 1) { - Ok(n) => assert_eq!(n.get_num(), (1 << 55) - 1), - _ => unreachable!(), - } - - match Fixnum::build_with_checked(-(1 << 55)) { - Ok(n) => assert_eq!(n.get_num(), -(1 << 55)), - _ => unreachable!(), - } - - if Fixnum::build_with_checked(-(1 << 55) - 1).is_ok() { - unreachable!() - } - - match Fixnum::build_with_checked(-1) { - Ok(n) => assert_eq!(-n, Fixnum::build_with(1)), - _ => unreachable!(), - } + assert_eq!( + -Fixnum::build_with_checked(-1i64).expect("-1 fits in Fixnum"), + Fixnum::build_with(1) + ); // float @@ -1192,7 +850,7 @@ mod tests { let float_ptr = float_alloc!(float, wam.machine_st.arena); let cell = HeapCellValue::from(float_ptr); - assert_eq!(cell.get_tag(), HeapCellValueTag::F64); + assert_eq!(cell.get_tag(), HeapCellValueTag::F64Offset); // char @@ -1200,8 +858,8 @@ mod tests { let char_cell = char_as_cell!(c); read_heap_cell!(char_cell, - (HeapCellValueTag::Char, c) => { - assert_eq!(c, 'c'); + (HeapCellValueTag::Atom, (c, _arity)) => { + assert_eq!(&*c.as_str(), "c"); } _ => { unreachable!() } ); @@ -1210,8 +868,8 @@ mod tests { let cyrillic_char_cell = char_as_cell!(c); read_heap_cell!(cyrillic_char_cell, - (HeapCellValueTag::Char, c) => { - assert_eq!(c, 'Ћ'); + (HeapCellValueTag::Atom, (c, _arity)) => { + assert_eq!(&*c.as_str(), "Ћ"); } _ => { unreachable!() } ); diff --git a/src/arithmetic.rs b/src/arithmetic.rs index e5d6b114..f7a86569 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -7,6 +7,7 @@ use crate::debray_allocator::*; use crate::forms::*; use crate::instructions::*; use crate::iterators::*; +use crate::offset_table::*; use crate::targets::QueryInstruction; use crate::types::*; @@ -88,7 +89,7 @@ impl<'a> ArithInstructionIterator<'a> { #[derive(Debug)] pub(crate) enum ArithTermRef<'a> { - Literal(&'a Literal), + Literal(Literal), Op(Atom, usize), // name, arity. Var(Level, &'a Cell, VarPtr), } @@ -117,7 +118,7 @@ impl<'a> Iterator for ArithInstructionIterator<'a> { self.push_subterm(lvl.child_level(), &subterms[child_num]); } } - TermIterState::Literal(_, _, c) => return Some(Ok(ArithTermRef::Literal(c))), + TermIterState::Literal(_, _, c) => return Some(Ok(ArithTermRef::Literal(*c))), TermIterState::Var(lvl, cell, var_ptr) => { return Some(Ok(ArithTermRef::Var(lvl, cell, var_ptr))); } @@ -137,6 +138,7 @@ impl<'a> Iterator for ArithInstructionIterator<'a> { #[derive(Debug)] pub(crate) struct ArithmeticEvaluator<'a> { marker: &'a mut DebrayAllocator, + f64_tbl: &'a F64Table, interm: Vec, interm_c: usize, } @@ -155,11 +157,18 @@ impl<'a> ArithmeticTermIter<'a> for &'a Term { } } -fn push_literal(interm: &mut Vec, c: &Literal) -> Result<(), ArithmeticError> { +fn push_literal( + f64_tbl: &F64Table, + interm: &mut Vec, + c: &Literal, +) -> Result<(), ArithmeticError> { match c { Literal::Fixnum(n) => interm.push(ArithmeticTerm::Number(Number::Fixnum(*n))), Literal::Integer(n) => interm.push(ArithmeticTerm::Number(Number::Integer(*n))), - Literal::Float(n) => interm.push(ArithmeticTerm::Number(Number::Float(*n.as_ptr()))), + &Literal::F64Offset(offset) => { + let n = f64_tbl.get_entry(offset); + interm.push(ArithmeticTerm::Number(Number::Float(n))); + } Literal::Rational(n) => interm.push(ArithmeticTerm::Number(Number::Rational(*n))), Literal::Atom(name) if name == &atom!("e") => interm.push(ArithmeticTerm::Number( Number::Float(OrderedFloat(std::f64::consts::E)), @@ -177,9 +186,14 @@ fn push_literal(interm: &mut Vec, c: &Literal) -> Result<(), Ari } impl<'a> ArithmeticEvaluator<'a> { - pub(crate) fn new(marker: &'a mut DebrayAllocator, target_int: usize) -> Self { + pub(crate) fn new( + marker: &'a mut DebrayAllocator, + f64_tbl: &'a F64Table, + target_int: usize, + ) -> Self { ArithmeticEvaluator { marker, + f64_tbl, interm: Vec::new(), interm_c: target_int, } @@ -317,7 +331,7 @@ impl<'a> ArithmeticEvaluator<'a> { for term_ref in src.iter()? { match term_ref? { - ArithTermRef::Literal(c) => push_literal(&mut self.interm, c)?, + ArithTermRef::Literal(c) => push_literal(self.f64_tbl, &mut self.interm, &c)?, ArithTermRef::Var(lvl, cell, name) => { let var_num = name.to_var_num().unwrap(); @@ -357,9 +371,8 @@ impl<'a> ArithmeticEvaluator<'a> { pub(crate) fn rnd_i(n: &'_ Number, arena: &mut Arena) -> Result { match n { &Number::Integer(i) => { - let result = (&*i).try_into(); - if let Ok(value) = result { - Ok(fixnum!(Number, value, arena)) + if let Ok(value) = Fixnum::build_with_checked(&*i) { + Ok(Number::Fixnum(value)) } else { Ok(*n) } @@ -368,11 +381,14 @@ pub(crate) fn rnd_i(n: &'_ Number, arena: &mut Arena) -> Result { let f = f.floor(); - const I64_MIN_TO_F: OrderedFloat = OrderedFloat(i64::MIN as f64); - const I64_MAX_TO_F: OrderedFloat = OrderedFloat(i64::MAX as f64); + const FIXNUM_MIN_TO_F: OrderedFloat = OrderedFloat(Fixnum::MIN as f64); + const FIXNUM_MAX_TO_F: OrderedFloat = OrderedFloat(Fixnum::MAX as f64); - if I64_MIN_TO_F <= f && f <= I64_MAX_TO_F { - Ok(fixnum!(Number, f.into_inner() as i64, arena)) + if (FIXNUM_MIN_TO_F..=FIXNUM_MAX_TO_F).contains(&f) { + Ok(Number::Fixnum( + // Safety: We checked that the value is in range + unsafe { Fixnum::build_with_unchecked(f.into_inner() as i64) }, + )) } else { Ok(Number::Integer(arena_alloc!( Integer::try_from(classify_float(f.0)?).unwrap_or_else(|_| { @@ -385,8 +401,8 @@ pub(crate) fn rnd_i(n: &'_ Number, arena: &mut Arena) -> Result { let floor = r.floor(); - if let Ok(value) = (&floor).try_into() { - Ok(fixnum!(Number, value, arena)) + if let Ok(value) = Fixnum::build_with_checked(&floor) { + Ok(Number::Fixnum(value)) } else { Ok(Number::Integer(arena_alloc!(floor, arena))) } @@ -649,11 +665,11 @@ impl Ord for Number { } } -impl TryFrom for Number { +impl TryFrom<(HeapCellValue, &'_ F64Table)> for Number { type Error = (); #[inline] - fn try_from(value: HeapCellValue) -> Result { + fn try_from((value, f64_tbl): (HeapCellValue, &F64Table)) -> Result { read_heap_cell!(value, (HeapCellValueTag::Cons, c) => { match_untyped_arena_ptr!(c, @@ -668,8 +684,9 @@ impl TryFrom for Number { } ) } - (HeapCellValueTag::F64, n) => { - Ok(Number::Float(*n)) + (HeapCellValueTag::F64Offset, offset) => { + let n = f64_tbl.get_entry(offset); + Ok(Number::Float(n)) } (HeapCellValueTag::Fixnum | HeapCellValueTag::CutPoint, n) => { Ok(Number::Fixnum(n)) diff --git a/src/atom_table.rs b/src/atom_table.rs index 60b0b3c7..d7796d73 100644 --- a/src/atom_table.rs +++ b/src/atom_table.rs @@ -23,15 +23,124 @@ use indexmap::IndexSet; use scryer_modular_bitfield::prelude::*; +#[bitfield] +#[repr(u64)] +#[derive(Copy, Clone, Debug)] +pub struct AtomCell { + name: B48, + arity: B8, + #[allow(unused)] + f: bool, + #[allow(unused)] + m: bool, + #[allow(unused)] + is_inlined: bool, + #[allow(unused)] + tag: B5, +} + +const INLINED_ATOM_MAX_LEN: usize = 6; + +const_assert!(INLINED_ATOM_MAX_LEN < mem::size_of::()); +const_assert!(mem::size_of::() == 8); + +const_assert!(INLINED_ATOM_MAX_LEN < mem::size_of::()); +const_assert!(mem::size_of::() == 8); + +impl AtomCell { + #[inline] + fn new_static(index: u64) -> Self { + // upper 23 bits of index must be 0 + debug_assert_eq!(index & !((1 << 49) - 1), 0); + AtomCell::new() + .with_name(index) + .with_arity(0u8) + .with_m(false) + .with_f(false) + .with_is_inlined(false) + .with_tag(HeapCellValueTag::Atom as u8) + } + + #[inline] + fn new_inlined(string: &str, arity: u8) -> Self { + debug_assert!(string.len() <= INLINED_ATOM_MAX_LEN); + + let mut string_buf: [u8; 8] = [0u8; 8]; + string_buf[..string.len()].copy_from_slice(string.as_bytes()); + let encoding = u64::from_le_bytes(string_buf); + + AtomCell::new() + .with_name(encoding) + .with_arity(arity) + .with_m(false) + .with_f(false) + .with_is_inlined(true) + .with_tag(HeapCellValueTag::Atom as u8) + } + + #[inline] + pub fn new_char_inlined(c: char) -> Self { + if c == '\u{0}' { + return Self::new_static(NULL_ATOM.flat_index()); + } + + let mut char_buf = [0u8; 8]; + c.encode_utf8(&mut char_buf); + + let encoding = u64::from_le_bytes(char_buf); + + AtomCell::new() + .with_name(encoding) + .with_arity(0u8) + .with_m(false) + .with_f(false) + .with_is_inlined(true) + .with_tag(HeapCellValueTag::Atom as u8) + } + + #[inline] + pub fn build_with(atom_index: u64, arity: u8) -> Self { + debug_assert!((arity as usize) <= MAX_ARITY); + + AtomCell::new() + .with_name(atom_index >> 1) + .with_arity(arity) + .with_f(false) + .with_m(false) + .with_is_inlined(atom_index & 1 == 1) + .with_tag(HeapCellValueTag::Atom as u8) + } + + #[inline] + pub fn get_name(self) -> Atom { + Atom { + index: (self.name() << 1) | self.is_inlined() as u64, + } + } + + #[inline] + pub fn get_arity(self) -> usize { + self.arity() as usize + } + + #[inline] + pub fn get_name_and_arity(self) -> (Atom, usize) { + (self.get_name(), self.get_arity()) + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Atom { pub index: u64, } -const_assert!(mem::size_of::() == 8); - include!(concat!(env!("OUT_DIR"), "/static_atoms.rs")); +// populate these in STRINGS so they can be used from build_functor +const _: Atom = atom!("."); +const _: Atom = atom!("[]"); +const NULL_ATOM: Atom = atom!("\0"); + impl<'a> From<&'a Atom> for Atom { #[inline] fn from(atom: &'a Atom) -> Self { @@ -39,17 +148,6 @@ impl<'a> From<&'a Atom> for Atom { } } -impl From for Atom { - #[inline] - fn from(value: bool) -> Self { - if value { - atom!("true") - } else { - atom!("false") - } - } -} - impl indexmap::Equivalent for str { fn equivalent(&self, key: &Atom) -> bool { &*key.as_str() == self @@ -120,25 +218,23 @@ impl Hash for Atom { #[inline] fn hash(&self, hasher: &mut H) { self.as_str().hash(hasher) - // hasher.write_usize(self.index) } } pub enum AtomString<'a> { Static(&'a str), + Inlined([u8; 8]), Dynamic(AtomTableRef), } -impl AtomString<'_> { - pub fn map(self, f: F) -> Self - where - for<'a> F: FnOnce(&'a str) -> &'a str, - { - match self { - Self::Static(reference) => Self::Static(f(reference)), - Self::Dynamic(guard) => Self::Dynamic(AtomTableRef::map(guard, f)), - } - } +#[inline(always)] +fn inlined_to_str(bytes: &[u8; 8]) -> &str { + let slice_len = bytes + .iter() + .position(|&b| b == 0u8) + .unwrap_or(INLINED_ATOM_MAX_LEN); + + unsafe { str::from_utf8_unchecked(&bytes[..slice_len]) } } impl std::fmt::Debug for AtomString<'_> { @@ -158,6 +254,7 @@ impl std::ops::Deref for AtomString<'_> { fn deref(&self) -> &Self::Target { match self { Self::Static(reference) => reference, + Self::Inlined(inlined) => inlined_to_str(inlined), Self::Dynamic(guard) => guard.deref(), } } @@ -175,13 +272,32 @@ impl rustyline::completion::Candidate for AtomString<'_> { } impl Atom { - #[inline(always)] - pub fn is_static(self) -> bool { - (self.index as usize) < STRINGS.len() << 3 + #[inline] + fn new_inlined(string: &str) -> Self { + AtomCell::new_inlined(string, 0).get_name() } #[inline(always)] - pub fn as_ptr(self) -> Option> { + fn is_static(self) -> bool { + if self.is_inlined() { + true + } else { + (self.flat_index() as usize) < STRINGS.len() + } + } + + #[inline] + pub(crate) fn flat_index(self) -> u64 { + self.index >> 1 + } + + #[inline(always)] + pub(crate) fn is_inlined(self) -> bool { + self.index & 1 == 1 + } + + #[inline(always)] + fn as_ptr(self) -> Option> { if self.is_static() { None } else { @@ -192,7 +308,7 @@ impl Atom { let ptr = buf .block .base - .add((self.index as usize) - (STRINGS.len() << 3)); + .add(self.flat_index() as usize - STRINGS.len()); // TODO use std::ptr::from_raw_parts instead when feature ptr_metadata is stable rust-lang/rust#81513 let atom_data = &*(std::ptr::slice_from_raw_parts(ptr, 0) as *const AtomData); let len = atom_data.header.len(); @@ -208,8 +324,11 @@ impl Atom { #[inline(always)] pub fn len(self) -> usize { - if self.is_static() { - STRINGS[(self.index >> 3) as usize].len() + if let Some(s) = self.inlined_str() { + s.len() + } else if self.is_static() { + let index = self.flat_index(); + STRINGS[index as usize].len() } else { let len: u64 = self.as_ptr().unwrap().header.len(); len as usize @@ -220,11 +339,6 @@ impl Atom { self.len() == 0 } - #[inline(always)] - pub fn flat_index(self) -> u64 { - self.index >> 3 - } - pub fn as_char(self) -> Option { let s = self.as_str(); let mut it = s.chars(); @@ -239,14 +353,26 @@ impl Atom { } } + #[inline] + fn inlined_str<'a>(&self) -> Option> { + if self.is_inlined() { + Some(AtomString::Inlined(self.flat_index().to_le_bytes())) + } else { + None + } + } + #[inline] pub fn as_str(&self) -> AtomString<'static> { - if self.is_static() { - AtomString::Static(STRINGS[(self.index >> 3) as usize]) + if let Some(s) = self.inlined_str() { + s + } else if self.is_static() { + let index = self.flat_index() as usize; + AtomString::Static(STRINGS[index]) } else if let Some(ptr) = self.as_ptr() { AtomString::Dynamic(AtomTableRef::map(ptr, |ptr| &ptr.data)) } else { - AtomString::Static(STRINGS[(self.index >> 3) as usize]) + AtomString::Static(STRINGS[(self.index >> 1) as usize]) } } @@ -342,6 +468,10 @@ impl AtomTable { } pub fn build_with(atom_table: &AtomTable, string: &str) -> Atom { + if !string.is_empty() && string.len() <= INLINED_ATOM_MAX_LEN && !string.contains('\u{0}') { + return Atom::new_inlined(string); + } + loop { let mut block_epoch = atom_table.inner.read(); let mut table_epoch = block_epoch.table.read(); @@ -386,13 +516,18 @@ impl AtomTable { } }; - let ptr_base = block_epoch.block.base as usize; + let ptr_base = block_epoch.block.base.addr(); write_to_ptr(string, len_ptr); - let atom = Atom { - index: ((STRINGS.len() << 3) + len_ptr as usize - ptr_base) as u64, - }; + let atom = AtomCell::new() + .with_name((STRINGS.len() + len_ptr.addr() - ptr_base) as u64) + .with_arity(0) + .with_f(false) + .with_m(false) + .with_is_inlined(false) + .with_tag(HeapCellValueTag::Atom as u8) + .get_name(); let mut table = table_epoch.clone(); table.insert(atom); @@ -409,57 +544,3 @@ impl AtomTable { unsafe impl Send for AtomTable {} unsafe impl Sync for AtomTable {} - -#[bitfield] -#[repr(u64)] -#[derive(Copy, Clone, Debug)] -pub struct AtomCell { - name: B46, - arity: B10, - #[allow(unused)] - f: bool, - #[allow(unused)] - m: bool, - #[allow(unused)] - tag: B6, -} - -impl AtomCell { - #[inline] - pub fn build_with(name: u64, arity: u16, tag: HeapCellValueTag) -> Self { - if arity > 0 { - debug_assert!(arity as usize <= MAX_ARITY); - - AtomCell::new() - .with_name(name) - .with_arity(arity) - .with_f(false) - .with_tag(tag as u8) - } else { - AtomCell::new() - .with_name(name) - .with_f(false) - .with_tag(tag as u8) - } - } - - #[inline] - pub fn get_index(self) -> usize { - self.name() as usize - } - - #[inline] - pub fn get_name(self) -> Atom { - Atom::from((self.get_index() as u64) << 3) - } - - #[inline] - pub fn get_arity(self) -> usize { - self.arity() as usize - } - - #[inline] - pub fn get_name_and_arity(self) -> (Atom, usize) { - (Atom::from((self.get_index() as u64) << 3), self.get_arity()) - } -} diff --git a/src/codegen.rs b/src/codegen.rs index 5de85584..afde0ab7 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -6,6 +6,7 @@ use crate::forms::*; use crate::indexing::*; use crate::instructions::*; use crate::iterators::*; +use crate::offset_table::F64Table; use crate::parser::ast::*; use crate::targets::*; use crate::types::*; @@ -269,10 +270,10 @@ impl CodeGenSettings { } #[derive(Debug)] -pub(crate) struct CodeGenerator<'a> { - pub(crate) atom_tbl: &'a AtomTable, +pub(crate) struct CodeGenerator<'f64_tbl> { marker: DebrayAllocator, settings: CodeGenSettings, + f64_tbl: &'f64_tbl F64Table, pub(crate) skeleton: PredicateSkeleton, } @@ -319,33 +320,12 @@ impl DebrayAllocator { } } -// if the final argument of the structure is a Literal::Index, -// decrement the arity of the PutStructure instruction by 1. -fn trim_structure_by_last_arg(instr: &mut Instruction, last_arg: &Term) { - match instr { - Instruction::PutStructure(_, ref mut arity, _) - | Instruction::GetStructure(.., ref mut arity, _) => { - if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg { - // it is acceptable if arity == 0 is the result of - // this decrement. call/N will have to read the index - // constant for '$call_inline' to succeed. to find it, - // it must know the heap location of the index. - // self.store must stop before reading the atom into a - // register. - - *arity -= 1; - } - } - _ => {} - } -} - trait AddToFreeList<'a, Target: CompilationTarget<'a>> { fn add_term_to_free_list(&mut self, r: RegType); fn add_subterm_to_free_list(&mut self, term: &Term); } -impl<'a, 'b> AddToFreeList<'a, FactInstruction> for CodeGenerator<'b> { +impl<'a> AddToFreeList<'a, FactInstruction> for CodeGenerator<'_> { fn add_term_to_free_list(&mut self, r: RegType) { self.marker.add_reg_to_free_list(r); } @@ -353,7 +333,7 @@ impl<'a, 'b> AddToFreeList<'a, FactInstruction> for CodeGenerator<'b> { fn add_subterm_to_free_list(&mut self, _term: &Term) {} } -impl<'a, 'b> AddToFreeList<'a, QueryInstruction> for CodeGenerator<'b> { +impl<'a> AddToFreeList<'a, QueryInstruction> for CodeGenerator<'_> { #[inline(always)] fn add_term_to_free_list(&mut self, _r: RegType) {} @@ -375,12 +355,12 @@ fn structure_cell(term: &Term) -> Option<&Cell> { } } -impl<'b> CodeGenerator<'b> { - pub(crate) fn new(atom_tbl: &'b AtomTable, settings: CodeGenSettings) -> Self { +impl<'f64_tbl> CodeGenerator<'f64_tbl> { + pub(crate) fn new(f64_tbl: &'f64_tbl F64Table, settings: CodeGenSettings) -> Self { CodeGenerator { - atom_tbl, marker: DebrayAllocator::new(), settings, + f64_tbl, skeleton: PredicateSkeleton::new(), } } @@ -446,99 +426,99 @@ impl<'b> CodeGenerator<'b> { }; } - fn compile_target<'a, Target, Iter>(&mut self, iter: Iter, term_loc: GenContext) -> CodeDeque + fn compile_target<'a, Target, Iter>(&mut self, iter: Iter, context: GenContext) -> CodeDeque where Target: crate::targets::CompilationTarget<'a>, Iter: Iterator>, - CodeGenerator<'b>: AddToFreeList<'a, Target>, + CodeGenerator<'f64_tbl>: AddToFreeList<'a, Target>, { let mut target = CodeDeque::new(); for term in iter { match term { TermRef::AnonVar(lvl @ Level::Shallow) => { - if let GenContext::Head = term_loc { + if let GenContext::Head = context { self.marker.advance_arg(); } else { self.marker - .mark_anon_var::(lvl, term_loc, &mut target); + .mark_anon_var::(lvl, context, &mut target); } } TermRef::Clause(lvl, cell, name, terms) => { - self.marker - .mark_non_var::(lvl, term_loc, cell, &mut target); - target.push_back(Target::to_structure(lvl, name, terms.len(), cell.get())); + let terms_range = + if let Some(subterm @ Term::Literal(_, Literal::CodeIndexOffset(_))) = + terms.last() + { + self.subterm_to_instr::(subterm, context, &mut target); + 0..terms.len() - 1 + } else { + 0..terms.len() + }; - as AddToFreeList<'a, Target>>::add_term_to_free_list( + self.marker + .mark_non_var::(lvl, context, cell, &mut target); + target.push_back(Target::to_structure(lvl, name, terms_range.end, cell.get())); + + >::add_term_to_free_list( self, cell.get(), ); - if let Some(instr) = target.back_mut() { - if let Some(term) = terms.last() { - trim_structure_by_last_arg(instr, term); - } + for subterm in &terms[terms_range.clone()] { + self.subterm_to_instr::(subterm, context, &mut target); } - for subterm in terms { - self.subterm_to_instr::(subterm, term_loc, &mut target); - } - - for subterm in terms { - as AddToFreeList<'a, Target>>::add_subterm_to_free_list( + for subterm in &terms[terms_range] { + >::add_subterm_to_free_list( self, subterm, ); } } TermRef::Cons(lvl, cell, head, tail) => { self.marker - .mark_non_var::(lvl, term_loc, cell, &mut target); + .mark_non_var::(lvl, context, cell, &mut target); target.push_back(Target::to_list(lvl, cell.get())); - as AddToFreeList<'a, Target>>::add_term_to_free_list( + >::add_term_to_free_list( self, cell.get(), ); - self.subterm_to_instr::(head, term_loc, &mut target); - self.subterm_to_instr::(tail, term_loc, &mut target); + self.subterm_to_instr::(head, context, &mut target); + self.subterm_to_instr::(tail, context, &mut target); - as AddToFreeList<'a, Target>>::add_subterm_to_free_list( + >::add_subterm_to_free_list( self, head, ); - as AddToFreeList<'a, Target>>::add_subterm_to_free_list( + >::add_subterm_to_free_list( self, tail, ); } - TermRef::Literal(lvl @ Level::Shallow, cell, Literal::String(ref string)) => { - self.marker - .mark_non_var::(lvl, term_loc, cell, &mut target); - target.push_back(Target::to_pstr(lvl, *string, cell.get(), false)); - } TermRef::Literal(lvl @ Level::Shallow, cell, constant) => { self.marker - .mark_non_var::(lvl, term_loc, cell, &mut target); + .mark_non_var::(lvl, context, cell, &mut target); target.push_back(Target::to_constant(lvl, *constant, cell.get())); } TermRef::PartialString(lvl, cell, string, tail) => { self.marker - .mark_non_var::(lvl, term_loc, cell, &mut target); - let atom = AtomTable::build_with(self.atom_tbl, string); + .mark_non_var::(lvl, context, cell, &mut target); - target.push_back(Target::to_pstr(lvl, atom, cell.get(), true)); - self.subterm_to_instr::(tail, term_loc, &mut target); + target.push_back(Target::to_pstr(lvl, string.clone(), cell.get())); + self.subterm_to_instr::(tail, context, &mut target); } - TermRef::CompleteString(lvl, cell, atom) => { + TermRef::CompleteString(lvl, cell, string) => { self.marker - .mark_non_var::(lvl, term_loc, cell, &mut target); - target.push_back(Target::to_pstr(lvl, atom, cell.get(), false)); + .mark_non_var::(lvl, context, cell, &mut target); + + target.push_back(Target::to_pstr(lvl, string.clone(), cell.get())); + target.push_back(Target::constant_subterm(Literal::Atom(atom!("[]")))); } TermRef::Var(lvl @ Level::Shallow, cell, var) => { self.marker.mark_var::( var.to_var_num().unwrap(), lvl, cell, - term_loc, + context, &mut target, ); } @@ -600,9 +580,7 @@ impl<'b> CodeGenerator<'b> { compare_number_instr!(cmp, at_1, at_2) } InlinedClauseType::IsAtom(..) => match &terms[0] { - Term::Literal(_, Literal::Char(_)) - | Term::Literal(_, Literal::Atom(atom!("[]"))) - | Term::Literal(_, Literal::Atom(..)) => { + Term::Literal(_, Literal::Atom(..)) => { instr!("$succeed") } Term::Var(ref vr, ref name) => { @@ -630,9 +608,6 @@ impl<'b> CodeGenerator<'b> { | Term::CompleteString(..) => { instr!("$fail") } - Term::Literal(_, Literal::String(_)) => { - instr!("$fail") - } Term::Literal(..) => { instr!("$succeed") } @@ -654,8 +629,7 @@ impl<'b> CodeGenerator<'b> { Term::Clause(..) | Term::Cons(..) | Term::PartialString(..) - | Term::CompleteString(..) - | Term::Literal(_, Literal::String(..)) => { + | Term::CompleteString(..) => { instr!("$succeed") } Term::Var(ref vr, ref name) => { @@ -695,7 +669,7 @@ impl<'b> CodeGenerator<'b> { } }, InlinedClauseType::IsFloat(..) => match terms[0] { - Term::Literal(_, Literal::Float(_)) => { + Term::Literal(_, Literal::F64Offset(_)) => { instr!("$succeed") } Term::Var(ref vr, ref name) => { @@ -716,7 +690,7 @@ impl<'b> CodeGenerator<'b> { } }, InlinedClauseType::IsNumber(..) => match terms[0] { - Term::Literal(_, Literal::Float(_)) + Term::Literal(_, Literal::F64Offset(_)) | Term::Literal(_, Literal::Rational(_)) | Term::Literal(_, Literal::Integer(_)) | Term::Literal(_, Literal::Fixnum(_)) => { @@ -810,7 +784,6 @@ impl<'b> CodeGenerator<'b> { // inlined predicates are never counted, so this overrides nothing. self.add_call(code, call_instr, CallPolicy::Counted); - Ok(()) } @@ -821,7 +794,7 @@ impl<'b> CodeGenerator<'b> { term_loc: GenContext, arg: usize, ) -> Result { - let mut evaluator = ArithmeticEvaluator::new(&mut self.marker, target_int); + let mut evaluator = ArithmeticEvaluator::new(&mut self.marker, self.f64_tbl, target_int); evaluator.compile_is(term, term_loc, arg) } @@ -891,7 +864,7 @@ impl<'b> CodeGenerator<'b> { Term::Literal( _, c @ Literal::Integer(_) - | c @ Literal::Float(_) + | c @ Literal::F64Offset(_) | c @ Literal::Rational(_) | c @ Literal::Fixnum(_), ) => { @@ -909,7 +882,6 @@ impl<'b> CodeGenerator<'b> { let at = at.unwrap_or(interm!(1)); self.add_call(code, instr!("is", temp_v!(1), at), call_policy); - Ok(()) } @@ -924,9 +896,9 @@ impl<'b> CodeGenerator<'b> { while let Some(clause_item) = clause_iter.next() { match clause_item { - ClauseItem::Chunk(chunk) => { - for (idx, term) in chunk.iter().enumerate() { - let term_loc = if idx + 1 < chunk.len() { + ClauseItem::Chunk { terms } => { + for (idx, term) in terms.iter().enumerate() { + let term_loc = if idx + 1 < terms.len() { GenContext::Mid(chunk_num) } else { self.marker.in_tail_position = clause_iter.in_tail_position(); @@ -1147,27 +1119,24 @@ impl<'b> CodeGenerator<'b> { 'outer: for (right, clause) in clauses.iter().enumerate() { if let Some(args) = clause.args() { for (instantiated_arg_index, arg) in args.iter().enumerate() { - match arg { - Term::Var(..) | Term::AnonVar => {} - _ => { - if optimal_index != instantiated_arg_index { - if left >= right { - optimal_index = instantiated_arg_index; - continue 'outer; - } - - subseqs.push(ClauseSpan { - left, - right, - instantiated_arg_index: optimal_index, - }); - + if !matches!(arg, Term::Var(..) | Term::AnonVar) { + if optimal_index != instantiated_arg_index { + if left >= right { optimal_index = instantiated_arg_index; - left = right; + continue 'outer; } - continue 'outer; + subseqs.push(ClauseSpan { + left, + right, + instantiated_arg_index: optimal_index, + }); + + optimal_index = instantiated_arg_index; + left = right; } + + continue 'outer; } } } @@ -1260,7 +1229,7 @@ impl<'b> CodeGenerator<'b> { let index = code.len(); if clauses_len > 1 || self.settings.is_extensible { - code_offsets.index_term(arg, index, &mut clause_index_info, self.atom_tbl); + code_offsets.index_term(arg, index, &mut clause_index_info); } } diff --git a/src/debray_allocator.rs b/src/debray_allocator.rs index d44c36cb..53c7f206 100644 --- a/src/debray_allocator.rs +++ b/src/debray_allocator.rs @@ -1,6 +1,6 @@ use crate::allocator::*; use crate::codegen::SubsumedBranchHits; -use crate::forms::Level; +use crate::forms::{GenContext, Level}; use crate::instructions::*; use crate::machine::disjuncts::VarData; use crate::parser::ast::*; @@ -251,7 +251,7 @@ impl DebrayAllocator { } } - if self.branch_stack.len() > 0 { + if !self.branch_stack.is_empty() { for var_num in subsumed_hits { self.branch_stack.add_branch_occurrence(var_num); } @@ -534,7 +534,9 @@ impl DebrayAllocator { VarAlloc::Temp { safety, .. } => { *safety = VarSafetyStatus::unneeded(branch_designator); } - _ => unreachable!(), + _ => { + unreachable!() + } } } @@ -679,7 +681,7 @@ impl Allocator for DebrayAllocator { lvl: Level, term_loc: GenContext, code: &mut CodeDeque, - ) { + ) -> RegType { let r = RegType::Temp(self.alloc_reg_to_non_var()); match lvl { @@ -696,6 +698,8 @@ impl Allocator for DebrayAllocator { code.push_back(Target::argument_to_variable(r, k)); } }; + + r } fn mark_non_var<'a, Target: CompilationTarget<'a>>( diff --git a/src/ffi.rs b/src/ffi.rs index 4832bad1..55bb525b 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -324,7 +324,7 @@ impl ForeignFunctionTable { let mut pointer_args = Self::build_pointer_args(&mut args, &function_impl.args, &mut self.structs)?; - return unsafe { + unsafe { macro_rules! call_and_return { ($type:ty) => {{ let mut n: Box = Box::new(0); @@ -410,7 +410,7 @@ impl ForeignFunctionTable { } _ => unreachable!(), } - }; + } } fn read_struct( @@ -517,7 +517,7 @@ impl Value { fn as_ptr(&mut self) -> Result<*mut c_void, FFIError> { match self { Value::CString(ref mut cstr) => Ok(&mut *cstr as *mut _ as *mut c_void), - Value::Int(n) => Ok(*n as *mut c_void), + Value::Int(n) => Ok(std::ptr::with_exposed_provenance_mut(*n as usize)), _ => Err(FFIError::ValueCast), } } diff --git a/src/forms.rs b/src/forms.rs index 148d567f..26f37b3e 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -1,11 +1,12 @@ use crate::arena::*; use crate::atom_table::*; +use crate::functor_macro::*; use crate::instructions::*; use crate::machine::disjuncts::VarData; -use crate::machine::heap::*; use crate::machine::loader::PredicateQueue; use crate::machine::machine_errors::*; use crate::machine::machine_indices::*; +use crate::offset_table::OffsetTable; use crate::parser::ast::*; use crate::parser::dashu::{Integer, Rational}; use crate::parser::parser::CompositeOpDesc; @@ -26,18 +27,6 @@ use std::path::PathBuf; pub type PredicateKey = (Atom, usize); // name, arity. -/* -// vars of predicate, toplevel offset. Vec is always a vector -// of vars (we get their adjoining cells this way). -pub type JumpStub = Vec; -*/ - -#[derive(Debug)] -pub enum TopLevel { - Fact(Fact, VarData), // Term, line_num, col_num - Rule(Rule, VarData), // Rule, line_num, col_num -} - #[derive(Debug, Clone, Copy)] pub enum AppendOrPrepend { Append, @@ -82,6 +71,28 @@ pub enum CallPolicy { Counted, } +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum GenContext { + Head, + Mid(usize), + Last(usize), // Mid & Last: chunk_num +} + +impl GenContext { + #[inline] + pub fn chunk_num(&self) -> usize { + match self { + GenContext::Head => 0, + &GenContext::Mid(cn) | &GenContext::Last(cn) => cn, + } + } + + #[inline] + pub fn is_last(self) -> bool { + matches!(self, GenContext::Last(_)) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ChunkType { Head, @@ -121,7 +132,7 @@ impl ChunkType { #[derive(Debug)] pub enum ChunkedTerms { Branch(Vec>), - Chunk(VecDeque), + Chunk { terms: VecDeque }, } #[derive(Debug)] @@ -161,22 +172,30 @@ impl ChunkedTermVec { #[inline] pub fn add_chunk(&mut self) { - self.chunk_vec - .push_back(ChunkedTerms::Chunk(VecDeque::from(vec![]))); + let chunk = ChunkedTerms::Chunk { + terms: VecDeque::from(vec![]), + }; + self.chunk_vec.push_back(chunk); } pub fn push_chunk_term(&mut self, term: QueryTerm) { match self.chunk_vec.back_mut() { Some(ChunkedTerms::Branch(_)) => { - self.chunk_vec - .push_back(ChunkedTerms::Chunk(VecDeque::from(vec![term]))); + let chunk = ChunkedTerms::Chunk { + terms: VecDeque::from(vec![term]), + }; + + self.chunk_vec.push_back(chunk); } - Some(ChunkedTerms::Chunk(chunk)) => { - chunk.push_back(term); + Some(ChunkedTerms::Chunk { terms, .. }) => { + terms.push_back(term); } None => { - self.chunk_vec - .push_back(ChunkedTerms::Chunk(VecDeque::from(vec![term]))); + let chunk = ChunkedTerms::Chunk { + terms: VecDeque::from(vec![term]), + }; + + self.chunk_vec.push_back(chunk); } } } @@ -353,9 +372,9 @@ pub enum ModuleSource { impl ModuleSource { pub(crate) fn as_functor_stub(&self) -> MachineStub { - match self { + match *self { ModuleSource::Library(name) => { - functor!(atom!("library"), [atom(name)]) + functor!(atom!("library"), [atom_as_cell(name)]) } ModuleSource::File(name) => { functor!(name) @@ -608,9 +627,9 @@ impl Default for Number { impl fmt::Display for Number { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Number::Float(fl) => write!(f, "{}", fl), - Number::Integer(n) => write!(f, "{}", n), - Number::Rational(r) => write!(f, "{}", r), + Number::Float(fl) => write!(f, "{fl}"), + Number::Integer(n) => write!(f, "{n}"), + Number::Rational(r) => write!(f, "{r}"), Number::Fixnum(n) => write!(f, "{}", n.get_num()), } } @@ -679,17 +698,18 @@ impl ArenaFrom for Number { impl ArenaFrom for Number { #[inline] fn arena_from(value: u32, _arena: &mut Arena) -> Number { - Number::Fixnum(Fixnum::build_with(value as i64)) + Number::Fixnum(Fixnum::build_with(value)) } } impl ArenaFrom for Number { #[inline] fn arena_from(value: i32, _arena: &mut Arena) -> Number { - Number::Fixnum(Fixnum::build_with(value as i64)) + Number::Fixnum(Fixnum::build_with(value)) } } +/* impl ArenaFrom for Literal { #[inline] fn arena_from(value: Number, arena: &mut Arena) -> Literal { @@ -701,6 +721,21 @@ impl ArenaFrom for Literal { } } } +*/ + +impl ArenaFrom for HeapCellValue { + #[inline] + fn arena_from(value: u64, arena: &mut Arena) -> HeapCellValue { + HeapCellValue::from(fixnum!(Literal, value as i64, arena)) + } +} + +impl ArenaFrom for HeapCellValue { + #[inline] + fn arena_from(value: usize, arena: &mut Arena) -> HeapCellValue { + HeapCellValue::arena_from(value as u64, arena) + } +} impl ArenaFrom for HeapCellValue { #[inline] @@ -708,7 +743,7 @@ impl ArenaFrom for HeapCellValue { match value { Number::Fixnum(n) => fixnum_as_cell!(n), Number::Integer(n) => typed_arena_ptr_as_cell!(n), - Number::Float(OrderedFloat(n)) => HeapCellValue::from(float_alloc!(n, arena)), + Number::Float(n) => HeapCellValue::from(arena.f64_tbl.build_with(n)), Number::Rational(n) => typed_arena_ptr_as_cell!(n), } } @@ -771,10 +806,10 @@ impl Number { } } -#[derive(Debug, Clone)] +#[derive(Debug, Copy, Clone)] pub(crate) enum OptArgIndexKey { - Literal(usize, usize, Literal, Vec), // index, IndexingCode location, opt arg, alternatives - List(usize, usize), // index, IndexingCode location + Literal(usize, usize, Literal, Option), // index, IndexingCode location, opt arg, alternatives + List(usize, usize), // index, IndexingCode location None, Structure(usize, usize, Atom, usize), // index, IndexingCode location, name, arity } diff --git a/src/functor_macro.rs b/src/functor_macro.rs new file mode 100644 index 00000000..591fa185 --- /dev/null +++ b/src/functor_macro.rs @@ -0,0 +1,798 @@ +//! A macro to construct functor terms ready to be written to the WAM +//! heap. + +use crate::atom_table::*; +use crate::instructions::IndexingCodePtr; +use crate::machine::heap::Heap; +use crate::parser::ast::Fixnum; +use crate::types::*; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum FunctorElement { + AbsoluteCell(HeapCellValue), + Cell(HeapCellValue), + InnerFunctor(u64, Vec), + String(u64, String), +} + +// helper macros +macro_rules! count { + () => (0); + ( $x:tt $($xs:tt)* ) => (1 + count!($($xs)*)); +} + +// core macros + +/* + * functor! is more declarative now, with fewer effects and more + * work done at compile time using const functions. With these + * advantages come new quirks: expressions must generally be wrapped + * in round parentheses for rustc to parse them. See the tests module + * below for examples, especially those involving atom! + * subexpressions. + */ + +macro_rules! functor { + ($name:expr) => ({ + vec![FunctorElement::Cell(atom_as_cell!($name))] + }); + ($name:expr, [$($dt:ident($($value:tt),*)),+]) => ({ + build_functor!([$($dt($($value),*)),*], + [FunctorElement::Cell(atom_as_cell!($name, count!($($dt) *)))], + 1, + []) + }); +} + +macro_rules! inner_functor { + ($name:expr, $res_len:expr, [$($dt:ident($($value:tt),*)),+]) => ({ + build_functor!([$($dt($($value),*)),*], + [FunctorElement::Cell(atom_as_cell!($name, count!($($dt) *)))], + 1 + $res_len, + []) + }); +} + +macro_rules! build_functor { + ([], [$($res:expr),*], $res_len:expr, [$($subfunctor:expr),*]) => ({ + vec![$($res,)* $($subfunctor),*] + }); + ([indexing_code_ptr($e:expr) $(, $dt:ident($($value:tt),*))*], + [$($res:expr),*], + $res_len:expr, + [$($subfunctor:expr),*]) => ({ + let (inner_functor, cell_size) = indexing_code_ptr($e); + let referent = if cell_size == 1 { + heap_loc_as_cell!(1u64 + count!($($dt)*) + $res_len) + } else { + str_loc_as_cell!(1u64 + count!($($dt)*) + $res_len) + }; + + build_functor!([$($dt($($value),*)),*], + [$($res, )* FunctorElement::Cell(referent)], + 1 + cell_size + $res_len, + [$($subfunctor, )* FunctorElement::InnerFunctor(cell_size, inner_functor)]) + }); + ([fixnum($e:expr) $(, $dt:ident($($value:tt),*))*], + [$($res:expr),*], + $res_len:expr, + [$($subfunctor:expr),*]) => ({ + build_functor!([$($dt($($value),*)),*], + [$($res, )* FunctorElement::Cell(fixnum_as_cell!(/*FIXME this is not safe*/ unsafe{Fixnum::build_with_unchecked($e as i64)}))], + 1 + $res_len, + [$($subfunctor),*]) + }); + ([cell($e:expr) $(, $dt:ident($($value:tt),*))*], + [$($res:expr),*], + $res_len:expr, + [$($subfunctor:expr),*]) => ({ + build_functor!([$($dt($($value),*)),*], + [$($res, )* FunctorElement::AbsoluteCell($e)], + 1 + $res_len, + [$($subfunctor),*]) + }); + ([literal($e:expr) $(, $dt:ident($($value:tt),*))*], + [$($res:expr),*], + $res_len:expr, + [$($subfunctor:expr),*]) => ({ + build_functor!([$($dt($($value),*)),*], + [$($res, )* FunctorElement::AbsoluteCell(HeapCellValue::from($e))], + 1 + $res_len, + [$($subfunctor),*]) + }); + ([number($n:expr, $arena:expr) $(, $dt:ident($($value:tt),*))*], + [$($res:expr),*], + $res_len:expr, + [$($subfunctor:expr),*]) => ({ + let number_cell = HeapCellValue::arena_from($n, $arena); + + build_functor!([$($dt($($value),*)),*], + [$($res, )* FunctorElement::Cell(number_cell)], + 1 + $res_len, + [$($subfunctor),*]) + }); + ([list([]) $(, $dt:ident($($value:tt),*))*], + [$($res:expr),*], + $res_len:expr, + [$($subfunctor:expr),*]) => ({ + build_functor!([$($dt($($value),*)),*], + [$($res, )* FunctorElement::Cell(empty_list_as_cell!())], + 1 + $res_len, + [$($subfunctor),*]) + }); + ([list([$id:ident($($id_value:tt),*) $(, $in_dt:ident($($in_value:tt),*))*]) $(, $dt:ident($($value:tt),*))*], + [$($res:expr),*], + $res_len:expr, + [$($subfunctor:expr),*]) => ({ + build_functor!([functor((atom!(".")), [$id($($id_value),*), list([$($in_dt($($in_value),*)),*])]) + $(, $dt($($value),*))*], + [$($res),*], + $res_len, + [$($subfunctor),*]) + }); + ([string($s:expr) $(, $dt:ident($($value:tt),*))*], [$($res:expr),*], $res_len:expr, [$($subfunctor:expr),*]) => ({ + #[allow(unused_parens)] + let string = $s; + let pstr_len = cell_index!(Heap::compute_pstr_size(&string)) as u64; + let result_len = 1 + count!($($dt)*) + $res_len; + + build_functor!([$($dt($($value),*)),*], + [$($res, )* FunctorElement::Cell(pstr_loc_as_cell!(heap_index!(result_len as usize) as u64))], + 1 + $res_len + pstr_len, + [$($subfunctor, )* FunctorElement::String(pstr_len, string)]) + }); + ([atom_as_cell($n:expr) $(, $dt:ident($($value:tt),*))*], [$($res:expr),*], $res_len:expr, [$($subfunctor:expr),*]) => ({ + build_functor!([$($dt($($value),*)),*], + [$($res, )* FunctorElement::Cell(atom_as_cell!($n))], + 1 + $res_len, + [$($subfunctor),*]) + }); + ([functor($stub:expr) $(, $dt:ident($($value:tt),*))*], [$($res:expr),*], $res_len:expr, [$($subfunctor:expr),*]) => ({ + let result_len = 1u64 + count!($($dt)*) + $res_len; + let inner_functor_size = cell_index!(Heap::compute_functor_byte_size(&$stub)) as u64; + + build_functor!([$($dt($($value),*)),*], + [$($res, )* FunctorElement::Cell(str_loc_as_cell!(result_len))], + 1 + $res_len + inner_functor_size, + [$($subfunctor, )* + FunctorElement::InnerFunctor(inner_functor_size, $stub)]) + }); + ([$id:ident($n:expr) $(, $dt:ident($($value:tt),*))*], [$($res:expr),*], $res_len:expr, [$($subfunctor:expr),*]) => ({ + build_functor!([$($dt($($value),*)),*], + [$($res, )* FunctorElement::Cell($id!($n))], + 1 + $res_len, + [$($subfunctor),*]) + }); + ([functor($name:expr, [$($in_dt:ident($($in_value:tt),*)),+]) $(, $dt:ident($($value:tt),*))*], + [$($res:expr),*], + $res_len:expr, + [$($subfunctor:expr),*]) => ({ + let result_len = 1u64 + count!($($dt)*) + $res_len; + let inner_functor = inner_functor!($name, 0, [$($in_dt($($in_value),*)),*]); + let inner_functor_size = cell_index!(Heap::compute_functor_byte_size(&inner_functor)) as u64; + + build_functor!([$($dt($($value),*)),*], + [$($res, )* FunctorElement::Cell(str_loc_as_cell!(result_len))], + 1 + $res_len + inner_functor_size, + [$($subfunctor, )* + FunctorElement::InnerFunctor(inner_functor_size, inner_functor)]) + }); +} + +pub(crate) fn indexing_code_ptr(code_ptr: IndexingCodePtr) -> (Vec, u64) { + match code_ptr { + IndexingCodePtr::DynamicExternal(o) => { + (functor!(atom!("dynamic_external"), [fixnum(o)]), 2) + } + IndexingCodePtr::External(o) => (functor!(atom!("external"), [fixnum(o)]), 2), + IndexingCodePtr::Internal(o) => (functor!(atom!("internal"), [fixnum(o)]), 2), + IndexingCodePtr::Fail => (vec![FunctorElement::Cell(atom_as_cell!(atom!("fail")))], 1), + } +} + +pub(crate) fn variadic_functor( + name: Atom, + arity: usize, + iter: impl Iterator>, +) -> Vec { + let mut arg_vec = vec![ + FunctorElement::Cell(atom_as_cell!(name, arity)), + FunctorElement::Cell(list_loc_as_cell!(2)), + ]; + + let key_value_pairs: Vec<_> = iter.collect(); + let num_items = key_value_pairs.len(); + let mut functor_offset = 0; + + for (idx, kv_func) in key_value_pairs.iter().enumerate() { + let functor_size = cell_index!(Heap::compute_functor_byte_size(kv_func)); + + arg_vec.push(FunctorElement::Cell(str_loc_as_cell!( + 2 + num_items * 2 + functor_offset + ))); + arg_vec.push(FunctorElement::Cell(list_loc_as_cell!(4 + 2 * idx))); + + functor_offset += functor_size; + } + + arg_vec.pop(); + arg_vec.push(FunctorElement::Cell(empty_list_as_cell!())); + + arg_vec.extend(key_value_pairs.into_iter().map(|kv_func| { + let inner_functor_size = cell_index!(Heap::compute_functor_byte_size(&kv_func)); + FunctorElement::InnerFunctor(inner_functor_size as u64, kv_func) + })); + + arg_vec +} + +#[cfg(test)] +#[allow(unused_parens)] +mod tests { + use super::*; + use indexmap::indexmap; + use std::string::String; + use FunctorElement::*; + + #[test] + fn basic_terms() { + let functor = functor!( + atom!("first"), + [atom_as_cell((atom!("a"))), char_as_cell('c')] + ); + + assert_eq!(functor.len(), 3); + + assert_eq!(functor[0], Cell(atom_as_cell!(atom!("first"), 2))); + assert_eq!(functor[1], Cell(atom_as_cell!(atom!("a")))); + assert_eq!(functor[2], Cell(char_as_cell!('c'))); + + let functor = functor!( + atom!("second"), + [ + atom_as_cell((atom!("a"))), + functor((atom!("b")), [fixnum(1), fixnum(2)]), + char_as_cell('c') + ] + ); + + assert_eq!(functor.len(), 5); + + assert_eq!(functor[0], Cell(atom_as_cell!(atom!("second"), 3))); + assert_eq!(functor[1], Cell(atom_as_cell!(atom!("a")))); + assert_eq!(functor[2], Cell(str_loc_as_cell!(4))); + assert_eq!(functor[3], Cell(char_as_cell!('c'))); + assert_eq!( + functor[4], + InnerFunctor(3, functor!(atom!("b"), [fixnum(1), fixnum(2)])) + ); + + let functor = functor!( + atom!("third"), + [ + atom_as_cell((atom!("a"))), + functor((atom!("b")), [fixnum(1), fixnum(2)]), + functor((atom!("c")), [fixnum(1), fixnum(2)]), + char_as_cell('c') + ] + ); + + assert_eq!(functor.len(), 7); + + assert_eq!(functor[0], Cell(atom_as_cell!(atom!("third"), 4))); + assert_eq!(functor[1], Cell(atom_as_cell!(atom!("a")))); + assert_eq!(functor[2], Cell(str_loc_as_cell!(5))); + assert_eq!(functor[3], Cell(str_loc_as_cell!(8))); + assert_eq!(functor[4], Cell(char_as_cell!('c'))); + assert_eq!( + functor[5], + InnerFunctor(3, functor!(atom!("b"), [fixnum(1), fixnum(2)])) + ); + assert_eq!( + functor[6], + InnerFunctor(3, functor!(atom!("c"), [fixnum(1), fixnum(2)])) + ); + + let functor = functor!( + atom!("fourth"), + [ + atom_as_cell((atom!("a"))), + functor((atom!("b")), [fixnum(1), fixnum(2)]), + functor((atom!("c")), [fixnum(1)]), + functor((atom!("d")), [fixnum(453), fixnum(2)]), + char_as_cell('c') + ] + ); + + assert_eq!(functor.len(), 9); + + assert_eq!(functor[0], Cell(atom_as_cell!(atom!("fourth"), 5))); + assert_eq!(functor[1], Cell(atom_as_cell!(atom!("a")))); + assert_eq!(functor[2], Cell(str_loc_as_cell!(6))); + assert_eq!(functor[3], Cell(str_loc_as_cell!(9))); + assert_eq!(functor[4], Cell(str_loc_as_cell!(11))); + assert_eq!(functor[5], Cell(char_as_cell!('c'))); + assert_eq!( + functor[6], + InnerFunctor(3, functor!(atom!("b"), [fixnum(1), fixnum(2)])) + ); + assert_eq!( + functor[7], + InnerFunctor(2, functor!(atom!("c"), [fixnum(1)])) + ); + assert_eq!( + functor[8], + InnerFunctor(3, functor!(atom!("d"), [fixnum(453), fixnum(2)])) + ); + } + + #[test] + fn basic_terms_in_heap() { + let functor = functor!( + atom!("first"), + [atom_as_cell((atom!("a"))), char_as_cell('b')] + ); + + assert_eq!(functor.len(), 3); + + let mut heap = Heap::new(); + let mut functor_writer = Heap::functor_writer(functor); + let loc = functor_writer(&mut heap).unwrap(); + + assert_eq!(loc, str_loc_as_cell!(0)); + + assert_eq!(heap[0], atom_as_cell!(atom!("first"), 2)); + assert_eq!(heap[1], atom_as_cell!(atom!("a"))); + assert_eq!(heap[2], char_as_cell!('b')); + + heap.truncate(2); + + let functor = functor!( + atom!("second"), + [ + atom_as_cell((atom!("a"))), + functor((atom!("b")), [fixnum(1), fixnum(2)]), + functor((atom!("c")), [fixnum(1), fixnum(2)]), + char_as_cell('b') + ] + ); + + assert_eq!(functor.len(), 7); + + let mut functor_writer = Heap::functor_writer(functor); + let loc = functor_writer(&mut heap).unwrap(); + + assert_eq!(loc, str_loc_as_cell!(2)); + + assert_eq!(heap[2], atom_as_cell!(atom!("second"), 4)); + assert_eq!(heap[3], atom_as_cell!(atom!("a"))); + assert_eq!(heap[4], str_loc_as_cell!(7)); + assert_eq!(heap[5], str_loc_as_cell!(10)); + assert_eq!(heap[6], char_as_cell!('b')); + assert_eq!(heap[7], atom_as_cell!(atom!("b"), 2)); + assert_eq!(heap[8], fixnum_as_cell!(Fixnum::build_with(1))); + assert_eq!(heap[9], fixnum_as_cell!(Fixnum::build_with(2))); + assert_eq!(heap[10], atom_as_cell!(atom!("c"), 2)); + assert_eq!(heap[11], fixnum_as_cell!(Fixnum::build_with(1))); + assert_eq!(heap[12], fixnum_as_cell!(Fixnum::build_with(2))); + } + + #[test] + fn nested_functors() { + let functor = functor!( + atom!("first"), + [ + atom_as_cell((atom!("a"))), + functor( + (atom!("d")), + [ + fixnum(1), + functor( + (atom!("b")), + [atom_as_cell((atom!("c"))), char_as_cell('c')] + ) + ] + ), + functor((atom!("e")), [fixnum(453), fixnum(2)]), + char_as_cell('b') + ] + ); + + assert_eq!(functor.len(), 7); + + assert_eq!(functor[0], Cell(atom_as_cell!(atom!("first"), 4))); + assert_eq!(functor[1], Cell(atom_as_cell!(atom!("a")))); + assert_eq!(functor[2], Cell(str_loc_as_cell!(5))); + assert_eq!(functor[3], Cell(str_loc_as_cell!(11))); + assert_eq!(functor[4], Cell(char_as_cell!('b'))); + assert_eq!( + functor[5], + InnerFunctor( + 6, + vec![ + Cell(atom_as_cell!(atom!("d"), 2)), + Cell(fixnum_as_cell!(Fixnum::build_with(1))), + Cell(str_loc_as_cell!(3)), + InnerFunctor( + 3, + functor!(atom!("b"), [atom_as_cell((atom!("c"))), char_as_cell('c')]) + ) + ] + ) + ); + assert_eq!( + functor[6], + InnerFunctor(3, functor!(atom!("e"), [fixnum(453), fixnum(2)])) + ); + } + + #[test] + fn nested_functors_in_heap() { + let functor = functor!( + atom!("first"), + [ + atom_as_cell((atom!("a"))), + functor( + (atom!("second")), + [ + fixnum(1), + functor( + (atom!("third")), + [atom_as_cell((atom!("b"))), char_as_cell('c')] + ) + ] + ), + functor((atom!("fourth")), [fixnum(453), fixnum(2)]), + char_as_cell('b') + ] + ); + + let mut heap = Heap::new(); + let mut functor_writer = Heap::functor_writer(functor); + let loc = functor_writer(&mut heap).unwrap(); + + assert_eq!(loc, str_loc_as_cell!(0)); + + assert_eq!(heap.cell_len(), 14); + + assert_eq!(heap[0], atom_as_cell!(atom!("first"), 4)); + assert_eq!(heap[1], atom_as_cell!(atom!("a"))); + assert_eq!(heap[2], str_loc_as_cell!(5)); + assert_eq!(heap[3], str_loc_as_cell!(11)); + assert_eq!(heap[4], char_as_cell!('b')); + assert_eq!(heap[5], atom_as_cell!(atom!("second"), 2)); + assert_eq!(heap[6], fixnum_as_cell!(Fixnum::build_with(1))); + assert_eq!(heap[7], str_loc_as_cell!(8)); + assert_eq!(heap[8], atom_as_cell!(atom!("third"), 2)); + assert_eq!(heap[9], atom_as_cell!(atom!("b"))); + assert_eq!(heap[10], char_as_cell!('c')); + assert_eq!(heap[11], atom_as_cell!(atom!("fourth"), 2)); + assert_eq!(heap[12], fixnum_as_cell!(Fixnum::build_with(453))); + assert_eq!(heap[13], fixnum_as_cell!(Fixnum::build_with(2))); + } + + #[test] + fn functors_with_strings_in_heap() { + let functor = functor!(atom!("first"), [string((String::from("a string")))]); + + assert_eq!(functor.len(), 3); + + let mut heap = Heap::new(); + let mut functor_writer = Heap::functor_writer(functor); + let loc = functor_writer(&mut heap).unwrap(); + + assert_eq!(loc, str_loc_as_cell!(0)); + assert_eq!(heap.cell_len(), 5); + + assert_eq!(heap[0], atom_as_cell!(atom!("first"), 1)); + assert_eq!(heap[1], pstr_loc_as_cell!(heap_index!(2))); + assert_eq!( + heap.slice_to_str(heap_index!(2), "a string".len()), + "a string" + ); + assert_eq!(heap[4], empty_list_as_cell!()); + + heap.truncate(0); + + let functor = functor!( + atom!("second"), + [string((String::from("a stuttered\0 string")))] + ); + + let mut functor_writer = Heap::functor_writer(functor); + functor_writer(&mut heap).unwrap(); + + assert_eq!(heap.cell_len(), 10); + + assert_eq!(heap[0], atom_as_cell!(atom!("second"), 1)); + assert_eq!(heap[1], pstr_loc_as_cell!(heap_index!(2))); + assert_eq!( + heap.slice_to_str(heap_index!(2), "a stuttered".len()), + "a stuttered" + ); + assert_eq!(heap[4], list_loc_as_cell!(5)); + assert_eq!(heap[5], char_as_cell!('\u{0}')); + assert_eq!(heap[6], pstr_loc_as_cell!(heap_index!(7))); + assert_eq!( + heap.slice_to_str(heap_index!(7), " string".len()), + " string" + ); + assert_eq!(heap[9], empty_list_as_cell!()); + } + + #[test] + fn functors_with_lists_in_heap() { + let functor = functor!( + atom!("first"), + [list([fixnum(1), atom_as_cell((atom!("a"))), fixnum(2)])] + ); + + assert_eq!(functor.len(), 3); + + let mut heap = Heap::new(); + let mut functor_writer = Heap::functor_writer(functor); + + functor_writer(&mut heap).unwrap(); + + assert_eq!(heap.cell_len(), 11); + + assert_eq!(heap[0], atom_as_cell!(atom!("first"), 1)); + assert_eq!(heap[1], str_loc_as_cell!(2)); + assert_eq!(heap[2], atom_as_cell!(atom!("."), 2)); + assert_eq!(heap[3], fixnum_as_cell!(Fixnum::build_with(1))); + assert_eq!(heap[4], str_loc_as_cell!(5)); + assert_eq!(heap[5], atom_as_cell!(atom!("."), 2)); + assert_eq!(heap[6], atom_as_cell!(atom!("a"))); + assert_eq!(heap[7], str_loc_as_cell!(8)); + assert_eq!(heap[8], atom_as_cell!(atom!("."), 2)); + assert_eq!(heap[9], fixnum_as_cell!(Fixnum::build_with(2))); + assert_eq!(heap[10], empty_list_as_cell!()); + } + + #[test] + fn inlined_atoms() { + let atom_table = AtomTable::new(); + let inlined = AtomTable::build_with(&atom_table, "inline"); + + assert!(inlined.is_inlined()); + assert_eq!(&*inlined.as_str(), "inline"); + + let non_inlined = AtomTable::build_with(&atom_table, "longer non-inlined atom"); + + assert!(!non_inlined.is_inlined()); + assert_eq!(&*non_inlined.as_str(), "longer non-inlined atom"); + } + + #[test] + fn functors_with_indexing_code_ptr() { + let code_ptr = IndexingCodePtr::Internal(0); + let functor = functor!( + atom!("first"), + [ + string((String::from("a string"))), + indexing_code_ptr(code_ptr) + ] + ); + + let mut heap = Heap::new(); + let mut functor_writer = Heap::functor_writer(functor); + + functor_writer(&mut heap).unwrap(); + + assert_eq!(heap.cell_len(), 8); + + assert_eq!(heap[0], atom_as_cell!(atom!("first"), 2)); + assert_eq!(heap[1], pstr_loc_as_cell!(heap_index!(3))); + assert_eq!(heap[2], str_loc_as_cell!(6)); + assert_eq!( + heap.slice_to_str(heap_index!(3), "a string".len()), + "a string" + ); + assert_eq!(heap[5], empty_list_as_cell!()); + assert_eq!(heap[6], atom_as_cell!(atom!("internal"), 1)); + assert_eq!(heap[7], fixnum_as_cell!(Fixnum::build_with(0))); + + heap.truncate(0); + + let functor = functor!( + atom!("second"), + [ + string((String::from("a string"))), + functor( + (atom!("third")), + [ + atom_as_cell((atom!("a"))), + string((String::from("another string"))), + indexing_code_ptr(code_ptr) + ] + ) + ] + ); + + let mut functor_writer = Heap::functor_writer(functor); + functor_writer(&mut heap).unwrap(); + + assert_eq!(heap.cell_len(), 15); + + assert_eq!(heap[0], atom_as_cell!(atom!("second"), 2)); + assert_eq!(heap[1], pstr_loc_as_cell!(heap_index!(3))); + assert_eq!(heap[2], str_loc_as_cell!(6)); + assert_eq!( + heap.slice_to_str(heap_index!(3), "a string".len()), + "a string" + ); + assert_eq!(heap[5], empty_list_as_cell!()); + assert_eq!(heap[6], atom_as_cell!(atom!("third"), 3)); + assert_eq!(heap[7], atom_as_cell!(atom!("a"))); + assert_eq!(heap[8], pstr_loc_as_cell!(heap_index!(10))); + assert_eq!(heap[9], str_loc_as_cell!(13)); + assert_eq!( + heap.slice_to_str(heap_index!(10), "another string".len()), + "another string" + ); + assert_eq!(heap[12], empty_list_as_cell!()); + assert_eq!(heap[13], atom_as_cell!(atom!("internal"), 1)); + assert_eq!(heap[14], fixnum_as_cell!(Fixnum::build_with(0))); + + let functor = functor!( + atom!("fourth"), + [ + string((String::from("a string"))), + functor( + (atom!("a")), + [ + functor( + (atom!("fifth")), + [ + fixnum(5), + string((String::from("another string"))), + indexing_code_ptr(code_ptr) + ] + ), + string((String::from("and another"))) + ] + ) + ] + ); + + heap.truncate(0); + + let mut functor_writer = Heap::functor_writer(functor); + functor_writer(&mut heap).unwrap(); + + assert_eq!(heap.cell_len(), 21); + + assert_eq!(heap[0], atom_as_cell!(atom!("fourth"), 2)); + assert_eq!(heap[1], pstr_loc_as_cell!(heap_index!(3))); + assert_eq!(heap[2], str_loc_as_cell!(6)); + assert_eq!( + heap.slice_to_str(heap_index!(3), "a string".len()), + "a string" + ); + assert_eq!(heap[5], empty_list_as_cell!()); + assert_eq!(heap[6], atom_as_cell!(atom!("a"), 2)); + assert_eq!(heap[7], str_loc_as_cell!(9)); + assert_eq!(heap[8], pstr_loc_as_cell!(heap_index!(18))); // <-- wrong! + assert_eq!(heap[9], atom_as_cell!(atom!("fifth"), 3)); + assert_eq!(heap[10], fixnum_as_cell!(Fixnum::build_with(5))); + assert_eq!(heap[11], pstr_loc_as_cell!(heap_index!(13))); + assert_eq!(heap[12], str_loc_as_cell!(16)); + assert_eq!( + heap.slice_to_str(heap_index!(13), "another string".len()), + "another string" + ); + assert_eq!(heap[15], empty_list_as_cell!()); + assert_eq!(heap[16], atom_as_cell!(atom!("internal"), 1)); + assert_eq!(heap[17], fixnum_as_cell!(Fixnum::build_with(0))); + assert_eq!( + heap.slice_to_str(heap_index!(18), "and another".len()), + "and another" + ); + assert_eq!(heap[20], empty_list_as_cell!()); + + let constants = indexmap![ + atom_as_cell!(atom!("a")) => IndexingCodePtr::External(2), + atom_as_cell!(atom!("d")) => IndexingCodePtr::External(7), + ]; + + let functor = variadic_functor( + atom!("switch_on_constants"), + 1, + constants + .iter() + .map(|(c, ptr)| functor!(atom!(":"), [cell((*c)), indexing_code_ptr((*ptr))])), + ); + + heap.truncate(0); + + let mut functor_writer = Heap::functor_writer(functor); + functor_writer(&mut heap).unwrap(); + + assert_eq!(heap[0], atom_as_cell!(atom!("switch_on_constants"), 1)); + assert_eq!(heap[1], list_loc_as_cell!(2)); + assert_eq!(heap[2], str_loc_as_cell!(6)); + assert_eq!(heap[3], list_loc_as_cell!(4)); + assert_eq!(heap[4], str_loc_as_cell!(11)); + assert_eq!(heap[5], empty_list_as_cell!()); + assert_eq!(heap[6], atom_as_cell!(atom!(":"), 2)); + assert_eq!(heap[7], atom_as_cell!(atom!("a"))); + assert_eq!(heap[8], str_loc_as_cell!(9)); + assert_eq!(heap[9], atom_as_cell!(atom!("external"), 1)); + assert_eq!(heap[10], fixnum_as_cell!(Fixnum::build_with(2))); + assert_eq!(heap[11], atom_as_cell!(atom!(":"), 2)); + assert_eq!(heap[12], atom_as_cell!(atom!("d"))); + assert_eq!(heap[13], str_loc_as_cell!(14)); + assert_eq!(heap[14], atom_as_cell!(atom!("external"), 1)); + assert_eq!(heap[15], fixnum_as_cell!(Fixnum::build_with(7))); + } + + #[test] + fn undefined_procedure_functor() { + // existence_error + let culprit = functor!(atom!("/"), [atom_as_cell((atom!("a"))), fixnum(1)]); + + let stub = functor!( + atom!("existence_error"), + [ + atom_as_cell((atom!("procedure"))), + functor((culprit.clone())) + ] + ); + + println!("{stub:?}"); + + // now the error form + let lineless_error_form = functor!(atom!("error"), [functor(stub), functor(culprit)]); + + println!("{lineless_error_form:?}"); + + let mut heap = Heap::new(); + let mut functor_writer = Heap::functor_writer(lineless_error_form); + + functor_writer(&mut heap).unwrap(); + + assert_eq!(heap[0], atom_as_cell!(atom!("error"), 2)); + assert_eq!(heap[1], str_loc_as_cell!(3)); + assert_eq!(heap[2], str_loc_as_cell!(9)); + assert_eq!(heap[3], atom_as_cell!(atom!("existence_error"), 2)); + assert_eq!(heap[4], atom_as_cell!(atom!("procedure"))); + assert_eq!(heap[5], str_loc_as_cell!(6)); // is str_loc_as_cell!(3) + assert_eq!(heap[6], atom_as_cell!(atom!("/"), 2)); + assert_eq!(heap[7], atom_as_cell!(atom!("a"))); + assert_eq!(heap[8], fixnum_as_cell!(Fixnum::build_with(1))); + assert_eq!(heap[9], atom_as_cell!(atom!("/"), 2)); + assert_eq!(heap[10], atom_as_cell!(atom!("a"))); + assert_eq!(heap[11], fixnum_as_cell!(Fixnum::build_with(1))); + } + + #[test] + fn argless_functor() { + let name = functor!(atom!("[]")); + + assert_eq!(name.len(), 1); + + let mut heap = Heap::new(); + let mut functor_writer = Heap::functor_writer(name); + let loc = functor_writer(&mut heap).unwrap(); + + assert_eq!(loc, heap_loc_as_cell!(0)); + } + + #[test] + fn predefined_subfunctors() { + let stub = functor!(atom!("sub"), [atom_as_cell((atom!("[]")))]); + let name = functor!(atom!("super"), [functor(stub)]); + + let mut heap = Heap::new(); + let mut functor_writer = Heap::functor_writer(name); + + functor_writer(&mut heap).unwrap(); + + assert_eq!(heap.cell_len(), 4); + + assert_eq!(heap[0], atom_as_cell!(atom!("super"), 1)); + assert_eq!(heap[1], str_loc_as_cell!(2)); + assert_eq!(heap[2], atom_as_cell!(atom!("sub"), 1)); + assert_eq!(heap[3], empty_list_as_cell!()); + } +} diff --git a/src/heap_iter.rs b/src/heap_iter.rs index 61fde0eb..97489a6b 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -116,24 +116,10 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> { } } (HeapCellValueTag::PStrLoc, h) => { - let h = if self.heap[h].get_tag() == HeapCellValueTag::PStr { - h - } else { - debug_assert_eq!(self.heap[h].get_tag(), HeapCellValueTag::PStrOffset); - self.heap[h].get_value() as usize - }; + let tail_idx = self.heap.scan_slice_to_str(h).tail_idx; - if self.heap[h].get_mark_bit() == self.mark_phase { - continue; - } - - self.heap[h].set_mark_bit(self.mark_phase); - - if self.heap[h].get_tag() == HeapCellValueTag::PStr { - let value = self.heap[h+1]; - self.heap[h+1].set_mark_bit(self.mark_phase); - self.iter_stack.push(value); - } + self.heap[tail_idx].set_mark_bit(self.mark_phase); + self.iter_stack.push(self.heap[tail_idx]); } _ => { } @@ -249,7 +235,7 @@ impl ListElisionPolicy for NonListElider { #[derive(Debug)] pub struct StackfulPreOrderHeapIter<'a, ElideLists> { - pub heap: &'a mut Vec, + pub heap: &'a mut Heap, pub machine_stack: &'a mut Stack, stack: Vec, h: IterStackLoc, @@ -264,8 +250,6 @@ impl<'a, ElideLists> Drop for StackfulPreOrderHeapIter<'a, ElideLists> { cell.set_forwarding_bit(false); cell.set_mark_bit(false); } - - self.heap.pop(); } } @@ -358,9 +342,8 @@ impl<'a, ElideLists> StackfulPreOrderHeapIter<'a, ElideLists> { impl<'a, ElideLists: ListElisionPolicy> StackfulPreOrderHeapIter<'a, ElideLists> { #[inline] - fn new(heap: &'a mut Vec, stack: &'a mut Stack, cell: HeapCellValue) -> Self { - let h = IterStackLoc::iterable_loc(heap.len(), HeapOrStackTag::Heap); - heap.push(cell); + fn new(heap: &'a mut Heap, stack: &'a mut Stack, root_loc: usize) -> Self { + let h = IterStackLoc::iterable_loc(root_loc, HeapOrStackTag::Heap); Self { heap, @@ -385,8 +368,7 @@ impl<'a, ElideLists: ListElisionPolicy> StackfulPreOrderHeapIter<'a, ElideLists> } (HeapCellValueTag::Str | HeapCellValueTag::AttrVar | - HeapCellValueTag::Var | - HeapCellValueTag::PStrLoc, vh) => { + HeapCellValueTag::Var, vh) => { if self.heap[vh].get_mark_bit() { self.read_cell_mut(loc).set_forwarding_bit(true); } @@ -428,7 +410,7 @@ impl<'a, ElideLists: ListElisionPolicy> StackfulPreOrderHeapIter<'a, ElideLists> } read_heap_cell!(*cell, - (HeapCellValueTag::Str | HeapCellValueTag::PStrLoc, vh) => { + (HeapCellValueTag::Str, vh) => { let loc = IterStackLoc::iterable_loc(vh, HeapOrStackTag::Heap); self.push_if_unmarked(loc); @@ -459,20 +441,30 @@ impl<'a, ElideLists: ListElisionPolicy> StackfulPreOrderHeapIter<'a, ElideLists> self.push_if_unmarked(loc); self.stack.push(IterStackLoc::mark_loc(vs, HeapOrStackTag::Stack)); } - (HeapCellValueTag::PStrOffset, offset) => { - self.push_if_unmarked(IterStackLoc::iterable_loc(offset, HeapOrStackTag::Heap)); - self.stack.push(IterStackLoc::iterable_loc((h.value()+1) as usize, HeapOrStackTag::Heap)); + (HeapCellValueTag::PStrLoc, vh) => { + let cell = *cell; + let tail_idx = self.heap.scan_slice_to_str(vh).tail_idx; - return Some(self.read_cell(h)); - } - (HeapCellValueTag::PStr) => { - let tail_loc = IterStackLoc::iterable_loc((h.value()+1) as usize, HeapOrStackTag::Heap); + // forward the current PStrLoc cell if the zero + // byte at the end of the string buffer + // is marked + let buf_bytes = self.heap[tail_idx - 1].into_bytes(); - self.push_if_unmarked(IterStackLoc::iterable_loc(h.value() as usize, HeapOrStackTag::Heap)); - self.stack.push(tail_loc); - self.forward_if_referent_marked(tail_loc); + if buf_bytes[7] != 0u8 { + let cell = self.read_cell_mut(h); + cell.set_forwarding_bit(true); + } - return Some(self.read_cell(h)); + // now mark it as if were a HeapCellValue, even + // though it's not! this is fine as long as its tag + // is never inspected, which it isn't. + + self.push_if_unmarked( + IterStackLoc::iterable_loc(tail_idx - 1, HeapOrStackTag::Heap), + ); + self.stack.push(IterStackLoc::mark_loc(tail_idx, HeapOrStackTag::Heap)); + + return Some(cell); } (HeapCellValueTag::Atom, (_name, arity)) => { let l = h.value() as usize; @@ -512,7 +504,7 @@ impl<'a, ElideLists: ListElisionPolicy> Iterator for StackfulPreOrderHeapIter<'a #[inline(always)] pub(crate) fn cycle_detecting_stackless_preorder_iter( - heap: &'_ mut [HeapCellValue], + heap: &'_ mut Heap, start: usize, ) -> CycleDetectingIter<'_, true> { // const generics argument of true so that cycle discovery stops @@ -522,17 +514,17 @@ pub(crate) fn cycle_detecting_stackless_preorder_iter( #[inline(always)] pub(crate) fn stackful_preorder_iter<'a, ElideLists: ListElisionPolicy>( - heap: &'a mut Vec, + heap: &'a mut Heap, stack: &'a mut Stack, - cell: HeapCellValue, + root_loc: usize, ) -> StackfulPreOrderHeapIter<'a, ElideLists> { - StackfulPreOrderHeapIter::new(heap, stack, cell) + StackfulPreOrderHeapIter::new(heap, stack, root_loc) } #[derive(Debug)] pub(crate) struct PostOrderIterator { focus: IterStackLoc, - base_iter: Iter, + pub(crate) base_iter: Iter, base_iter_valid: bool, parent_stack: Vec<(usize, HeapCellValue, IterStackLoc)>, // number of children, parent node, focus. } @@ -581,7 +573,7 @@ impl Iterator for PostOrderIterator { (HeapCellValueTag::Lis) => { self.parent_stack.push((2, item, focus)); } - (HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset) => { + (HeapCellValueTag::PStrLoc) => { // HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset) => { self.parent_stack.push((1, item, focus)); } _ => { @@ -610,32 +602,10 @@ impl FocusedHeapIter for PostOrderIterator { } } -impl PostOrderIterator { - /* return true if the term at heap offset idx_loc is a - * direct/inlined subterm of a structure at the focus of - * self.stack.last(). this function is used to determine, e.g., - * ownership of inlined code indices. - */ - #[inline] - pub(crate) fn direct_subterm_of_str(&self, idx_loc: usize) -> bool { - if let Some((_child_count, item, focus)) = self.parent_stack.last() { - read_heap_cell!(item, - (HeapCellValueTag::Atom, (_name, arity)) => { - let focus = focus.value() as usize; - return focus + arity >= idx_loc && focus < idx_loc; - } - _ => {} - ); - } - - false - } -} - pub(crate) type LeftistPostOrderHeapIter<'a, ElideLists> = PostOrderIterator>; -impl<'a, ElideLists: ListElisionPolicy> LeftistPostOrderHeapIter<'a, ElideLists> { +impl LeftistPostOrderHeapIter<'_, ElideLists> { #[inline] pub fn pop_stack(&mut self) { if let Some((child_count, ..)) = self.parent_stack.last() { @@ -657,14 +627,16 @@ impl<'a, ElideLists: ListElisionPolicy> LeftistPostOrderHeapIter<'a, ElideLists> pub(crate) fn stackful_post_order_iter<'a, ElideLists: ListElisionPolicy>( heap: &'a mut Heap, stack: &'a mut Stack, - cell: HeapCellValue, + root_loc: usize, ) -> LeftistPostOrderHeapIter<'a, ElideLists> { - PostOrderIterator::new(StackfulPreOrderHeapIter::new(heap, stack, cell)) + PostOrderIterator::new(StackfulPreOrderHeapIter::new(heap, stack, root_loc)) } #[cfg(test)] mod tests { use super::*; + + use crate::functor_macro::*; use crate::machine::gc::IteratorUMP; use crate::machine::mock_wam::*; @@ -673,7 +645,7 @@ mod tests { #[inline(always)] pub(crate) fn stackless_preorder_iter( - heap: &mut [HeapCellValue], + heap: &mut Heap, start: usize, ) -> StacklessPreOrderHeapIter { StacklessPreOrderHeapIter::::new(heap, start) @@ -692,15 +664,20 @@ mod tests { fn heap_stackless_iter_tests() { let mut wam = MockWAM::new(); + // clear the heap of resource error data etc + wam.machine_st.heap.clear(); + let f_atom = atom!("f"); let a_atom = atom!("a"); let b_atom = atom!("b"); - wam.machine_st - .heap - .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); + let mut functor_writer = Heap::functor_writer(functor!( + f_atom, + [atom_as_cell(a_atom), atom_as_cell(b_atom)] + )); - wam.machine_st.heap.push(str_loc_as_cell!(0)); + let cell = functor_writer(&mut wam.machine_st.heap).unwrap(); + wam.machine_st.heap.push_cell(cell).unwrap(); { let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 3); @@ -725,17 +702,19 @@ mod tests { wam.machine_st.heap.clear(); - wam.machine_st.heap.extend(functor!( + let mut functor_writer = Heap::functor_writer(functor!( f_atom, [ - atom(a_atom), - atom(b_atom), - atom(a_atom), - cell(str_loc_as_cell!(0)) + atom_as_cell(a_atom), + atom_as_cell(b_atom), + atom_as_cell(a_atom), + str_loc_as_cell(0) ] )); - wam.machine_st.heap.push(str_loc_as_cell!(0)); + let cell = functor_writer(&mut wam.machine_st.heap).unwrap(); + + wam.machine_st.heap.push_cell(cell).unwrap(); for _ in 0..20 { let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 5); @@ -745,50 +724,6 @@ mod tests { atom_as_cell!(f_atom, 4) ); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), str_loc_as_cell!(0)); - - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(a_atom) - ); - - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(b_atom) - ); - - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(a_atom) - ); - - assert_eq!(iter.next(), None); - } - - all_cells_unmarked(&wam.machine_st.heap); - - wam.machine_st.heap.clear(); - - wam.machine_st.heap.push(str_loc_as_cell!(1)); - - wam.machine_st.heap.extend(functor!( - f_atom, - [ - atom(a_atom), - atom(b_atom), - atom(a_atom), - cell(str_loc_as_cell!(1)) - ] - )); - - for _ in 0..200000 { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); - - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - atom_as_cell!(f_atom, 4) - ); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), str_loc_as_cell!(1)); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), atom_as_cell!(a_atom) @@ -810,7 +745,7 @@ mod tests { wam.machine_st.heap.clear(); { - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + wam.machine_st.heap.push_cell(heap_loc_as_cell!(0)).unwrap(); let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); @@ -826,11 +761,15 @@ mod tests { wam.machine_st.heap.clear(); // term is: [a, b] - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(atom_as_cell!(a_atom)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(atom_as_cell!(b_atom)); - wam.machine_st.heap.push(empty_list_as_cell!()); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(atom_as_cell!(a_atom)); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(atom_as_cell!(b_atom)); + section.push_cell(empty_list_as_cell!()); + }); { let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); @@ -862,10 +801,8 @@ mod tests { all_cells_unmarked(&wam.machine_st.heap); - wam.machine_st.heap.pop(); - // now make the list cyclic. - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + wam.machine_st.heap[4] = heap_loc_as_cell!(0); { let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); @@ -896,181 +833,96 @@ mod tests { wam.machine_st.heap.clear(); - // first a 'dangling' partial string, later modified to be a two-part complete string, - // then a three-part cyclic string involving an uncompacted list of chars. - let pstr_var_cell = - put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); - let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; + // first a 'dangling' partial string, later modified to be a + // two-part complete string, then a three-part cyclic string + // involving an uncompacted list of chars. - wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + wam.machine_st.heap.allocate_pstr("abc ").unwrap(); + + wam.machine_st.heap.push_cell(heap_loc_as_cell!(1)).unwrap(); + wam.machine_st.heap.push_cell(pstr_loc_as_cell!(0)).unwrap(); { let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 2); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(0) + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), heap_loc_as_cell!(1), ); - assert!(iter.next().is_none()); } - assert_eq!(wam.machine_st.heap[0], pstr_cell); - assert_eq!(wam.machine_st.heap[1], heap_loc_as_cell!(1)); + wam.machine_st.heap[1] = pstr_loc_as_cell!(heap_index!(3)); - wam.machine_st.heap[1] = pstr_loc_as_cell!(3); + wam.machine_st.heap.allocate_pstr("def").unwrap(); - let pstr_second_var_cell = - put_partial_string(&mut wam.machine_st.heap, "def", &wam.machine_st.atom_tbl); - - let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; + wam.machine_st.heap.push_cell(heap_loc_as_cell!(4)).unwrap(); + wam.machine_st.heap.push_cell(pstr_loc_as_cell!(0)).unwrap(); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 2); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 5); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(0) + ); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(heap_index!(3)) + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), heap_loc_as_cell!(4), ); - assert!(iter.next().is_none()); } - assert_eq!(wam.machine_st.heap[0], pstr_cell); - assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(3)); - assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(0)); - assert_eq!(wam.machine_st.heap[3], pstr_second_cell); - assert_eq!(wam.machine_st.heap[4], heap_loc_as_cell!(4)); - - wam.machine_st.heap.pop(); - wam.machine_st.heap.push(pstr_loc_as_cell!(5)); - wam.machine_st.heap.push(pstr_offset_as_cell!(0)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(2))); - - wam.machine_st.heap[2] = heap_loc_as_cell!(4); + wam.machine_st.heap[4] = pstr_loc_as_cell!(heap_index!(3) + 2); { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 2); + let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 5); - let pstr_offset_cell = pstr_offset_as_cell!(0); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - fixnum_as_cell!(Fixnum::build_with(2)) + pstr_loc_as_cell!(0) + ); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(heap_index!(3)) + ); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(heap_index!(3) + 2) ); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); - assert_eq!(iter.next(), None); } - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(3) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_loc_as_cell!(5) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - pstr_offset_as_cell!(0) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[6]), - fixnum_as_cell!(Fixnum::build_with(2)) - ); - - wam.machine_st.heap.truncate(4); - - wam.machine_st.heap.pop(); - wam.machine_st - .heap - .push(pstr_loc_as_cell!(wam.machine_st.heap.len() + 1)); - - wam.machine_st.heap.push(pstr_offset_as_cell!(0)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(0i64))); - - wam.machine_st.heap.push(pstr_loc_as_cell!(0)); - - { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 6); - let pstr_offset_cell = pstr_offset_as_cell!(0); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - pstr_loc_as_cell!(4) - ); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - fixnum_as_cell!(Fixnum::build_with(0)) - ); - - assert_eq!(iter.next(), None); - } - - all_cells_unmarked(&wam.machine_st.heap); - - wam.machine_st.heap[5] = fixnum_as_cell!(Fixnum::build_with(1i64)); - - { - let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 6); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - pstr_loc_as_cell!(4) - ); - - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - pstr_offset_as_cell!(0) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - pstr_offset_as_cell!(0) - ); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - fixnum_as_cell!(Fixnum::build_with(1)) - ); - - assert_eq!(iter.next(), None); - } - - assert_eq!(wam.machine_st.heap[4], pstr_offset_as_cell!(0)); - assert_eq!( - wam.machine_st.heap[5], - fixnum_as_cell!(Fixnum::build_with(1i64)) - ); - - all_cells_unmarked(&wam.machine_st.heap); - wam.machine_st.heap.clear(); - let functor = functor!(f_atom, [atom(a_atom), atom(b_atom), atom(b_atom)]); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(str_loc_as_cell!(5)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(str_loc_as_cell!(5)); - wam.machine_st.heap.push(empty_list_as_cell!()); + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(str_loc_as_cell!(5)); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(str_loc_as_cell!(5)); + section.push_cell(empty_list_as_cell!()); + }); - wam.machine_st.heap.extend(functor); + let mut functor_writer = Heap::functor_writer(functor!( + f_atom, + [ + atom_as_cell(a_atom), + atom_as_cell(b_atom), + atom_as_cell(b_atom) + ] + )); + + functor_writer(&mut wam.machine_st.heap).unwrap(); { let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); @@ -1240,11 +1092,15 @@ mod tests { wam.machine_st.heap.clear(); - wam.machine_st.heap.push(heap_loc_as_cell!(1)); - wam.machine_st.heap.push(heap_loc_as_cell!(2)); - wam.machine_st.heap.push(heap_loc_as_cell!(3)); - wam.machine_st.heap.push(heap_loc_as_cell!(3)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(heap_loc_as_cell!(1)); + section.push_cell(heap_loc_as_cell!(2)); + section.push_cell(heap_loc_as_cell!(3)); + section.push_cell(heap_loc_as_cell!(3)); + section.push_cell(heap_loc_as_cell!(0)); + }); { let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 4); @@ -1275,9 +1131,13 @@ mod tests { wam.machine_st.heap.clear(); // print L = [L|L]. - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(list_loc_as_cell!(1)); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(list_loc_as_cell!(1)); + }); { let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); @@ -1313,21 +1173,25 @@ mod tests { wam.machine_st.heap.clear(); // term is [X,f(Y),Z]. - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(heap_loc_as_cell!(1)); - wam.machine_st.heap.push(heap_loc_as_cell!(3)); // 2 - wam.machine_st.heap.push(list_loc_as_cell!(4)); // 3 - wam.machine_st.heap.push(str_loc_as_cell!(6)); // 4 - wam.machine_st.heap.push(heap_loc_as_cell!(8)); - wam.machine_st.heap.push(atom_as_cell!(f_atom, 1)); // 6 - wam.machine_st.heap.push(heap_loc_as_cell!(11)); // 7 - wam.machine_st.heap.push(list_loc_as_cell!(9)); - wam.machine_st.heap.push(heap_loc_as_cell!(9)); - wam.machine_st.heap.push(empty_list_as_cell!()); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); - wam.machine_st.heap.push(attr_var_as_cell!(11)); // linked from 7. - wam.machine_st.heap.push(heap_loc_as_cell!(12)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(heap_loc_as_cell!(1)); + section.push_cell(heap_loc_as_cell!(3)); // 2 + section.push_cell(list_loc_as_cell!(4)); // 3 + section.push_cell(str_loc_as_cell!(6)); // 4 + section.push_cell(heap_loc_as_cell!(8)); + section.push_cell(atom_as_cell!(f_atom, 1)); // 6 + section.push_cell(heap_loc_as_cell!(11)); // 7 + section.push_cell(list_loc_as_cell!(9)); + section.push_cell(heap_loc_as_cell!(9)); + section.push_cell(empty_list_as_cell!()); + + section.push_cell(attr_var_as_cell!(11)); // linked from 7. + section.push_cell(heap_loc_as_cell!(12)); + section.push_cell(heap_loc_as_cell!(0)); + }); { let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 13); @@ -1369,22 +1233,25 @@ mod tests { let clpz_atom = atom!("clpz"); let p_atom = atom!("p"); - wam.machine_st.heap.pop(); - wam.machine_st.heap.pop(); + wam.machine_st.heap.truncate(12); - wam.machine_st.heap.push(heap_loc_as_cell!(13)); // 12 - wam.machine_st.heap.push(list_loc_as_cell!(14)); // 13 - wam.machine_st.heap.push(str_loc_as_cell!(16)); // 14 - wam.machine_st.heap.push(heap_loc_as_cell!(19)); // 15 - wam.machine_st.heap.push(atom_as_cell!(clpz_atom, 2)); // 16 - wam.machine_st.heap.push(atom_as_cell!(a_atom)); // 17 - wam.machine_st.heap.push(atom_as_cell!(b_atom)); // 18 - wam.machine_st.heap.push(list_loc_as_cell!(20)); // 19 - wam.machine_st.heap.push(str_loc_as_cell!(22)); // 20 - wam.machine_st.heap.push(empty_list_as_cell!()); // 21 - wam.machine_st.heap.push(atom_as_cell!(p_atom, 1)); // 22 - wam.machine_st.heap.push(heap_loc_as_cell!(23)); // 23 - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(heap_loc_as_cell!(13)); // 12 + section.push_cell(list_loc_as_cell!(14)); // 13 + section.push_cell(str_loc_as_cell!(16)); // 14 + section.push_cell(heap_loc_as_cell!(19)); // 15 + section.push_cell(atom_as_cell!(clpz_atom, 2)); // 16 + section.push_cell(atom_as_cell!(a_atom)); // 17 + section.push_cell(atom_as_cell!(b_atom)); // 18 + section.push_cell(list_loc_as_cell!(20)); // 19 + section.push_cell(str_loc_as_cell!(22)); // 20 + section.push_cell(empty_list_as_cell!()); // 21 + section.push_cell(atom_as_cell!(p_atom, 1)); // 22 + section.push_cell(heap_loc_as_cell!(23)); // 23 + section.push_cell(heap_loc_as_cell!(0)); + }); { let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 24); @@ -1524,7 +1391,8 @@ mod tests { { wam.machine_st .heap - .push(fixnum_as_cell!(Fixnum::build_with(0))); + .push_cell(fixnum_as_cell!(Fixnum::build_with(0))) + .unwrap(); let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); @@ -1540,11 +1408,14 @@ mod tests { wam.machine_st.heap.clear(); - wam.machine_st.heap.push(str_loc_as_cell!(1)); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); - wam.machine_st.heap.push(atom_as_cell!(atom!("g"), 2)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); - wam.machine_st.heap.push(atom_as_cell!(atom!("y"))); + writer.write_with(|section| { + section.push_cell(str_loc_as_cell!(1)); + section.push_cell(atom_as_cell!(atom!("g"), 2)); + section.push_cell(heap_loc_as_cell!(0)); + section.push_cell(atom_as_cell!(atom!("y"))); + }); { let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); @@ -1571,10 +1442,14 @@ mod tests { wam.machine_st.heap.clear(); - wam.machine_st.heap.push(atom_as_cell!(atom!("g"), 2)); - wam.machine_st.heap.push(str_loc_as_cell!(0)); - wam.machine_st.heap.push(atom_as_cell!(atom!("y"))); - wam.machine_st.heap.push(str_loc_as_cell!(0)); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(atom_as_cell!(atom!("g"), 2)); + section.push_cell(str_loc_as_cell!(0)); + section.push_cell(atom_as_cell!(atom!("y"))); + section.push_cell(str_loc_as_cell!(0)); + }); { let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 3); @@ -1598,16 +1473,20 @@ mod tests { wam.machine_st.heap.clear(); - wam.machine_st.heap.push(str_loc_as_cell!(1)); - wam.machine_st.heap.push(atom_as_cell!(atom!("g"), 2)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); - wam.machine_st.heap.push(atom_as_cell!(atom!("y"))); - wam.machine_st.heap.push(atom_as_cell!(atom!("="), 2)); - wam.machine_st.heap.push(atom_as_cell!(atom!("X"))); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); - wam.machine_st.heap.push(list_loc_as_cell!(8)); - wam.machine_st.heap.push(str_loc_as_cell!(4)); - wam.machine_st.heap.push(empty_list_as_cell!()); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(str_loc_as_cell!(1)); + section.push_cell(atom_as_cell!(atom!("g"), 2)); + section.push_cell(heap_loc_as_cell!(0)); + section.push_cell(atom_as_cell!(atom!("y"))); + section.push_cell(atom_as_cell!(atom!("="), 2)); + section.push_cell(atom_as_cell!(atom!("X"))); + section.push_cell(heap_loc_as_cell!(0)); + section.push_cell(list_loc_as_cell!(8)); + section.push_cell(str_loc_as_cell!(4)); + section.push_cell(empty_list_as_cell!()); + }); { let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 7); @@ -1665,10 +1544,14 @@ mod tests { wam.machine_st.heap.clear(); - wam.machine_st.heap.push(atom_as_cell!(atom!("f"), 2)); - wam.machine_st.heap.push(heap_loc_as_cell!(1)); - wam.machine_st.heap.push(heap_loc_as_cell!(1)); - wam.machine_st.heap.push(str_loc_as_cell!(0)); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(atom_as_cell!(atom!("f"), 2)); + section.push_cell(heap_loc_as_cell!(1)); + section.push_cell(heap_loc_as_cell!(1)); + section.push_cell(str_loc_as_cell!(0)); + }); { let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 3); @@ -1697,29 +1580,25 @@ mod tests { wam.machine_st.heap.clear(); - // representation of one of the heap terms as in issue #1384. - wam.machine_st.heap.push(list_loc_as_cell!(7)); // 0 - wam.machine_st.heap.push(heap_loc_as_cell!(0)); // 1 - wam.machine_st.heap.push(list_loc_as_cell!(3)); // 2 - wam.machine_st.heap.push(list_loc_as_cell!(5)); // 3 - wam.machine_st.heap.push(empty_list_as_cell!()); // 4 - wam.machine_st.heap.push(heap_loc_as_cell!(2)); // 5 - wam.machine_st.heap.push(heap_loc_as_cell!(2)); // 6 - wam.machine_st.heap.push(empty_list_as_cell!()); // 7 - wam.machine_st.heap.push(heap_loc_as_cell!(3)); // 8 + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + // representation of one of the heap terms as in issue #1384. + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(7)); // 0 + section.push_cell(heap_loc_as_cell!(0)); // 1 + section.push_cell(list_loc_as_cell!(3)); // 2 + section.push_cell(list_loc_as_cell!(5)); // 3 + section.push_cell(empty_list_as_cell!()); // 4 + section.push_cell(heap_loc_as_cell!(2)); // 5 + section.push_cell(heap_loc_as_cell!(2)); // 6 + section.push_cell(empty_list_as_cell!()); // 7 + section.push_cell(heap_loc_as_cell!(3)); // 8 + section.push_cell(heap_loc_as_cell!(0)); // 9 + }); { let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 9); - /* - while let Some(_) = iter.next() { - print_heap_terms(iter.heap.iter(), 0); - println!(""); - } - */ - assert_eq!( unmark_cell_bits!(iter.next().unwrap()), list_loc_as_cell!(7) @@ -1763,19 +1642,28 @@ mod tests { fn heap_stackful_iter_tests() { let mut wam = MockWAM::new(); + // clear the heap of resource error data etc + wam.machine_st.heap.clear(); + let f_atom = atom!("f"); let a_atom = atom!("a"); let b_atom = atom!("b"); - wam.machine_st - .heap - .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); + let mut functor_writer = Heap::functor_writer(functor!( + f_atom, + [atom_as_cell(a_atom), atom_as_cell(b_atom)] + )); + + let cell = functor_writer(&mut wam.machine_st.heap).unwrap(); + let h = wam.machine_st.heap.cell_len(); + + wam.machine_st.heap.push_cell(cell).unwrap(); { let mut iter = StackfulPreOrderHeapIter::::new( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - str_loc_as_cell!(0), + h, ); assert_eq!( @@ -1796,21 +1684,26 @@ mod tests { wam.machine_st.heap.clear(); - wam.machine_st.heap.extend(functor!( + let mut functor_writer = Heap::functor_writer(functor!( f_atom, [ - atom(a_atom), - atom(b_atom), - atom(a_atom), - cell(str_loc_as_cell!(0)) + atom_as_cell(a_atom), + atom_as_cell(b_atom), + atom_as_cell(a_atom), + str_loc_as_cell(0) ] )); + let cell = functor_writer(&mut wam.machine_st.heap).unwrap(); + let h = wam.machine_st.heap.cell_len(); + + wam.machine_st.heap.push_cell(cell).unwrap(); + for _ in 0..20 { let mut iter = StackfulPreOrderHeapIter::::new( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - str_loc_as_cell!(0), + h, ); assert_eq!( @@ -1837,12 +1730,12 @@ mod tests { wam.machine_st.heap.clear(); { - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + wam.machine_st.heap.push_cell(heap_loc_as_cell!(0)).unwrap(); let mut iter = StackfulPreOrderHeapIter::::new( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 0, ); let mut var = heap_loc_as_cell!(0); @@ -1863,18 +1756,18 @@ mod tests { { // mutually referencing variables. - wam.machine_st.heap.push(heap_loc_as_cell!(1)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + wam.machine_st.heap.push_cell(heap_loc_as_cell!(1)).unwrap(); + wam.machine_st.heap.push_cell(heap_loc_as_cell!(0)).unwrap(); let mut iter = StackfulPreOrderHeapIter::::new( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 0, ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - heap_loc_as_cell!(0) + heap_loc_as_cell!(1) ); assert_eq!(iter.next(), None); @@ -1883,17 +1776,21 @@ mod tests { wam.machine_st.heap.clear(); // term is: [a, b] - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(atom_as_cell!(a_atom)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(atom_as_cell!(b_atom)); - wam.machine_st.heap.push(empty_list_as_cell!()); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(atom_as_cell!(a_atom)); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(atom_as_cell!(b_atom)); + section.push_cell(empty_list_as_cell!()); + }); { let mut iter = StackfulPreOrderHeapIter::::new( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 0, ); assert_eq!( @@ -1920,16 +1817,14 @@ mod tests { assert_eq!(iter.next(), None); } - wam.machine_st.heap.pop(); - // now make the list cyclic. - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + wam.machine_st.heap[4] = heap_loc_as_cell!(0); { let mut iter = StackfulPreOrderHeapIter::::new( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 4, ); // the cycle will be iterated twice before being detected. @@ -1961,7 +1856,7 @@ mod tests { let mut iter = StackfulPreOrderHeapIter::::new( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 0, ); // cut the iteration short to check that all cells are @@ -1992,146 +1887,116 @@ mod tests { // two-part complete string, then a three-part cyclic string // involving an uncompacted list of chars. - let pstr_var_cell = - put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); - let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; + wam.machine_st.heap.allocate_pstr("abc ").unwrap(); + + wam.machine_st.heap.push_cell(heap_loc_as_cell!(1)).unwrap(); + wam.machine_st.heap.push_cell(pstr_loc_as_cell!(0)).unwrap(); { let mut iter = StackfulPreOrderHeapIter::::new( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 2, ); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - heap_loc_as_cell!(1), + pstr_loc_as_cell!(0) ); - - assert_eq!(iter.next(), None); - } - - // here - - wam.machine_st.heap.pop(); - wam.machine_st.heap.push(heap_loc_as_cell!(2)); - - let pstr_second_var_cell = - put_partial_string(&mut wam.machine_st.heap, "def", &wam.machine_st.atom_tbl); - let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; - - { - let mut iter = stackful_preorder_iter::( - &mut wam.machine_st.heap, - &mut wam.machine_st.stack, - heap_loc_as_cell!(0), - ); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - heap_loc_as_cell!(3), + heap_loc_as_cell!(1) ); - - assert_eq!(iter.next(), None); + assert!(iter.next().is_none()); } - wam.machine_st.heap.pop(); - wam.machine_st - .heap - .push(pstr_loc_as_cell!(wam.machine_st.heap.len() + 1)); + wam.machine_st.heap[1] = pstr_loc_as_cell!(heap_index!(3)); + wam.machine_st.heap.allocate_pstr("def").unwrap(); - wam.machine_st.heap.push(pstr_offset_as_cell!(0)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(0i64))); + wam.machine_st.heap.push_cell(heap_loc_as_cell!(4)).unwrap(); + wam.machine_st.heap.push_cell(pstr_loc_as_cell!(0)).unwrap(); { let mut iter = stackful_preorder_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - pstr_loc_as_cell!(0), + 5, ); - let pstr_offset_cell = pstr_offset_as_cell!(0); - - // pstr_offset_cell.set_forwarding_bit(true); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell); assert_eq!( - iter.next().unwrap(), - fixnum_as_cell!(Fixnum::build_with(0i64)) + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(0) + ); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(heap_index!(3)) + ); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + heap_loc_as_cell!(4), ); - assert_eq!(iter.next(), None); } - /* - { - let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0); - let string: String = iter.chars().collect(); - assert_eq!(string, "abc def"); - } - */ - - wam.machine_st.heap.pop(); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(1i64))); + wam.machine_st.heap[4] = pstr_loc_as_cell!(heap_index!(3) + 2); { let mut iter = stackful_preorder_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - pstr_loc_as_cell!(0), + 5, ); - let pstr_offset_cell = pstr_offset_as_cell!(0); - - // pstr_offset_cell.set_forwarding_bit(true); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell); assert_eq!( - iter.next().unwrap(), - fixnum_as_cell!(Fixnum::build_with(1i64)) + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(0) ); - - let h = iter.focus(); - - assert_eq!(h.value(), 5); - assert_eq!(unmark_cell_bits!(iter.heap[4]), pstr_offset_as_cell!(0)); assert_eq!( - unmark_cell_bits!(iter.heap[5]), - fixnum_as_cell!(Fixnum::build_with(1i64)) + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(heap_index!(3)) + ); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(heap_index!(3) + 2) + ); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(heap_index!(3) + 2) ); - assert_eq!(iter.next(), None); } wam.machine_st.heap.clear(); - let functor = functor!(f_atom, [atom(a_atom), atom(b_atom), atom(b_atom)]); + let functor = functor!( + f_atom, + [ + atom_as_cell(a_atom), + atom_as_cell(b_atom), + atom_as_cell(b_atom) + ] + ); - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(str_loc_as_cell!(5)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(str_loc_as_cell!(5)); - wam.machine_st.heap.push(empty_list_as_cell!()); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); - wam.machine_st.heap.extend(functor); + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(str_loc_as_cell!(5)); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(str_loc_as_cell!(5)); + section.push_cell(empty_list_as_cell!()); + }); + + let mut functor_writer = Heap::functor_writer(functor); + let cell = functor_writer(&mut wam.machine_st.heap).unwrap(); + + wam.machine_st.heap.push_cell(cell).unwrap(); { let mut iter = StackfulPreOrderHeapIter::::new( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 0, ); assert_eq!( @@ -2194,7 +2059,7 @@ mod tests { let mut iter = stackful_preorder_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 0, ); assert_eq!( @@ -2255,15 +2120,19 @@ mod tests { wam.machine_st.heap.clear(); - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(list_loc_as_cell!(1)); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(list_loc_as_cell!(1)); + }); { let mut iter = StackfulPreOrderHeapIter::::new( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 0, ); let mut cyclic_link = list_loc_as_cell!(1); @@ -2290,46 +2159,66 @@ mod tests { wam.machine_st.heap.clear(); - wam.machine_st.heap.push(pstr_as_cell!(atom!("a string"))); - wam.machine_st.heap.push(empty_list_as_cell!()); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_pstr("a string"); + section.push_cell(empty_list_as_cell!()); + section.push_cell(pstr_loc_as_cell!(0)); + }); + + assert_eq!(wam.machine_st.heap.cell_len(), 4); { let mut iter = stackful_preorder_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), - ); - - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - pstr_as_cell!(atom!("a string")) + 3, ); + assert_eq!(iter.heap.slice_to_str(0, "a string".len()), "a string"); + assert_eq!(iter.next().unwrap(), pstr_loc_as_cell!(0)); assert_eq!(iter.next().unwrap(), empty_list_as_cell!()); - assert_eq!(iter.next(), None); } - all_cells_unmarked(&wam.machine_st.heap); + assert_eq!( + wam.machine_st.heap.slice_to_str(0, "a string".len()), + "a string" + ); + assert_eq!( + wam.machine_st.heap[1], + HeapCellValue::build_with(HeapCellValueTag::Cons, 0) + ); + + for idx in 2..=3 { + assert!(!wam.machine_st.heap[idx].get_mark_bit()); + assert!(!wam.machine_st.heap[idx].get_forwarding_bit()); + } wam.machine_st.heap.clear(); - wam.machine_st.heap.push(str_loc_as_cell!(1)); - wam.machine_st.heap.push(atom_as_cell!(atom!("g"), 2)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); - wam.machine_st.heap.push(atom_as_cell!(atom!("y"))); - wam.machine_st.heap.push(atom_as_cell!(atom!("="), 2)); - wam.machine_st.heap.push(atom_as_cell!(atom!("X"))); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); - wam.machine_st.heap.push(list_loc_as_cell!(8)); - wam.machine_st.heap.push(str_loc_as_cell!(4)); - wam.machine_st.heap.push(empty_list_as_cell!()); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(str_loc_as_cell!(1)); + section.push_cell(atom_as_cell!(atom!("g"), 2)); + section.push_cell(heap_loc_as_cell!(0)); + section.push_cell(atom_as_cell!(atom!("y"))); + section.push_cell(atom_as_cell!(atom!("="), 2)); + section.push_cell(atom_as_cell!(atom!("X"))); + section.push_cell(heap_loc_as_cell!(0)); + section.push_cell(list_loc_as_cell!(8)); + section.push_cell(str_loc_as_cell!(4)); + section.push_cell(empty_list_as_cell!()); + section.push_cell(heap_loc_as_cell!(0)); + }); { let mut iter = stackful_preorder_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 10, ); assert_eq!( @@ -2355,19 +2244,28 @@ mod tests { fn heap_stackful_post_order_iter() { let mut wam = MockWAM::new(); + // clear the heap of resource error data etc + wam.machine_st.heap.clear(); + let f_atom = atom!("f"); let a_atom = atom!("a"); let b_atom = atom!("b"); - wam.machine_st - .heap - .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); + let mut functor_writer = Heap::functor_writer(functor!( + f_atom, + [atom_as_cell(a_atom), atom_as_cell(b_atom)] + )); + + let cell = functor_writer(&mut wam.machine_st.heap).unwrap(); + let h = wam.machine_st.heap.cell_len(); + + wam.machine_st.heap.push_cell(cell).unwrap(); { let mut iter = stackful_post_order_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - str_loc_as_cell!(0), + h, ); assert_eq!( @@ -2388,22 +2286,27 @@ mod tests { wam.machine_st.heap.clear(); - wam.machine_st.heap.extend(functor!( + let mut functor_writer = Heap::functor_writer(functor!( f_atom, [ - atom(a_atom), - atom(b_atom), - atom(a_atom), - cell(str_loc_as_cell!(0)) + atom_as_cell(a_atom), + atom_as_cell(b_atom), + atom_as_cell(a_atom), + str_loc_as_cell(0) ] )); + let cell = functor_writer(&mut wam.machine_st.heap).unwrap(); + let h = wam.machine_st.heap.cell_len(); + + wam.machine_st.heap.push_cell(cell).unwrap(); + for _ in 0..20 { // 0000 { let mut iter = stackful_post_order_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - str_loc_as_cell!(0), + h, ); assert_eq!( @@ -2418,9 +2321,7 @@ mod tests { unmark_cell_bits!(iter.next().unwrap()), atom_as_cell!(a_atom) ); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), str_loc_as_cell!(0)); - assert_eq!( unmark_cell_bits!(iter.next().unwrap()), atom_as_cell!(f_atom, 4) @@ -2432,12 +2333,12 @@ mod tests { wam.machine_st.heap.clear(); { - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + wam.machine_st.heap.push_cell(heap_loc_as_cell!(0)).unwrap(); let mut iter = stackful_post_order_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 0, ); let mut var = heap_loc_as_cell!(0); @@ -2458,13 +2359,13 @@ mod tests { { // mutually referencing variables. - wam.machine_st.heap.push(heap_loc_as_cell!(1)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + wam.machine_st.heap.push_cell(heap_loc_as_cell!(1)).unwrap(); + wam.machine_st.heap.push_cell(heap_loc_as_cell!(0)).unwrap(); let mut iter = stackful_post_order_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 1, ); assert_eq!( @@ -2478,17 +2379,21 @@ mod tests { wam.machine_st.heap.clear(); // term is: [a, b] - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(atom_as_cell!(a_atom)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(atom_as_cell!(b_atom)); - wam.machine_st.heap.push(empty_list_as_cell!()); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(atom_as_cell!(a_atom)); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(atom_as_cell!(b_atom)); + section.push_cell(empty_list_as_cell!()); + }); { let mut iter = stackful_post_order_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 0, ); assert_eq!( @@ -2515,16 +2420,14 @@ mod tests { assert_eq!(iter.next(), None); } - wam.machine_st.heap.pop(); - // now make the list cyclic. - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + wam.machine_st.heap[4] = heap_loc_as_cell!(0); { let mut iter = stackful_post_order_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 4, ); // the cycle will be iterated twice before being detected. @@ -2556,7 +2459,7 @@ mod tests { let mut iter = stackful_post_order_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 0, ); // cut the iteration short to check that all cells are @@ -2587,127 +2490,116 @@ mod tests { // two-part complete string, then a three-part cyclic string // involving an uncompacted list of chars. - let pstr_var_cell = - put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); - let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; + wam.machine_st.heap.allocate_pstr("abc ").unwrap(); + + wam.machine_st.heap.push_cell(heap_loc_as_cell!(1)).unwrap(); + wam.machine_st.heap.push_cell(pstr_loc_as_cell!(0)).unwrap(); { let mut iter = stackful_post_order_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - pstr_loc_as_cell!(0), + 2, ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), heap_loc_as_cell!(1), ); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(0) + ); assert_eq!(iter.next(), None); } - wam.machine_st.heap.pop(); - wam.machine_st.heap.push(pstr_loc_as_cell!(2)); + wam.machine_st.heap[1] = pstr_loc_as_cell!(heap_index!(3)); + wam.machine_st.heap.allocate_pstr("def").unwrap(); - let pstr_second_var_cell = - put_partial_string(&mut wam.machine_st.heap, "def", &wam.machine_st.atom_tbl); - let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; + wam.machine_st.heap.push_cell(heap_loc_as_cell!(4)).unwrap(); + wam.machine_st.heap.push_cell(pstr_loc_as_cell!(0)).unwrap(); { let mut iter = stackful_post_order_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - pstr_loc_as_cell!(0), + 5, ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - heap_loc_as_cell!(3), + heap_loc_as_cell!(4), + ); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(heap_index!(3)) + ); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(0) ); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(iter.next(), None); } - wam.machine_st.heap.pop(); - wam.machine_st - .heap - .push(pstr_loc_as_cell!(wam.machine_st.heap.len() + 1)); - - wam.machine_st.heap.push(pstr_offset_as_cell!(0)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(0i64))); + wam.machine_st.heap[4] = pstr_loc_as_cell!(heap_index!(3) + 2); { let mut iter = stackful_post_order_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - pstr_loc_as_cell!(0), + 5, ); assert_eq!( - iter.next().unwrap(), - fixnum_as_cell!(Fixnum::build_with(0i64)) + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(heap_index!(3) + 2) ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - pstr_offset_as_cell!(0) - ); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - - assert_eq!(iter.next(), None); - } - - wam.machine_st.heap.pop(); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(1i64))); - - { - let mut iter = stackful_post_order_iter::( - &mut wam.machine_st.heap, - &mut wam.machine_st.stack, - pstr_loc_as_cell!(0), - ); - - assert_eq!( - iter.next().unwrap(), - fixnum_as_cell!(Fixnum::build_with(1i64)) + pstr_loc_as_cell!(heap_index!(3) + 2) ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - pstr_offset_as_cell!(0) + pstr_loc_as_cell!(heap_index!(3)) + ); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(0) ); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(iter.next(), None); } wam.machine_st.heap.clear(); - let functor = functor!(f_atom, [atom(a_atom), atom(b_atom), atom(b_atom)]); + let functor = functor!( + f_atom, + [ + atom_as_cell(a_atom), + atom_as_cell(b_atom), + atom_as_cell(b_atom) + ] + ); - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(str_loc_as_cell!(5)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(str_loc_as_cell!(5)); - wam.machine_st.heap.push(empty_list_as_cell!()); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); - wam.machine_st.heap.extend(functor); + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(str_loc_as_cell!(5)); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(str_loc_as_cell!(5)); + section.push_cell(empty_list_as_cell!()); + }); + + let mut functor_writer = Heap::functor_writer(functor); + let cell = functor_writer(&mut wam.machine_st.heap).unwrap(); + + wam.machine_st.heap.push_cell(cell).unwrap(); { let mut iter = stackful_post_order_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 0, ); assert_eq!( @@ -2771,7 +2663,7 @@ mod tests { let mut iter = stackful_post_order_iter::( &mut wam.machine_st.heap, &mut wam.machine_st.stack, - heap_loc_as_cell!(0), + 0, ); assert_eq!( @@ -2838,15 +2730,20 @@ mod tests { fn heap_stackless_post_order_iter() { let mut wam = MockWAM::new(); + // clear the heap of resource error data etc + wam.machine_st.heap.clear(); + let f_atom = atom!("f"); let a_atom = atom!("a"); let b_atom = atom!("b"); - wam.machine_st - .heap - .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); + let mut functor_writer = Heap::functor_writer(functor!( + f_atom, + [atom_as_cell(a_atom), atom_as_cell(b_atom)] + )); - wam.machine_st.heap.push(str_loc_as_cell!(0)); + let cell = functor_writer(&mut wam.machine_st.heap).unwrap(); + wam.machine_st.heap.push_cell(cell).unwrap(); { let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 3); @@ -2869,17 +2766,18 @@ mod tests { wam.machine_st.heap.clear(); - wam.machine_st.heap.extend(functor!( + let mut functor_writer = Heap::functor_writer(functor!( f_atom, [ - atom(a_atom), - atom(b_atom), - atom(a_atom), - cell(str_loc_as_cell!(0)) + atom_as_cell(a_atom), + atom_as_cell(b_atom), + atom_as_cell(a_atom), + str_loc_as_cell(0) ] )); - wam.machine_st.heap.push(str_loc_as_cell!(0)); + let cell = functor_writer(&mut wam.machine_st.heap).unwrap(); + wam.machine_st.heap.push_cell(cell).unwrap(); for _ in 0..20 { let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 5); @@ -2910,7 +2808,7 @@ mod tests { wam.machine_st.heap.clear(); { - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + wam.machine_st.heap.push_cell(heap_loc_as_cell!(0)).unwrap(); let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0); @@ -2925,8 +2823,8 @@ mod tests { { // mutually referencing variables. - wam.machine_st.heap.push(heap_loc_as_cell!(1)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + wam.machine_st.heap.push_cell(heap_loc_as_cell!(1)).unwrap(); + wam.machine_st.heap.push_cell(heap_loc_as_cell!(0)).unwrap(); let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0); @@ -2946,11 +2844,15 @@ mod tests { wam.machine_st.heap.clear(); // term is: [a, b] - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(atom_as_cell!(a_atom)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(atom_as_cell!(b_atom)); - wam.machine_st.heap.push(empty_list_as_cell!()); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(atom_as_cell!(a_atom)); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(atom_as_cell!(b_atom)); + section.push_cell(empty_list_as_cell!()); + }); { let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0); @@ -2979,10 +2881,8 @@ mod tests { assert_eq!(iter.next(), None); } - wam.machine_st.heap.pop(); - // now make the list cyclic. - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + wam.machine_st.heap[4] = heap_loc_as_cell!(0); { let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0); @@ -3038,16 +2938,27 @@ mod tests { assert_eq!(wam.machine_st.heap[4], heap_loc_as_cell!(0)); wam.machine_st.heap.clear(); + } + + #[test] + fn heap_stackless_post_order_iter_pstr() { + let mut wam = MockWAM::new(); + + let f_atom = atom!("f"); + let a_atom = atom!("a"); + let b_atom = atom!("b"); + + // clear the heap of resource error data etc + wam.machine_st.heap.clear(); // first a 'dangling' partial string, later modified to be a // two-part complete string, then a three-part cyclic string // involving an uncompacted list of chars. - let pstr_var_cell = - put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); - let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; + wam.machine_st.heap.allocate_pstr("abc ").unwrap(); - wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + wam.machine_st.heap.push_cell(heap_loc_as_cell!(1)).unwrap(); + wam.machine_st.heap.push_cell(pstr_loc_as_cell!(0)).unwrap(); { let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 2); @@ -3056,108 +2967,79 @@ mod tests { unmark_cell_bits!(iter.next().unwrap()), heap_loc_as_cell!(1), ); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(0) + ); assert_eq!(iter.next(), None); } - wam.machine_st.heap.pop(); - wam.machine_st.heap.pop(); - wam.machine_st.heap.push(pstr_loc_as_cell!(2)); + wam.machine_st.heap[2] = heap_loc_as_cell!(2); + assert_eq!(wam.machine_st.heap.cell_len(), 3); - let pstr_second_var_cell = - put_partial_string(&mut wam.machine_st.heap, "def", &wam.machine_st.atom_tbl); + wam.machine_st.heap.allocate_pstr("def").unwrap(); + assert_eq!(wam.machine_st.heap.cell_len(), 4); - let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; - - wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + wam.machine_st.heap.push_cell(pstr_loc_as_cell!(0)).unwrap(); + assert_eq!(wam.machine_st.heap.cell_len(), 5); { let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 4); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - heap_loc_as_cell!(3), + heap_loc_as_cell!(1), + ); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + pstr_loc_as_cell!(0) ); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(iter.next(), None); } all_cells_unmarked(&wam.machine_st.heap); - wam.machine_st.heap.pop(); - wam.machine_st - .heap - .push(pstr_loc_as_cell!(wam.machine_st.heap.len() + 1)); - - wam.machine_st.heap.push(pstr_offset_as_cell!(0)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(0))); - - wam.machine_st.heap.push(pstr_loc_as_cell!(0)); + wam.machine_st.heap[4] = pstr_loc_as_cell!(heap_index!(3) + 2); { - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 7); - let mut pstr_loc_cell = pstr_loc_as_cell!(0); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 4); - pstr_loc_cell.set_forwarding_bit(true); - - // assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(0i64))); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), - heap_loc_as_cell!(3) + pstr_loc_as_cell!(heap_index!(3) + 2) ); - - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(iter.next(), None); } all_cells_unmarked(&wam.machine_st.heap); - wam.machine_st.heap.pop(); - wam.machine_st.heap.pop(); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(1))); - - wam.machine_st.heap.push(pstr_loc_as_cell!(0)); - - { - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 7); - - //assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(1))); - assert_eq!( - unmark_cell_bits!(iter.next().unwrap()), - heap_loc_as_cell!(3) - ); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); - assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); - assert_eq!(iter.next(), None); - } - wam.machine_st.heap.clear(); - let functor = functor!(f_atom, [atom(a_atom), atom(b_atom), atom(b_atom)]); + let functor = functor!( + f_atom, + [ + atom_as_cell(a_atom), + atom_as_cell(b_atom), + atom_as_cell(b_atom) + ] + ); - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(str_loc_as_cell!(5)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(str_loc_as_cell!(5)); - wam.machine_st.heap.push(empty_list_as_cell!()); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); - wam.machine_st.heap.extend(functor); + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(str_loc_as_cell!(5)); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(str_loc_as_cell!(5)); + section.push_cell(empty_list_as_cell!()); + }); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + let mut functor_writer = Heap::functor_writer(functor); + functor_writer(&mut wam.machine_st.heap).unwrap(); { - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 9); + let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), diff --git a/src/heap_print.rs b/src/heap_print.rs index f6f0c8f3..2186c8ed 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -8,11 +8,10 @@ use crate::parser::dashu::{ibig, Integer, Rational}; use crate::forms::*; use crate::heap_iter::*; use crate::machine::heap::*; -use crate::machine::machine_indices::*; -use crate::machine::machine_state::pstr_loc_and_offset; use crate::machine::partial_string::*; use crate::machine::stack::*; use crate::machine::streams::*; +use crate::offset_table::*; use crate::types::*; use dashu::base::Signed; @@ -25,7 +24,6 @@ use std::convert::TryFrom; use std::iter::once; use std::net::{IpAddr, TcpListener}; use std::rc::Rc; -use std::sync::Arc; /* contains the location, name, precision and Specifier of the parent op. */ #[derive(Debug, Copy, Clone)] @@ -206,11 +204,12 @@ impl NumberFocus { #[derive(Debug, Clone, Copy)] struct CommaSeparatedCharList { - pstr: PartialString, - offset: usize, + // pstr: PartialString, + // offset: usize, + pstr_loc: usize, max_depth: usize, - end_cell: HeapCellValue, - end_h: Option, + // end_cell: HeapCellValue, + // end_h: Option, } #[derive(Debug, Clone)] @@ -389,17 +388,20 @@ fn is_numbered_var(name: Atom, arity: usize) -> bool { #[inline] fn negated_op_needs_bracketing( iter: &StackfulPreOrderHeapIter, + f64_tbl: &F64Table, op_dir: &OpDir, op: &Option, ) -> bool { if let Some(ref op) = op { op.is_negative_sign() - && iter.leftmost_leaf_has_property(op_dir, |addr| match Number::try_from(addr) { - Ok(Number::Fixnum(n)) => n.get_num() > 0, - Ok(Number::Float(OrderedFloat(f))) => f > 0f64, - Ok(Number::Integer(n)) => n.is_positive(), - Ok(Number::Rational(n)) => n.is_positive(), - _ => false, + && iter.leftmost_leaf_has_property(op_dir, |addr| { + match Number::try_from((addr, f64_tbl)) { + Ok(Number::Fixnum(n)) => n.get_num() > 0, + Ok(Number::Float(OrderedFloat(f))) => f > 0f64, + Ok(Number::Integer(n)) => n.is_positive(), + Ok(Number::Rational(n)) => n.is_positive(), + _ => false, + } }) } else { false @@ -473,7 +475,7 @@ pub fn fmt_float(mut fl: f64) -> String { pub struct HCPrinter<'a, Outputter> { outputter: Outputter, iter: StackfulPreOrderHeapIter<'a, ListElider>, - atom_tbl: Arc, + arena: &'a Arena, op_dir: &'a OpDir, state_stack: Vec, toplevel_spec: Option, @@ -488,9 +490,24 @@ pub struct HCPrinter<'a, Outputter> { pub double_quotes: bool, } +fn ambiguity_check( + outputter: &impl HCValueOutputter, + quoted: bool, + last_item_idx: usize, + atom: &str, +) -> bool { + let tail = &outputter.as_str()[last_item_idx..]; + + if atom == "," || !quoted || non_quoted_token(atom.chars()) { + requires_space(tail, atom) + } else { + requires_space(tail, "'") + } +} + macro_rules! push_space_if_amb { ($self:expr, $atom:expr, $action:block) => { - if $self.ambiguity_check($atom) { + if ambiguity_check(&$self.outputter, $self.quoted, $self.last_item_idx, $atom) { $self.outputter.push_char(' '); $action; } else { @@ -516,28 +533,47 @@ pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option } } - match Number::try_from(addr) { - Ok(Number::Fixnum(n)) if n.get_num() >= 0 => { - Some(numbervar(offset + Integer::from(n.get_num()))) + read_heap_cell!(addr, + (HeapCellValueTag::Cons, c) => { + match_untyped_arena_ptr!(c, + (ArenaHeaderTag::Integer, n) => { + if !n.is_negative() { + Some(numbervar(Integer::from(offset + &*n))) + } else { + None + } + } + _ => { + None + } + ) } - Ok(Number::Integer(n)) if !n.is_negative() => Some(numbervar(Integer::from(offset + &*n))), - _ => None, - } + (HeapCellValueTag::Fixnum, n) => { + if n.get_num() >= 0 { + Some(numbervar(offset + Integer::from(n.get_num()))) + } else { + None + } + } + _ => { + None + } + ) } impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { pub fn new( heap: &'a mut Heap, - atom_tbl: Arc, stack: &'a mut Stack, + arena: &'a Arena, op_dir: &'a OpDir, output: Outputter, - cell: HeapCellValue, + term_loc: usize, ) -> Self { HCPrinter { outputter: output, - iter: stackful_preorder_iter(heap, stack, cell), - atom_tbl, + iter: stackful_preorder_iter(heap, stack, term_loc), + arena, op_dir, state_stack: vec![], toplevel_spec: None, @@ -553,17 +589,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } } - #[inline] - fn ambiguity_check(&self, atom: &str) -> bool { - let tail = &self.outputter.as_str()[self.last_item_idx..]; - - if atom == "," || !self.quoted || non_quoted_token(atom.chars()) { - requires_space(tail, atom) - } else { - requires_space(tail, "'") - } - } - fn set_parent_of_first_op(&mut self, parent_op: Option) { if let Some(op) = parent_op { if op.is_left() && op.is_prefix() { @@ -806,13 +831,13 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { read_heap_cell!(cell, (HeapCellValueTag::Lis | HeapCellValueTag::Str, h) => { - Some(format!("{}", h)) + Some(format!("{h}")) } (HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => { - Some(format!("_{}", h)) + Some(format!("_{h}")) } (HeapCellValueTag::StackVar, h) => { - Some(format!("_s_{}", h)) + Some(format!("_s_{h}")) } _ => { None @@ -878,7 +903,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } else { debug_assert!(cell.is_ref()); - let h = cell.get_value() as usize; + let h = if cell.get_tag() == HeapCellValueTag::PStrLoc { + self.iter.focus().value() + } else { + cell.get_value() + } as usize; + self.iter.push_stack(IterStackLoc::iterable_loc( h, HeapOrStackTag::Heap, @@ -908,7 +938,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { orig_cell = cell; continue; } - } + }; } } @@ -972,13 +1002,13 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { #[inline] fn print_ip_addr(&mut self, ip: IpAddr) { push_char!(self, '\''); - append_str!(self, &format!("{}", ip)); + append_str!(self, &format!("{ip}")); push_char!(self, '\''); } #[inline] fn print_raw_ptr(&mut self, ptr: *const ArenaHeader) { - append_str!(self, &format!("0x{:x}", ptr as *const u8 as usize)); + append_str!(self, &format!("0x{:x}", ptr.addr())); } fn print_number(&mut self, max_depth: usize, n: NumberFocus, op: &Option) { @@ -1009,7 +1039,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.print_rational(max_depth, r, *op); } n => { - let output_str = format!("{}", n); + let output_str = format!("{n}"); push_space_if_amb!(self, &output_str, { append_str!(self, &output_str); @@ -1056,7 +1086,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { match self.op_dir.get(&(atom!("rdiv"), Fixity::In)) { Some(op_desc) => { if r.is_int() { - let output_str = format!("{}", r); + let output_str = format!("{r}"); push_space_if_amb!(self, &output_str, { append_str!(self, &output_str); @@ -1123,9 +1153,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { // returns true if max_depth limit is reached and ellipsis is printed. fn print_string_as_functor(&mut self, focus: usize, max_depth: &mut usize) -> bool { - let iter = HeapPStrIter::new(self.iter.heap, focus); + let mut iter = HeapPStrIter::new(self.iter.heap, focus); + let mut char_count = 0; - for (char_count, c) in iter.chars().enumerate() { + while let Some(iteratee) = iter.next() { if self.check_max_depth(max_depth) { if char_count > 0 { self.state_stack.push(TokenOrRedirect::Close); @@ -1135,13 +1166,34 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { return true; } - append_str!(self, "'.'"); - push_char!(self, '('); + macro_rules! emit_char { + ($c:expr) => {{ + append_str!(self, "'.'"); + push_char!(self, '('); - print_char!(self, self.quoted, c); - push_char!(self, ','); + print_char!(self, self.quoted, $c); + push_char!(self, ','); - self.state_stack.push(TokenOrRedirect::Close); + self.state_stack.push(TokenOrRedirect::Close); + char_count += 1; + }}; + } + + match iteratee { + PStrIteratee::Char { value, .. } => { + emit_char!(value); + } + PStrIteratee::PStrSlice { + slice_loc, + slice_len, + } => { + let s = iter.heap.slice_to_str(slice_loc, slice_len); + + for c in s.chars() { + emit_char!(c); + } + } + } } false @@ -1152,7 +1204,8 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { fn print_proper_string(&mut self, focus: usize, max_depth: usize) { push_char!(self, '"'); - let iter = HeapPStrIter::new(self.iter.heap, focus); + let mut iter = HeapPStrIter::new(self.iter.heap, focus); + let char_to_string = |c: char| { // refrain from quoting characters other than '"' and '\' // unless self.quoted is true. @@ -1164,19 +1217,45 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { }; if max_depth == 0 { - for c in iter.chars() { - for c in char_to_string(c).chars() { - push_char!(self, c); + while let Some(iteratee) = iter.next() { + let iter: Box> = match iteratee { + PStrIteratee::Char { value: c, .. } => Box::new(std::iter::once(c)), + PStrIteratee::PStrSlice { + slice_loc, + slice_len, + } => { + let s = iter.heap.slice_to_str(slice_loc, slice_len); + Box::new(s.chars()) + } + }; + + for c in iter { + for c in char_to_string(c).chars() { + push_char!(self, c); + } } } } else { let mut char_count = 0; - for c in iter.chars().take(max_depth) { - char_count += 1; + while let Some(iteratee) = iter.next() { + let iter: Box> = match iteratee { + PStrIteratee::Char { value: c, .. } => Box::new(std::iter::once(c)), + PStrIteratee::PStrSlice { + slice_loc, + slice_len, + } => { + let s = iter.heap.slice_to_str(slice_loc, slice_len); + Box::new(s.chars()) + } + }; - for c in char_to_string(c).chars() { - push_char!(self, c); + for c in iter.take(max_depth - char_count) { + char_count += 1; + + for c in char_to_string(c).chars() { + push_char!(self, c); + } } } @@ -1194,10 +1273,9 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.iter.pop_stack(); self.iter.pop_stack(); } - HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset => { + HeapCellValueTag::PStrLoc => { self.iter.pop_stack(); } - HeapCellValueTag::CStr => {} _ => { unreachable!(); } @@ -1208,19 +1286,15 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let focus = self.iter.focus(); let mut heap_pstr_iter = HeapPStrIter::new(self.iter.heap, focus.value() as usize); - let next_h; - let next_hare; - - if heap_pstr_iter.next().is_some() { - next_h = heap_pstr_iter.focus; - next_hare = heap_pstr_iter.focus(); + let is_cyclic = if heap_pstr_iter.next().is_some() { for _ in heap_pstr_iter.by_ref() {} + heap_pstr_iter.is_cyclic() } else { return self.push_list(max_depth); - } + }; let end_h = heap_pstr_iter.focus(); - let end_cell = heap_pstr_iter.focus; + let end_cell = heap_pstr_iter.heap[end_h]; // heap_pstr_iter.focus; if self.check_max_depth(&mut max_depth) { self.remove_list_children(focus.value() as usize); @@ -1230,7 +1304,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let at_cdr = self.outputter.ends_with("|"); - if self.double_quotes && !self.ignore_ops && end_cell.is_string_terminator(self.iter.heap) { + if self.double_quotes + && !self.ignore_ops + && !is_cyclic + && end_cell.is_string_terminator(self.iter.heap) + { self.remove_list_children(focus.value() as usize); return self.print_proper_string(focus.value() as usize, max_depth); } @@ -1261,37 +1339,27 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { (HeapCellValueTag::Lis) => { self.push_list(max_depth) } - _ => { + (HeapCellValueTag::PStrLoc, h) => { let switch = Rc::new(Cell::new((!at_cdr, 0))); let switch = self.close_list(switch); - let (h, offset) = pstr_loc_and_offset(self.iter.heap, focus.value() as usize); - - let offset = offset.get_num() as usize; - let tag = value.get_tag(); - - let end_h = if tag == HeapCellValueTag::PStrOffset { - // remove the fixnum offset from the iterator stack so we don't - // print an extraneous number. pstr offset value cells are never - // used by the iterator to mark cyclic terms so the removal is safe. - self.iter.pop_stack(); - Some(next_hare) - // Some(end_h) - } else { - None - }; - if !self.max_depth_exhausted(max_depth) { - let pstr = cell_as_string!(self.iter.heap[h]); - self.state_stack.push(TokenOrRedirect::CommaSeparatedCharList(CommaSeparatedCharList { - pstr, offset, max_depth, end_cell: next_h, end_h, - })); + self.state_stack.push( + TokenOrRedirect::CommaSeparatedCharList( + CommaSeparatedCharList { + pstr_loc: h, max_depth, + } + ), + ); } else { self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); } self.open_list(switch); } + _ => { + unreachable!() + } ); } } @@ -1384,7 +1452,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { || if let Some(ref op) = op { if self.numbervars && arity == 1 && name == atom!("$VAR") { !self.iter.immediate_leaf_has_property(|addr| { - match Number::try_from(addr) { + match Number::try_from((addr, &self.arena.f64_tbl)) { Ok(Number::Integer(n)) => (*n).sign() == Sign::Positive, Ok(Number::Fixnum(n)) => n.get_num() >= 0, Ok(Number::Float(f)) => f >= OrderedFloat(0f64), @@ -1458,7 +1526,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.state_stack.push(TokenOrRedirect::NumberFocus( max_depth, - NumberFocus::Unfocused(Number::Fixnum(Fixnum::build_with(port as i64))), + NumberFocus::Unfocused(Number::Fixnum(Fixnum::build_with(port))), None, )); self.state_stack.push(TokenOrRedirect::Comma); @@ -1469,23 +1537,29 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } } - fn print_index_ptr(&mut self, index_ptr: IndexPtr, max_depth: usize) { + fn print_index_ptr(&mut self, idx: CodeIndexOffset, max_depth: usize) { if self.format_struct(max_depth, 1, atom!("$index_ptr")) { let atom = self.state_stack.pop().unwrap(); self.state_stack.pop(); self.state_stack.pop(); - let offset = if index_ptr.is_undefined() || index_ptr.is_dynamic_undefined() { + let idx_ptr = self.arena.code_index_tbl.get_entry(idx); + + let offset = if idx_ptr.is_undefined() || idx_ptr.is_dynamic_undefined() { TokenOrRedirect::Atom(atom!("undefined")) } else { - let idx = index_ptr.p() as i64; + let idx_ptr_p = idx_ptr.p() as i64; - TokenOrRedirect::NumberFocus( - max_depth, - NumberFocus::Unfocused(Number::Fixnum(Fixnum::build_with(idx))), - None, - ) + if let Ok(n) = Fixnum::build_with_checked(idx_ptr_p) { + TokenOrRedirect::NumberFocus( + max_depth, + NumberFocus::Unfocused(Number::Fixnum(n)), + None, + ) + } else { + TokenOrRedirect::Atom(atom!("out_of_bounds_idx_ptr")) + } }; self.state_stack.push(offset); @@ -1521,32 +1595,26 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { fn print_comma_separated_char_list(&mut self, char_list: CommaSeparatedCharList) { let CommaSeparatedCharList { - pstr, - offset, + pstr_loc, max_depth, - end_cell, - end_h, } = char_list; - let pstr_str = pstr.as_str_from(offset); - if let Some(c) = pstr_str.chars().next() { - let offset = offset + c.len_utf8(); + let c = self.iter.heap.char_at(pstr_loc); + if c != '\u{0}' { if !self.max_depth_exhausted(max_depth) { self.state_stack .push(TokenOrRedirect::CommaSeparatedCharList( CommaSeparatedCharList { - pstr, - offset, + pstr_loc: pstr_loc + c.len_utf8(), max_depth: max_depth.saturating_sub(1), - end_cell, - end_h, }, )); let max_depth_allows = self.max_depth == 0 || max_depth > 1; + let next_c = self.iter.heap.char_at(pstr_loc + c.len_utf8()); - if max_depth_allows && pstr_str.chars().nth(1).is_some() { + if max_depth_allows && next_c != '\u{0}' { self.state_stack.push(TokenOrRedirect::Comma); } @@ -1558,12 +1626,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } else if self.max_depth_exhausted(max_depth) { self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); self.state_stack.push(TokenOrRedirect::HeadTailSeparator); - } else if end_cell != empty_list_as_cell!() { - if let Some(end_h) = end_h { - self.iter - .push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap)); - } - + } else { self.state_stack .push(TokenOrRedirect::FunctorRedirect(max_depth + 1)); self.state_stack.push(TokenOrRedirect::HeadTailSeparator); @@ -1576,7 +1639,8 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { is_functor_redirect: bool, mut max_depth: usize, ) { - let negated_operand = negated_op_needs_bracketing(&self.iter, self.op_dir, &op); + let negated_operand = + negated_op_needs_bracketing(&self.iter, &self.arena.f64_tbl, self.op_dir, &op); let addr = match self.check_for_seen(&mut max_depth) { Some(addr) => addr, @@ -1658,10 +1722,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { (HeapCellValueTag::Atom, (name, arity)) => { print_struct(self, name, arity); } - (HeapCellValueTag::Char, c) => { - let name = AtomTable::build_with(&self.atom_tbl, &String::from(c)); - print_struct(self, name, 0); - } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.iter.heap[s]) .get_name_and_arity(); @@ -1682,13 +1742,17 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { }); } } + (HeapCellValueTag::CodeIndexOffset, idx) => { + self.print_index_ptr(idx, self.max_depth); + } (HeapCellValueTag::Fixnum | HeapCellValueTag::CutPoint, n) => { self.print_number(max_depth, NumberFocus::Unfocused(Number::Fixnum(n)), &op); } - (HeapCellValueTag::F64, f) => { - self.print_number(max_depth, NumberFocus::Unfocused(Number::Float(*f)), &op); + (HeapCellValueTag::F64Offset, offset) => { + let f = self.arena.f64_tbl.get_entry(offset); + self.print_number(max_depth, NumberFocus::Unfocused(Number::Float(f)), &op); } - (HeapCellValueTag::CStr | HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset) => { + (HeapCellValueTag::PStrLoc) => { self.print_list_like(max_depth); } (HeapCellValueTag::Lis) => { @@ -1725,8 +1789,22 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { (ArenaHeaderTag::Dropped, _value) => { self.print_impromptu_atom(atom!("$dropped_value")); } - (ArenaHeaderTag::IndexPtr, index_ptr) => { - self.print_index_ptr(*index_ptr, max_depth); + (ArenaHeaderTag::ChildProcess, process) => { + + let process_atom = atom!("$process"); + + if self.format_struct(max_depth, 1, process_atom) { + let atom = TokenOrRedirect::NumberFocus(max_depth, NumberFocus::Unfocused(Number::Fixnum(Fixnum::build_with(process.id()))), op); + + let process_root = self.state_stack.pop().unwrap(); + + self.state_stack.pop(); + self.state_stack.pop(); + + self.state_stack.push(atom); + self.state_stack.push(TokenOrRedirect::Open); + self.state_stack.push(process_root); + } } _ => { } @@ -1825,6 +1903,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { mod tests { use super::*; + use crate::functor_macro::*; use crate::machine::mock_wam::*; #[test] @@ -1832,23 +1911,30 @@ mod tests { fn term_printing_tests() { let mut wam = MockWAM::new(); + // clear the heap of resource error data etc + wam.machine_st.heap.clear(); + let f_atom = atom!("f"); let a_atom = atom!("a"); let b_atom = atom!("b"); let c_atom = atom!("c"); - wam.machine_st - .heap - .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); + let mut functor_writer = Heap::functor_writer(functor!( + f_atom, + [atom_as_cell(a_atom), atom_as_cell(b_atom)] + )); + + let cell = functor_writer(&mut wam.machine_st.heap).unwrap(); + wam.machine_st.heap.push_cell(cell).unwrap(); { let printer = HCPrinter::new( &mut wam.machine_st.heap, - Arc::clone(&wam.machine_st.atom_tbl), &mut wam.machine_st.stack, + &wam.machine_st.arena, &wam.op_dir, PrinterOutputter::new(), - heap_loc_as_cell!(0), + 3, ); let output = printer.print(); @@ -1860,24 +1946,28 @@ mod tests { wam.machine_st.heap.clear(); - wam.machine_st.heap.extend(functor!( + let mut functor_writer = Heap::functor_writer(functor!( f_atom, [ - atom(a_atom), - atom(b_atom), - atom(a_atom), - cell(str_loc_as_cell!(0)) + atom_as_cell(a_atom), + atom_as_cell(b_atom), + atom_as_cell(a_atom), + str_loc_as_cell(0) ] )); + let cell = functor_writer(&mut wam.machine_st.heap).unwrap(); + + wam.machine_st.heap.push_cell(cell).unwrap(); + { let printer = HCPrinter::new( &mut wam.machine_st.heap, - Arc::clone(&wam.machine_st.atom_tbl), &mut wam.machine_st.stack, + &wam.machine_st.arena, &wam.op_dir, PrinterOutputter::new(), - heap_loc_as_cell!(0), + 5, ); let output = printer.print(); @@ -1889,19 +1979,23 @@ mod tests { wam.machine_st.heap.clear(); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + // print L = [L|L]. - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(list_loc_as_cell!(1)); + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(list_loc_as_cell!(1)); + }); { let printer = HCPrinter::new( &mut wam.machine_st.heap, - Arc::clone(&wam.machine_st.atom_tbl), &mut wam.machine_st.stack, + &wam.machine_st.arena, &wam.op_dir, PrinterOutputter::new(), - heap_loc_as_cell!(0), + 0, ); let output = printer.print(); @@ -1910,11 +2004,11 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, - Arc::clone(&wam.machine_st.atom_tbl), &mut wam.machine_st.stack, + &wam.machine_st.arena, &wam.op_dir, PrinterOutputter::new(), - heap_loc_as_cell!(0), + 0, ); printer @@ -1930,24 +2024,35 @@ mod tests { wam.machine_st.heap.clear(); - let functor = functor!(f_atom, [atom(a_atom), atom(b_atom), atom(b_atom)]); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(str_loc_as_cell!(5)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(str_loc_as_cell!(5)); - wam.machine_st.heap.push(empty_list_as_cell!()); + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(str_loc_as_cell!(5)); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(str_loc_as_cell!(5)); + section.push_cell(empty_list_as_cell!()); + }); - wam.machine_st.heap.extend(functor); + let mut functor_writer = Heap::functor_writer(functor!( + f_atom, + [ + atom_as_cell(a_atom), + atom_as_cell(b_atom), + atom_as_cell(b_atom) + ] + )); + + functor_writer(&mut wam.machine_st.heap).unwrap(); { let printer = HCPrinter::new( &mut wam.machine_st.heap, - Arc::clone(&wam.machine_st.atom_tbl), &mut wam.machine_st.stack, + &wam.machine_st.arena, &wam.op_dir, PrinterOutputter::new(), - heap_loc_as_cell!(0), + 0, ); let output = printer.print(); @@ -1962,11 +2067,11 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, - Arc::clone(&wam.machine_st.atom_tbl), &mut wam.machine_st.stack, + &wam.machine_st.arena, &wam.op_dir, PrinterOutputter::new(), - heap_loc_as_cell!(0), + 0, ); let output = printer.print(); @@ -1979,11 +2084,11 @@ mod tests { { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, - Arc::clone(&wam.machine_st.atom_tbl), &mut wam.machine_st.stack, + &wam.machine_st.arena, &wam.op_dir, PrinterOutputter::new(), - heap_loc_as_cell!(0), + 0, ); printer @@ -1999,23 +2104,28 @@ mod tests { // issue #382 wam.machine_st.heap.clear(); - wam.machine_st.heap.push(list_loc_as_cell!(1)); - for idx in 0..3000 { - wam.machine_st.heap.push(heap_loc_as_cell!(2 * idx + 1)); - wam.machine_st.heap.push(list_loc_as_cell!(2 * idx + 2 + 1)); - } + let mut writer = wam.machine_st.heap.reserve(6002).unwrap(); - wam.machine_st.heap.push(empty_list_as_cell!()); + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + + for idx in 0..3000 { + section.push_cell(heap_loc_as_cell!(2 * idx + 1)); + section.push_cell(list_loc_as_cell!(2 * idx + 2 + 1)); + } + + section.push_cell(empty_list_as_cell!()); + }); { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, - Arc::clone(&wam.machine_st.atom_tbl), &mut wam.machine_st.stack, + &wam.machine_st.arena, &wam.op_dir, PrinterOutputter::new(), - heap_loc_as_cell!(0), + 0, ); printer.max_depth = 5; @@ -2029,16 +2139,19 @@ mod tests { wam.machine_st.heap.clear(); - put_partial_string(&mut wam.machine_st.heap, "abc", &wam.machine_st.atom_tbl); + wam.machine_st.heap.allocate_pstr("abc").unwrap(); + + wam.machine_st.heap.push_cell(heap_loc_as_cell!(1)).unwrap(); + wam.machine_st.heap.push_cell(pstr_loc_as_cell!(0)).unwrap(); { let printer = HCPrinter::new( &mut wam.machine_st.heap, - Arc::clone(&wam.machine_st.atom_tbl), &mut wam.machine_st.stack, + &wam.machine_st.arena, &wam.op_dir, PrinterOutputter::new(), - pstr_loc_as_cell!(0), + 2, ); let output = printer.print(); @@ -2048,25 +2161,27 @@ mod tests { all_cells_unmarked(&wam.machine_st.heap); - wam.machine_st.heap.pop(); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); - wam.machine_st.heap.push(list_loc_as_cell!(2)); + writer.write_with(|section| { + section.push_cell(atom_as_cell!(a_atom)); + section.push_cell(list_loc_as_cell!(5)); + section.push_cell(atom_as_cell!(b_atom)); + section.push_cell(list_loc_as_cell!(7)); + section.push_cell(atom_as_cell!(c_atom)); + section.push_cell(empty_list_as_cell!()); + }); - wam.machine_st.heap.push(atom_as_cell!(a_atom)); - wam.machine_st.heap.push(list_loc_as_cell!(4)); - wam.machine_st.heap.push(atom_as_cell!(b_atom)); - wam.machine_st.heap.push(list_loc_as_cell!(6)); - wam.machine_st.heap.push(atom_as_cell!(c_atom)); - wam.machine_st.heap.push(empty_list_as_cell!()); + wam.machine_st.heap[1] = list_loc_as_cell!(3); { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, - Arc::clone(&wam.machine_st.atom_tbl), &mut wam.machine_st.stack, + &wam.machine_st.arena, &wam.op_dir, PrinterOutputter::new(), - heap_loc_as_cell!(0), + 2, ); printer.double_quotes = true; diff --git a/src/indexing.rs b/src/indexing.rs index f4cb22ae..345533ad 100644 --- a/src/indexing.rs +++ b/src/indexing.rs @@ -3,6 +3,7 @@ use crate::parser::ast::*; use crate::forms::*; use crate::instructions::*; +use crate::types::HeapCellValue; use fxhash::FxBuildHasher; use indexmap::IndexMap; @@ -112,7 +113,7 @@ impl<'a> IndexingCodeMergingPtr<'a> { match constant_key { Some(OptArgIndexKey::Literal(_, _, constant, _)) => { - constants.insert(*constant, constant_ptr); + constants.insert(HeapCellValue::from(*constant), constant_ptr); } _ if constant_ptr.is_external() => { // this must be a defunct clause, because it's been deleted @@ -144,7 +145,7 @@ impl<'a> IndexingCodeMergingPtr<'a> { fn add_static_indexed_choice_for_constant( &mut self, external: usize, - constant: Literal, + constant: HeapCellValue, index: usize, ) { let third_level_index = if self.append_or_prepend.is_append() { @@ -181,7 +182,7 @@ impl<'a> IndexingCodeMergingPtr<'a> { fn add_dynamic_indexed_choice_for_constant( &mut self, external: usize, - constant: Literal, + constant: HeapCellValue, index: usize, ) { let third_level_index = if self.append_or_prepend.is_append() { @@ -235,8 +236,8 @@ impl<'a> IndexingCodeMergingPtr<'a> { fn index_overlapping_constant( &mut self, - orig_constant: Literal, - overlapping_constant: Literal, + orig_constant: HeapCellValue, + overlapping_constant: HeapCellValue, index: usize, ) { loop { @@ -316,7 +317,7 @@ impl<'a> IndexingCodeMergingPtr<'a> { } } - fn index_constant(&mut self, constant: Literal, index: usize) { + fn index_constant(&mut self, constant: HeapCellValue, index: usize) { loop { let indexing_code_len = self.indexing_code.len(); @@ -633,12 +634,15 @@ pub(crate) fn merge_clause_index( match &opt_arg_index_key { OptArgIndexKey::Literal(_, index_loc, constant, ref overlapping_constants) => { let offset = new_clause_loc - index_loc + 1; - merging_ptr.index_constant(*constant, offset); + merging_ptr.index_constant(HeapCellValue::from(*constant), offset); - for overlapping_constant in overlapping_constants { + if let Some(overlapping_constant) = overlapping_constants { merging_ptr.offset = 0; - - merging_ptr.index_overlapping_constant(*constant, *overlapping_constant, offset); + merging_ptr.index_overlapping_constant( + HeapCellValue::from(*constant), + HeapCellValue::from(*overlapping_constant), + offset, + ); } } OptArgIndexKey::Structure(_, index_loc, name, arity) => { @@ -664,7 +668,7 @@ pub(crate) fn merge_clause_index( pub(crate) fn remove_constant_indices( constant: Literal, - overlapping_constants: &[Literal], + overlapping_constants: Option, indexing_code: &mut [IndexingLine], offset: usize, ) { @@ -693,7 +697,7 @@ pub(crate) fn remove_constant_indices( let mut constants_index = 0; - for constant in iter { + for constant in iter.map(|l| HeapCellValue::from(*l)) { loop { match &mut indexing_code[index] { IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant( @@ -701,8 +705,6 @@ pub(crate) fn remove_constant_indices( )) => { constants_index = index; - let constant = *constant; - match constants.get(&constant).cloned() { Some(IndexingCodePtr::DynamicExternal(_)) | Some(IndexingCodePtr::External(_)) @@ -740,7 +742,7 @@ pub(crate) fn remove_constant_indices( IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant( ref mut constants, )) => { - constants.insert(*constant, ext); + constants.insert(constant, ext); } _ => { unreachable!() @@ -773,7 +775,7 @@ pub(crate) fn remove_constant_indices( IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant( ref mut constants, )) => { - constants.insert(*constant, ext); + constants.insert(constant, ext); } _ => { unreachable!() @@ -1033,7 +1035,7 @@ pub(crate) fn remove_index( ) { match opt_arg_index_key { OptArgIndexKey::Literal(_, _, constant, ref overlapping_constants) => { - remove_constant_indices(*constant, overlapping_constants, indexing_code, clause_loc); + remove_constant_indices(*constant, *overlapping_constants, indexing_code, clause_loc); } OptArgIndexKey::Structure(_, _, name, arity) => { remove_structure_index(*name, *arity, indexing_code, clause_loc); @@ -1095,56 +1097,26 @@ fn uncap_choice_seq_with_try(prelude: &mut [IndexedChoiceInstruction]) { } } -pub(crate) fn constant_key_alternatives( - constant: Literal, - atom_tbl: &AtomTable, - // arena: &mut Arena, -) -> Vec { - let mut constants = vec![]; +pub(crate) fn constant_key_alternatives(constant: Literal) -> Option { + let n = match &constant { + Literal::Rational(n) if n.denominator().is_one() => n.numerator(), + Literal::Integer(n) => n, + _ => return None, + }; - match constant { - Literal::Atom(ref name) => { - if let Some(c) = name.as_char() { - constants.push(Literal::Char(c)); - } - } - Literal::Char(c) => { - let atom = AtomTable::build_with(atom_tbl, &c.to_string()); - constants.push(Literal::Atom(atom)); - } - /* - // constant_to_literal takes care of the downward conversion from Integer to Fixnum - // if possible. - Literal::Fixnum(ref n) => { - constants.push(Literal::Integer(arena_alloc!(n, arena))); - } - */ - Literal::Integer(ref n) => { - let result = (&**n).try_into(); - if let Ok(value) = result { - Fixnum::build_with_checked(value) - .map(|n| { - constants.push(Literal::Fixnum(n)); - }) - .unwrap(); - } - } - _ => {} - } - - constants + Fixnum::build_with_checked(n).map(Literal::Fixnum).ok() } #[derive(Debug)] pub(crate) struct StaticCodeIndices { - constants: IndexMap, FxBuildHasher>, + constants: IndexMap, FxBuildHasher>, lists: VecDeque, structures: IndexMap<(Atom, usize), VecDeque, FxBuildHasher>, } #[derive(Debug)] pub(crate) struct DynamicCodeIndices { - constants: IndexMap, FxBuildHasher>, + constants: IndexMap, FxBuildHasher>, lists: VecDeque, structures: IndexMap<(Atom, usize), VecDeque, FxBuildHasher>, } @@ -1156,7 +1128,7 @@ pub(crate) trait Indexer { fn constants( &mut self, - ) -> &mut IndexMap, FxBuildHasher>; + ) -> &mut IndexMap, FxBuildHasher>; fn lists(&mut self) -> &mut VecDeque; fn structures( &mut self, @@ -1204,7 +1176,7 @@ impl Indexer for StaticCodeIndices { #[inline] fn constants( &mut self, - ) -> &mut IndexMap, FxBuildHasher> { + ) -> &mut IndexMap, FxBuildHasher> { &mut self.constants } @@ -1328,7 +1300,7 @@ impl Indexer for DynamicCodeIndices { } #[inline] - fn constants(&mut self) -> &mut IndexMap, FxBuildHasher> { + fn constants(&mut self) -> &mut IndexMap, FxBuildHasher> { &mut self.constants } @@ -1447,14 +1419,13 @@ impl CodeOffsets { self.indices.lists().push_back(index); } - fn index_constant( - &mut self, - atom_tbl: &AtomTable, - constant: Literal, - index: usize, - ) -> Vec { - let overlapping_constants = constant_key_alternatives(constant, atom_tbl); - let code = self.indices.constants().entry(constant).or_default(); + fn index_constant(&mut self, constant: Literal, index: usize) -> Option { + let overlapping_constant_opt = constant_key_alternatives(constant); + let code = self + .indices + .constants() + .entry(HeapCellValue::from(constant)) + .or_default(); let is_initial_index = code.is_empty(); code.push_back(I::compute_index( @@ -1463,8 +1434,8 @@ impl CodeOffsets { self.non_counted_bt, )); - for constant in &overlapping_constants { - let code = self.indices.constants().entry(*constant).or_default(); + if let Some(constant) = overlapping_constant_opt.map(HeapCellValue::from) { + let code = self.indices.constants().entry(constant).or_default(); let is_initial_index = code.is_empty(); let index = I::compute_index(is_initial_index, index, self.non_counted_bt); @@ -1472,7 +1443,7 @@ impl CodeOffsets { code.push_back(index); } - overlapping_constants + overlapping_constant_opt } fn index_structure(&mut self, name: Atom, arity: usize, index: usize) -> usize { @@ -1494,14 +1465,13 @@ impl CodeOffsets { optimal_arg: &Term, index: usize, clause_index_info: &mut ClauseIndexInfo, - atom_tbl: &AtomTable, ) { match optimal_arg { &Term::Clause(_, atom!("."), ref terms) if terms.len() == 2 => { clause_index_info.opt_arg_index_key = OptArgIndexKey::List(self.optimal_index, 0); self.index_list(index); } - &Term::Cons(..) | &Term::Literal(_, Literal::String(_)) | &Term::PartialString(..) => { + &Term::Cons(..) | &Term::PartialString(..) | &Term::CompleteString(..) => { clause_index_info.opt_arg_index_key = OptArgIndexKey::List(self.optimal_index, 0); self.index_list(index); } @@ -1512,7 +1482,7 @@ impl CodeOffsets { self.index_structure(name, terms.len(), index); } &Term::Literal(_, constant) => { - let overlapping_constants = self.index_constant(atom_tbl, constant, index); + let overlapping_constants = self.index_constant(constant, index); clause_index_info.opt_arg_index_key = OptArgIndexKey::Literal(self.optimal_index, 0, constant, overlapping_constants); diff --git a/src/iterators.rs b/src/iterators.rs index b3140ee9..8e2e43dd 100644 --- a/src/iterators.rs +++ b/src/iterators.rs @@ -6,6 +6,7 @@ use crate::parser::ast::*; use std::cell::Cell; use std::collections::VecDeque; use std::iter::*; +use std::rc::Rc; use std::vec::Vec; #[allow(clippy::borrowed_box)] @@ -15,27 +16,11 @@ pub(crate) enum TermRef<'a> { Cons(Level, &'a Cell, &'a Term, &'a Term), Literal(Level, &'a Cell, &'a Literal), Clause(Level, &'a Cell, Atom, &'a Vec), - PartialString(Level, &'a Cell, &'a String, &'a Box), - CompleteString(Level, &'a Cell, Atom), + PartialString(Level, &'a Cell, Rc, &'a Box), + CompleteString(Level, &'a Cell, Rc), Var(Level, &'a Cell, VarPtr), } -/* -impl<'a> TermRef<'a> { - pub(crate) fn level(&self) -> Level { - match self { - TermRef::AnonVar(lvl) | - TermRef::Cons(lvl, ..) | - TermRef::Literal(lvl, ..) | - TermRef::Var(lvl, ..) | - TermRef::Clause(lvl, ..) | - TermRef::CompleteString(lvl, ..) | - TermRef::PartialString(lvl, ..) => *lvl, - } - } -} -*/ - #[allow(clippy::borrowed_box)] #[derive(Debug)] pub(crate) enum TermIterState<'a> { @@ -44,9 +29,9 @@ pub(crate) enum TermIterState<'a> { Literal(Level, &'a Cell, &'a Literal), InitialCons(Level, &'a Cell, &'a Term, &'a Term), FinalCons(Level, &'a Cell, &'a Term, &'a Term), - InitialPartialString(Level, &'a Cell, &'a String, &'a Box), - FinalPartialString(Level, &'a Cell, &'a String, &'a Box), - CompleteString(Level, &'a Cell, Atom), + InitialPartialString(Level, &'a Cell, Rc, &'a Box), + FinalPartialString(Level, &'a Cell, Rc, &'a Box), + CompleteString(Level, &'a Cell, Rc), Var(Level, &'a Cell, VarPtr), } @@ -62,9 +47,11 @@ impl<'a> TermIterState<'a> { } Term::Literal(cell, constant) => TermIterState::Literal(lvl, cell, constant), Term::PartialString(cell, string_buf, tail) => { - TermIterState::InitialPartialString(lvl, cell, string_buf, tail) + TermIterState::InitialPartialString(lvl, cell, string_buf.clone(), tail) + } + Term::CompleteString(cell, string) => { + TermIterState::CompleteString(lvl, cell, string.clone()) } - Term::CompleteString(cell, atom) => TermIterState::CompleteString(lvl, cell, *atom), Term::Var(cell, var_ptr) => TermIterState::Var(lvl, cell, var_ptr.clone()), } } @@ -182,11 +169,11 @@ impl<'a> Iterator for QueryIterator<'a> { .push(TermIterState::FinalPartialString(lvl, cell, string, tail)); self.push_subterm(lvl.child_level(), tail); } - TermIterState::FinalPartialString(lvl, cell, atom, tail) => { - return Some(TermRef::PartialString(lvl, cell, atom, tail)); + TermIterState::FinalPartialString(lvl, cell, string, tail) => { + return Some(TermRef::PartialString(lvl, cell, string, tail)); } - TermIterState::CompleteString(lvl, cell, atom) => { - return Some(TermRef::CompleteString(lvl, cell, atom)); + TermIterState::CompleteString(lvl, cell, string) => { + return Some(TermRef::CompleteString(lvl, cell, string)); } TermIterState::FinalCons(lvl, cell, head, tail) => { return Some(TermRef::Cons(lvl, cell, head, tail)); @@ -242,16 +229,20 @@ impl<'a> FactIterator<'a> { head.as_ref(), tail.as_ref(), )], - Term::PartialString(cell, string_buf, tail) => { + Term::PartialString(cell, string, tail) => { vec![TermIterState::InitialPartialString( Level::Root, cell, - string_buf, + string.clone(), tail, )] } - Term::CompleteString(cell, atom) => { - vec![TermIterState::CompleteString(Level::Root, cell, *atom)] + Term::CompleteString(cell, string) => { + vec![TermIterState::CompleteString( + Level::Root, + cell, + string.clone(), + )] } Term::Literal(cell, constant) => { vec![TermIterState::Literal(Level::Root, cell, constant)] @@ -336,7 +327,7 @@ pub(crate) enum ClauseItem<'a> { FirstBranch(usize), NextBranch, BranchEnd(usize), - Chunk(&'a VecDeque), + Chunk { terms: &'a VecDeque }, } #[derive(Debug)] @@ -412,8 +403,8 @@ impl<'a> Iterator for ClauseIterator<'a> { self.state_stack .push(ClauseIteratorState::RemainingBranches(branches, 0)); } - ChunkedTerms::Chunk(chunk) => { - return Some(ClauseItem::Chunk(chunk)); + ChunkedTerms::Chunk { ref terms } => { + return Some(ClauseItem::Chunk { terms }); } } } diff --git a/src/lib.rs b/src/lib.rs index fcb65ff0..598110ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,8 +11,11 @@ pub(crate) mod macros; pub(crate) mod atom_table; #[macro_use] pub(crate) mod arena; +pub(crate) mod offset_table; #[macro_use] pub(crate) mod parser; +#[macro_use] +pub(crate) mod functor_macro; mod allocator; mod arithmetic; pub(crate) mod codegen; diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index bea783f7..3100f7f9 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -125,7 +125,7 @@ call(_, _, _, _, _, _, _, _, _). % % The flags that Scryer Prolog support are: % -% * `max_arity`: The max arity a predicate can have in Prolog. On Scryer is set to 1023. Read only. +% * `max_arity`: The max arity a predicate can have in Prolog. On Scryer is set to 255. Read only. % * `bounded`: `true` if integer arithmethic is bounded between some min/max values. On Scryer is always set % to `false` since it supports unbounded integer arithmethic. Read only. % * `integer_rounding_function`: Describes the rounding donde by `//` and `rem` functions. On Scryer is @@ -145,8 +145,8 @@ call(_, _, _, _, _, _, _, _, _). % `fail` (the call silently fails) and `warn` (the call fails and a warning about the undefined predicate is printed). % * `answer_write_options`: Additional write options used by the top level for writing answers. % -current_prolog_flag(Flag, Value) :- Flag == max_arity, !, Value = 1023. -current_prolog_flag(max_arity, 1023). +current_prolog_flag(Flag, Value) :- Flag == max_arity, !, Value = 255. +current_prolog_flag(max_arity, 255). current_prolog_flag(Flag, Value) :- Flag == bounded, !, Value = false. current_prolog_flag(bounded, false). current_prolog_flag(Flag, Value) :- Flag == integer_rounding_function, !, Value == toward_zero. @@ -1719,20 +1719,27 @@ must_be_number(N, PI) :- ). -chars_or_vars(Cs, _) :- +chars_or_vars(Cs, PI) :- + ( '$is_partial_string'(Cs) -> + % use a fast test for the expected case + true + ; chars_or_vars_(Cs, PI) + ). + +chars_or_vars_(Cs, _) :- ( var(Cs) -> ! ; Cs == [] -> ! ). -chars_or_vars([C|Cs], PI) :- +chars_or_vars_([C|Cs], PI) :- ( nonvar(C) -> ( atom(C), atom_length(C, 1) -> - chars_or_vars(Cs, PI) + chars_or_vars_(Cs, PI) ; throw(error(type_error(character, C), PI)) ) - ; chars_or_vars(Cs, PI) + ; chars_or_vars_(Cs, PI) ). diff --git a/src/lib/iso_ext.pl b/src/lib/iso_ext.pl index 5c779b10..fc36f5dd 100644 --- a/src/lib/iso_ext.pl +++ b/src/lib/iso_ext.pl @@ -22,6 +22,7 @@ but they're not part of the ISO Prolog standard at the moment. copy_term/3]). :- use_module(library(error), [can_be/2, + must_be/2, domain_error/3, instantiation_error/1, type_error/3]). @@ -275,17 +276,15 @@ call_with_inference_limit(_, _, R, Bb, B) :- ; nonvar(R) ). -%% partial_string(String, L, L0) +%% partial_string(String, Ls0, Ls) % % Explicitly construct a partial string "manually". It can be used as an optimized append/3. % It's not recommended to use this predicate in application code. -partial_string(String, L, L0) :- +partial_string(String, Ls0, Ls) :- + must_be(chars, String), ( String == [] -> - L = L0 - ; catch(atom_chars(Atom, String), - error(E, _), - throw(error(E, partial_string/3))), - '$create_partial_string'(Atom, L, L0) + Ls0 = Ls + ; '$create_partial_string'(String, Ls0, Ls) ). %% partial_string(+String) diff --git a/src/lib/pio.pl b/src/lib/pio.pl index f13c9b85..0204a19e 100644 --- a/src/lib/pio.pl +++ b/src/lib/pio.pl @@ -39,7 +39,8 @@ % represented as a list of characters. phrase_from_stream(GRBody, Stream) :- - stream_to_lazy_list(Stream, Ls), + stream_property(Stream, reposition(Reposition)), + stream_to_lazy_list(Reposition, Stream, Ls), phrase(GRBody, Ls). %% phrase_from_file(+GRBody, +File) @@ -64,7 +65,7 @@ phrase_from_file(NT, File, Options) :- ; Type = text ), setup_call_cleanup( - open(File, read, Stream, Options), + open(File, read, Stream, [reposition(true)|Options]), phrase_from_stream(NT, Stream), close(Stream) ) @@ -73,34 +74,41 @@ phrase_from_file(NT, File, Options) :- % How many chars to read from stream and buffer in each step chars_to_read(4096). -stream_to_lazy_list(Stream, Ls) :- - get_stream_buffer_position(Stream, Pos), - freeze(Ls, render_step(Stream, Pos, Ls)). +stream_to_lazy_list(Reposition, Stream, Ls) :- + get_stream_buffer_position(Reposition, Stream, Pos), + freeze(Ls, render_step(Reposition, Stream, Pos, Ls)). -render_step(Stream, Pos, Ls) :- - set_stream_buffer_position(Stream, Pos), - ( buffer_at_end_of_stream(Stream) -> +render_step(Reposition, Stream, Pos, Ls) :- + set_stream_buffer_position(Reposition, Stream, Pos), + ( buffer_at_end_of_stream(Reposition, Stream) -> Ls = [] ; chars_to_read(CharsToRead), - buffer_get_n_chars(Stream, CharsToRead, Chars), + buffer_get_n_chars(Reposition, Stream, CharsToRead, Chars), partial_string(Chars, Ls, Ls0), - stream_to_lazy_list(Stream, Ls0) + stream_to_lazy_list(Reposition, Stream, Ls0) ). -buffer_at_end_of_stream(Stream) :- +buffer_at_end_of_stream(true, Stream) :- at_end_of_stream(Stream). +buffer_at_end_of_stream(false, Stream) :- stream_bufferids(Stream, _, BufferPosId, _), bb_get(BufferPosId, Pos), Pos = eof. -get_stream_buffer_position(Stream, Pos) :- +get_stream_buffer_position(true, Stream, Pos) :- + stream_property(Stream, position(Pos)). +get_stream_buffer_position(false, Stream, Pos) :- stream_bufferids(Stream, _, BufferPosId, _), bb_get(BufferPosId, Pos). -set_stream_buffer_position(Stream, Pos) :- +set_stream_buffer_position(true, Stream, Pos) :- + set_stream_position(Stream, Pos). +set_stream_buffer_position(false, Stream, Pos) :- stream_bufferids(Stream, _, BufferPosId, _), bb_put(BufferPosId, Pos). -buffer_get_n_chars(Stream, N, Chars) :- +buffer_get_n_chars(true, Stream, N, Chars) :- + get_n_chars(Stream, N, Chars). +buffer_get_n_chars(false, Stream, N, Chars) :- stream_bufferids(Stream, BufferId, BufferPosId, BufferLenId), buffer_prepare_for_n(Stream, BufferId, BufferPosId, BufferLenId, N), bb_get(BufferId, Buffer), diff --git a/src/lib/process.pl b/src/lib/process.pl new file mode 100644 index 00000000..57c71426 --- /dev/null +++ b/src/lib/process.pl @@ -0,0 +1,224 @@ +:- module(process, [ + process_create/3, + process_id/2, + process_release/1, + process_wait/2, + process_wait/3, + process_kill/1 +]). + +:- use_module(library(error)). +:- use_module(library(iso_ext)). +:- use_module(library(lists), [member/2, maplist/2, maplist/3, append/2]). +:- use_module(library(reif), [tfilter/3, memberd_t/3]). + + +%% process_create(+Exe, +Args:list, +Options). +% +% Create a new process by executing the executable Exe and passing it the Arguments Args. +% +% Note: On windows please take note of [windows argument splitting](https://doc.rust-lang.org/std/process/index.html#windows-argument-splitting). +% +% Options is a list consisting of the following options: +% +% * `cwd(+Path)` Set the processes working directory to `Path` +% * `process(-Process)` `Process` will be assigned a process handle for the spawned process +% * `env(+List)` Don't inherit environment variables and set the variables defined in `List` +% * `environment(+List)` Inherit environment variables and set/override the variables defined in `List` +% * `stdin(Spec)`, `stdout(Spec)` or `stderr(Spec)` defines how to redirect the spawned processes io streams +% +% The elements of `List` in `env(List)`/`environment(List)` List must be string pairs using `=/2`. +% `env/1` and `environment/1` may not be both specified. +% +% The following stdio `Spec` are available: +% +% * `std` inherit the current processes original stdio streams (does currently not account for stdio being changed by `set_input` or `set_output`) +% * `file(+Path)` attach the strea to the file at `Path` +% * `null` discards writes and behaves as eof for read. Equivalent to using `file(/dev/null)` +% * `pipe(-Steam)` create a new pipe and assigne one end to the created process and the other end to `Stream` +% +% Specifying an option multiple times is an error, when an option is not specified the following defaults apply: +% +% - `cwd(".")` +% - `environment([])` +% - `stdin(std)`, `stdout(std)`, `stderr(std)` +% +process_create(Exe, Args, Options) :- call_with_error_context(process_create_(Exe, Args, Options), predicate-process_create/3). + +process_create_(Exe, Args, Options) :- + must_be(chars, Exe), + must_be(list, Args), + maplist(must_be(chars), Args), + must_be(list, Options), + check_options( + [ + option([stdin], valid_stdio, stdin(std), stdin(Stdin)), + option([stdout], valid_stdio, stdout(std), stdout(Stdout)), + option([stderr], valid_stdio, stderr(std), stderr(Stderr)), + option([env, environment], valid_env, environment([]), Env), + option([process], valid_uninit_process, process(_), process(Process)), + option([cwd], valid_cwd, cwd("."), cwd(Cwd)) + ], + Options, + process_create_option + ), + Stdin =.. Stdin1, + Stdout =.. Stdout1, + Stderr =.. Stderr1, + simplify_env(Env, Env1), + '$process_create'(Exe, Args, Stdin1, Stdout1, Stderr1, Env1, Cwd, Process). + +%% process_id(+Process, -Pid). +% +process_id(Process, Pid) :- call_with_error_context(process_id_(Process, Pid), predicate-process_id/2). + +process_id_(Process, Pid) :- + valid_process(Process), + must_be(var, Pid), + '$process_id'(Process, Pid). + +%% process_wait(+Process, Status). +% +% See `process_create/3` with `Options = []` +% +process_wait(Process, Status) :- call_with_error_context(process_wait(Process, Status, []), predicate-process_wait/2). + + +%% process_wait(+Process, Status, Options). +% +% Wait for the process behind the process handle `Process` to exit. +% +% When the process exits regulary `Status` will be unified with `exit(Exit)` where `Exit` is the processes exit code. +% When the process exits was killed `Status` will be unified with `killed(Signal)` where `Signal` is the signal number that killed the process. +% When the process doesn't exit before the timeout `Status` will be unified with `timeout`. +% +% `Options` is a a list of the following options +% +% * timeout(Timeout) supported values for `Timeout` are 0 or `infinite` +% +% Each options may be specified at most once, when an option is not specified the following defaults apply: +% +% - timeout(infinite) +% +process_wait(Process, Status, Options) :- call_with_error_context(process_wait_(Process, Status, Options), predicate-process_wait/3). + +process_wait_(Process, Status, Options) :- + valid_process(Process), + check_options( + [ + option([timeout], valid_timeout, timeout(infinite), timeout(Timeout)) + ], + Options, + process_wait_option + ), + '$process_wait'(Process, Exit, Timeout), + Exit = Status. + +valid_timeout(timeout(infinite)). +valid_timeout(timeout(0)). + + +%% process_kill(+Process). +% +% Kill the process using the process handle `Process`. +% On Unix this sends SIGKILL. +% +% Only works for processes spawned with `process_create/3` that have not yet been release with `process_release/1` +% +process_kill(Process) :- call_with_error_context(process_kill_(Process), predicate-process_kill/1). + +process_kill_(Process) :- + valid_process(Process), + '$process_kill'(Process). + +%% process_release(+Process) +% +% wait for the process to exit (if not already) and release process handle `Process` +% +% It's an error if `Process` is not a valid process handle +% +process_release(Process) :- call_with_error_context(process_release_(Process), predicate-process_release/1). + +process_release_(Process) :- + valid_process(Process), + process_wait(Process, _), + '$process_release'(Process). + + +must_be_known_options(Valid, Options, Domain) :- call_with_error_context(must_be_known_options_(Valid, [], Options, Domain),predicate-must_be_known_options/3). + +must_be_known_options_(_, _, [], _). +must_be_known_options_(Valid, Found, [X|XS], Domain) :- + ( functor(X, Option, 1) -> true + ; domain_error(Domain, X, []) + ) , + ( member(Option, Found) -> domain_error(non_duplicate_options, Option , []) + ; member(Option, Valid) -> true + ; domain_error(Domain, Option, []) + ), + must_be_known_options_(Valid, [Option | Found], XS, Domain). + +check_options(KnownOptions, Options, Domain) :- call_with_error_context(check_options_(KnownOptions, Options, Domain), predicate-check_options/3). + +check_options_(KnownOptions, Options, Domain) :- + maplist(option_names, KnownOptions, Namess), + append(Namess, Names), + must_be_known_options(Names, Options, Domain), + extract_options(KnownOptions, Options). + +option_names(option(Names,_,_,_), Names). + +extract_options(KnownOptions, Options) :- call_with_error_context(extract_options_(KnownOptions, Options), predicate-extract_options/2). + +extract_options_([], _). +extract_options_([X | XS], Options) :- + option(Kinds, Pred, Default, Choice) = X, + tfilter(find_option(Kinds), Options, Solutions), + ( Solutions = [] -> Choice = Default + ; Solutions = [Provided] -> functor(Pred, Name, Arity), ArityP1 is Arity+1, call_with_error_context(call(Pred, Provided),predicate-Name/ArityP1), Choice = Provided + ; domain_error(non_conflicting_options, Solutions, []) + ), + extract_options_(XS, Options). + +find_option(Names, Found, T) :- + functor(Found, Name, 1), + memberd_t(Name, Names, T). + +valid_stdio(IO) :- arg(1, IO, Arg), + ( var(Arg) -> instantiation_error([]) + ; valid_stdio_(Arg) -> true + ; domain_error(stdio_spec, Arg, []) + ). + +valid_stdio_(std). +valid_stdio_(null). +valid_stdio_(pipe(Stream)) :- must_be(var, Stream). +valid_stdio_(file(Path)) :- must_be(chars, Path). + +valid_env(env(E)) :- + must_be(list, E), + ( valid_env_(E) -> true + ; domain_error(process_create_option, env(E), []) + ). +valid_env(environment(E)) :- + must_be(list, E), + ( valid_env_(E) -> true + ; domain_error(process_create_option, environment(E), []) + ). + +valid_env_([]). +valid_env_([N=V|ES]) :- + must_be(chars, N), + must_be(chars, V), + valid_env_(ES). + +valid_uninit_process(process(Process)) :- must_be(var, Process). + +valid_process(Process) :- var(Process) -> instantiation_error([]) ; true. + +valid_cwd(cwd(Cwd)) :- must_be(chars, Cwd). + +simplify_env(E, [Kind, Envs1]) :- E =.. [Kind, Envs], simplify_env_(Envs, Envs1). + +simplify_env_([],[]). +simplify_env_([N=V|E],[[N, V]|E1]) :- simplify_env_(E, E1). diff --git a/src/lib/sgml.pl b/src/lib/sgml.pl index bccba1f3..b7933c9f 100644 --- a/src/lib/sgml.pl +++ b/src/lib/sgml.pl @@ -96,14 +96,14 @@ is_sgml_source([]). is_sgml_source([C|Cs]) :- must_be(chars, [C|Cs]). load_structure_([], [], _, _). -load_structure_([C|Cs], [E], Options, What) :- - load_(What, [C|Cs], E, Options). -load_structure_(file(Fs), [E], Options, What) :- +load_structure_([C|Cs], [E|Es], Options, What) :- + load_(What, [C|Cs], [E|Es], Options). +load_structure_(file(Fs), [E|Es], Options, What) :- once(phrase_from_file(seq(Cs), Fs)), - load_(What, Cs, E, Options). -load_structure_(stream(Stream), [E], Options, What) :- + load_(What, Cs, [E|Es], Options). +load_structure_(stream(Stream), [E|Es], Options, What) :- get_n_chars(Stream, _, Cs), - load_(What, Cs, E, Options). + load_(What, Cs, [E|Es], Options). load_(html, Cs, E, Options) :- '$load_html'(Cs, E, Options). load_(xml, Cs, E, Options) :- '$load_xml'(Cs, E, Options). diff --git a/src/loader.pl b/src/loader.pl index 12341b42..f2d77009 100644 --- a/src/loader.pl +++ b/src/loader.pl @@ -299,7 +299,7 @@ expand_term_goals(Terms0, Terms) :- ( atom(Module) -> prolog_load_context(module, Target), module_expanded_head_variables(Head2, HeadVars), - catch(expand_goal(Body0, Target, Body1, HeadVars, []), + catch('$call'(loader:expand_goal(Body0, Target, Body1, HeadVars, [])), error(type_error(callable, Pred), _), ( loader:print_goal_expansion_warning(Pred), builtins:(Body1 = Body0) @@ -309,7 +309,7 @@ expand_term_goals(Terms0, Terms) :- ) ; module_expanded_head_variables(Head1, HeadVars), prolog_load_context(module, Target), - catch(expand_goal(Body0, Target, Body1, HeadVars, []), + catch('$call'(loader:expand_goal(Body0, Target, Body1, HeadVars, [])), error(type_error(callable, Pred), _), ( loader:print_goal_expansion_warning(Pred), builtins:(Body1 = Body0) diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index 43ea34c2..c0c3f2bb 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -11,6 +11,7 @@ use crate::forms::*; use crate::heap_iter::*; use crate::machine::machine_errors::*; use crate::machine::machine_state::*; +use crate::offset_table::*; use crate::parser::ast::*; use crate::parser::dashu::{Integer, Rational}; use crate::types::*; @@ -48,7 +49,7 @@ macro_rules! drop_iter_on_err { }; } -fn zero_divisor_eval_error(stub_gen: impl Fn() -> FunctorStub + 'static) -> MachineStubGen { +fn zero_divisor_eval_error(stub_gen: impl Fn() -> MachineStub + 'static) -> MachineStubGen { Box::new(move |machine_st| { let eval_error = machine_st.evaluation_error(EvalError::ZeroDivisor); let stub = stub_gen(); @@ -57,7 +58,7 @@ fn zero_divisor_eval_error(stub_gen: impl Fn() -> FunctorStub + 'static) -> Mach }) } -fn undefined_eval_error(stub_gen: impl Fn() -> FunctorStub + 'static) -> MachineStubGen { +fn undefined_eval_error(stub_gen: impl Fn() -> MachineStub + 'static) -> MachineStubGen { Box::new(move |machine_st| { let eval_error = machine_st.evaluation_error(EvalError::Undefined); let stub = stub_gen(); @@ -69,7 +70,7 @@ fn undefined_eval_error(stub_gen: impl Fn() -> FunctorStub + 'static) -> Machine fn numerical_type_error( valid_type: ValidType, n: Number, - stub_gen: impl Fn() -> FunctorStub + 'static, + stub_gen: impl Fn() -> MachineStub + 'static, ) -> MachineStubGen { Box::new(move |machine_st| { let type_error = machine_st.type_error(valid_type, n); @@ -197,11 +198,10 @@ pub(crate) fn neg(n: Number, arena: &mut Arena) -> Number { pub(crate) fn abs(n: Number, arena: &mut Arena) -> Number { match n { Number::Fixnum(n) => { - if let Some(n) = n.get_num().checked_abs() { - fixnum!(Number, n, arena) + if let Some(n) = n.checked_abs() { + Number::Fixnum(n) } else { - let arena_int = Integer::from(n.get_num()); - Number::arena_from(arena_int.abs(), arena) + Number::arena_from(Integer::from(Fixnum::MAX + 1), arena) } } Number::Integer(n) => { @@ -528,7 +528,7 @@ pub(crate) fn min(n1: Number, n2: Number) -> Result { pub fn rational_from_number( n: Number, - stub_gen: impl Fn() -> FunctorStub + 'static, + stub_gen: impl Fn() -> MachineStub + 'static, arena: &mut Arena, ) -> Result, MachineStubGen> { match n { @@ -1104,7 +1104,7 @@ pub(crate) fn round(num: Number, arena: &mut Arena) -> Result Result { match n1 { - Number::Fixnum(n) => Ok(Number::Fixnum(Fixnum::build_with(!n.get_num()))), + Number::Fixnum(n) => Ok(Number::Fixnum(!n)), Number::Integer(n1) => Ok(Number::arena_from(Integer::from(!&*n1), arena)), _ => { let stub_gen = || { @@ -1124,9 +1124,12 @@ impl MachineState { &ArithmeticTerm::Reg(r) => { let value = self.store(self.deref(self[r])); - match Number::try_from(value) { + match Number::try_from((value, &self.arena.f64_tbl)) { Ok(n) => Ok(n), - Err(_) => self.arith_eval_by_metacall(value), + Err(_) => { + self.heap[0] = value; + self.arith_eval_by_metacall(0) + } } } &ArithmeticTerm::Interm(i) => Ok(mem::replace( @@ -1140,7 +1143,7 @@ impl MachineState { pub fn get_rational( &mut self, at: &ArithmeticTerm, - caller: impl Fn() -> FunctorStub + 'static, + caller: impl Fn() -> MachineStub + 'static, ) -> Result, MachineStub> { let n = self.get_number(at)?; @@ -1152,11 +1155,11 @@ impl MachineState { pub(crate) fn arith_eval_by_metacall( &mut self, - value: HeapCellValue, + term_loc: usize, ) -> Result { let stub_gen = || functor_stub(atom!("is"), 2); let mut iter = - stackful_post_order_iter::(&mut self.heap, &mut self.stack, value); + stackful_post_order_iter::(&mut self.heap, &mut self.stack, term_loc); while let Some(value) = iter.next() { if value.get_forwarding_bit() { @@ -1169,7 +1172,7 @@ impl MachineState { (HeapCellValueTag::Str, s) => { cell_as_atom_cell!(self.heap[s]).get_name_and_arity() } - (HeapCellValueTag::Lis | HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset | + (HeapCellValueTag::Lis | // HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset | HeapCellValueTag::PStrLoc) => { (atom!("."), 2) } @@ -1385,8 +1388,9 @@ impl MachineState { (HeapCellValueTag::Fixnum, n) => { self.interms.push(Number::Fixnum(n)); } - (HeapCellValueTag::F64, fl) => { - self.interms.push(Number::Float(*fl)); + (HeapCellValueTag::F64Offset, offset) => { + let fl = self.arena.f64_tbl.get_entry(offset); + self.interms.push(Number::Float(fl)); } (HeapCellValueTag::Cons, ptr) => { match_untyped_arena_ptr!(ptr, @@ -1449,7 +1453,7 @@ mod tests { parse_and_write_parsed_term_to_heap(&mut wam, "3 + 4 - 1 + 2.", &op_dir).unwrap(); assert_eq!( - wam.arith_eval_by_metacall(heap_loc_as_cell!(term_write_result.heap_loc)), + wam.arith_eval_by_metacall(term_write_result.heap_loc), Ok(Number::Fixnum(Fixnum::build_with(8))), ); @@ -1459,7 +1463,7 @@ mod tests { parse_and_write_parsed_term_to_heap(&mut wam, "5 * 4 - 1.", &op_dir).unwrap(); assert_eq!( - wam.arith_eval_by_metacall(heap_loc_as_cell!(term_write_result.heap_loc)), + wam.arith_eval_by_metacall(term_write_result.heap_loc), Ok(Number::Fixnum(Fixnum::build_with(19))), ); @@ -1469,7 +1473,7 @@ mod tests { parse_and_write_parsed_term_to_heap(&mut wam, "sign(-1).", &op_dir).unwrap(); assert_eq!( - wam.arith_eval_by_metacall(heap_loc_as_cell!(term_write_result.heap_loc)), + wam.arith_eval_by_metacall(term_write_result.heap_loc), Ok(Number::Fixnum(Fixnum::build_with(-1))) ); } diff --git a/src/machine/attributed_variables.rs b/src/machine/attributed_variables.rs index d84822fe..4aaf45ee 100644 --- a/src/machine/attributed_variables.rs +++ b/src/machine/attributed_variables.rs @@ -6,7 +6,6 @@ use crate::types::*; use indexmap::IndexSet; use std::cmp::Ordering; -use std::vec::IntoIter; pub(super) type Bindings = Vec<(usize, HeapCellValue)>; @@ -55,32 +54,36 @@ impl MachineState { self.attr_var_init.bindings.push((h, addr)); } - fn populate_var_and_value_lists(&mut self) -> (HeapCellValue, HeapCellValue) { + fn populate_var_and_value_lists(&mut self) -> Result<(HeapCellValue, HeapCellValue), usize> { + let size = self.attr_var_init.bindings.len(); + let iter = self .attr_var_init .bindings .iter() .map(|(ref h, _)| attr_var_as_cell!(*h)); - let var_list_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, iter)); + let var_list_addr = sized_iter_to_heap_list(&mut self.heap, size, iter)?; let iter = self.attr_var_init.bindings.drain(0..).map(|(_, ref v)| *v); - let value_list_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, iter)); + let value_list_addr = sized_iter_to_heap_list(&mut self.heap, size, iter)?; - (var_list_addr, value_list_addr) + Ok((var_list_addr, value_list_addr)) } - fn verify_attributes(&mut self) { + fn verify_attributes(&mut self) -> Result<(), usize> { for (h, _) in &self.attr_var_init.bindings { self.heap[*h] = attr_var_as_cell!(*h); } - let (var_list_addr, value_list_addr) = self.populate_var_and_value_lists(); + let (var_list_addr, value_list_addr) = self.populate_var_and_value_lists()?; self[temp_v!(1)] = var_list_addr; self[temp_v!(2)] = value_list_addr; + + Ok(()) } - pub(super) fn gather_attr_vars_created_since(&mut self, b: usize) -> IntoIter { + pub(super) fn gather_attr_vars_created_since(&mut self, b: usize) -> Vec { let mut attr_vars: Vec<_> = if b >= self.attr_var_init.attr_var_queue.len() { vec![] } else { @@ -104,10 +107,10 @@ impl MachineState { }); attr_vars.dedup(); - attr_vars.into_iter() + attr_vars } - pub(super) fn verify_attr_interrupt(&mut self, p: usize, arity: usize) { + pub(super) fn verify_attr_interrupt(&mut self, p: usize, arity: usize) -> Result<(), usize> { self.allocate(arity + 3); let e = self.e; @@ -117,23 +120,40 @@ impl MachineState { and_frame[i] = self.registers[i]; } - and_frame[arity + 1] = fixnum_as_cell!(Fixnum::build_with(self.b0 as i64)); - and_frame[arity + 2] = fixnum_as_cell!(Fixnum::build_with(self.num_of_args as i64)); - and_frame[arity + 3] = fixnum_as_cell!(Fixnum::build_with(self.attr_var_init.cp as i64)); + and_frame[arity + 1] = + fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(self.b0 as i64) } + ); + and_frame[arity + 2] = + fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(self.num_of_args as i64) } + ); + and_frame[arity + 3] = + fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(self.attr_var_init.cp as i64) } + ); - self.verify_attributes(); + self.verify_attributes()?; self.num_of_args = 3; self.b0 = self.b; self.p = p; + + Ok(()) } pub(super) fn attr_vars_of_term(&mut self, cell: HeapCellValue) -> Vec { + debug_assert!(cell.is_ref()); + let mut seen_set = IndexSet::new(); let mut seen_vars = vec![]; - let mut iter = - stackful_preorder_iter::(&mut self.heap, &mut self.stack, cell); + self.heap[0] = cell; + + let mut iter = stackful_preorder_iter::(&mut self.heap, &mut self.stack, 0); while let Some(value) = iter.next() { read_heap_cell!(value, diff --git a/src/machine/compile.rs b/src/machine/compile.rs index e58f3553..8e799009 100644 --- a/src/machine/compile.rs +++ b/src/machine/compile.rs @@ -133,7 +133,7 @@ fn merge_indices( ); retraction_info.push_record(RetractionRecord::AddedIndex( - skeleton[clause_index].opt_arg_index_key.clone(), + skeleton[clause_index].opt_arg_index_key, clause_loc, )); } else { @@ -246,7 +246,7 @@ fn remove_index_from_subsequence( // appear anywhere inside an Internal record. retraction_info.push_record(RetractionRecord::RemovedIndex( index_loc, - opt_arg_index_key.clone(), + *opt_arg_index_key, offset, )); } @@ -700,31 +700,25 @@ fn remove_non_leading_clause( } } -fn finalize_retract( +fn finalize_retract<'a, LS: LoadState<'a>>( + payload: &mut >::LoaderFieldType, key: PredicateKey, compilation_target: CompilationTarget, skeleton: &mut PredicateSkeleton, code_index: CodeIndex, target_pos: usize, index_ptr_opt: Option, - retraction_info: &mut RetractionInfo, ) -> usize { let clause_clause_loc = delete_from_skeleton( compilation_target, key, skeleton, target_pos, - retraction_info, + &mut payload.retraction_info, ); if let Some(index_ptr) = index_ptr_opt { - set_code_index( - retraction_info, - &compilation_target, - key, - code_index, - index_ptr, - ); + set_code_index::(payload, &compilation_target, key, code_index, index_ptr); } clause_clause_loc @@ -814,7 +808,7 @@ fn prepend_compiled_clause( skeleton.clauses[0].clause_start = clause_loc + 2; retraction_info.push_record(RetractionRecord::AddedIndex( - skeleton.clauses[0].opt_arg_index_key.clone(), + skeleton.clauses[0].opt_arg_index_key, skeleton.clauses[0].clause_start, )); @@ -1076,7 +1070,7 @@ fn append_compiled_clause( skeleton.clauses[target_pos].opt_arg_index_key += index_loc - 1; retraction_info.push_record(RetractionRecord::AddedIndex( - skeleton.clauses[target_pos].opt_arg_index_key.clone(), + skeleton.clauses[target_pos].opt_arg_index_key, skeleton.clauses[target_pos].clause_start, )); @@ -1237,12 +1231,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { settings: CodeGenSettings, ) -> Result { let mut preprocessor = Preprocessor::new(settings); + let clause = preprocessor.try_term_to_tl(self, term)?; - let clause = self.try_term_to_tl(term, &mut preprocessor)?; - // let queue = preprocessor.parse_queue(self)?; - - let mut cg = CodeGenerator::new(&LS::machine_st(&mut self.payload).atom_tbl, settings); + let f64_tbl = &LS::machine_st(&mut self.payload).arena.f64_tbl; + let mut cg = CodeGenerator::new(f64_tbl, settings); let clause_code = cg.compile_predicate(vec![clause])?; Ok(StandaloneCompileResult { @@ -1257,7 +1250,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { mut predicates: PredicateQueue, settings: CodeGenSettings, ) -> Result { - let code_index = self.get_or_insert_code_index(key, predicates.compilation_target); + let code_idx = self.get_or_insert_code_index(key, predicates.compilation_target); LS::err_on_builtin_overwrite(self, key)?; @@ -1268,11 +1261,12 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { let mut preprocessor = Preprocessor::new(settings); for term in predicates.predicates.drain(0..) { - clauses.push(self.try_term_to_tl(term, &mut preprocessor)?); + clauses.push(preprocessor.try_term_to_tl(self, term)?); } - let mut cg = CodeGenerator::new(&LS::machine_st(&mut self.payload).atom_tbl, settings); + let f64_tbl = &LS::machine_st(&mut self.payload).arena.f64_tbl; + let mut cg = CodeGenerator::new(f64_tbl, settings); let mut code = cg.compile_predicate(clauses)?; if settings.is_extensible { @@ -1331,9 +1325,14 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { ); } + let index_ptr = LS::machine_st(&mut self.payload) + .arena + .code_index_tbl + .get_entry(code_idx.into()); + print_overwrite_warning( &predicates.compilation_target, - code_index.get(), + index_ptr, key, settings.is_dynamic(), ); @@ -1344,16 +1343,16 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { IndexPtr::index(code_ptr) }; - set_code_index( - &mut self.payload.retraction_info, + set_code_index::( + &mut self.payload, &predicates.compilation_target, key, - code_index, + code_idx, index_ptr, ); self.wam_prelude.code.extend(code); - Ok(code_index) + Ok(code_idx) } fn extend_local_predicate_skeleton( @@ -1555,19 +1554,19 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { self.push_back_to_local_predicate_skeleton(&compilation_target, &key, code_len); - let code_index = self.get_or_insert_code_index(key, compilation_target); + let code_idx = self.get_or_insert_code_index(key, compilation_target); if let Some(new_code_ptr) = result { - set_code_index( - &mut self.payload.retraction_info, + set_code_index::( + &mut self.payload, &compilation_target, key, - code_index, + code_idx, new_code_ptr, ); } - Ok(code_index) + Ok(code_idx) } AppendOrPrepend::Prepend => { let clause_index_info = standalone_skeleton.clauses.pop_back().unwrap(); @@ -1597,17 +1596,17 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { self.push_front_to_local_predicate_skeleton(&compilation_target, &key, code_len); - let code_index = self.get_or_insert_code_index(key, compilation_target); + let code_idx = self.get_or_insert_code_index(key, compilation_target); - set_code_index( - &mut self.payload.retraction_info, + set_code_index::( + &mut self.payload, &compilation_target, key, - code_index, + code_idx, new_code_ptr, ); - Ok(code_index) + Ok(code_idx) } } } @@ -1655,7 +1654,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { pub(super) fn retract_clause(&mut self, key: PredicateKey, target_pos: usize) -> usize { let payload_compilation_target = self.payload.compilation_target; - let code_index = self.get_or_insert_code_index(key, payload_compilation_target); + let code_idx_offset = self.get_or_insert_code_index(key, payload_compilation_target); let skeleton = self .wam_prelude @@ -1733,14 +1732,14 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { None }; - return finalize_retract( + return finalize_retract::( + &mut self.payload, key, payload_compilation_target, skeleton, - code_index, + code_idx_offset, target_pos, index_ptr_opt, - &mut self.payload.retraction_info, ); } None => { @@ -1762,14 +1761,14 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { ) }; - return finalize_retract( + return finalize_retract::( + &mut self.payload, key, payload_compilation_target, skeleton, - code_index, + code_idx_offset, target_pos, index_ptr_opt, - &mut self.payload.retraction_info, ); } } @@ -1992,14 +1991,14 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { } }; - finalize_retract( + finalize_retract::( + &mut self.payload, key, payload_compilation_target, skeleton, - code_index, + code_idx_offset, target_pos, index_ptr_opt, - &mut self.payload.retraction_info, ) } } @@ -2226,18 +2225,22 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { }; let predicates = self.payload.predicates.take(); - let code_index = self.compile(key, predicates, settings)?; + let offset = self.compile(key, predicates, settings)?; if let Some(filename) = self.listing_src_file_name() { if let Some(ref mut module) = self.wam_prelude.indices.modules.get_mut(&filename) { - let index_ptr = code_index.get(); - let code_index = *module.code_dir.entry(key).or_insert(code_index); + let index_ptr = LS::machine_st(&mut self.payload) + .arena + .code_index_tbl + .get_entry(offset.into()); - set_code_index( - &mut self.payload.retraction_info, + let offset = *module.code_dir.entry(key).or_insert(offset); + + set_code_index::( + &mut self.payload, &CompilationTarget::Module(filename), key, - code_index, + offset, index_ptr, ); } diff --git a/src/machine/copier.rs b/src/machine/copier.rs index c02854ff..78658c4a 100644 --- a/src/machine/copier.rs +++ b/src/machine/copier.rs @@ -1,12 +1,71 @@ +use fxhash::FxBuildHasher; +use indexmap::IndexSet; + use crate::atom_table::*; use crate::machine::get_structure_index; +use crate::machine::heap::*; use crate::machine::stack::*; use crate::types::*; -use std::mem; -use std::ops::IndexMut; +use scryer_modular_bitfield::specifiers::*; +use scryer_modular_bitfield::*; -type Trail = Vec<(Ref, HeapCellValue)>; +use std::collections::BTreeMap; +use std::mem; +use std::ops::{IndexMut, Range}; + +#[derive(BitfieldSpecifier, Copy, Clone, Debug)] +#[bits = 6] +enum TrailRefTag { + HeapCell = 0b001011, + StackCell = 0b001101, + AttrVar = 0b010001, + PStrLoc = 0b001111, +} + +#[bitfield] +#[repr(u64)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +struct TrailRef { + val: B56, + #[allow(unused)] + m: bool, + #[allow(unused)] + f: bool, + tag: TrailRefTag, +} + +impl TrailRef { + #[inline(always)] + fn heap_cell(h: usize) -> Self { + TrailRef::new() + .with_tag(TrailRefTag::HeapCell) + .with_val(h as u64) + } + + #[inline(always)] + fn stack_cell(h: usize) -> Self { + TrailRef::new() + .with_tag(TrailRefTag::StackCell) + .with_val(h as u64) + } + + #[inline(always)] + fn attr_var(h: usize) -> Self { + TrailRef::new() + .with_tag(TrailRefTag::AttrVar) + .with_val(h as u64) + } + + #[inline(always)] + fn pstr_loc(h: usize) -> Self { + TrailRef::new() + .with_tag(TrailRefTag::PStrLoc) + .with_val(h as u64) + } +} + +type Trail = Vec<(TrailRef, HeapCellValue)>; #[derive(Debug, Clone, Copy)] pub enum AttrVarPolicy { @@ -17,22 +76,39 @@ pub enum AttrVarPolicy { pub trait CopierTarget: IndexMut { fn store(&self, value: HeapCellValue) -> HeapCellValue; fn deref(&self, value: HeapCellValue) -> HeapCellValue; - fn push(&mut self, value: HeapCellValue); fn push_attr_var_queue(&mut self, attr_var_loc: usize); fn stack(&mut self) -> &mut Stack; fn threshold(&self) -> usize; + // returns the tail location of the pstr on success + fn as_slice_from<'a>(&'a self, from: usize) -> Box + 'a>; + fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result; + fn reserve(&mut self, num_cells: usize) -> Result; + fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), usize>; } pub(crate) fn copy_term( target: T, addr: HeapCellValue, attr_var_policy: AttrVarPolicy, -) { +) -> Result { let mut copy_term_state = CopyTermState::new(target, attr_var_policy); + let old_threshold = copy_term_state.target.threshold(); - copy_term_state.copy_term_impl(addr); - copy_term_state.copy_attr_var_lists(); + copy_term_state.copy_term_impl(addr)?; + copy_term_state.copy_attr_var_lists()?; copy_term_state.unwind_trail(); + + let new_threshold = copy_term_state.target.threshold(); + copy_term_state.copy_pstrs()?; + + Ok(new_threshold - old_threshold) +} + +#[derive(Debug)] +pub struct PStrData { + pre_old_h_tail_loc: usize, + post_old_h_tail_loc: usize, + post_old_h_pstr_loc_locs: IndexSet, } #[derive(Debug)] @@ -43,6 +119,9 @@ struct CopyTermState { target: T, attr_var_policy: AttrVarPolicy, attr_var_list_locs: Vec<(usize, HeapCellValue)>, + // keys of pstr_loc_locs are byte indices rounded down to the + // nearest cell boundary + pstr_loc_locs: BTreeMap, } impl CopyTermState { @@ -54,6 +133,7 @@ impl CopyTermState { target, attr_var_policy, attr_var_list_locs: vec![], + pstr_loc_locs: BTreeMap::new(), } } @@ -64,17 +144,17 @@ impl CopyTermState { fn trail_list_cell(&mut self, addr: usize, threshold: usize) { let trail_item = mem::replace(&mut self.target[addr], list_loc_as_cell!(threshold)); - self.trail.push((Ref::heap_cell(addr), trail_item)); + self.trail.push((TrailRef::heap_cell(addr), trail_item)); } - fn copy_list(&mut self, addr: usize) { + fn copy_list(&mut self, addr: usize) -> Result<(), usize> { for offset in 0..2 { read_heap_cell!(self.target[addr + offset], (HeapCellValueTag::Lis, h) => { if h >= self.old_h { *self.value_at_scan() = list_loc_as_cell!(h); self.scan += 1; - return; + return Ok(()); } } _ => { @@ -83,14 +163,10 @@ impl CopyTermState { } let threshold = self.target.threshold(); + self.target.copy_slice_to_end(addr..addr + 2)?; *self.value_at_scan() = list_loc_as_cell!(threshold); - for i in 0..2 { - let hcv = self.target[addr + i]; - self.target.push(hcv); - } - let cdr = self .target .store(self.target.deref(heap_loc_as_cell!(addr + 1))); @@ -113,80 +189,108 @@ impl CopyTermState { } self.scan += 1; + Ok(()) } - fn copy_partial_string(&mut self, scan_tag: HeapCellValueTag, pstr_loc: usize) { - read_heap_cell!(self.target[pstr_loc], - (HeapCellValueTag::PStrLoc, h) => { - debug_assert!(h >= self.old_h); - - *self.value_at_scan() = match scan_tag { - HeapCellValueTag::PStrLoc => { - pstr_loc_as_cell!(h) - } - tag => { - debug_assert_eq!(tag, HeapCellValueTag::PStrOffset); - pstr_offset_as_cell!(h) - } - }; - + fn copy_partial_string(&mut self, pstr_loc: usize) -> Result<(), usize> { + match self.pstr_loc_locs.range_mut(..=pstr_loc).next_back() { + Some(( + _prev_pstr_loc, + &mut PStrData { + pre_old_h_tail_loc, + ref mut post_old_h_pstr_loc_locs, + .. + }, + )) if pre_old_h_tail_loc >= cell_index!(pstr_loc) => { + post_old_h_pstr_loc_locs.insert(self.scan); self.scan += 1; - return; - } - (HeapCellValueTag::Var, h) => { - debug_assert!(h >= self.old_h); - debug_assert_eq!(scan_tag, HeapCellValueTag::PStrOffset); - - *self.value_at_scan() = pstr_offset_as_cell!(h); - self.scan += 1; - - return; + return Ok(()); } _ => {} - ); + } - let threshold = self.target.threshold(); + let offset = self + .target + .as_slice_from(pstr_loc) + .take_while(|b| *b != 0u8) + .count(); - let replacement = read_heap_cell!(self.target[pstr_loc], - (HeapCellValueTag::CStr) => { - debug_assert_eq!(scan_tag, HeapCellValueTag::PStrOffset); + let left_pstr_boundary = cell_index!(pstr_loc + offset); + let flag = u64::from_be_bytes(self.target[left_pstr_boundary].into_bytes()); + let pstr_loc_idx = cell_index!(pstr_loc); - *self.value_at_scan() = pstr_offset_as_cell!(threshold); - self.target.push(self.target[pstr_loc]); + if flag == 1 { + if left_pstr_boundary != pstr_loc_idx { + let mut pstr_data = self + .pstr_loc_locs + .remove(&heap_index!(left_pstr_boundary)) + .unwrap(); - heap_loc_as_cell!(threshold) + pstr_data.post_old_h_pstr_loc_locs.insert(self.scan); + self.pstr_loc_locs + .insert(heap_index!(cell_index!(pstr_loc)), pstr_data); + + let old_cell = self.target[pstr_loc_idx]; + self.target[pstr_loc_idx] = HeapCellValue::from_bytes(u64::to_be_bytes(1)); + self.trail + .push((TrailRef::pstr_loc(pstr_loc_idx), old_cell)); + } else { + let pstr_data = self + .pstr_loc_locs + .get_mut(&heap_index!(left_pstr_boundary)) + .unwrap(); + pstr_data.post_old_h_pstr_loc_locs.insert(self.scan); } - _ => { - *self.value_at_scan() = if scan_tag == HeapCellValueTag::PStrLoc { - pstr_loc_as_cell!(threshold) - } else { - debug_assert_eq!(scan_tag, HeapCellValueTag::PStrOffset); - pstr_offset_as_cell!(threshold) - }; + } else { + let old_cell = self.target[pstr_loc_idx]; + self.target[pstr_loc_idx] = HeapCellValue::from_bytes(u64::to_be_bytes(1)); + self.trail + .push((TrailRef::pstr_loc(pstr_loc_idx), old_cell)); - self.target.push(self.target[pstr_loc]); - self.target.push(self.target[pstr_loc + 1]); + let old_tail_idx = if (pstr_loc + offset + 1) % Heap::heap_cell_alignment() == 0 { + cell_index!(pstr_loc + offset) + 2 + } else { + cell_index!(pstr_loc + offset) + 1 + }; - pstr_loc_as_cell!(threshold) - } - ); + let tail_cell = self.target[old_tail_idx]; + + let new_tail_idx = self.target.threshold(); + let mut writer = self.target.reserve(1)?; + + writer.write_with(|section| { + section.push_cell(tail_cell); + }); + + let mut post_old_h_pstr_loc_locs = IndexSet::with_hasher(FxBuildHasher::default()); + post_old_h_pstr_loc_locs.insert(self.scan); + + let pstr_data = PStrData { + pre_old_h_tail_loc: old_tail_idx, + post_old_h_tail_loc: new_tail_idx, + post_old_h_pstr_loc_locs, + }; + + self.pstr_loc_locs + .insert(heap_index!(pstr_loc_idx), pstr_data); + } self.scan += 1; - - let trail_item = mem::replace(&mut self.target[pstr_loc], replacement); - self.trail.push((Ref::heap_cell(pstr_loc), trail_item)); + Ok(()) } - fn copy_attr_var_lists(&mut self) { + fn copy_attr_var_lists(&mut self) -> Result<(), usize> { while !self.attr_var_list_locs.is_empty() { - let iter = std::mem::take(&mut self.attr_var_list_locs); + let mut list_loc_vec = std::mem::take(&mut self.attr_var_list_locs); - for (threshold, list_loc) in iter { + while let Some((threshold, list_loc)) = list_loc_vec.pop() { self.target[threshold] = list_loc_as_cell!(self.target.threshold()); self.target.push_attr_var_queue(threshold - 1); - self.copy_attr_var_list(list_loc); + self.copy_attr_var_list(list_loc)?; } } + + Ok(()) } /* @@ -194,48 +298,49 @@ impl CopyTermState { * structure which is ensured by this function and not at all by * the vanilla copier. */ - fn copy_attr_var_list(&mut self, mut list_addr: HeapCellValue) { + fn copy_attr_var_list(&mut self, mut list_addr: HeapCellValue) -> Result<(), usize> { while let HeapCellValueTag::Lis = list_addr.get_tag() { let threshold = self.target.threshold(); let heap_loc = list_addr.get_value() as usize; let str_loc = self.target[heap_loc].get_value() as usize; + let str_cell = self.target[str_loc]; + let mut writer = self.target.reserve(3).unwrap(); - self.target.push(heap_loc_as_cell!(threshold + 2)); - self.target.push(heap_loc_as_cell!(threshold + 1)); + writer.write_with(|section| { + section.push_cell(heap_loc_as_cell!(threshold + 2)); + section.push_cell(heap_loc_as_cell!(threshold + 1)); - read_heap_cell!(self.target[str_loc], - (HeapCellValueTag::Atom) => { - self.target.push(self.target[str_loc]); + if str_cell.to_atom().is_some() { + section.push_cell(str_cell); } - (HeapCellValueTag::Str) => { - self.copy_term_impl(self.target[str_loc]); - } - _ => { - unreachable!(); - } - ); + }); + debug_assert_eq!(str_cell.get_tag(), HeapCellValueTag::Str); + + self.copy_term_impl(str_cell)?; list_addr = self.target[heap_loc + 1]; if HeapCellValueTag::Lis == list_addr.get_tag() { self.target[threshold + 1] = list_loc_as_cell!(self.target.threshold()); } } + + Ok(()) } - fn reinstantiate_var(&mut self, addr: HeapCellValue, frontier: usize) { + fn reinstantiate_var(&mut self, addr: HeapCellValue, frontier: usize) -> Result<(), usize> { read_heap_cell!(addr, (HeapCellValueTag::Var, h) => { self.target[frontier] = heap_loc_as_cell!(frontier); self.target[h] = heap_loc_as_cell!(frontier); - self.trail.push((Ref::heap_cell(h), heap_loc_as_cell!(h))); + self.trail.push((TrailRef::heap_cell(h), heap_loc_as_cell!(h))); } (HeapCellValueTag::StackVar, s) => { self.target[frontier] = heap_loc_as_cell!(frontier); self.target.stack()[s] = heap_loc_as_cell!(frontier); - self.trail.push((Ref::stack_cell(s), stack_loc_as_cell!(s))); + self.trail.push((TrailRef::stack_cell(s), stack_loc_as_cell!(s))); } (HeapCellValueTag::AttrVar, h) => { let threshold = if let AttrVarPolicy::DeepCopy = self.attr_var_policy { @@ -247,14 +352,18 @@ impl CopyTermState { self.target[frontier] = heap_loc_as_cell!(threshold); self.target[h] = heap_loc_as_cell!(threshold); - self.trail.push((Ref::attr_var(h), attr_var_as_cell!(h))); + self.trail.push((TrailRef::attr_var(h), attr_var_as_cell!(h))); if let AttrVarPolicy::DeepCopy = self.attr_var_policy { - self.target.push(attr_var_as_cell!(threshold)); - self.target.push(heap_loc_as_cell!(threshold + 1)); + let mut writer = self.target.reserve(2)?; + + writer.write_with(|section| { + section.push_cell(attr_var_as_cell!(threshold)); + section.push_cell(heap_loc_as_cell!(threshold + 1)); + }); let old_list_link = self.target[h + 1]; - self.trail.push((Ref::heap_cell(h + 1), old_list_link)); + self.trail.push((TrailRef::heap_cell(h + 1), old_list_link)); self.target[h + 1] = heap_loc_as_cell!(threshold + 1); if old_list_link.get_tag() == HeapCellValueTag::Lis { @@ -266,9 +375,11 @@ impl CopyTermState { unreachable!() } ); + + Ok(()) } - fn copy_var(&mut self, addr: HeapCellValue) { + fn copy_var(&mut self, addr: HeapCellValue) -> Result<(), usize> { let index = addr.get_value() as usize; let rd = self.target.deref(addr); let ra = self.target.store(rd); @@ -278,7 +389,7 @@ impl CopyTermState { if h >= self.old_h { *self.value_at_scan() = ra; self.scan += 1; - return; + return Ok(()); } } (HeapCellValueTag::Lis, h) => { @@ -292,47 +403,52 @@ impl CopyTermState { ); self.scan += 1; - return; + return Ok(()); } } _ => {} ); if rd == ra { - self.reinstantiate_var(ra, self.scan); + self.reinstantiate_var(ra, self.scan)?; self.scan += 1; } else { *self.value_at_scan() = ra; } + + Ok(()) } - fn copy_structure(&mut self, addr: usize) { + fn copy_structure(&mut self, addr: usize) -> Result<(), usize> { read_heap_cell!(self.target[addr], - (HeapCellValueTag::Atom, (name, arity)) => { + (HeapCellValueTag::Atom, (_name, arity)) => { let threshold = self.target.threshold(); - *self.value_at_scan() = str_loc_as_cell!(threshold); + let index_cell = self.target[addr.saturating_sub(1)]; + + let str_cell = if get_structure_index(index_cell).is_some() { + // copy the index pointer trailing this + // inlined or expanded goal. + let mut writer = self.target.reserve(1).unwrap(); + + writer.write_with(|section| { + section.push_cell(index_cell); + }); + + str_loc_as_cell!(threshold + 1) + } else { + str_loc_as_cell!(threshold) + }; + + *self.value_at_scan() = str_cell; + self.target.copy_slice_to_end(addr .. addr + 1 + arity)?; let trail_item = mem::replace( &mut self.target[addr], - str_loc_as_cell!(threshold), + str_cell, ); - self.trail.push((Ref::heap_cell(addr), trail_item)); - self.target.push(atom_as_cell!(name, arity)); - - for i in 0..arity { - let hcv = self.target[addr + 1 + i]; - self.target.push(hcv); - } - - let index_cell = self.target[addr + 1 + arity]; - - if get_structure_index(index_cell).is_some() { - // copy the index pointer trailing this - // inlined or expanded goal. - self.target.push(index_cell); - } + self.trail.push((TrailRef::heap_cell(addr), trail_item)); } (HeapCellValueTag::Str, h) => { *self.value_at_scan() = str_loc_as_cell!(h); @@ -343,11 +459,16 @@ impl CopyTermState { ); self.scan += 1; + Ok(()) } - fn copy_term_impl(&mut self, addr: HeapCellValue) { + fn copy_term_impl(&mut self, addr: HeapCellValue) -> Result<(), usize> { self.scan = self.target.threshold(); - self.target.push(addr); + let mut writer = self.target.reserve(1)?; + + writer.write_with(|section| { + section.push_cell(addr); + }); while self.scan < self.target.threshold() { let addr = *self.value_at_scan(); @@ -356,37 +477,64 @@ impl CopyTermState { (HeapCellValueTag::Lis, h) => { if h >= self.old_h { self.scan += 1; + continue; } else { - self.copy_list(h); + self.copy_list(h) } } (HeapCellValueTag::AttrVar | HeapCellValueTag::Var) => { - self.copy_var(addr); + self.copy_var(addr) } (HeapCellValueTag::Str, h) => { - self.copy_structure(h); + self.copy_structure(h) } - (HeapCellValueTag::PStrLoc | HeapCellValueTag::PStrOffset, pstr_loc) => { - self.copy_partial_string(addr.get_tag(), pstr_loc); + (HeapCellValueTag::PStrLoc, pstr_loc) => { + self.copy_partial_string(pstr_loc) } _ => { self.scan += 1; + continue; } - ); + )?; } + + Ok(()) } - fn unwind_trail(mut self) { - for (r, value) in self.trail { - let index = r.get_value() as usize; + fn copy_pstrs(&mut self) -> Result<(), usize> { + while let Some((least_pstr_loc, pstr_data)) = self.pstr_loc_locs.pop_first() { + let threshold = heap_index!(self.target.threshold()); - match r.get_tag() { - RefTag::AttrVar | RefTag::HeapCell => { + for pstr_loc_loc in pstr_data.post_old_h_pstr_loc_locs { + let pstr_loc = self.target[pstr_loc_loc].get_value() as usize; + self.target[pstr_loc_loc] = + pstr_loc_as_cell!(threshold + pstr_loc - least_pstr_loc); + } + + self.target.copy_pstr_to_threshold(least_pstr_loc)?; + + let mut writer = self.target.reserve(1)?; + + writer.write_with(|section| { + section.push_cell(heap_loc_as_cell!(pstr_data.post_old_h_tail_loc)); + }); + } + + Ok(()) + } + + fn unwind_trail(&mut self) { + for (r, value) in self.trail.drain(..) { + let index = r.val() as usize; + + match r.tag() { + TrailRefTag::AttrVar | TrailRefTag::HeapCell => { self.target[index] = value; self.target[index].set_mark_bit(false); self.target[index].set_forwarding_bit(false); } - RefTag::StackCell => self.target.stack()[index] = value, + TrailRefTag::StackCell => self.target.stack()[index] = value, + TrailRefTag::PStrLoc => self.target[index] = value, } } } @@ -395,19 +543,26 @@ impl CopyTermState { #[cfg(test)] mod tests { use super::*; + use crate::functor_macro::*; use crate::machine::mock_wam::*; #[test] fn copier_tests() { let mut wam = MockWAM::new(); + // clear the heap of resource error data etc + wam.machine_st.heap.clear(); + let f_atom = atom!("f"); let a_atom = atom!("a"); let b_atom = atom!("b"); - wam.machine_st - .heap - .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); + let mut functor_writer = Heap::functor_writer(functor!( + f_atom, + [atom_as_cell(a_atom), atom_as_cell(b_atom)] + )); + + functor_writer(&mut wam.machine_st.heap).unwrap(); assert_eq!(wam.machine_st.heap[0], atom_as_cell!(f_atom, 2)); assert_eq!(wam.machine_st.heap[1], atom_as_cell!(a_atom)); @@ -415,7 +570,7 @@ mod tests { { let wam = TermCopyingMockWAM { wam: &mut wam }; - copy_term(wam, str_loc_as_cell!(0), AttrVarPolicy::DeepCopy); + copy_term(wam, str_loc_as_cell!(0), AttrVarPolicy::DeepCopy).unwrap(); } // check that the original heap state is still intact. @@ -430,69 +585,266 @@ mod tests { wam.machine_st.heap.clear(); - let pstr_var_cell = - put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); - let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; + let mut writer = wam.machine_st.heap.reserve(4).unwrap(); - wam.machine_st.heap.pop(); - wam.machine_st.heap.push(pstr_loc_as_cell!(2)); + writer.write_with(|section| { + section.push_pstr("abc "); + section.push_cell(pstr_loc_as_cell!(heap_index!(2))); - let pstr_second_var_cell = - put_partial_string(&mut wam.machine_st.heap, "def", &wam.machine_st.atom_tbl); - let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; - - wam.machine_st.heap.pop(); - wam.machine_st - .heap - .push(pstr_loc_as_cell!(wam.machine_st.heap.len() + 1)); - - wam.machine_st.heap.push(pstr_offset_as_cell!(0)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(0i64))); + section.push_pstr("def"); + section.push_cell(pstr_loc_as_cell!(0)); + }); { let wam = TermCopyingMockWAM { wam: &mut wam }; - copy_term(wam, pstr_loc_as_cell!(0), AttrVarPolicy::DeepCopy); + copy_term(wam, pstr_loc_as_cell!(0), AttrVarPolicy::DeepCopy).unwrap(); } - print_heap_terms(wam.machine_st.heap[6..].iter(), 6); - - assert_eq!(wam.machine_st.heap[0], pstr_cell); - assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(2)); - assert_eq!(wam.machine_st.heap[2], pstr_second_cell); - assert_eq!(wam.machine_st.heap[3], pstr_loc_as_cell!(4)); - assert_eq!(wam.machine_st.heap[4], pstr_offset_as_cell!(0)); + assert_eq!(wam.machine_st.heap.slice_to_str(0, "abc ".len()), "abc "); + assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(heap_index!(2))); assert_eq!( - wam.machine_st.heap[5], - fixnum_as_cell!(Fixnum::build_with(0i64)) + wam.machine_st + .heap + .slice_to_str(heap_index!(2), "def".len()), + "def" ); + assert_eq!(wam.machine_st.heap[3], pstr_loc_as_cell!(0)); + + assert_eq!(wam.machine_st.heap[4], pstr_loc_as_cell!(heap_index!(7))); + assert_eq!(wam.machine_st.heap[5], pstr_loc_as_cell!(heap_index!(9))); + assert_eq!(wam.machine_st.heap[6], pstr_loc_as_cell!(heap_index!(7))); - assert_eq!(wam.machine_st.heap[7], pstr_cell); - assert_eq!(wam.machine_st.heap[8], pstr_loc_as_cell!(9)); - assert_eq!(wam.machine_st.heap[9], pstr_second_cell); - assert_eq!(wam.machine_st.heap[10], pstr_loc_as_cell!(11)); - assert_eq!(wam.machine_st.heap[11], pstr_offset_as_cell!(7)); assert_eq!( - wam.machine_st.heap[12], - fixnum_as_cell!(Fixnum::build_with(0i64)) + wam.machine_st + .heap + .slice_to_str(heap_index!(7), "abc ".len()), + "abc " ); + assert_eq!(wam.machine_st.heap[8], heap_loc_as_cell!(5)); + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(9), "def".len()), + "def" + ); + assert_eq!(wam.machine_st.heap[10], heap_loc_as_cell!(6)); wam.machine_st.heap.clear(); - wam.machine_st.heap.extend(functor!( - f_atom, - [ - atom(a_atom), - atom(b_atom), - atom(a_atom), - cell(str_loc_as_cell!(0)) - ] - )); + let mut writer = wam.machine_st.heap.reserve(4).unwrap(); + + writer.write_with(|section| { + section.push_pstr("abc "); + section.push_cell(pstr_loc_as_cell!(heap_index!(2) + 9)); + + section.push_pstr("defdefdefdef"); + section.push_cell(pstr_loc_as_cell!(0)); + }); { let wam = TermCopyingMockWAM { wam: &mut wam }; - copy_term(wam, str_loc_as_cell!(0), AttrVarPolicy::DeepCopy); + copy_term(wam, pstr_loc_as_cell!(0), AttrVarPolicy::DeepCopy).unwrap(); + } + + assert_eq!(wam.machine_st.heap.slice_to_str(0, "abc ".len()), "abc "); + assert_eq!( + wam.machine_st.heap[1], + pstr_loc_as_cell!(heap_index!(2) + 9) + ); + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(2), "defdefdefdef".len()), + "defdefdefdef" + ); + assert_eq!(wam.machine_st.heap[4], pstr_loc_as_cell!(0)); + + assert_eq!(wam.machine_st.heap[5], pstr_loc_as_cell!(heap_index!(8))); + assert_eq!( + wam.machine_st.heap[6], + pstr_loc_as_cell!(heap_index!(10) + 1) + ); + assert_eq!(wam.machine_st.heap[7], pstr_loc_as_cell!(heap_index!(8))); + + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(8), "abc ".len()), + "abc " + ); + assert_eq!(wam.machine_st.heap[9], heap_loc_as_cell!(6)); + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(10), "fdef".len()), + "fdef" + ); + assert_eq!(wam.machine_st.heap[11], heap_loc_as_cell!(7)); + + wam.machine_st.heap.clear(); + + let mut writer = wam.machine_st.heap.reserve(4).unwrap(); + + writer.write_with(|section| { + section.push_pstr("012345678912345"); + section.push_cell(pstr_loc_as_cell!(heap_index!(0))); + }); + + { + let wam = TermCopyingMockWAM { wam: &mut wam }; + copy_term(wam, pstr_loc_as_cell!(0), AttrVarPolicy::DeepCopy).unwrap(); + } + + assert_eq!( + wam.machine_st.heap.slice_to_str(0, "012345678912345".len()), + "012345678912345" + ); + assert_eq!(wam.machine_st.heap[3], pstr_loc_as_cell!(heap_index!(0))); + + assert_eq!(wam.machine_st.heap[4], pstr_loc_as_cell!(heap_index!(6))); + + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(6), "012345678912345".len()), + "012345678912345" + ); + assert_eq!(wam.machine_st.heap[5], pstr_loc_as_cell!(heap_index!(6))); + + wam.machine_st.heap.clear(); + + let mut writer = wam.machine_st.heap.reserve(4).unwrap(); + + writer.write_with(|section| { + section.push_pstr("012345678912345"); + section.push_cell(pstr_loc_as_cell!(heap_index!(0) + 9)); + }); + + { + let wam = TermCopyingMockWAM { wam: &mut wam }; + copy_term(wam, pstr_loc_as_cell!(0), AttrVarPolicy::DeepCopy).unwrap(); + } + + assert_eq!( + wam.machine_st.heap.slice_to_str(0, "012345678912345".len()), + "012345678912345" + ); + assert_eq!( + wam.machine_st.heap[3], + pstr_loc_as_cell!(heap_index!(0) + 9) + ); + + assert_eq!(wam.machine_st.heap[4], pstr_loc_as_cell!(heap_index!(6))); + assert_eq!( + wam.machine_st.heap[5], + pstr_loc_as_cell!(heap_index!(6) + 9) + ); + + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(6), "012345678912345".len()), + "012345678912345" + ); + assert_eq!(wam.machine_st.heap[9], heap_loc_as_cell!(5)); + + wam.machine_st.heap.clear(); + + let mut writer = wam.machine_st.heap.reserve(4).unwrap(); + + writer.write_with(|section| { + section.push_pstr("012345678912345"); + section.push_cell(pstr_loc_as_cell!(heap_index!(0) + 7)); + }); + + { + let wam = TermCopyingMockWAM { wam: &mut wam }; + copy_term(wam, pstr_loc_as_cell!(11), AttrVarPolicy::DeepCopy).unwrap(); + } + + assert_eq!( + wam.machine_st.heap.slice_to_str(0, "012345678912345".len()), + "012345678912345" + ); + assert_eq!( + wam.machine_st.heap[3], + pstr_loc_as_cell!(heap_index!(0) + 7) + ); + + assert_eq!( + wam.machine_st.heap[4], + pstr_loc_as_cell!(heap_index!(6) + 11) + ); + assert_eq!( + wam.machine_st.heap[5], + pstr_loc_as_cell!(heap_index!(6) + 7) + ); + + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(6), "012345678912345".len()), + "012345678912345" + ); + assert_eq!(wam.machine_st.heap[9], heap_loc_as_cell!(5)); + + wam.machine_st.heap.clear(); + + let mut writer = wam.machine_st.heap.reserve(4).unwrap(); + + writer.write_with(|section| { + section.push_pstr("012345678912345"); + section.push_cell(pstr_loc_as_cell!(heap_index!(0) + 12)); + }); + + { + let wam = TermCopyingMockWAM { wam: &mut wam }; + copy_term(wam, pstr_loc_as_cell!(11), AttrVarPolicy::DeepCopy).unwrap(); + } + + assert_eq!( + wam.machine_st.heap.slice_to_str(0, "012345678912345".len()), + "012345678912345" + ); + assert_eq!( + wam.machine_st.heap[3], + pstr_loc_as_cell!(heap_index!(0) + 12) + ); + + assert_eq!( + wam.machine_st.heap[4], + pstr_loc_as_cell!(heap_index!(6) + 3) + ); + assert_eq!( + wam.machine_st.heap[5], + pstr_loc_as_cell!(heap_index!(6) + 4) + ); + + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(6), "8912345".len()), + "8912345" + ); + assert_eq!(wam.machine_st.heap[8], heap_loc_as_cell!(5)); + + wam.machine_st.heap.clear(); + + let mut functor_writer = Heap::functor_writer(functor!( + f_atom, + [ + atom_as_cell(a_atom), + atom_as_cell(b_atom), + atom_as_cell(a_atom), + str_loc_as_cell(0) + ] + )); + + functor_writer(&mut wam.machine_st.heap).unwrap(); + + { + let wam = TermCopyingMockWAM { wam: &mut wam }; + copy_term(wam, str_loc_as_cell!(0), AttrVarPolicy::DeepCopy).unwrap(); } assert_eq!(wam.machine_st.heap[0], atom_as_cell!(f_atom, 4)); @@ -500,7 +852,6 @@ mod tests { assert_eq!(wam.machine_st.heap[2], atom_as_cell!(b_atom)); assert_eq!(wam.machine_st.heap[3], atom_as_cell!(a_atom)); assert_eq!(wam.machine_st.heap[4], str_loc_as_cell!(0)); - assert_eq!(wam.machine_st.heap[5], str_loc_as_cell!(6)); assert_eq!(wam.machine_st.heap[6], atom_as_cell!(f_atom, 4)); assert_eq!(wam.machine_st.heap[7], atom_as_cell!(a_atom)); diff --git a/src/machine/cycle_detection.rs b/src/machine/cycle_detection.rs index 6df242ac..a800a9b6 100644 --- a/src/machine/cycle_detection.rs +++ b/src/machine/cycle_detection.rs @@ -1,4 +1,5 @@ use crate::atom_table::*; +use crate::machine::heap::*; use crate::types::*; /* Use the pointer reversal technique of the Deutsch-Schorr-Waite @@ -11,7 +12,7 @@ use crate::types::*; * - Cells are only marked during the backward phase * - Visiting subterms of a visited compound does not immediately shift to the backward phase * - The heads of LIS structures are both marked and forwarded rather - * than just forwarded to distinguish them from tails; + * than just forwarded to distinguish them from tails * continue_forwarding() checks for this before entering the forward * phase * @@ -22,7 +23,7 @@ use crate::types::*; #[derive(Debug)] pub(crate) struct CycleDetectingIter<'a, const STOP_AT_CYCLES: bool> { - pub(crate) heap: &'a mut [HeapCellValue], + pub(crate) heap: &'a mut Heap, start: usize, current: usize, next: u64, @@ -31,7 +32,7 @@ pub(crate) struct CycleDetectingIter<'a, const STOP_AT_CYCLES: bool> { } impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> { - pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self { + pub(crate) fn new(heap: &'a mut Heap, start: usize) -> Self { heap[start].set_forwarding_bit(true); let next = heap[start].get_value(); @@ -127,7 +128,7 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> { self.current = next; self.next = temp; - if self.next < self.heap.len() as u64 { + if self.next < self.heap.cell_len() as u64 { return Some(HeapCellValue::build_with(tag, next as u64)); } } @@ -136,10 +137,7 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> { let cell = self.heap[h]; let arity = cell_as_atom_cell!(self.heap[h]).get_arity(); - let last_cell_loc = match self.traverse_subterm(h + 1, arity) { - Some(last_cell_loc) => last_cell_loc, - None => return None, - }; + let last_cell_loc = self.traverse_subterm(h + 1, arity)?; if last_cell_loc == h { if self.backward() { @@ -170,10 +168,7 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> { let mut cell = self.heap[self.current]; cell.set_value(self.next); - let last_cell_loc = match self.traverse_subterm(self.next as usize, 2) { - Some(last_cell_loc) => last_cell_loc, - None => return None, - }; + let last_cell_loc = self.traverse_subterm(self.next as usize, 2)?; if self.cycle_detection_active() { for idx in (self.next as usize..last_cell_loc).rev() { @@ -205,10 +200,9 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> { } HeapCellValueTag::PStrLoc => { let h = self.next as usize; - let cell = self.heap[h]; - let last_cell_loc = h + 1; + let tail_idx = self.heap.scan_slice_to_str(h).tail_idx; - if self.heap[last_cell_loc].get_forwarding_bit() { + if self.heap[tail_idx].get_forwarding_bit() { if self.cycle_detection_active() { self.cycle_found = true; return None; @@ -219,45 +213,13 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> { continue; } - self.heap[last_cell_loc].set_forwarding_bit(true); + self.heap[tail_idx].set_forwarding_bit(true); - self.next = self.heap[last_cell_loc].get_value(); - self.heap[last_cell_loc].set_value(self.current as u64); - self.current = last_cell_loc; + self.next = self.heap[tail_idx].get_value(); + self.heap[tail_idx].set_value(self.current as u64); + self.current = tail_idx; - return Some(cell); - } - HeapCellValueTag::PStrOffset => { - let h = self.next as usize; - let cell = self.heap[h]; - let last_cell_loc = h + 1; - - if self.heap[h].get_tag() == HeapCellValueTag::PStr { - if self.heap[last_cell_loc].get_forwarding_bit() { - if self.cycle_detection_active() { - self.cycle_found = true; - return None; - } else if self.backward() { - return None; - } - - continue; - } - - self.heap[last_cell_loc].set_forwarding_bit(true); - - self.next = self.heap[last_cell_loc].get_value(); - self.heap[last_cell_loc].set_value(self.current as u64); - self.current = last_cell_loc; - } else { - debug_assert!(self.heap[h].get_tag() == HeapCellValueTag::CStr); - - self.next = self.heap[h].get_value(); - self.heap[h].set_value(self.current as u64); - self.current = h; - } - - return Some(cell); + return Some(pstr_loc_as_cell!(h)); } tag @ HeapCellValueTag::Atom => { let cell = HeapCellValue::build_with(tag, self.next); @@ -269,11 +231,6 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> { return None; } } - HeapCellValueTag::PStr => { - if self.backward() { - return None; - } - } _ => { return Some(self.backward_and_return()); } diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index 651b3db7..4ecac7bc 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -200,10 +200,12 @@ impl VarData { match build_stack.front_mut() { Some(ChunkedTerms::Branch(_)) => { - build_stack.push_front(ChunkedTerms::Chunk(VecDeque::from(vec![term]))); + build_stack.push_front(ChunkedTerms::Chunk { + terms: VecDeque::from(vec![term]), + }); } - Some(ChunkedTerms::Chunk(chunk)) => { - chunk.push_front(term); + Some(ChunkedTerms::Chunk { terms, .. }) => { + terms.push_front(term); } None => { unreachable!() @@ -423,7 +425,6 @@ impl VariableClassifier { // "probe_head_var". note the difference between it // and "probe_body_var". let branch_info_v = self.branch_map.entry(var_ptr.clone()).or_default(); - let needs_new_branch = branch_info_v.is_empty(); if needs_new_branch { @@ -578,7 +579,7 @@ impl VariableClassifier { mut terms, ) if terms.len() == 3 => { if let Some(last_arg) = terms.last() { - if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg { + if let Term::Literal(_, Literal::CodeIndexOffset(_)) = last_arg { terms.pop(); state_stack.push(TraversalState::Term(Term::Clause( Cell::default(), @@ -801,7 +802,7 @@ impl VariableClassifier { self.call_policy, )); } - Term::Literal(_, Literal::Atom(atom!("!")) | Literal::Char('!')) => { + Term::Literal(_, Literal::Atom(atom!("!"))) => { let (var_num, is_global) = if let Some(var_num) = self.global_cut_var_num_override { (var_num, false) @@ -879,10 +880,10 @@ impl BranchMap { for var_info in chunk.vars.iter_mut() { if var_info.lvl == Level::Shallow { - let term_loc = var_info.chunk_type.to_gen_context(chunk.chunk_num); + let context = var_info.chunk_type.to_gen_context(chunk.chunk_num); temp_var_data .use_set - .insert((term_loc, var_info.classify_info.arg_c)); + .insert((context, var_info.classify_info.arg_c)); } } diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 57d3e5ae..8a8921e6 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -1,5 +1,6 @@ use crate::arena::*; use crate::atom_table::*; +use crate::functor_macro::*; use crate::instructions::*; use crate::machine::arithmetic_ops::*; use crate::machine::machine_errors::*; @@ -24,7 +25,10 @@ macro_rules! try_or_throw { match $e { Ok(val) => val, Err(msg) => { - $s.throw_exception(msg); + if !msg.is_empty() { + $s.throw_exception(msg); + } + $s.backtrack(); continue; } @@ -32,6 +36,15 @@ macro_rules! try_or_throw { }}; } +macro_rules! backtrack_on_resource_error { + ($machine_st:expr, $val:expr) => { + step_or_resource_error!($machine_st, $val, { + $machine_st.backtrack(); + continue; + }) + }; +} + macro_rules! increment_call_count { ($s:expr) => {{ if !$s.increment_call_count() { @@ -55,6 +68,15 @@ macro_rules! try_or_throw_gen { }}; } +macro_rules! push_cell { + ($machine_st:expr, $cell:expr) => {{ + step_or_resource_error!($machine_st, $machine_st.heap.push_cell($cell), { + $machine_st.backtrack(); + continue; + }) + }}; +} + static INSTRUCTIONS_PER_INTERRUPT_POLL: usize = 256; impl MachineState { @@ -113,12 +135,12 @@ impl MachineState { } pub fn copy_term(&mut self, attr_var_policy: AttrVarPolicy) { - let old_h = self.heap.len(); + let old_h = self.heap.cell_len(); let a1 = self.registers[1]; let a2 = self.registers[2]; - copy_term(CopyTerm::new(self), a1, attr_var_policy); + step_or_resource_error!(self, copy_term(CopyTerm::new(self), a1, attr_var_policy)); unify_fn!(*self, heap_loc_as_cell!(old_h), a2); } @@ -135,10 +157,12 @@ impl MachineState { list.dedup_by(|v1, v2| compare_term_test!(self, *v1, *v2) == Some(Ordering::Equal)); - let heap_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, list.into_iter())); + let heap_addr = resource_error_call_result!( + self, + sized_iter_to_heap_list(&mut self.heap, list.len(), list.into_iter(),) + ); let target_addr = self.registers[2]; - unify_fn!(*self, target_addr, heap_addr); Ok(()) } @@ -160,8 +184,14 @@ impl MachineState { compare_term_test!(self, a1.0, a2.0, var_comparison).unwrap_or(Ordering::Less) }); - let key_pairs = key_pairs.into_iter().map(|kp| kp.1); - let heap_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, key_pairs)); + let heap_addr = resource_error_call_result!( + self, + sized_iter_to_heap_list( + &mut self.heap, + key_pairs.len(), + key_pairs.into_iter().map(|kp| kp.1), + ) + ); let target_addr = self.registers[2]; @@ -201,18 +231,15 @@ impl MachineState { v } (HeapCellValueTag::PStrLoc | - HeapCellValueTag::Lis | - HeapCellValueTag::CStr) => { + HeapCellValueTag::Lis) => { l } (HeapCellValueTag::Fixnum | HeapCellValueTag::CutPoint | - HeapCellValueTag::Char | - HeapCellValueTag::F64) => { + HeapCellValueTag::F64Offset) => { c } (HeapCellValueTag::Atom, (_name, arity)) => { - // if arity == 0 { c } else { s } debug_assert!(arity == 0); c } @@ -242,53 +269,6 @@ impl MachineState { ) } - #[inline(always)] - pub(crate) fn constant_to_literal(&self, addr: HeapCellValue) -> Literal { - read_heap_cell!(addr, - (HeapCellValueTag::Char, c) => { - Literal::Char(c) - } - (HeapCellValueTag::Fixnum, n) => { - Literal::Fixnum(n) - } - (HeapCellValueTag::F64, f) => { - Literal::Float(f.as_offset()) - } - (HeapCellValueTag::Atom, (atom, arity)) => { - debug_assert_eq!(arity, 0); - Literal::Atom(atom) - } - (HeapCellValueTag::Str, s) => { - Literal::Atom(cell_as_atom_cell!(self.heap[s]).get_name()) - } - (HeapCellValueTag::Cons, cons_ptr) => { - match_untyped_arena_ptr!(cons_ptr, - (ArenaHeaderTag::Rational, r) => { - Literal::Rational(r) - } - (ArenaHeaderTag::Integer, n) => { - let result = (&*n).try_into(); - - match result { - Ok(fixnum) => if let Ok(n) = Fixnum::build_with_checked(fixnum) { - Literal::Fixnum(n) - } else { - Literal::Integer(n) - }, - Err(_) => Literal::Integer(n) - } - } - _ => { - unreachable!() - } - ) - } - _ => { - unreachable!() - } - ) - } - #[inline(always)] pub(crate) fn select_switch_on_structure_index( &self, @@ -464,9 +444,9 @@ impl Machine { } } IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(hm)) => { - let lit = self.machine_st.constant_to_literal(addr); + // let lit = self.machine_st.constant_to_literal(addr); - let offset = match hm.get(&lit) { + let offset = match hm.get(&addr) { Some(offset) => *offset, _ => IndexingCodePtr::Fail, }; @@ -566,19 +546,36 @@ impl Machine { let p = self.machine_st.p; // Find the boundaries of the current predicate - self.indices.code_dir.sort_by(|_, a, _, b| a.cmp(b)); + self.indices.code_dir.sort_by(|_, a, _, b| { + let a = self.machine_st.arena.code_index_tbl.get_entry((*a).into()); + let b = self.machine_st.arena.code_index_tbl.get_entry((*b).into()); + + a.cmp(&b) + }); let predicate_idx = self .indices .code_dir - .binary_search_by_key(&p, |_, x| x.get().p() as usize) + .binary_search_by_key(&p, |_, x| -> usize { + self.machine_st + .arena + .code_index_tbl + .get_entry((*x).into()) + .p() as usize + }) .unwrap_or_else(|x| x - 1); let current_pred_start = self .indices .code_dir .get_index(predicate_idx) - .map(|x| x.1.p() as usize) + .map(|idx| { + self.machine_st + .arena + .code_index_tbl + .get_entry((*idx.1).into()) + .p() as usize + }) .unwrap(); debug_assert!(current_pred_start <= p); @@ -587,7 +584,13 @@ impl Machine { .indices .code_dir .get_index(predicate_idx + 1) - .map(|x| x.1.p() as usize) + .map(|idx| { + self.machine_st + .arena + .code_index_tbl + .get_entry((*idx.1).into()) + .p() as usize + }) .unwrap_or(self.code.len()); debug_assert!(current_pred_end >= p); @@ -1039,9 +1042,13 @@ impl Machine { match self.find_living_dynamic_else(p + next_i) { Some(_) => { self.machine_st.registers - [self.machine_st.num_of_args + 1] = fixnum_as_cell!( - Fixnum::build_with(self.machine_st.cc as i64) - ); + [self.machine_st.num_of_args + 1] = + fixnum_as_cell!(unsafe { + /* FIXME this is not safe */ + Fixnum::build_with_unchecked( + self.machine_st.cc as i64, + ) + }); self.machine_st.num_of_args += 1; self.try_me_else(next_i); @@ -1060,10 +1067,11 @@ impl Machine { .prelude .num_cells; - self.machine_st.cc = cell_as_fixnum!( + self.machine_st.cc = unsafe { self.machine_st.stack [stack_loc!(OrFrame, self.machine_st.b, n - 1)] - ) + .to_fixnum_or_cut_point_unchecked() + } .get_num() as usize; @@ -1113,7 +1121,12 @@ impl Machine { Some(_) => { self.machine_st.registers [self.machine_st.num_of_args + 1] = fixnum_as_cell!( - Fixnum::build_with(self.machine_st.cc as i64) + /* FIXME this is not safe */ + unsafe { + Fixnum::build_with_unchecked( + self.machine_st.cc as i64, + ) + } ); self.machine_st.num_of_args += 1; @@ -1133,10 +1146,11 @@ impl Machine { .prelude .num_cells; - self.machine_st.cc = cell_as_fixnum!( + self.machine_st.cc = unsafe { self.machine_st.stack [stack_loc!(OrFrame, self.machine_st.b, n - 1)] - ) + .to_fixnum_or_cut_point_unchecked() + } .get_num() as usize; @@ -1192,7 +1206,10 @@ impl Machine { &Instruction::GetLevel(r) => { let b0 = self.machine_st.b0; - self.machine_st[r] = fixnum_as_cell!(Fixnum::as_cutpoint(b0 as i64)); + self.machine_st[r] = fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(b0 as i64) }.as_cutpoint() + ); self.machine_st.p += 1; } &Instruction::GetPrevLevel(r) => { @@ -1203,12 +1220,18 @@ impl Machine { .prelude .b; - self.machine_st[r] = fixnum_as_cell!(Fixnum::as_cutpoint(prev_b as i64)); + self.machine_st[r] = fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(prev_b as i64) }.as_cutpoint() + ); self.machine_st.p += 1; } &Instruction::GetCutPoint(r) => { self.machine_st[r] = - fixnum_as_cell!(Fixnum::as_cutpoint(self.machine_st.b as i64)); + fixnum_as_cell!(/* FIXME this is not safe */ unsafe { + Fixnum::build_with_unchecked(self.machine_st.b as i64) + } + .as_cutpoint()); self.machine_st.p += 1; } &Instruction::Cut(r) => { @@ -1245,22 +1268,32 @@ impl Machine { self.machine_st.allocate(num_cells); } &Instruction::DefaultCallAcyclicTerm => { - let addr = self.machine_st.registers[1]; + let addr = self.deref_register(1); - if self.machine_st.is_cyclic_term(addr) { - self.machine_st.backtrack(); - } else { - self.machine_st.p += 1; + if addr.is_ref() { + self.machine_st.heap[0] = addr; + + if self.machine_st.is_cyclic_term(0) { + self.machine_st.backtrack(); + continue; + } } + + self.machine_st.p += 1; } &Instruction::DefaultExecuteAcyclicTerm => { - let addr = self.machine_st.registers[1]; + let addr = self.deref_register(1); - if self.machine_st.is_cyclic_term(addr) { - self.machine_st.backtrack(); - } else { - self.machine_st.p = self.machine_st.cp; + if addr.is_ref() { + self.machine_st.heap[0] = addr; + + if self.machine_st.is_cyclic_term(0) { + self.machine_st.backtrack(); + continue; + } } + + self.machine_st.p = self.machine_st.cp; } &Instruction::DefaultCallArg => { try_or_throw!(self.machine_st, self.machine_st.try_arg()); @@ -1497,24 +1530,34 @@ impl Machine { step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallAcyclicTerm => { - let addr = self.machine_st.registers[1]; + let addr = self.deref_register(1); - if self.machine_st.is_cyclic_term(addr) { - self.machine_st.backtrack(); - } else { - increment_call_count!(self.machine_st); - self.machine_st.p += 1; + if addr.is_ref() { + self.machine_st.heap[0] = addr; + + if self.machine_st.is_cyclic_term(0) { + self.machine_st.backtrack(); + continue; + } } + + increment_call_count!(self.machine_st); + self.machine_st.p += 1; } &Instruction::ExecuteAcyclicTerm => { - let addr = self.machine_st.registers[1]; + let addr = self.deref_register(1); - if self.machine_st.is_cyclic_term(addr) { - self.machine_st.backtrack(); - } else { - increment_call_count!(self.machine_st); - self.machine_st.p = self.machine_st.cp; + if addr.is_ref() { + self.machine_st.heap[0] = addr; + + if self.machine_st.is_cyclic_term(0) { + self.machine_st.backtrack(); + continue; + } } + + increment_call_count!(self.machine_st); + self.machine_st.p = self.machine_st.cp; } &Instruction::CallArg => { try_or_throw!(self.machine_st, self.machine_st.try_arg()); @@ -2282,9 +2325,6 @@ impl Machine { self.machine_st.backtrack(); } } - (HeapCellValueTag::Char) => { - self.machine_st.p += 1; - } _ => { self.machine_st.backtrack(); } @@ -2313,9 +2353,6 @@ impl Machine { self.machine_st.backtrack(); } } - (HeapCellValueTag::Char) => { - self.machine_st.p = self.machine_st.cp; - } _ => { self.machine_st.backtrack(); } @@ -2327,7 +2364,7 @@ impl Machine { .store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, - (HeapCellValueTag::Char | HeapCellValueTag::Fixnum | HeapCellValueTag::F64 | + (HeapCellValueTag::Fixnum | HeapCellValueTag::F64Offset | HeapCellValueTag::Cons) => { self.machine_st.p += 1; } @@ -2359,7 +2396,7 @@ impl Machine { .store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, - (HeapCellValueTag::Char | HeapCellValueTag::Fixnum | HeapCellValueTag::F64 | + (HeapCellValueTag::Fixnum | HeapCellValueTag::F64Offset | HeapCellValueTag::Cons) => { self.machine_st.p = self.machine_st.cp; } @@ -2392,8 +2429,8 @@ impl Machine { read_heap_cell!(d, (HeapCellValueTag::Lis | - HeapCellValueTag::PStrLoc | - HeapCellValueTag::CStr) => { + HeapCellValueTag::PStrLoc) => { + // HeapCellValueTag::CStr) => { self.machine_st.p += 1; } (HeapCellValueTag::Str, s) => { @@ -2425,8 +2462,8 @@ impl Machine { read_heap_cell!(d, (HeapCellValueTag::Lis | - HeapCellValueTag::PStrLoc | - HeapCellValueTag::CStr) => { + HeapCellValueTag::PStrLoc) => { + // HeapCellValueTag::CStr) => { self.machine_st.p = self.machine_st.cp; } (HeapCellValueTag::Str, s) => { @@ -2456,7 +2493,7 @@ impl Machine { .machine_st .store(self.machine_st.deref(self.machine_st[r])); - match Number::try_from(d) { + match Number::try_from((d, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(_) | Number::Integer(_)) => { self.machine_st.p += 1; } @@ -2477,7 +2514,7 @@ impl Machine { .machine_st .store(self.machine_st.deref(self.machine_st[r])); - match Number::try_from(d) { + match Number::try_from((d, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(_) | Number::Integer(_)) => { self.machine_st.p = self.machine_st.cp; } @@ -2498,7 +2535,7 @@ impl Machine { .machine_st .store(self.machine_st.deref(self.machine_st[r])); - match Number::try_from(d) { + match Number::try_from((d, &self.machine_st.arena.f64_tbl)) { Ok(_) => { self.machine_st.p += 1; } @@ -2512,7 +2549,7 @@ impl Machine { .machine_st .store(self.machine_st.deref(self.machine_st[r])); - match Number::try_from(d) { + match Number::try_from((d, &self.machine_st.arena.f64_tbl)) { Ok(_) => { self.machine_st.p = self.machine_st.cp; } @@ -2574,7 +2611,7 @@ impl Machine { .machine_st .store(self.machine_st.deref(self.machine_st[r])); - match Number::try_from(d) { + match Number::try_from((d, &self.machine_st.arena.f64_tbl)) { Ok(Number::Float(_)) => { self.machine_st.p += 1; } @@ -2588,7 +2625,7 @@ impl Machine { .machine_st .store(self.machine_st.deref(self.machine_st[r])); - match Number::try_from(d) { + match Number::try_from((d, &self.machine_st.arena.f64_tbl)) { Ok(Number::Float(_)) => { self.machine_st.p = self.machine_st.cp; } @@ -2661,8 +2698,8 @@ impl Machine { } } } - &Instruction::CallNamed(arity, name, ref idx) => { - let idx = idx.get(); + &Instruction::CallNamed(arity, name, idx) => { + let idx = self.machine_st.arena.code_index_tbl.get_entry(idx.into()); try_or_throw!(self.machine_st, self.try_call(name, arity, idx)); @@ -2672,8 +2709,8 @@ impl Machine { increment_call_count!(self.machine_st); } } - &Instruction::ExecuteNamed(arity, name, ref idx) => { - let idx = idx.get(); + &Instruction::ExecuteNamed(arity, name, idx) => { + let idx = self.machine_st.arena.code_index_tbl.get_entry(idx.into()); try_or_throw!(self.machine_st, self.try_execute(name, arity, idx)); @@ -2683,8 +2720,8 @@ impl Machine { increment_call_count!(self.machine_st); } } - &Instruction::DefaultCallNamed(arity, name, ref idx) => { - let idx = idx.get(); + &Instruction::DefaultCallNamed(arity, name, idx) => { + let idx = self.machine_st.arena.code_index_tbl.get_entry(idx.into()); try_or_throw!(self.machine_st, self.try_call(name, arity, idx)); @@ -2692,8 +2729,8 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::DefaultExecuteNamed(arity, name, ref idx) => { - let idx = idx.get(); + &Instruction::DefaultExecuteNamed(arity, name, idx) => { + let idx = self.machine_st.arena.code_index_tbl.get_entry(idx.into()); try_or_throw!(self.machine_st, self.try_execute(name, arity, idx)); @@ -2712,8 +2749,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } &Instruction::GetConstant(_, c, reg) => { - let value = self.machine_st.deref(self.machine_st[reg]); - self.machine_st.write_literal_to_var(value, c); + unify!(self.machine_st, self.machine_st[reg], c); step_or_fail!(self, self.machine_st.p += 1); } &Instruction::GetList(_, reg) => { @@ -2722,17 +2758,7 @@ impl Machine { read_heap_cell!(store_v, (HeapCellValueTag::PStrLoc, h) => { - let (h, n) = pstr_loc_and_offset(&self.machine_st.heap, h); - - self.machine_st.s = HeapPtr::PStrChar(h, n.get_num() as usize); - self.machine_st.s_offset = 0; - self.machine_st.mode = MachineMode::Read; - } - (HeapCellValueTag::CStr) => { - let h = self.machine_st.heap.len(); - self.machine_st.heap.push(store_v); - - self.machine_st.s = HeapPtr::PStrChar(h, 0); + self.machine_st.s = HeapPtr::PStr(h); self.machine_st.s_offset = 0; self.machine_st.mode = MachineMode::Read; } @@ -2755,9 +2781,9 @@ impl Machine { self.machine_st.mode = MachineMode::Read; } (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { - let h = self.machine_st.heap.len(); + let h = self.machine_st.heap.cell_len(); - self.machine_st.heap.push(list_loc_as_cell!(h+1)); + push_cell!(self.machine_st, list_loc_as_cell!(h+1)); self.machine_st.bind(store_v.as_var().unwrap(), heap_loc_as_cell!(h)); self.machine_st.mode = MachineMode::Write; @@ -2770,35 +2796,143 @@ impl Machine { self.machine_st.p += 1; } - &Instruction::GetPartialString(_, string, reg, has_tail) => { - let deref_v = self.machine_st.deref(self.machine_st[reg]); - let store_v = self.machine_st.store(deref_v); + &Instruction::GetPartialString(_, ref string, reg) => { + self.machine_st.heap[0] = self.machine_st[reg]; - read_heap_cell!(store_v, - (HeapCellValueTag::Str | - HeapCellValueTag::Lis | - HeapCellValueTag::PStrLoc | - HeapCellValueTag::CStr) => { - self.machine_st.match_partial_string(store_v, string, has_tail); - } - (HeapCellValueTag::AttrVar | - HeapCellValueTag::StackVar | - HeapCellValueTag::Var) => { - let target_cell = self.machine_st.push_str_to_heap( - &string.as_str(), - has_tail, - ); + let mut h = 0; + let mut string_cursor = string.as_str(); - self.machine_st.bind( - store_v.as_var().unwrap(), - target_cell, - ); - } - _ => { - self.machine_st.backtrack(); - continue; - } - ); + while let Some(c) = string_cursor.chars().next() { + read_heap_cell!(self.machine_st.heap[h], + (HeapCellValueTag::PStrLoc, pstr_loc) => { + let heap_slice = &self.machine_st.heap.as_slice()[pstr_loc ..]; + + match compare_pstr_slices(heap_slice, string_cursor.as_bytes()) { + PStrSegmentCmpResult::Continue(v1, v2) => { + // for v2, the value of a TailIndex mustn't ever be read + // since string does not lie in the heap. + match (v1, v2) { + (PStrContinuable::TailIndex(tail_idx), PStrContinuable::TailIndex(_)) => { + self.machine_st.s = HeapPtr::HeapCell(tail_idx + cell_index!(pstr_loc)); + self.machine_st.s_offset = 0; + self.machine_st.mode = MachineMode::Read; + + break; + } + (PStrContinuable::TailIndex(tail_idx), PStrContinuable::PStrOffset(pos)) => { + h = tail_idx + cell_index!(pstr_loc); + string_cursor = &string_cursor[pos ..]; + } + (PStrContinuable::PStrOffset(pos), PStrContinuable::TailIndex(_)) => { + self.machine_st.s = HeapPtr::PStr(pstr_loc); + self.machine_st.s_offset = pos; + self.machine_st.mode = MachineMode::Read; + + break; + } + _ => unreachable!(), + } + } + _ => { + self.machine_st.fail = true; + break; + } + } + } + (HeapCellValueTag::Lis, l) => { + let cell = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[l])); + + if let Some(d) = cell.as_char() { + if c != d { + self.machine_st.fail = true; + break; + } + } else if let Some(r) = cell.as_var() { + self.machine_st.bind(r, char_as_cell!(c)); + } else { + self.machine_st.fail = true; + } + + if self.machine_st.fail { + break; + } else { + h = l+1; + string_cursor = &string_cursor[c.len_utf8() ..]; + + if string_cursor.is_empty() { + self.machine_st.s = HeapPtr::HeapCell(h); + self.machine_st.s_offset = 0; + self.machine_st.mode = MachineMode::Read; + } + } + } + (HeapCellValueTag::Str, s) => { + let cell = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[s+1])); + + if let Some(d) = cell.as_char() { + if c != d { + self.machine_st.fail = true; + break; + } + } else if let Some(r) = cell.as_var() { + self.machine_st.bind(r, char_as_cell!(c)); + } else { + self.machine_st.fail = true; + } + + if self.machine_st.fail { + break; + } + + h = s+2; + string_cursor = &string_cursor[c.len_utf8() ..]; + + if string_cursor.is_empty() { + self.machine_st.s = HeapPtr::HeapCell(h); + self.machine_st.s_offset = 0; + self.machine_st.mode = MachineMode::Read; + } + } + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, v) => { + if h == v { + let target_cell = backtrack_on_resource_error!( + self.machine_st, + self.machine_st.heap.allocate_pstr(string_cursor) + ); + + self.machine_st.bind( + self.machine_st.heap[h].as_var().unwrap(), + target_cell, + ); + + self.machine_st.mode = MachineMode::Write; + break; + } else { + h = v; + } + } + (HeapCellValueTag::StackVar, s) => { + debug_assert_eq!(h, 0); + + let target_cell = backtrack_on_resource_error!( + self.machine_st, + self.machine_st.heap.allocate_pstr(string_cursor) + ); + + self.machine_st.bind( + Ref::stack_cell(s), + target_cell, + ); + + self.machine_st.mode = MachineMode::Write; + break; + } + _ => { + self.machine_st.fail = true; + break; + } + ); + } step_or_fail!(self, self.machine_st.p += 1); } @@ -2825,10 +2959,10 @@ impl Machine { ); } (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { - let h = self.machine_st.heap.len(); + let h = self.machine_st.heap.cell_len(); - self.machine_st.heap.push(str_loc_as_cell!(h+1)); - self.machine_st.heap.push(atom_as_cell!(name, arity)); + push_cell!(self.machine_st, str_loc_as_cell!(h+1)); + push_cell!(self.machine_st, atom_as_cell!(name, arity)); self.machine_st.bind(store_v.as_var().unwrap(), heap_loc_as_cell!(h)); self.machine_st.mode = MachineMode::Write; @@ -2861,19 +2995,18 @@ impl Machine { &Instruction::UnifyConstant(v) => { match self.machine_st.mode { MachineMode::Read => { - let addr = self.machine_st.read_s(); - - self.machine_st.write_literal_to_var(addr, v); + let (addr, s_offset_incr) = self.machine_st.read_s(); + unify!(&mut self.machine_st, addr, v); if self.machine_st.fail { self.machine_st.backtrack(); continue; } else { - self.machine_st.s_offset += 1; + self.machine_st.s_offset += s_offset_incr; } } MachineMode::Write => { - self.machine_st.heap.push(v); + push_cell!(self.machine_st, v); } } @@ -2883,7 +3016,7 @@ impl Machine { match self.machine_st.mode { MachineMode::Read => { let reg_addr = self.machine_st[reg]; - let value = self.machine_st.read_s(); + let (value, s_offset_incr) = self.machine_st.read_s(); unify_fn!(&mut self.machine_st, reg_addr, value); @@ -2891,24 +3024,24 @@ impl Machine { self.machine_st.backtrack(); continue; } else { - self.machine_st.s_offset += 1; + self.machine_st.s_offset += s_offset_incr; } } MachineMode::Write => { let value = self .machine_st .store(self.machine_st.deref(self.machine_st[reg])); - let h = self.machine_st.heap.len(); + let h = self.machine_st.heap.cell_len(); read_heap_cell!(value, (HeapCellValueTag::Var | HeapCellValueTag::AttrVar, hc) => { let value = self.machine_st.heap[hc]; - self.machine_st.heap.push(value); + push_cell!(self.machine_st, value); self.machine_st.s_offset += 1; } _ => { - self.machine_st.heap.push(heap_loc_as_cell!(h)); + push_cell!(self.machine_st, heap_loc_as_cell!(h)); (self.machine_st.bind_fn)( &mut self.machine_st, Ref::heap_cell(h), @@ -2924,13 +3057,13 @@ impl Machine { &Instruction::UnifyVariable(reg) => { match self.machine_st.mode { MachineMode::Read => { - self.machine_st[reg] = self.machine_st.read_s(); - self.machine_st.s_offset += 1; + let (value, s_offset_incr) = self.machine_st.read_s(); + self.machine_st[reg] = value; + self.machine_st.s_offset += s_offset_incr; } MachineMode::Write => { - let h = self.machine_st.heap.len(); - - self.machine_st.heap.push(heap_loc_as_cell!(h)); + let h = self.machine_st.heap.cell_len(); + push_cell!(self.machine_st, heap_loc_as_cell!(h)); self.machine_st[reg] = heap_loc_as_cell!(h); } } @@ -2941,7 +3074,7 @@ impl Machine { match self.machine_st.mode { MachineMode::Read => { let reg_addr = self.machine_st[reg]; - let value = self.machine_st.read_s(); + let (value, s_offset_incr) = self.machine_st.read_s(); unify_fn!(&mut self.machine_st, reg_addr, value); @@ -2949,12 +3082,12 @@ impl Machine { self.machine_st.backtrack(); continue; } else { - self.machine_st.s_offset += 1; + self.machine_st.s_offset += s_offset_incr; } } MachineMode::Write => { - let h = self.machine_st.heap.len(); - self.machine_st.heap.push(heap_loc_as_cell!(h)); + let h = self.machine_st.heap.cell_len(); + push_cell!(self.machine_st, heap_loc_as_cell!(h)); let addr = self.machine_st.store(self.machine_st[reg]); (self.machine_st.bind_fn)( @@ -2966,7 +3099,7 @@ impl Machine { // the former code of this match arm was: // let addr = self.machine_st.store(self.machine_st[reg]); - // self.machine_st.heap.push(HeapCellValue::Addr(addr)); + // push_cell!(self.machine_st, HeapCellValue::Addr(addr)); // the old code didn't perform the occurs // check when enabled and so it was changed to @@ -2979,14 +3112,24 @@ impl Machine { } &Instruction::UnifyVoid(n) => { match self.machine_st.mode { - MachineMode::Read => { - self.machine_st.s_offset += n; - } + MachineMode::Read => match &self.machine_st.s { + HeapPtr::HeapCell(_) => self.machine_st.s_offset += n, + &HeapPtr::PStr(pstr_loc) => { + debug_assert!(n <= 2); + let mut char_iter = self.machine_st.heap.char_iter(pstr_loc); + + // this only matters in the case that n == 1, but the case + // analysis isn't worth doing since the effect is benign if n == + // 2 + self.machine_st.s_offset += + char_iter.next().unwrap().len_utf8(); + } + }, MachineMode::Write => { - let h = self.machine_st.heap.len(); + let h = self.machine_st.heap.cell_len(); for i in h..h + n { - self.machine_st.heap.push(heap_loc_as_cell!(i)); + push_cell!(self.machine_st, heap_loc_as_cell!(i)); } } } @@ -3048,10 +3191,14 @@ impl Machine { match self.find_living_dynamic(oi, ii + 1) { Some(_) => { self.machine_st.registers - [self.machine_st.num_of_args + 1] = - fixnum_as_cell!(Fixnum::build_with( - self.machine_st.cc as i64 - )); + [self.machine_st.num_of_args + 1] = fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { + Fixnum::build_with_unchecked( + self.machine_st.cc as i64, + ) + } + ); self.machine_st.num_of_args += 1; self.indexed_try(offset); @@ -3073,10 +3220,11 @@ impl Machine { .prelude .num_cells; - self.machine_st.cc = cell_as_fixnum!( + self.machine_st.cc = unsafe { self.machine_st.stack [stack_loc!(OrFrame, b, n - 1)] - ) + .to_fixnum_or_cut_point_unchecked() + } .get_num() as usize; @@ -3118,38 +3266,26 @@ impl Machine { } } } - &Instruction::PutConstant(_, c, reg) => { - self.machine_st[reg] = c; + &Instruction::PutConstant(_, cell, reg) => { + self.machine_st[reg] = cell; self.machine_st.p += 1; } &Instruction::PutList(_, reg) => { - self.machine_st[reg] = list_loc_as_cell!(self.machine_st.heap.len()); + self.machine_st[reg] = list_loc_as_cell!(self.machine_st.heap.cell_len()); self.machine_st.p += 1; } - &Instruction::PutPartialString(_, string, reg, has_tail) => { - let pstr_addr = if has_tail { - if string != atom!("") { - let h = self.machine_st.heap.len(); - self.machine_st.heap.push(string_as_pstr_cell!(string)); + &Instruction::PutPartialString(_, ref string, reg) => { + self.machine_st[reg] = backtrack_on_resource_error!( + self.machine_st, + self.machine_st.heap.allocate_pstr(string) + ); - // the tail will be pushed by the next - // instruction, so don't push one here. - - pstr_loc_as_cell!(h) - } else { - empty_list_as_cell!() - } - } else { - string_as_cstr_cell!(string) - }; - - self.machine_st[reg] = pstr_addr; self.machine_st.p += 1; } &Instruction::PutStructure(name, arity, reg) => { - let h = self.machine_st.heap.len(); + let h = self.machine_st.heap.cell_len(); - self.machine_st.heap.push(atom_as_cell!(name, arity)); + push_cell!(self.machine_st, atom_as_cell!(name, arity)); self.machine_st[reg] = str_loc_as_cell!(h); self.machine_st.p += 1; @@ -3163,9 +3299,9 @@ impl Machine { if addr.is_protected(self.machine_st.e) { self.machine_st.registers[arg] = addr; } else { - let h = self.machine_st.heap.len(); + let h = self.machine_st.heap.cell_len(); - self.machine_st.heap.push(heap_loc_as_cell!(h)); + push_cell!(self.machine_st, heap_loc_as_cell!(h)); (self.machine_st.bind_fn)( &mut self.machine_st, Ref::heap_cell(h), @@ -3189,8 +3325,8 @@ impl Machine { self.machine_st.registers[arg] = self.machine_st[norm]; } RegType::Temp(_) => { - let h = self.machine_st.heap.len(); - self.machine_st.heap.push(heap_loc_as_cell!(h)); + let h = self.machine_st.heap.cell_len(); + push_cell!(self.machine_st, heap_loc_as_cell!(h)); self.machine_st[norm] = heap_loc_as_cell!(h); self.machine_st.registers[arg] = heap_loc_as_cell!(h); @@ -3200,7 +3336,7 @@ impl Machine { self.machine_st.p += 1; } &Instruction::SetConstant(c) => { - self.machine_st.heap.push(c); + push_cell!(self.machine_st, c); self.machine_st.p += 1; } &Instruction::SetLocalValue(reg) => { @@ -3208,37 +3344,37 @@ impl Machine { let stored_v = self.machine_st.store(addr); if stored_v.is_stack_var() { - let h = self.machine_st.heap.len(); - self.machine_st.heap.push(heap_loc_as_cell!(h)); + let h = self.machine_st.heap.cell_len(); + push_cell!(self.machine_st, heap_loc_as_cell!(h)); (self.machine_st.bind_fn)( &mut self.machine_st, Ref::heap_cell(h), stored_v, ); } else { - self.machine_st.heap.push(stored_v); + push_cell!(self.machine_st, stored_v); } self.machine_st.p += 1; } &Instruction::SetVariable(reg) => { - let h = self.machine_st.heap.len(); + let h = self.machine_st.heap.cell_len(); - self.machine_st.heap.push(heap_loc_as_cell!(h)); + push_cell!(self.machine_st, heap_loc_as_cell!(h)); self.machine_st[reg] = heap_loc_as_cell!(h); self.machine_st.p += 1; } &Instruction::SetValue(reg) => { let heap_val = self.machine_st.store(self.machine_st[reg]); - self.machine_st.heap.push(heap_val); + push_cell!(self.machine_st, heap_val); self.machine_st.p += 1; } &Instruction::SetVoid(n) => { - let h = self.machine_st.heap.len(); + let h = self.machine_st.heap.cell_len(); for i in h..h + n { - self.machine_st.heap.push(heap_loc_as_cell!(i)); + push_cell!(self.machine_st, heap_loc_as_cell!(i)); } self.machine_st.p += 1; @@ -3355,11 +3491,11 @@ impl Machine { } &Instruction::CallCopyToLiftedHeap => { self.copy_to_lifted_heap(); - self.machine_st.p += 1; + step_or_fail!(self, self.machine_st.p += 1); } &Instruction::ExecuteCopyToLiftedHeap => { self.copy_to_lifted_heap(); - self.machine_st.p = self.machine_st.cp; + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallCreatePartialString => { self.create_partial_string(); @@ -4265,11 +4401,11 @@ impl Machine { } &Instruction::CallSetBall => { self.set_ball(); - self.machine_st.p += 1; + step_or_fail!(self, self.machine_st.p += 1); } &Instruction::ExecuteSetBall => { self.set_ball(); - self.machine_st.p = self.machine_st.cp; + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallPushBallStack => { self.push_ball_stack(); @@ -4604,19 +4740,19 @@ impl Machine { step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallLoadHTML => { - self.load_html(); + backtrack_on_resource_error!(self.machine_st, self.load_html()); step_or_fail!(self, self.machine_st.p += 1); } &Instruction::ExecuteLoadHTML => { - self.load_html(); + backtrack_on_resource_error!(self.machine_st, self.load_html()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallLoadXML => { - self.load_xml(); + backtrack_on_resource_error!(self.machine_st, self.load_xml()); step_or_fail!(self, self.machine_st.p += 1); } &Instruction::ExecuteLoadXML => { - self.load_xml(); + backtrack_on_resource_error!(self.machine_st, self.load_xml()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallGetEnv => { @@ -4651,6 +4787,46 @@ impl Machine { self.shell(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallProcessCreate => { + try_or_throw!(self.machine_st, self.process_create()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteProcessCreate => { + try_or_throw!(self.machine_st, self.process_create()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallProcessId => { + try_or_throw!(self.machine_st, self.process_id()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteProcessId => { + try_or_throw!(self.machine_st, self.process_id()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallProcessWait => { + try_or_throw!(self.machine_st, self.process_wait()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteProcessWait => { + try_or_throw!(self.machine_st, self.process_wait()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallProcessKill => { + try_or_throw!(self.machine_st, self.process_kill()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteProcessKill => { + try_or_throw!(self.machine_st, self.process_kill()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallProcessRelease => { + try_or_throw!(self.machine_st, self.process_release()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteProcessRelease => { + try_or_throw!(self.machine_st, self.process_release()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } &Instruction::CallPid => { self.pid(); step_or_fail!(self, self.machine_st.p += 1); @@ -5093,13 +5269,17 @@ impl Machine { let r = self.machine_st.registers[2]; let r = self.machine_st.store(self.machine_st.deref(r)); - let h = self.machine_st.heap.len(); - self.machine_st - .heap - .extend(functor!(atom!("-"), [fixnum(n), fixnum(p)])); + let mut writer = + Heap::functor_writer(functor!(atom!("-"), [fixnum(n), fixnum(p)])); + + let str_cell = backtrack_on_resource_error!( + &mut self.machine_st, + writer(&mut self.machine_st.heap) + ); let r = r.as_var().unwrap(); - self.machine_st.bind(r, str_loc_as_cell!(h)); + + self.machine_st.bind(r, str_cell); step_or_fail!(self, self.machine_st.p += 1); } @@ -5111,13 +5291,17 @@ impl Machine { let r = self.machine_st.registers[2]; let r = self.machine_st.store(self.machine_st.deref(r)); - let h = self.machine_st.heap.len(); - self.machine_st - .heap - .extend(functor!(atom!("-"), [fixnum(n), fixnum(p)])); + let mut writer = + Heap::functor_writer(functor!(atom!("-"), [fixnum(n), fixnum(p)])); + + let str_cell = backtrack_on_resource_error!( + &mut self.machine_st, + writer(&mut self.machine_st.heap) + ); let r = r.as_var().unwrap(); - self.machine_st.bind(r, str_loc_as_cell!(h)); + + self.machine_st.bind(r, str_cell); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } @@ -5128,7 +5312,7 @@ impl Machine { let l = self.machine_st.registers[3]; let l = self.machine_st.store(self.machine_st.deref(l)); - let l = match Number::try_from(l) { + let l = match Number::try_from((l, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(l)) => l.get_num() as usize, _ => unreachable!(), }; @@ -5136,7 +5320,7 @@ impl Machine { let p = self.machine_st.registers[4]; let p = self.machine_st.store(self.machine_st.deref(p)); - let p = match Number::try_from(p) { + let p = match Number::try_from((p, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(p)) => p.get_num() as usize, _ => unreachable!(), }; @@ -5158,8 +5342,11 @@ impl Machine { .machine_st .store(self.machine_st.deref(self.machine_st.registers[5])); - self.machine_st - .unify_fixnum(Fixnum::build_with(n as i64), r); + self.machine_st.unify_fixnum( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(n as i64) }, + r, + ); } self.machine_st.call_at_index(2, p); @@ -5171,7 +5358,7 @@ impl Machine { let l = self.machine_st.registers[3]; let l = self.machine_st.store(self.machine_st.deref(l)); - let l = match Number::try_from(l) { + let l = match Number::try_from((l, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(l)) => l.get_num() as usize, _ => unreachable!(), }; @@ -5179,7 +5366,7 @@ impl Machine { let p = self.machine_st.registers[4]; let p = self.machine_st.store(self.machine_st.deref(p)); - let p = match Number::try_from(p) { + let p = match Number::try_from((p, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(p)) => p.get_num() as usize, _ => unreachable!(), }; @@ -5201,8 +5388,11 @@ impl Machine { .machine_st .store(self.machine_st.deref(self.machine_st.registers[5])); - self.machine_st - .unify_fixnum(Fixnum::build_with(n as i64), r); + self.machine_st.unify_fixnum( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(n as i64) }, + r, + ); } self.machine_st.execute_at_index(2, p); diff --git a/src/machine/gc.rs b/src/machine/gc.rs index e37178bf..7c79eba0 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -1,12 +1,21 @@ -#![allow(dead_code)] +#[cfg(test)] +use fxhash::FxBuildHasher; +#[cfg(test)] +use indexmap::IndexMap; +#[cfg(test)] +use std::collections::BTreeMap; +#[cfg(test)] use crate::atom_table::*; +#[cfg(test)] use crate::machine::heap::*; +#[cfg(test)] use crate::types::*; #[cfg(test)] use crate::heap_iter::{FocusedHeapIter, HeapOrStackTag, IterStackLoc}; +#[cfg(test)] pub(crate) trait UnmarkPolicy { fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter) -> Option where @@ -30,10 +39,12 @@ pub(crate) trait UnmarkPolicy { } } +#[cfg(test)] pub(crate) struct IteratorUMP { mark_phase: bool, } +#[cfg(test)] fn invert_marker(iter: &mut StacklessPreOrderHeapIter) { if iter.heap[iter.start].get_forwarding_bit() { while !iter.backward() {} @@ -47,6 +58,7 @@ fn invert_marker(iter: &mut StacklessPreOrderHeapIter) { while iter.forward().is_some() {} } +#[cfg(test)] impl UnmarkPolicy for IteratorUMP { #[inline(always)] fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter) -> Option { @@ -65,8 +77,10 @@ impl UnmarkPolicy for IteratorUMP { } } +#[cfg(test)] struct MarkerUMP {} +#[cfg(test)] impl UnmarkPolicy for MarkerUMP { #[inline(always)] fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter) -> Option { @@ -94,13 +108,77 @@ impl UnmarkPolicy for MarkerUMP { } } +#[cfg(test)] +#[derive(Debug)] +struct PStrLocValuesMap { + hit_set: BTreeMap, + pstr_loc_locs: IndexMap, +} + +#[cfg(test)] +impl PStrLocValuesMap { + #[inline] + fn new() -> Self { + Self { + hit_set: BTreeMap::default(), + pstr_loc_locs: IndexMap::with_hasher(FxBuildHasher::default()), + } + } + + fn progress_pstr_marking(&mut self, heap_slice: &[u8], pstr_loc: usize) -> usize { + match self.hit_set.range(..=pstr_loc).next_back() { + Some((_prev_pstr_loc, &tail_idx)) if pstr_loc < heap_index!(tail_idx) => { + return tail_idx; + } + _ => {} + } + + let delimiter = match self.hit_set.range(pstr_loc + 1..).next() { + Some((&prev_pstr_loc, _)) => prev_pstr_loc, + None => heap_slice.len(), + }; + + match heap_slice[pstr_loc..delimiter] + .iter() + .position(|b| *b == 0u8) + { + Some(zero_byte_offset) => { + let tail_idx = if (zero_byte_offset + 1) % Heap::heap_cell_alignment() == 0 { + cell_index!(pstr_loc + zero_byte_offset) + 2 + } else { + cell_index!(pstr_loc + zero_byte_offset) + 1 + }; + self.hit_set.insert(pstr_loc, tail_idx); + tail_idx + } + None => { + let tail_idx = self.hit_set.remove(&delimiter).unwrap(); + self.hit_set.insert(pstr_loc, tail_idx); + tail_idx //None + } + } + } + + #[inline] + fn pstr_loc_loc_value(&self, pstr_loc_loc: usize) -> Option { + self.pstr_loc_locs.get(&pstr_loc_loc).cloned() + } + + #[inline] + fn insert_pstr_loc_value(&mut self, pstr_loc_loc: usize, pstr_loc: usize) { + self.pstr_loc_locs.insert(pstr_loc_loc, pstr_loc); + } +} + +#[cfg(test)] #[derive(Debug)] pub(crate) struct StacklessPreOrderHeapIter<'a, UMP: UnmarkPolicy> { - pub(crate) heap: &'a mut [HeapCellValue], + pub(crate) heap: &'a mut Heap, start: usize, current: usize, next: u64, iter_state: UMP, + pstr_loc_values: PStrLocValuesMap, } #[cfg(test)] @@ -111,6 +189,7 @@ impl<'a> FocusedHeapIter for StacklessPreOrderHeapIter<'a, IteratorUMP> { } } +#[cfg(test)] impl<'a, UMP: UnmarkPolicy> Drop for StacklessPreOrderHeapIter<'a, UMP> { fn drop(&mut self) { UMP::invert_marker(self); @@ -123,8 +202,9 @@ impl<'a, UMP: UnmarkPolicy> Drop for StacklessPreOrderHeapIter<'a, UMP> { } } +#[cfg(test)] impl<'a> StacklessPreOrderHeapIter<'a, MarkerUMP> { - pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self { + pub(crate) fn new(heap: &'a mut Heap, start: usize) -> Self { heap[start].set_forwarding_bit(true); let next = heap[start].get_value(); @@ -134,13 +214,15 @@ impl<'a> StacklessPreOrderHeapIter<'a, MarkerUMP> { current: start, next, iter_state: MarkerUMP {}, + pstr_loc_values: PStrLocValuesMap::new(), } } } +#[cfg(test)] impl<'a> StacklessPreOrderHeapIter<'a, IteratorUMP> { #[cfg(test)] - pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self { + pub(crate) fn new(heap: &'a mut Heap, start: usize) -> Self { heap[start].set_forwarding_bit(true); let next = heap[start].get_value(); @@ -150,10 +232,12 @@ impl<'a> StacklessPreOrderHeapIter<'a, IteratorUMP> { current: start, next, iter_state: IteratorUMP { mark_phase: true }, + pstr_loc_values: PStrLocValuesMap::new(), } } } +#[cfg(test)] impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { fn backward_and_return(&mut self) -> HeapCellValue { let mut current = self.heap[self.current]; @@ -199,7 +283,7 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { return Some(cell); } - if self.next < self.heap.len() as u64 && UMP::report_var_link(self) { + if self.next < self.heap.cell_len() as u64 && UMP::report_var_link(self) { let tag = HeapCellValueTag::AttrVar; return Some(HeapCellValue::build_with(tag, next as u64)); } @@ -211,7 +295,7 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { return Some(cell); } - if self.next < self.heap.len() as u64 && UMP::report_var_link(self) { + if self.next < self.heap.cell_len() as u64 && UMP::report_var_link(self) { let tag = HeapCellValueTag::Var; return Some(HeapCellValue::build_with(tag, next as u64)); } @@ -226,8 +310,8 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { let arity = cell_as_atom_cell!(self.heap[h]).get_arity(); - for cell in &mut self.heap[h + 1..h + arity + 1] { - cell.set_forwarding_bit(true); + for idx in h + 1..=h + arity { + self.heap[idx].set_forwarding_bit(true); } let last_cell_loc = h + arity; @@ -254,49 +338,26 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { return Some(list_loc_as_cell!(last_cell_loc - 1)); } HeapCellValueTag::PStrLoc => { - let h = self.next as usize; + let pstr_loc = self.next as usize; - if self.heap[h + 1].get_forwarding_bit() { + let tail_idx = self + .pstr_loc_values + .progress_pstr_marking(self.heap.as_slice(), pstr_loc); + + self.pstr_loc_values + .insert_pstr_loc_value(self.current, pstr_loc); + + if self.heap[tail_idx].get_forwarding_bit() { return Some(self.backward_and_return()); } - let cell = self.heap[h]; + self.next = self.heap[tail_idx].get_value(); + self.heap[tail_idx].set_value(self.current as u64); + self.current = tail_idx; - let last_cell_loc = h + 1; + self.heap[tail_idx].set_forwarding_bit(true); - self.next = self.heap[last_cell_loc].get_value(); - self.heap[last_cell_loc].set_value(self.current as u64); - self.current = last_cell_loc; - - self.heap[last_cell_loc].set_forwarding_bit(true); - - return Some(cell); - } - HeapCellValueTag::PStrOffset => { - let h = self.next as usize; - let cell = self.heap[h]; - - let last_cell_loc = h + 1; - - if self.heap[last_cell_loc].get_forwarding_bit() { - return Some(self.backward_and_return()); - } - - if self.heap[h].get_tag() == HeapCellValueTag::PStr { - self.heap[last_cell_loc].set_forwarding_bit(true); - - self.next = self.heap[last_cell_loc].get_value(); - self.heap[last_cell_loc].set_value(self.current as u64); - self.current = last_cell_loc; - } else { - debug_assert!(self.heap[h].get_tag() == HeapCellValueTag::CStr); - - self.next = self.heap[h].get_value(); - self.heap[h].set_value(self.current as u64); - self.current = h; - } - - return Some(cell); + return Some(pstr_loc_as_cell!(pstr_loc)); } tag @ HeapCellValueTag::Atom => { let cell = HeapCellValue::build_with(tag, self.next); @@ -308,9 +369,32 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { return None; } } - HeapCellValueTag::PStr => { - if self.backward() { - return None; + HeapCellValueTag::Cons => { + match self + .pstr_loc_values + .hit_set + .range(..heap_index!(self.current + 1)) + .next_back() + { + Some((_prev_pstr_loc, &tail_idx)) if self.current + 1 == tail_idx => { + let pstr_loc_loc = self.heap[self.current].get_value() as usize; + let pstr_loc_val = self + .pstr_loc_values + .pstr_loc_loc_value(pstr_loc_loc) + .unwrap(); + + self.heap[self.current].set_value(self.next); + + self.next = pstr_loc_val as u64; + self.current = pstr_loc_loc; + + if self.backward() { + return None; + } + } + _ => { + return Some(self.backward_and_return()); + } } } _ => { @@ -351,6 +435,7 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { } } +#[cfg(test)] impl<'a, UMP: UnmarkPolicy> Iterator for StacklessPreOrderHeapIter<'a, UMP> { type Item = HeapCellValue; @@ -360,157 +445,164 @@ impl<'a, UMP: UnmarkPolicy> Iterator for StacklessPreOrderHeapIter<'a, UMP> { } } -pub fn mark_cells(heap: &mut Heap, start: usize) { - let mut iter = StacklessPreOrderHeapIter::::new(heap, start); - while iter.forward().is_some() {} -} - #[cfg(test)] mod tests { use super::*; + use crate::functor_macro::*; use crate::machine::mock_wam::*; + fn mark_cells(heap: &mut Heap, start: usize) { + let mut iter = StacklessPreOrderHeapIter::::new(heap, start); + while iter.forward().is_some() {} + } + #[test] fn heap_marking_tests() { let mut wam = MockWAM::new(); + // clear the heap of resource error data etc + wam.machine_st.heap.clear(); + let f_atom = atom!("f"); let a_atom = atom!("a"); let b_atom = atom!("b"); - wam.machine_st.heap.push(str_loc_as_cell!(1)); + let mut functor_writer = Heap::functor_writer(functor!( + f_atom, + [atom_as_cell(a_atom), atom_as_cell(b_atom)] + )); - wam.machine_st - .heap - .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); + let cell = functor_writer(&mut wam.machine_st.heap).unwrap(); + let h = wam.machine_st.heap.cell_len(); - mark_cells(&mut wam.machine_st.heap, 0); + wam.machine_st.heap.push_cell(cell).unwrap(); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + mark_cells(&mut wam.machine_st.heap, h); + + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[0]), - str_loc_as_cell!(1) + unmark_cell_bits!(wam.machine_st.heap[3]), + str_loc_as_cell!(0) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), + unmark_cell_bits!(wam.machine_st.heap[0]), atom_as_cell!(f_atom, 2) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), + unmark_cell_bits!(wam.machine_st.heap[1]), atom_as_cell!(a_atom) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), + unmark_cell_bits!(wam.machine_st.heap[2]), atom_as_cell!(b_atom) ); wam.machine_st.heap.clear(); - wam.machine_st.heap.push(str_loc_as_cell!(1)); - - wam.machine_st.heap.extend(functor!( + let mut functor_writer = Heap::functor_writer(functor!( f_atom, [ - atom(a_atom), - atom(b_atom), - atom(a_atom), - cell(str_loc_as_cell!(1)) + atom_as_cell(a_atom), + atom_as_cell(b_atom), + atom_as_cell(a_atom), + str_loc_as_cell(1) ] )); - mark_cells(&mut wam.machine_st.heap, 0); + let cell = functor_writer(&mut wam.machine_st.heap).unwrap(); + let h = wam.machine_st.heap.cell_len(); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + wam.machine_st.heap.push_cell(cell).unwrap(); + + mark_cells(&mut wam.machine_st.heap, h); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); + + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[5]), + str_loc_as_cell!(0) + ); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), - str_loc_as_cell!(1) - ); - - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), atom_as_cell!(f_atom, 4) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), + unmark_cell_bits!(wam.machine_st.heap[1]), atom_as_cell!(a_atom) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), + unmark_cell_bits!(wam.machine_st.heap[2]), atom_as_cell!(b_atom) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[4]), + unmark_cell_bits!(wam.machine_st.heap[3]), atom_as_cell!(a_atom) ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - str_loc_as_cell!(1) - ); - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } + unmark_all_cells(&mut wam.machine_st.heap, 0); // make the structure doubly cyclic. - wam.machine_st.heap[2] = str_loc_as_cell!(1); + wam.machine_st.heap[1] = str_loc_as_cell!(0); - mark_cells(&mut wam.machine_st.heap, 0); + mark_cells(&mut wam.machine_st.heap, h); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); wam.machine_st.heap.clear(); - wam.machine_st.heap.push(str_loc_as_cell!(1)); - - wam.machine_st.heap.extend(functor!( + let mut functor_writer = Heap::functor_writer(functor!( f_atom, [ - atom(a_atom), - atom(b_atom), - atom(a_atom), - cell(str_loc_as_cell!(1)) + atom_as_cell(a_atom), + atom_as_cell(b_atom), + atom_as_cell(a_atom), + str_loc_as_cell(0) ] )); - mark_cells(&mut wam.machine_st.heap, 0); + let cell = functor_writer(&mut wam.machine_st.heap).unwrap(); + let h = wam.machine_st.heap.cell_len(); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + wam.machine_st.heap.push_cell(cell).unwrap(); + + mark_cells(&mut wam.machine_st.heap, h); + + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[0]), - str_loc_as_cell!(1) + unmark_cell_bits!(wam.machine_st.heap[5]), + str_loc_as_cell!(0) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), + unmark_cell_bits!(wam.machine_st.heap[0]), atom_as_cell!(f_atom, 4) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), + unmark_cell_bits!(wam.machine_st.heap[1]), atom_as_cell!(a_atom) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), + unmark_cell_bits!(wam.machine_st.heap[2]), atom_as_cell!(b_atom) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[4]), + unmark_cell_bits!(wam.machine_st.heap[3]), atom_as_cell!(a_atom) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - str_loc_as_cell!(1) + unmark_cell_bits!(wam.machine_st.heap[4]), + str_loc_as_cell!(0) ); wam.machine_st.heap.clear(); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + wam.machine_st.heap.push_cell(heap_loc_as_cell!(0)).unwrap(); mark_cells(&mut wam.machine_st.heap, 0); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), @@ -519,16 +611,20 @@ mod tests { wam.machine_st.heap.clear(); - // term is: [a, b] - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(atom_as_cell!(a_atom)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(atom_as_cell!(b_atom)); - wam.machine_st.heap.push(empty_list_as_cell!()); + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); + + writer.write_with(|section| { + // term is: [a, b] + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(atom_as_cell!(a_atom)); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(atom_as_cell!(b_atom)); + section.push_cell(empty_list_as_cell!()); + }); mark_cells(&mut wam.machine_st.heap, 0); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), @@ -551,18 +647,14 @@ mod tests { empty_list_as_cell!() ); - wam.machine_st.heap.pop(); - - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } + unmark_all_cells(&mut wam.machine_st.heap, 0); // now make the list cyclic. - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + wam.machine_st.heap[4] = heap_loc_as_cell!(0); mark_cells(&mut wam.machine_st.heap, 0); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), @@ -585,16 +677,14 @@ mod tests { heap_loc_as_cell!(0) ); - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } + unmark_all_cells(&mut wam.machine_st.heap, 0); // make the list doubly cyclic. wam.machine_st.heap[3] = heap_loc_as_cell!(0); mark_cells(&mut wam.machine_st.heap, 0); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); wam.machine_st.heap.clear(); @@ -603,15 +693,19 @@ mod tests { let stream_cell = HeapCellValue::from(ConsPtr::build_with(stream.as_ptr(), ConsPtrMaskTag::Cons)); - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(atom_as_cell!(a_atom)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(stream_cell); - wam.machine_st.heap.push(empty_list_as_cell!()); + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); + + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(atom_as_cell!(a_atom)); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(stream_cell); + section.push_cell(empty_list_as_cell!()); + }); mark_cells(&mut wam.machine_st.heap, 0); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), @@ -635,14 +729,18 @@ mod tests { // now a cycle of variables. - wam.machine_st.heap.push(heap_loc_as_cell!(1)); - wam.machine_st.heap.push(heap_loc_as_cell!(2)); - wam.machine_st.heap.push(heap_loc_as_cell!(3)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); + + writer.write_with(|section| { + section.push_cell(heap_loc_as_cell!(1)); + section.push_cell(heap_loc_as_cell!(2)); + section.push_cell(heap_loc_as_cell!(3)); + section.push_cell(heap_loc_as_cell!(0)); + }); mark_cells(&mut wam.machine_st.heap, 0); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), @@ -667,656 +765,472 @@ mod tests { // two-part complete string, then a three-part cyclic string // involving an uncompacted list of chars. - wam.machine_st.heap.push(pstr_loc_as_cell!(1)); + let pstr_cell = wam.machine_st.heap.allocate_pstr("abc ").unwrap(); - let pstr_var_cell = - put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); - let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; + wam.machine_st.heap.push_cell(heap_loc_as_cell!(1)).unwrap(); - mark_cells(&mut wam.machine_st.heap, 0); + let pstr_cell_loc = wam.machine_st.heap.cell_len(); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); - - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[0]), - pstr_loc_as_cell!(1) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), - heap_loc_as_cell!(2) - ); - - wam.machine_st.heap.pop(); - - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } - - wam.machine_st.heap.push(pstr_loc_as_cell!(3)); - - let pstr_second_var_cell = - put_partial_string(&mut wam.machine_st.heap, "def", &wam.machine_st.atom_tbl); - let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; - - mark_cells(&mut wam.machine_st.heap, 0); - - all_cells_marked_and_unforwarded(&wam.machine_st.heap); - - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), - pstr_loc_as_cell!(3) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[4]), - heap_loc_as_cell!(4) - ); - - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } - - wam.machine_st.heap.pop(); - wam.machine_st.heap.push(pstr_loc_as_cell!(5)); - wam.machine_st.heap.push(pstr_offset_as_cell!(1)); wam.machine_st .heap - .push(fixnum_as_cell!(Fixnum::build_with(2))); - wam.machine_st.heap.push(pstr_loc_as_cell!(5)); + .push_cell(pstr_loc_as_cell!(heap_index!(0))) + .unwrap(); + + mark_cells(&mut wam.machine_st.heap, pstr_cell_loc); + + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 1); + + unmark_all_cells(&mut wam.machine_st.heap, 1); + + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(0), "abc ".len()), + "abc " + ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[pstr_cell_loc]), + pstr_cell + ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[1]), + heap_loc_as_cell!(1) + ); + + wam.machine_st.heap[1] = pstr_loc_as_cell!(heap_index!(3)); + + wam.machine_st.heap.allocate_pstr("abcdef ").unwrap(); + wam.machine_st.heap.push_cell(heap_loc_as_cell!(5)).unwrap(); + + mark_cells(&mut wam.machine_st.heap, 2); + + assert!(wam.machine_st.heap[0].get_mark_bit()); + assert!(!wam.machine_st.heap[0].get_forwarding_bit()); + assert!(wam.machine_st.heap[1].get_mark_bit()); + assert!(!wam.machine_st.heap[1].get_forwarding_bit()); + assert!(wam.machine_st.heap[2].get_mark_bit()); + assert!(!wam.machine_st.heap[2].get_forwarding_bit()); + + assert!(wam.machine_st.heap[4].get_mark_bit()); + assert!(!wam.machine_st.heap[4].get_forwarding_bit()); + assert!(wam.machine_st.heap[5].get_mark_bit()); + assert!(!wam.machine_st.heap[5].get_forwarding_bit()); + + unmark_all_cells(&mut wam.machine_st.heap, 0); + + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(0), "abc ".len()), + "abc " + ); + assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(heap_index!(3))); + assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(heap_index!(0))); + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(3), "abcdef ".len()), + "abcdef " + ); + assert_eq!(wam.machine_st.heap[5], heap_loc_as_cell!(5)); + + // create a cycle offset two characters into the partial string at 0 + wam.machine_st.heap[5] = pstr_loc_as_cell!(heap_index!(0) + 2); + + mark_cells(&mut wam.machine_st.heap, 2); + + assert!(wam.machine_st.heap[0].get_mark_bit()); + assert!(!wam.machine_st.heap[0].get_forwarding_bit()); + assert!(wam.machine_st.heap[1].get_mark_bit()); + assert!(!wam.machine_st.heap[1].get_forwarding_bit()); + assert!(wam.machine_st.heap[2].get_mark_bit()); + assert!(!wam.machine_st.heap[2].get_forwarding_bit()); + + assert!(wam.machine_st.heap[4].get_mark_bit()); + assert!(!wam.machine_st.heap[4].get_forwarding_bit()); + assert!(wam.machine_st.heap[5].get_mark_bit()); + assert!(!wam.machine_st.heap[5].get_forwarding_bit()); + + wam.machine_st.heap[0].set_mark_bit(false); + wam.machine_st.heap[1].set_mark_bit(false); + wam.machine_st.heap[2].set_mark_bit(false); + wam.machine_st.heap[4].set_mark_bit(false); + wam.machine_st.heap[5].set_mark_bit(false); + + assert_eq!(wam.machine_st.heap.slice_to_str(0, "abc ".len()), "abc "); + assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(heap_index!(3))); + assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(heap_index!(0))); + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(3), "abcdef ".len()), + "abcdef " + ); + assert_eq!( + wam.machine_st.heap[5], + pstr_loc_as_cell!(heap_index!(0) + 2) + ); + + wam.machine_st.heap[5] = heap_loc_as_cell!(2); + wam.machine_st.heap.push_cell(heap_loc_as_cell!(2)).unwrap(); + + mark_cells(&mut wam.machine_st.heap, 6); + + assert!(wam.machine_st.heap[0].get_mark_bit()); + assert!(!wam.machine_st.heap[0].get_forwarding_bit()); + assert!(wam.machine_st.heap[1].get_mark_bit()); + assert!(!wam.machine_st.heap[1].get_forwarding_bit()); + assert!(wam.machine_st.heap[2].get_mark_bit()); + assert!(!wam.machine_st.heap[2].get_forwarding_bit()); + + assert!(wam.machine_st.heap[4].get_mark_bit()); + assert!(!wam.machine_st.heap[4].get_forwarding_bit()); + assert!(wam.machine_st.heap[5].get_mark_bit()); + assert!(!wam.machine_st.heap[5].get_forwarding_bit()); + assert!(wam.machine_st.heap[6].get_mark_bit()); + assert!(!wam.machine_st.heap[6].get_forwarding_bit()); + + wam.machine_st.heap[0].set_mark_bit(false); + wam.machine_st.heap[1].set_mark_bit(false); + wam.machine_st.heap[2].set_mark_bit(false); + wam.machine_st.heap[4].set_mark_bit(false); + wam.machine_st.heap[5].set_mark_bit(false); + wam.machine_st.heap[6].set_mark_bit(false); + + assert_eq!(wam.machine_st.heap.slice_to_str(0, "abc ".len()), "abc "); + assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(heap_index!(3))); + assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(heap_index!(0))); + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(3), "abcdef ".len()), + "abcdef " + ); + assert_eq!(wam.machine_st.heap[5], heap_loc_as_cell!(2)); + assert_eq!(wam.machine_st.heap[6], heap_loc_as_cell!(2)); + + wam.machine_st.heap[5] = pstr_loc_as_cell!(0); + + mark_cells(&mut wam.machine_st.heap, 2); + + assert!(wam.machine_st.heap[0].get_mark_bit()); + assert!(wam.machine_st.heap[1].get_mark_bit()); + assert!(wam.machine_st.heap[2].get_mark_bit()); + assert!(!wam.machine_st.heap[3].get_mark_bit()); + assert!(wam.machine_st.heap[4].get_mark_bit()); + assert!(wam.machine_st.heap[5].get_mark_bit()); + assert!(!wam.machine_st.heap[6].get_mark_bit()); + + assert!(!wam.machine_st.heap[0].get_forwarding_bit()); + assert!(!wam.machine_st.heap[1].get_forwarding_bit()); + assert!(!wam.machine_st.heap[2].get_forwarding_bit()); + assert!(!wam.machine_st.heap[3].get_forwarding_bit()); + assert!(!wam.machine_st.heap[4].get_forwarding_bit()); + assert!(!wam.machine_st.heap[5].get_forwarding_bit()); + assert!(!wam.machine_st.heap[6].get_forwarding_bit()); + + wam.machine_st.heap[0].set_mark_bit(false); + wam.machine_st.heap[1].set_mark_bit(false); + wam.machine_st.heap[2].set_mark_bit(false); + wam.machine_st.heap[4].set_mark_bit(false); + wam.machine_st.heap[5].set_mark_bit(false); + + assert_eq!(wam.machine_st.heap.slice_to_str(0, "abc ".len()), "abc "); + assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(heap_index!(3))); + assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(heap_index!(0))); + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(3), "abcdef ".len()), + "abcdef " + ); + assert_eq!(wam.machine_st.heap[5], pstr_loc_as_cell!(heap_index!(0))); + assert_eq!(wam.machine_st.heap[6], heap_loc_as_cell!(2)); + + wam.machine_st.heap.truncate(5); + + let mut writer = wam.machine_st.heap.reserve(2).unwrap(); + + writer.write_with(|section| { + section.push_cell(atom_as_cell!(atom!("irrelevant stuff"))); + section.push_cell(pstr_loc_as_cell!(heap_index!(0) + 2)); // offset two chars into pstr at 0 + }); + + wam.machine_st.heap.push_cell(heap_loc_as_cell!(6)).unwrap(); mark_cells(&mut wam.machine_st.heap, 7); - all_cells_marked_and_unforwarded(&wam.machine_st.heap[1..]); + // indices 0 and 3 - 4 are the beginning of one-cell partial + // strings, and they should be marked! despite the + // HeapCellValue casts otherwise not being sensible. + assert!(wam.machine_st.heap[0].get_mark_bit()); + assert!(wam.machine_st.heap[1].get_mark_bit()); + assert!(!wam.machine_st.heap[2].get_mark_bit()); + assert!(!wam.machine_st.heap[3].get_mark_bit()); + assert!(wam.machine_st.heap[4].get_mark_bit()); + assert!(wam.machine_st.heap[5].get_mark_bit()); + assert!(wam.machine_st.heap[6].get_mark_bit()); + assert!(wam.machine_st.heap[7].get_mark_bit()); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), - pstr_loc_as_cell!(3) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_loc_as_cell!(5) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - pstr_offset_as_cell!(1) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[6]), - fixnum_as_cell!(Fixnum::build_with(2)) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[7]), - pstr_loc_as_cell!(5) - ); + unmark_all_cells(&mut wam.machine_st.heap, 0); - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } + assert_eq!(wam.machine_st.heap.slice_to_str(0, "abc ".len()), "abc "); + assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(heap_index!(3))); + assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(heap_index!(0))); + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(3), "abcdef ".len()), + "abcdef " + ); + assert_eq!( + wam.machine_st.heap[5], + atom_as_cell!(atom!("irrelevant stuff")) + ); + assert_eq!( + wam.machine_st.heap[6], + pstr_loc_as_cell!(heap_index!(0) + 2) + ); + assert_eq!(wam.machine_st.heap[7], heap_loc_as_cell!(6)); + + wam.machine_st.heap.clear(); + + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(atom_as_cell!(atom!("irrelevant stuff"))); + section.push_pstr("abc "); + section.push_cell(pstr_loc_as_cell!(heap_index!(4))); + section.push_cell(atom_as_cell!(atom!("irrelevant stuff"))); + section.push_pstr("def"); + section.push_cell(pstr_loc_as_cell!(heap_index!(1) + 2)); + section.push_cell(atom_as_cell!(atom!("irrelevant stuff"))); + section.push_cell(pstr_loc_as_cell!(heap_index!(1) + 2)); + }); + + mark_cells(&mut wam.machine_st.heap, 7); + + assert!(!wam.machine_st.heap[0].get_mark_bit()); + assert!(wam.machine_st.heap[1].get_mark_bit()); + assert!(wam.machine_st.heap[2].get_mark_bit()); + assert!(!wam.machine_st.heap[3].get_mark_bit()); + assert!(wam.machine_st.heap[4].get_mark_bit()); + assert!(wam.machine_st.heap[5].get_mark_bit()); + assert!(!wam.machine_st.heap[6].get_mark_bit()); + + assert!(!wam.machine_st.heap[0].get_forwarding_bit()); + assert!(!wam.machine_st.heap[1].get_forwarding_bit()); + assert!(!wam.machine_st.heap[2].get_forwarding_bit()); + assert!(!wam.machine_st.heap[3].get_forwarding_bit()); + assert!(!wam.machine_st.heap[4].get_forwarding_bit()); + assert!(!wam.machine_st.heap[5].get_forwarding_bit()); + assert!(!wam.machine_st.heap[6].get_forwarding_bit()); + + unmark_all_cells(&mut wam.machine_st.heap, 0); + + assert_eq!( + wam.machine_st.heap[0], + atom_as_cell!(atom!("irrelevant stuff")) + ); + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(1), "abc ".len()), + "abc " + ); + assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(heap_index!(4))); + assert_eq!( + wam.machine_st.heap[3], + atom_as_cell!(atom!("irrelevant stuff")) + ); + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(4), "def".len()), + "def" + ); + assert_eq!( + wam.machine_st.heap[5], + pstr_loc_as_cell!(heap_index!(1) + 2) + ); + assert_eq!( + wam.machine_st.heap[6], + atom_as_cell!(atom!("irrelevant stuff")) + ); wam.machine_st.heap[7] = heap_loc_as_cell!(2); mark_cells(&mut wam.machine_st.heap, 7); - all_cells_marked_and_unforwarded(&wam.machine_st.heap[1..]); + assert!(!wam.machine_st.heap[0].get_mark_bit()); + assert!(wam.machine_st.heap[1].get_mark_bit()); + assert!(wam.machine_st.heap[2].get_mark_bit()); + assert!(!wam.machine_st.heap[3].get_mark_bit()); + assert!(wam.machine_st.heap[4].get_mark_bit()); + assert!(wam.machine_st.heap[5].get_mark_bit()); + assert!(!wam.machine_st.heap[6].get_mark_bit()); + assert!(wam.machine_st.heap[7].get_mark_bit()); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), - pstr_loc_as_cell!(3) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_loc_as_cell!(5) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - pstr_offset_as_cell!(1) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[6]), - fixnum_as_cell!(Fixnum::build_with(2)) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[7]), - heap_loc_as_cell!(2) - ); + unmark_all_cells(&mut wam.machine_st.heap, 0); - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); + for idx in 0..=6 { + assert!(!wam.machine_st.heap[idx].get_forwarding_bit()); } - wam.machine_st.heap[7] = pstr_loc_as_cell!(1); - - mark_cells(&mut wam.machine_st.heap, 7); - - all_cells_marked_and_unforwarded(&wam.machine_st.heap[1..]); - - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), - pstr_loc_as_cell!(3) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_loc_as_cell!(5) + wam.machine_st.heap[0], + atom_as_cell!(atom!("irrelevant stuff")) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - pstr_offset_as_cell!(1) + wam.machine_st + .heap + .slice_to_str(heap_index!(1), "abc ".len()), + "abc " + ); + assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(heap_index!(4))); + assert_eq!( + wam.machine_st.heap[3], + atom_as_cell!(atom!("irrelevant stuff")) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[6]), - fixnum_as_cell!(Fixnum::build_with(2)) + wam.machine_st + .heap + .slice_to_str(heap_index!(4), "def".len()), + "def" ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[7]), - pstr_loc_as_cell!(1) - ); - - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } - - wam.machine_st.heap[7] = heap_loc_as_cell!(0); - - mark_cells(&mut wam.machine_st.heap, 7); - - all_cells_marked_and_unforwarded(&wam.machine_st.heap[1..]); - - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), - pstr_loc_as_cell!(3) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[4]), - pstr_loc_as_cell!(5) + wam.machine_st.heap[5], + pstr_loc_as_cell!(heap_index!(1) + 2) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - pstr_offset_as_cell!(1) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[6]), - fixnum_as_cell!(Fixnum::build_with(2)) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[7]), - heap_loc_as_cell!(0) + wam.machine_st.heap[6], + atom_as_cell!(atom!("irrelevant stuff")) ); - wam.machine_st.heap.truncate(4); - - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } - - wam.machine_st - .heap - .push(atom_as_cell!(atom!("irrelevant stuff"))); - wam.machine_st.heap.push(pstr_offset_as_cell!(1)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(2))); - - // this is at index 7 - wam.machine_st.heap.push(pstr_loc_as_cell!(5)); + wam.machine_st.heap[7] = pstr_loc_as_cell!(heap_index!(4)); mark_cells(&mut wam.machine_st.heap, 7); assert!(!wam.machine_st.heap[0].get_mark_bit()); assert!(wam.machine_st.heap[1].get_mark_bit()); assert!(wam.machine_st.heap[2].get_mark_bit()); - assert!(wam.machine_st.heap[3].get_mark_bit()); + assert!(!wam.machine_st.heap[3].get_mark_bit()); assert!(wam.machine_st.heap[4].get_mark_bit()); assert!(wam.machine_st.heap[5].get_mark_bit()); - assert!(wam.machine_st.heap[6].get_mark_bit()); + assert!(!wam.machine_st.heap[6].get_mark_bit()); + assert!(wam.machine_st.heap[7].get_mark_bit()); + + unmark_all_cells(&mut wam.machine_st.heap, 0); + + for idx in 0..=6 { + assert!(!wam.machine_st.heap[idx].get_forwarding_bit()); + } - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), - pstr_loc_as_cell!(3) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[4]), + wam.machine_st.heap[0], atom_as_cell!(atom!("irrelevant stuff")) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - pstr_offset_as_cell!(1) + wam.machine_st + .heap + .slice_to_str(heap_index!(1), "abc ".len()), + "abc " + ); + assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(heap_index!(4))); + assert_eq!( + wam.machine_st.heap[3], + atom_as_cell!(atom!("irrelevant stuff")) ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[6]), - fixnum_as_cell!(Fixnum::build_with(2)) + wam.machine_st + .heap + .slice_to_str(heap_index!(4), "def".len()), + "def" ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[7]), - pstr_loc_as_cell!(5) + wam.machine_st.heap[5], + pstr_loc_as_cell!(heap_index!(1) + 2) + ); + assert_eq!( + wam.machine_st.heap[6], + atom_as_cell!(atom!("irrelevant stuff")) ); wam.machine_st.heap.clear(); - wam.machine_st - .heap - .push(atom_as_cell!(atom!("irrelevant stuff"))); - wam.machine_st.heap.push(pstr_cell); - wam.machine_st.heap.push(pstr_loc_as_cell!(4)); - wam.machine_st - .heap - .push(atom_as_cell!(atom!("irrelevant stuff"))); - wam.machine_st.heap.push(pstr_second_cell); - wam.machine_st.heap.push(pstr_loc_as_cell!(7)); - wam.machine_st - .heap - .push(atom_as_cell!(atom!("irrelevant stuff"))); - wam.machine_st.heap.push(pstr_offset_as_cell!(1)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(2))); + // embedded cyclic partial string - wam.machine_st.heap.push(pstr_loc_as_cell!(7)); + let mut writer = wam.machine_st.heap.reserve(8).unwrap(); - mark_cells(&mut wam.machine_st.heap, 9); + writer.write_with(|section| { + section.push_pstr("abc "); + section.push_cell(pstr_loc_as_cell!(heap_index!(0) + 3)); // 3 character offset into pstr_cell + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(pstr_loc_as_cell!(0)); + section.push_cell(empty_list_as_cell!()); + section.push_cell(heap_loc_as_cell!(2)); + }); - assert!(!wam.machine_st.heap[0].get_mark_bit()); - assert!(wam.machine_st.heap[1].get_mark_bit()); - assert!(wam.machine_st.heap[2].get_mark_bit()); - assert!(!wam.machine_st.heap[3].get_mark_bit()); - assert!(wam.machine_st.heap[4].get_mark_bit()); - assert!(wam.machine_st.heap[5].get_mark_bit()); - assert!(!wam.machine_st.heap[6].get_mark_bit()); - assert!(wam.machine_st.heap[7].get_mark_bit()); - assert!(wam.machine_st.heap[8].get_mark_bit()); + mark_cells(&mut wam.machine_st.heap, 5); + + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); + + unmark_all_cells(&mut wam.machine_st.heap, 0); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[0]), - atom_as_cell!(atom!("irrelevant stuff")) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), - pstr_loc_as_cell!(4) + wam.machine_st + .heap + .slice_to_str(heap_index!(0), "abc ".len()), + "abc " ); assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - atom_as_cell!(atom!("irrelevant stuff")) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - pstr_loc_as_cell!(7) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[6]), - atom_as_cell!(atom!("irrelevant stuff")) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[7]), - pstr_offset_as_cell!(1) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[8]), - fixnum_as_cell!(Fixnum::build_with(2)) - ); - - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } - - wam.machine_st.heap[9] = heap_loc_as_cell!(5); - - mark_cells(&mut wam.machine_st.heap, 9); - - assert!(!wam.machine_st.heap[0].get_mark_bit()); - assert!(wam.machine_st.heap[1].get_mark_bit()); - assert!(wam.machine_st.heap[2].get_mark_bit()); - assert!(!wam.machine_st.heap[3].get_mark_bit()); - assert!(wam.machine_st.heap[4].get_mark_bit()); - assert!(wam.machine_st.heap[5].get_mark_bit()); - assert!(!wam.machine_st.heap[6].get_mark_bit()); - assert!(wam.machine_st.heap[7].get_mark_bit()); - assert!(wam.machine_st.heap[8].get_mark_bit()); - - for cell in &wam.machine_st.heap { - assert!(!cell.get_forwarding_bit()); - } - - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[0]), - atom_as_cell!(atom!("irrelevant stuff")) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), - pstr_loc_as_cell!(4) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - atom_as_cell!(atom!("irrelevant stuff")) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - pstr_loc_as_cell!(7) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[6]), - atom_as_cell!(atom!("irrelevant stuff")) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[7]), - pstr_offset_as_cell!(1) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[8]), - fixnum_as_cell!(Fixnum::build_with(2)) - ); - - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } - - wam.machine_st.heap[9] = pstr_loc_as_cell!(4); - - mark_cells(&mut wam.machine_st.heap, 9); - - assert!(!wam.machine_st.heap[0].get_mark_bit()); - assert!(wam.machine_st.heap[1].get_mark_bit()); - assert!(wam.machine_st.heap[2].get_mark_bit()); - assert!(!wam.machine_st.heap[3].get_mark_bit()); - assert!(wam.machine_st.heap[4].get_mark_bit()); - assert!(wam.machine_st.heap[5].get_mark_bit()); - assert!(!wam.machine_st.heap[6].get_mark_bit()); - assert!(wam.machine_st.heap[7].get_mark_bit()); - assert!(wam.machine_st.heap[8].get_mark_bit()); - - for cell in &wam.machine_st.heap { - assert!(!cell.get_forwarding_bit()); - } - - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[0]), - atom_as_cell!(atom!("irrelevant stuff")) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), - pstr_loc_as_cell!(4) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - atom_as_cell!(atom!("irrelevant stuff")) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - pstr_loc_as_cell!(7) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[6]), - atom_as_cell!(atom!("irrelevant stuff")) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[7]), - pstr_offset_as_cell!(1) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[8]), - fixnum_as_cell!(Fixnum::build_with(2)) - ); - - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } - - mark_cells(&mut wam.machine_st.heap, 9); - - wam.machine_st.heap[9] = heap_loc_as_cell!(2); - - assert!(!wam.machine_st.heap[0].get_mark_bit()); - assert!(wam.machine_st.heap[1].get_mark_bit()); - assert!(wam.machine_st.heap[2].get_mark_bit()); - assert!(!wam.machine_st.heap[3].get_mark_bit()); - assert!(wam.machine_st.heap[4].get_mark_bit()); - assert!(wam.machine_st.heap[5].get_mark_bit()); - assert!(!wam.machine_st.heap[6].get_mark_bit()); - assert!(wam.machine_st.heap[7].get_mark_bit()); - assert!(wam.machine_st.heap[8].get_mark_bit()); - - for cell in &wam.machine_st.heap { - assert!(!cell.get_forwarding_bit()); - } - - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[0]), - atom_as_cell!(atom!("irrelevant stuff")) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), - pstr_loc_as_cell!(4) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - atom_as_cell!(atom!("irrelevant stuff")) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - pstr_loc_as_cell!(7) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[6]), - atom_as_cell!(atom!("irrelevant stuff")) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[7]), - pstr_offset_as_cell!(1) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[8]), - fixnum_as_cell!(Fixnum::build_with(2)) - ); - - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } - - wam.machine_st.heap[9] = pstr_loc_as_cell!(1); - - mark_cells(&mut wam.machine_st.heap, 9); - - assert!(!wam.machine_st.heap[0].get_mark_bit()); - assert!(wam.machine_st.heap[1].get_mark_bit()); - assert!(wam.machine_st.heap[2].get_mark_bit()); - assert!(!wam.machine_st.heap[3].get_mark_bit()); - assert!(wam.machine_st.heap[4].get_mark_bit()); - assert!(wam.machine_st.heap[5].get_mark_bit()); - assert!(!wam.machine_st.heap[6].get_mark_bit()); - assert!(wam.machine_st.heap[7].get_mark_bit()); - assert!(wam.machine_st.heap[8].get_mark_bit()); - - for cell in &wam.machine_st.heap { - assert!(!cell.get_forwarding_bit()); - } - - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[0]), - atom_as_cell!(atom!("irrelevant stuff")) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), - pstr_loc_as_cell!(4) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - atom_as_cell!(atom!("irrelevant stuff")) - ); - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_second_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - pstr_loc_as_cell!(7) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[6]), - atom_as_cell!(atom!("irrelevant stuff")) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[7]), - pstr_offset_as_cell!(1) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[8]), - fixnum_as_cell!(Fixnum::build_with(2)) - ); - - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } - - wam.machine_st.heap.clear(); - - // embedded cyclic partial string. - - wam.machine_st.heap.push(pstr_cell); - wam.machine_st.heap.push(pstr_loc_as_cell!(2)); - wam.machine_st.heap.push(pstr_offset_as_cell!(0)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(3))); - wam.machine_st.heap.push(list_loc_as_cell!(5)); - wam.machine_st.heap.push(pstr_loc_as_cell!(0)); - wam.machine_st.heap.push(empty_list_as_cell!()); - - mark_cells(&mut wam.machine_st.heap, 4); - - all_cells_marked_and_unforwarded(&wam.machine_st.heap); - - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } - - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), - pstr_offset_as_cell!(0) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - fixnum_as_cell!(Fixnum::build_with(3)) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[4]), - list_loc_as_cell!(5) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - pstr_loc_as_cell!(0) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[6]), - empty_list_as_cell!() - ); - - wam.machine_st.heap.clear(); - - wam.machine_st.heap.push(pstr_cell); - wam.machine_st.heap.push(pstr_loc_as_cell!(2)); - wam.machine_st.heap.push(pstr_offset_as_cell!(0)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(3))); - wam.machine_st.heap.push(list_loc_as_cell!(5)); - wam.machine_st.heap.push(pstr_loc_as_cell!(0)); - wam.machine_st.heap.push(heap_loc_as_cell!(4)); - - mark_cells(&mut wam.machine_st.heap, 4); - - all_cells_marked_and_unforwarded(&wam.machine_st.heap); - - for cell in &mut wam.machine_st.heap { - cell.set_mark_bit(false); - } - - assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - pstr_loc_as_cell!(2) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), - pstr_offset_as_cell!(0) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - fixnum_as_cell!(Fixnum::build_with(3)) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[4]), - list_loc_as_cell!(5) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[5]), - pstr_loc_as_cell!(0) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[6]), - heap_loc_as_cell!(4) + wam.machine_st.heap[1], + pstr_loc_as_cell!(heap_index!(0) + 3) ); + assert_eq!(wam.machine_st.heap[2], list_loc_as_cell!(3)); + assert_eq!(wam.machine_st.heap[3], pstr_loc_as_cell!(0)); + assert_eq!(wam.machine_st.heap[4], empty_list_as_cell!()); + assert_eq!(wam.machine_st.heap[5], heap_loc_as_cell!(2)); wam.machine_st.heap.clear(); // a chain of variables, ending in a self-referential variable. - wam.machine_st.heap.push(heap_loc_as_cell!(1)); - wam.machine_st.heap.push(heap_loc_as_cell!(2)); - wam.machine_st.heap.push(heap_loc_as_cell!(3)); - wam.machine_st.heap.push(heap_loc_as_cell!(3)); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(heap_loc_as_cell!(1)); + section.push_cell(heap_loc_as_cell!(2)); + section.push_cell(heap_loc_as_cell!(3)); + section.push_cell(heap_loc_as_cell!(3)); + }); mark_cells(&mut wam.machine_st.heap, 0); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[0]), - heap_loc_as_cell!(1) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[1]), - heap_loc_as_cell!(2) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[2]), - heap_loc_as_cell!(3) - ); - assert_eq!( - unmark_cell_bits!(wam.machine_st.heap[3]), - heap_loc_as_cell!(3) - ); + unmark_all_cells(&mut wam.machine_st.heap, 0); + + assert_eq!(wam.machine_st.heap[0], heap_loc_as_cell!(1)); + assert_eq!(wam.machine_st.heap[1], heap_loc_as_cell!(2)); + assert_eq!(wam.machine_st.heap[2], heap_loc_as_cell!(3)); + assert_eq!(wam.machine_st.heap[3], heap_loc_as_cell!(3)); wam.machine_st.heap.clear(); // print L = [L|L]. - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(list_loc_as_cell!(1)); + + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(list_loc_as_cell!(1)); + }); mark_cells(&mut wam.machine_st.heap, 0); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), @@ -1335,23 +1249,28 @@ mod tests { // term is [X,f(Y),Z]. // Z is an attributed variable, but has a variable attributes list. - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(heap_loc_as_cell!(1)); - wam.machine_st.heap.push(heap_loc_as_cell!(3)); // 2 - wam.machine_st.heap.push(list_loc_as_cell!(4)); // 3 - wam.machine_st.heap.push(str_loc_as_cell!(6)); // 4 - wam.machine_st.heap.push(heap_loc_as_cell!(8)); - wam.machine_st.heap.push(atom_as_cell!(f_atom, 1)); // 6 - wam.machine_st.heap.push(heap_loc_as_cell!(11)); // 7 - wam.machine_st.heap.push(list_loc_as_cell!(9)); - wam.machine_st.heap.push(heap_loc_as_cell!(9)); - wam.machine_st.heap.push(empty_list_as_cell!()); - wam.machine_st.heap.push(attr_var_as_cell!(11)); // linked from 7. - wam.machine_st.heap.push(heap_loc_as_cell!(12)); + + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(heap_loc_as_cell!(1)); + section.push_cell(heap_loc_as_cell!(3)); // 2 + section.push_cell(list_loc_as_cell!(4)); // 3 + section.push_cell(str_loc_as_cell!(6)); // 4 + section.push_cell(heap_loc_as_cell!(8)); + section.push_cell(atom_as_cell!(f_atom, 1)); // 6 + section.push_cell(heap_loc_as_cell!(11)); // 7 + section.push_cell(list_loc_as_cell!(9)); + section.push_cell(heap_loc_as_cell!(9)); + section.push_cell(empty_list_as_cell!()); + section.push_cell(attr_var_as_cell!(11)); // linked from 7. + section.push_cell(heap_loc_as_cell!(12)); + }); mark_cells(&mut wam.machine_st.heap, 0); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), @@ -1410,29 +1329,33 @@ mod tests { let clpz_atom = atom!("clpz"); let p_atom = atom!("p"); - for cell in &mut wam.machine_st.heap { + for idx in 0..wam.machine_st.heap.cell_len() { + let cell = &mut wam.machine_st.heap[idx]; cell.set_mark_bit(false); cell.set_forwarding_bit(false); } - wam.machine_st.heap.pop(); + wam.machine_st.heap[12] = heap_loc_as_cell!(13); - wam.machine_st.heap.push(heap_loc_as_cell!(13)); // 12 - wam.machine_st.heap.push(list_loc_as_cell!(14)); // 13 - wam.machine_st.heap.push(str_loc_as_cell!(16)); // 14 - wam.machine_st.heap.push(heap_loc_as_cell!(19)); // 15 - wam.machine_st.heap.push(atom_as_cell!(clpz_atom, 2)); // 16 - wam.machine_st.heap.push(atom_as_cell!(a_atom)); // 17 - wam.machine_st.heap.push(atom_as_cell!(b_atom)); // 18 - wam.machine_st.heap.push(list_loc_as_cell!(20)); // 19 - wam.machine_st.heap.push(str_loc_as_cell!(22)); // 20 - wam.machine_st.heap.push(empty_list_as_cell!()); // 21 - wam.machine_st.heap.push(atom_as_cell!(p_atom, 1)); // 22 - wam.machine_st.heap.push(heap_loc_as_cell!(23)); // 23 + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(14)); // 13 + section.push_cell(str_loc_as_cell!(16)); // 14 + section.push_cell(heap_loc_as_cell!(19)); // 15 + section.push_cell(atom_as_cell!(clpz_atom, 2)); // 16 + section.push_cell(atom_as_cell!(a_atom)); // 17 + section.push_cell(atom_as_cell!(b_atom)); // 18 + section.push_cell(list_loc_as_cell!(20)); // 19 + section.push_cell(str_loc_as_cell!(22)); // 20 + section.push_cell(empty_list_as_cell!()); // 21 + section.push_cell(atom_as_cell!(p_atom, 1)); // 22 + section.push_cell(heap_loc_as_cell!(23)); // 23 + }); mark_cells(&mut wam.machine_st.heap, 0); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), @@ -1531,23 +1454,32 @@ mod tests { heap_loc_as_cell!(23) ); - for cell in &mut wam.machine_st.heap { + for idx in 0..wam.machine_st.heap.cell_len() { + let cell = &mut wam.machine_st.heap[idx]; + cell.set_mark_bit(false); cell.set_forwarding_bit(false); } // push some unrelated nonsense cells to the heap and check that they // are unmarked after the marker has finished at 0. - wam.machine_st.heap.push(heap_loc_as_cell!(5)); - wam.machine_st.heap.push(heap_loc_as_cell!(5)); - wam.machine_st.heap.push(list_loc_as_cell!(5)); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(heap_loc_as_cell!(5)); + section.push_cell(heap_loc_as_cell!(5)); + section.push_cell(list_loc_as_cell!(5)); + }); mark_cells(&mut wam.machine_st.heap, 0); - all_cells_marked_and_unforwarded(&wam.machine_st.heap[0..24]); + for idx in 0..24 { + assert!(wam.machine_st.heap[idx].get_mark_bit()); + assert!(!wam.machine_st.heap[idx].get_forwarding_bit()); + } - for cell in &wam.machine_st.heap[24..] { - assert!(!cell.get_mark_bit()); + for idx in 24..wam.machine_st.heap.cell_len() { + assert!(!wam.machine_st.heap[idx].get_mark_bit()); } assert_eq!( @@ -1662,32 +1594,37 @@ mod tests { wam.machine_st.heap.clear(); wam.machine_st .heap - .push(fixnum_as_cell!(Fixnum::build_with(0))); + .push_cell(fixnum_as_cell!(Fixnum::build_with(0))) + .unwrap(); mark_cells(&mut wam.machine_st.heap, 0); - assert_eq!(wam.machine_st.heap.len(), 1); + assert_eq!(wam.machine_st.heap.cell_len(), 1); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); wam.machine_st.heap.clear(); - wam.machine_st.heap.push(str_loc_as_cell!(1)); - wam.machine_st.heap.push(atom_as_cell!(atom!("g"), 2)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); - wam.machine_st.heap.push(atom_as_cell!(atom!("y"))); - wam.machine_st.heap.push(atom_as_cell!(atom!("="), 2)); - wam.machine_st.heap.push(atom_as_cell!(atom!("X"))); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); - wam.machine_st.heap.push(list_loc_as_cell!(8)); - wam.machine_st.heap.push(str_loc_as_cell!(4)); - wam.machine_st.heap.push(empty_list_as_cell!()); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(str_loc_as_cell!(1)); + section.push_cell(atom_as_cell!(atom!("g"), 2)); + section.push_cell(heap_loc_as_cell!(0)); + section.push_cell(atom_as_cell!(atom!("y"))); + section.push_cell(atom_as_cell!(atom!("="), 2)); + section.push_cell(atom_as_cell!(atom!("X"))); + section.push_cell(heap_loc_as_cell!(0)); + section.push_cell(list_loc_as_cell!(8)); + section.push_cell(str_loc_as_cell!(4)); + section.push_cell(empty_list_as_cell!()); + }); mark_cells(&mut wam.machine_st.heap, 7); - assert_eq!(wam.machine_st.heap.len(), 10); + assert_eq!(wam.machine_st.heap.cell_len(), 10); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), @@ -1732,14 +1669,18 @@ mod tests { wam.machine_st.heap.clear(); - wam.machine_st.heap.push(atom_as_cell!(atom!("f"), 2)); - wam.machine_st.heap.push(heap_loc_as_cell!(1)); - wam.machine_st.heap.push(heap_loc_as_cell!(1)); - wam.machine_st.heap.push(str_loc_as_cell!(0)); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(atom_as_cell!(atom!("f"), 2)); + section.push_cell(heap_loc_as_cell!(1)); + section.push_cell(heap_loc_as_cell!(1)); + section.push_cell(str_loc_as_cell!(0)); + }); mark_cells(&mut wam.machine_st.heap, 3); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), @@ -1758,19 +1699,22 @@ mod tests { // representation of one of the heap terms as in issue #1384. - wam.machine_st.heap.push(list_loc_as_cell!(1)); - wam.machine_st.heap.push(empty_list_as_cell!()); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); - wam.machine_st.heap.push(empty_list_as_cell!()); - wam.machine_st.heap.push(heap_loc_as_cell!(2)); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); - wam.machine_st.heap.push(list_loc_as_cell!(5)); + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(empty_list_as_cell!()); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(heap_loc_as_cell!(0)); + section.push_cell(heap_loc_as_cell!(0)); + section.push_cell(empty_list_as_cell!()); + section.push_cell(heap_loc_as_cell!(2)); + section.push_cell(list_loc_as_cell!(5)); + }); mark_cells(&mut wam.machine_st.heap, 7); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), @@ -1805,23 +1749,27 @@ mod tests { // representation of one of the heap terms as in issue #1384. - wam.machine_st.heap.push(list_loc_as_cell!(7)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); // A = [B|[]]. - wam.machine_st.heap.push(list_loc_as_cell!(5)); // B = [A|A]. - wam.machine_st.heap.push(empty_list_as_cell!()); - wam.machine_st.heap.push(heap_loc_as_cell!(2)); - wam.machine_st.heap.push(heap_loc_as_cell!(2)); - wam.machine_st.heap.push(empty_list_as_cell!()); // C = [[]|B]. - wam.machine_st.heap.push(heap_loc_as_cell!(3)); - wam.machine_st.heap.push(heap_loc_as_cell!(0)); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(7)); + section.push_cell(heap_loc_as_cell!(0)); + section.push_cell(list_loc_as_cell!(3)); // A = [B|[]]. + section.push_cell(list_loc_as_cell!(5)); // B = [A|A]. + section.push_cell(empty_list_as_cell!()); + section.push_cell(heap_loc_as_cell!(2)); + section.push_cell(heap_loc_as_cell!(2)); + section.push_cell(empty_list_as_cell!()); // C = [[]|B]. + section.push_cell(heap_loc_as_cell!(3)); + section.push_cell(heap_loc_as_cell!(0)); + }); mark_cells(&mut wam.machine_st.heap, 9); assert!(wam.machine_st.heap[0].get_mark_bit()); assert!(!wam.machine_st.heap[1].get_mark_bit()); - all_cells_marked_and_unforwarded(&wam.machine_st.heap[2..]); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 2); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), @@ -1859,31 +1807,31 @@ mod tests { unmark_cell_bits!(wam.machine_st.heap[8]), heap_loc_as_cell!(3) ); + assert_eq!( + unmark_cell_bits!(wam.machine_st.heap[9]), + heap_loc_as_cell!(0) + ); wam.machine_st.heap.clear(); - wam.machine_st.heap.push(str_loc_as_cell!(1)); - wam.machine_st.heap.push(atom_as_cell!(atom!("+"), 2)); - wam.machine_st.heap.push(str_loc_as_cell!(4)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(2))); - wam.machine_st.heap.push(atom_as_cell!(atom!("-"), 2)); - wam.machine_st.heap.push(str_loc_as_cell!(7)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(1))); - wam.machine_st.heap.push(atom_as_cell!(atom!("+"), 2)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(3))); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(4))); + let mut writer = wam.machine_st.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(str_loc_as_cell!(1)); + section.push_cell(atom_as_cell!(atom!("+"), 2)); + section.push_cell(str_loc_as_cell!(4)); + section.push_cell(fixnum_as_cell!(Fixnum::build_with(2))); + section.push_cell(atom_as_cell!(atom!("-"), 2)); + section.push_cell(str_loc_as_cell!(7)); + section.push_cell(fixnum_as_cell!(Fixnum::build_with(1))); + section.push_cell(atom_as_cell!(atom!("+"), 2)); + section.push_cell(fixnum_as_cell!(Fixnum::build_with(3))); + section.push_cell(fixnum_as_cell!(Fixnum::build_with(4))); + }); mark_cells(&mut wam.machine_st.heap, 0); - all_cells_marked_and_unforwarded(&wam.machine_st.heap); + all_cells_marked_and_unforwarded(&wam.machine_st.heap, 0); assert_eq!( unmark_cell_bits!(wam.machine_st.heap[0]), diff --git a/src/machine/heap.rs b/src/machine/heap.rs index 0f42b8c6..e69050ab 100644 --- a/src/machine/heap.rs +++ b/src/machine/heap.rs @@ -1,89 +1,1088 @@ -use crate::arena::*; use crate::atom_table::*; -use crate::forms::*; -use crate::machine::machine_indices::*; -use crate::machine::partial_string::*; -use crate::parser::ast::*; +use crate::functor_macro::*; +use crate::machine::{ArenaHeaderTag, Fixnum, Integer}; use crate::types::*; -use crate::parser::dashu::{Integer, Rational}; - +use std::alloc; use std::convert::TryFrom; +use std::ops::{Bound, Index, IndexMut, Range, RangeBounds}; +use std::ptr; +use std::sync::Once; -pub(crate) type Heap = Vec; +const ALIGN: usize = Heap::heap_cell_alignment(); -impl From for HeapCellValue { +#[derive(Debug)] +pub struct Heap { + inner: InnerHeap, + resource_err_loc: usize, +} + +impl Drop for Heap { + fn drop(&mut self) { + if !self.inner.ptr.is_null() { + unsafe { + let layout = + alloc::Layout::from_size_align(self.inner.byte_cap, size_of::()) + .unwrap(); + alloc::dealloc(self.inner.ptr, layout); + } + } + } +} + +// TODO: verify the soundness of the various accesses to `ptr`, +// or rely on a Vec-like library with fallible allocations. +#[derive(Debug)] +struct InnerHeap { + ptr: *mut u8, + + /// # Safety + /// + /// Must be equal to zero when `ptr.is_null()`. + byte_len: usize, + + /// # Safety + /// + /// Must be equal to zero when `ptr.is_null()`. + byte_cap: usize, +} + +impl InnerHeap { + unsafe fn grow(&mut self) -> bool { + let new_cap = if self.byte_cap == 0 { + 256 * 256 * 8 + } else { + 2 * self.byte_cap + }; + + let new_layout = + alloc::Layout::from_size_align(new_cap, size_of::()).unwrap(); + + assert!( + new_layout.size() <= isize::MAX as usize, + "Allocation too large. We should probably GC (TODO)" + ); + + let new_ptr = if self.byte_cap == 0 { + alloc::alloc(new_layout) + } else { + let old_layout = + alloc::Layout::from_size_align(self.byte_cap, size_of::()).unwrap(); + alloc::realloc(self.ptr, old_layout, new_layout.size()) + }; + + if !new_ptr.is_null() { + self.ptr = new_ptr; + self.byte_cap = new_cap; + + true + } else { + false + } + } +} + +unsafe impl Send for Heap {} +unsafe impl Sync for Heap {} + +static RESOURCE_ERROR_OFFSET_INIT: Once = Once::new(); + +#[derive(Debug)] +pub struct HeapStringScan<'a> { + pub string: &'a str, + pub tail_idx: usize, +} + +// The heap_slice should be inside the heap +unsafe fn scan_slice_to_str(heap_slice: &[u8]) -> HeapStringScan { + let string_len = heap_slice + .iter() + .position(|b| *b == 0u8) + .unwrap_or(heap_slice.len()); + let zero_byte_addr = heap_slice.as_ptr().add(string_len); + + let sentinel_len = pstr_sentinel_length(zero_byte_addr.addr()); + let tail_idx = cell_index!( + (string_len + sentinel_len).next_multiple_of(ALIGN) + + if sentinel_len <= 1 { heap_index!(1) } else { 0 } + ); + + let str_slice = &heap_slice[..string_len]; + + HeapStringScan { + string: std::str::from_utf8_unchecked(str_slice), + tail_idx, + } +} + +// Same as scan_slice_to_str but assumes that the slice is from the start of a string. +// Can be used on strings out of the heap. +unsafe fn scan_slice_to_str_from_start(heap_slice: &[u8]) -> HeapStringScan { + let string_len = heap_slice + .iter() + .position(|b| *b == 0u8) + .unwrap_or(heap_slice.len()); + + let sentinel_len = pstr_sentinel_length(string_len); + let tail_idx = cell_index!( + (string_len + sentinel_len).next_multiple_of(ALIGN) + + if sentinel_len <= 1 { heap_index!(1) } else { 0 } + ); + + let str_slice = &heap_slice[..string_len]; + + HeapStringScan { + string: std::str::from_utf8_unchecked(str_slice), + tail_idx, + } +} + +#[derive(Debug, Clone, Copy)] +pub(crate) enum PStrContinuable { + PStrOffset(usize), + TailIndex(usize), +} + +impl PStrContinuable { #[inline] - fn from(literal: Literal) -> Self { - match literal { - Literal::Atom(name) => atom_as_cell!(name), - Literal::Char(c) => char_as_cell!(c), - Literal::CodeIndex(ptr) => { - untyped_arena_ptr_as_cell!(UntypedArenaPtr::from(ptr)) + pub(crate) fn offset_by(&self, pstr_loc: usize) -> HeapCellValue { + match self { + Self::PStrOffset(pstr_offset) => pstr_loc_as_cell!(pstr_loc + pstr_offset), + Self::TailIndex(tail_idx) => heap_loc_as_cell!(tail_idx + cell_index!(pstr_loc)), + } + } +} + +#[derive(Debug, Clone, Copy)] +pub(crate) enum PStrSegmentCmpResult { + Less, + Greater, + Continue(PStrContinuable, PStrContinuable), +} + +pub(crate) fn compare_pstr_slices(slice1: &[u8], slice2: &[u8]) -> PStrSegmentCmpResult { + debug_assert!(!slice1.is_empty() && !slice2.is_empty()); + let find_tail = |slice| unsafe { scan_slice_to_str(slice).tail_idx }; + + let calculate_result = |pos| { + use std::cmp::Ordering; + + if slice1.get(pos).cloned().unwrap_or(0) == 0 { + // subtract 1 from pos to offset the increment of scan_slice_to_str if the + // string is "\0\". + let tail1_idx = find_tail(&slice1[pos..]); + let offset_pos_1 = (ALIGN - slice1.as_ptr().align_offset(ALIGN)) % ALIGN; + + if slice2.get(pos).cloned().unwrap_or(0) == 0 { + let tail2_idx = find_tail(&slice2[pos..]); + let offset_pos_2 = (ALIGN - slice2.as_ptr().align_offset(ALIGN)) % ALIGN; + + PStrSegmentCmpResult::Continue( + PStrContinuable::TailIndex(tail1_idx + cell_index!(pos + offset_pos_1)), + PStrContinuable::TailIndex(tail2_idx + cell_index!(pos + offset_pos_2)), + ) + } else { + PStrSegmentCmpResult::Continue( + PStrContinuable::TailIndex(tail1_idx + cell_index!(pos)), + PStrContinuable::PStrOffset(pos), + ) } - Literal::Fixnum(n) => fixnum_as_cell!(n), - Literal::Integer(bigint_ptr) => { - typed_arena_ptr_as_cell!(bigint_ptr) + } else if slice2.get(pos).cloned().unwrap_or(0) == 0 { + let tail2_idx = find_tail(&slice2[pos..]); + let offset_pos_2 = (ALIGN - slice2.as_ptr().align_offset(ALIGN)) % ALIGN; + + PStrSegmentCmpResult::Continue( + PStrContinuable::PStrOffset(pos), + PStrContinuable::TailIndex(tail2_idx + cell_index!(pos + offset_pos_2)), + ) + } else { + // Compute 7-byte chunks with the mismatching character at pos in the middle of + // each. This way, the character of which the byte at pos is a part will be + // validated and reached eventually by the utf8_chunks() iterator. + + let slice1_range = pos.saturating_sub(3)..(pos + 4).min(slice1.len()); + let slice2_range = pos.saturating_sub(3)..(pos + 4).min(slice2.len()); + + let chars1_iter = slice1[slice1_range].utf8_chunks(); + let chars2_iter = slice2[slice2_range].utf8_chunks(); + + for (chunk1, chunk2) in chars1_iter.zip(chars2_iter) { + let result = chunk1.valid().cmp(chunk2.valid()); + + if result == Ordering::Greater { + return PStrSegmentCmpResult::Greater; + } else if result == Ordering::Less { + return PStrSegmentCmpResult::Less; + } } - Literal::Rational(bigint_ptr) => { - typed_arena_ptr_as_cell!(bigint_ptr) + + unreachable!() + } + }; + + match slice1 + .iter() + .zip(slice2.iter()) + .position(|(b1, b2)| b1 != b2 || *b1 == 0 || *b2 == 0) + { + Some(pos) => calculate_result(pos), + None => calculate_result(slice1.len().min(slice2.len())), + } +} + +#[derive(Debug)] +pub(crate) struct ReservedHeapSection { + heap_ptr: *mut u8, + heap_cell_len: usize, +} + +impl ReservedHeapSection { + #[inline] + pub(crate) fn cell_len(&self) -> usize { + self.heap_cell_len + } + + pub(crate) fn push_cell(&mut self, cell: HeapCellValue) { + unsafe { + ptr::write( + self.heap_ptr + .add(heap_index!(self.heap_cell_len)) + .cast::(), + cell, + ); + } + + self.heap_cell_len += 1; + } + + fn push_pstr_segment(&mut self, src: &str) -> usize { + if src.is_empty() { + return 0; + } + + let cells_written; + let str_byte_len = src.len(); + + unsafe { + ptr::copy_nonoverlapping( + src.as_ptr(), + self.heap_ptr.add(heap_index!(self.heap_cell_len)), + str_byte_len, + ); + + let zero_region_idx = heap_index!(self.heap_cell_len) + str_byte_len; + let align_offset = pstr_sentinel_length(zero_region_idx); + + ptr::write_bytes(self.heap_ptr.add(zero_region_idx), 0u8, align_offset); + + cells_written = if align_offset == 1 { + ptr::write_bytes( + self.heap_ptr.add(zero_region_idx + 1), + 0u8, + size_of::(), + ); + + // ensure there are at least two bytes in the boundary + // buffer separating the string data from the tail + // cell + cell_index!(src.len() + align_offset + size_of::()) + } else { + cell_index!(src.len() + align_offset) + }; + + self.heap_cell_len += cells_written; + } + + cells_written + } + + pub(crate) fn push_pstr(&mut self, mut src: &str) -> Option { + let anchor = self.cell_len(); + let mut ret = None; + + loop { + // Eat the first null chars + while let Some('\u{0}') = src.chars().next() { + match ret { + Some(_) => { + debug_assert_ne!(anchor, self.cell_len()); + self.push_cell(list_loc_as_cell!(self.cell_len() + 1)); + } + None => { + debug_assert_eq!(anchor, self.cell_len()); + ret = Some(list_loc_as_cell!(self.cell_len())); + } + } + + self.push_cell(char_as_cell!('\u{0}')); + src = &src[1..]; } - Literal::Float(f) => HeapCellValue::from(f.as_ptr()), - Literal::String(s) => { - if s == atom!("") { - empty_list_as_cell!() - } else { - string_as_cstr_cell!(s) + + if src.is_empty() { + return ret; + } + + if let Some(null_char_idx) = src.find('\u{0}') { + debug_assert_ne!(null_char_idx, 0); + + match ret { + Some(_) => { + debug_assert_ne!(anchor, self.cell_len()); + self.push_cell(pstr_loc_as_cell!(heap_index!(self.cell_len() + 1))); + } + None => { + debug_assert_eq!(anchor, self.cell_len()); + ret = Some(pstr_loc_as_cell!(heap_index!(self.cell_len()))); + } + } + + self.push_pstr_segment(&src[0..null_char_idx]); + + // Put the \x0\ + self.push_cell(list_loc_as_cell!(self.cell_len() + 1)); + self.push_cell(char_as_cell!('\u{0}')); + + src = &src[null_char_idx + 1..]; + + if src.is_empty() { + return ret; + } + } else { + match ret { + Some(_) => { + debug_assert_ne!(anchor, self.cell_len()); + self.push_cell(pstr_loc_as_cell!(heap_index!(self.cell_len() + 1))); + } + None => { + debug_assert_eq!(anchor, self.cell_len()); + ret = Some(pstr_loc_as_cell!(heap_index!(self.cell_len()))); + } + } + + self.push_pstr_segment(src); + return ret; + } + } + } + + pub(crate) fn functor_writer( + functor: Vec, + ) -> impl FnMut(&mut ReservedHeapSection) { + struct FunctorData<'a> { + functor: &'a Vec, + cell_offset: usize, + cursor: usize, + } + + move |section| { + let mut functor_stack = vec![FunctorData { + functor: &functor, + cell_offset: section.heap_cell_len, + cursor: 0, + }]; + + while let Some(FunctorData { + functor, + cell_offset, + mut cursor, + }) = functor_stack.pop() + { + while cursor < functor.len() { + match &functor[cursor] { + &FunctorElement::AbsoluteCell(cell) => { + section.push_cell(cell); + } + &FunctorElement::Cell(cell) => { + section.push_cell(cell + cell_offset); + } + FunctorElement::String(_cell_len, string) => { + if section.push_pstr(string).is_some() { + section.push_cell(empty_list_as_cell!()); + } + } + FunctorElement::InnerFunctor(_inner_size, succ_functor) => { + if cursor + 1 < functor.len() { + functor_stack.push(FunctorData { + functor, + cell_offset, + cursor: cursor + 1, + }); + } + + functor_stack.push(FunctorData { + functor: succ_functor, + cell_offset: section.heap_cell_len, + cursor: 0, + }); + + break; + } + } + + cursor += 1; } } } } } -impl TryFrom for Literal { - type Error = (); +impl Index for ReservedHeapSection { + type Output = HeapCellValue; - fn try_from(value: HeapCellValue) -> Result { - read_heap_cell!(value, - (HeapCellValueTag::Atom, (name, arity)) => { - if arity == 0 { - Ok(Literal::Atom(name)) - } else { - Err(()) + #[inline] + fn index(&self, idx: usize) -> &Self::Output { + debug_assert!(idx < self.heap_cell_len); + unsafe { &*self.heap_ptr.cast::().add(idx) } + } +} + +/// Computes the number of bytes required to pad a string of length `chunk_len` +/// with zeroes, such that `chunk_len + pstr_sentinel_length(chunk_len)` is a +/// multiple of `Heap::heap_cell_alignement()`. +fn pstr_sentinel_length(chunk_len: usize) -> usize { + let res = chunk_len.next_multiple_of(ALIGN) - chunk_len; + + // No bytes available in last chunk + if res == 0 { + ALIGN + } else { + res + } +} + +#[must_use] +#[derive(Debug)] +pub struct HeapWriter<'a> { + section: ReservedHeapSection, + heap_byte_len: &'a mut usize, +} + +pub(crate) struct HeapSectionWriteResult { + pub(crate) bytes_written: usize, + pub(crate) result: R, +} + +impl<'a> HeapWriter<'a> { + #[allow(dead_code)] + pub(crate) fn write_with_error_handling( + &mut self, + writer: impl FnOnce(&mut ReservedHeapSection) -> Result, + ) -> Result, E> { + let old_section_cell_len = self.section.heap_cell_len; + let result = writer(&mut self.section)?; + *self.heap_byte_len = heap_index!(self.section.heap_cell_len); + + // return the number of bytes written + Ok(HeapSectionWriteResult { + bytes_written: heap_index!(self.section.heap_cell_len - old_section_cell_len), + result, + }) + } + + pub(crate) fn write_with( + &mut self, + writer: impl FnOnce(&mut ReservedHeapSection) -> R, + ) -> HeapSectionWriteResult { + let old_section_cell_len = self.section.heap_cell_len; + let result = writer(&mut self.section); + *self.heap_byte_len = heap_index!(self.section.heap_cell_len); + + HeapSectionWriteResult { + bytes_written: heap_index!(self.section.heap_cell_len - old_section_cell_len), + result, + } + } +} + +impl<'a> Index for HeapWriter<'a> { + type Output = HeapCellValue; + + #[inline] + fn index(&self, idx: usize) -> &Self::Output { + debug_assert!(heap_index!(idx) < *self.heap_byte_len); + unsafe { + &*self + .section + .heap_ptr + .add(heap_index!(idx)) + .cast::() + } + } +} + +impl<'a> IndexMut for HeapWriter<'a> { + #[inline] + fn index_mut(&mut self, idx: usize) -> &mut Self::Output { + debug_assert!(heap_index!(idx) < *self.heap_byte_len); + unsafe { + &mut *self + .section + .heap_ptr + .add(heap_index!(idx)) + .cast::() + } + } +} + +impl<'a> SizedHeap for HeapWriter<'a> { + fn cell_len(&self) -> usize { + self.section.cell_len() + } + + fn scan_slice_to_str(&self, slice_loc: usize) -> HeapStringScan { + let HeapStringScan { string, tail_idx } = unsafe { + let slice = std::slice::from_raw_parts( + self.section.heap_ptr.byte_add(slice_loc), + heap_index!(self.section.heap_cell_len) - slice_loc, + ); + + scan_slice_to_str(slice) + }; + + HeapStringScan { + string, + tail_idx: cell_index!(slice_loc) + tail_idx, + } + } + + fn as_slice(&self) -> &[u8] { + unsafe { + std::slice::from_raw_parts( + self.section.heap_ptr, + heap_index!(self.section.heap_cell_len), + ) + } + } +} + +impl Heap { + pub(crate) fn new() -> Self { + Self { + inner: InnerHeap { + ptr: ptr::null_mut(), + byte_len: 0, + byte_cap: 0, + }, + resource_err_loc: 0, + } + } + + // takes a heap index, returns a cell index + #[inline] + pub const fn pstr_tail_idx(pstr_zero_byte_loc: usize) -> usize { + if (pstr_zero_byte_loc + 1) % Heap::heap_cell_alignment() == 0 { + cell_index!(pstr_zero_byte_loc) + 2 + } else { + cell_index!(pstr_zero_byte_loc) + 1 + } + } + + #[inline(always)] + unsafe fn grow(&mut self) -> bool { + self.inner.grow() + } + + #[inline] + fn resource_error_offset(&self) -> usize { + self.resource_err_loc + } + + pub(crate) fn with_cell_capacity(cap: usize) -> Result { + let ptr = unsafe { + let layout = alloc::Layout::from_size_align( + cap * size_of::(), + size_of::(), + ) + .unwrap(); + alloc::alloc(layout) + }; + + if ptr.is_null() { + panic!("could not allocate {} bytes for heap!", heap_index!(cap)) + } else { + Ok(Self { + inner: InnerHeap { + ptr, + byte_len: 0, + byte_cap: heap_index!(cap), + }, + // pstr_vec: bitvec![], + resource_err_loc: 0, + }) + } + } + + pub fn reserve(&mut self, num_cells: usize) -> Result { + let section; + let len = heap_index!(num_cells); + + loop { + unsafe { + if self.free_space() >= len { + section = ReservedHeapSection { + heap_ptr: self.inner.ptr, + heap_cell_len: self.cell_len(), + }; + break; + } else if !self.grow() { + return Err(self.resource_error_offset()); } } - (HeapCellValueTag::Char, c) => { - Ok(Literal::Char(c)) + } + + Ok(HeapWriter { + section, + heap_byte_len: &mut self.inner.byte_len, + }) + } + + pub(crate) fn last_cell(&mut self) -> Option { + if self.inner.byte_len == 0 { + None + } else { + unsafe { + Some(ptr::read( + self.inner.ptr.add(self.inner.byte_len - heap_index!(1)) + as *const HeapCellValue, + )) } - (HeapCellValueTag::Fixnum, n) => { - Ok(Literal::Fixnum(n)) + } + } + + pub(crate) fn append(&mut self, other_heap: &impl SizedHeap) -> Result<(), usize> { + let other_len = heap_index!(other_heap.cell_len()); + + loop { + if self.free_space() >= other_len { + let heap_slice = unsafe { + std::slice::from_raw_parts_mut( + self.inner.ptr.add(self.inner.byte_len), + other_len, + ) + }; + + heap_slice.copy_from_slice(other_heap.as_slice()); + self.inner.byte_len += heap_index!(other_heap.cell_len()); + break; + } else if unsafe { !self.grow() } { + return Err(self.resource_error_offset()); } - (HeapCellValueTag::F64, f) => { - Ok(Literal::Float(f.as_offset())) + } + + Ok(()) + } + + #[inline] + pub(crate) fn is_empty(&self) -> bool { + self.inner.byte_len == 0 + } + + pub(crate) fn clear(&mut self) { + unsafe { + let layout = + alloc::Layout::from_size_align(self.inner.byte_cap, size_of::()) + .unwrap(); + alloc::dealloc(self.inner.ptr, layout); + } + + self.inner.ptr = ptr::null_mut(); + self.inner.byte_len = 0; + self.inner.byte_cap = 0; + } + + pub(crate) fn store_resource_error(&mut self) { + RESOURCE_ERROR_OFFSET_INIT.call_once(move || { + let stub = functor!(atom!("resource_error"), [atom_as_cell((atom!("memory")))]); + self.resource_err_loc = cell_index!(self.inner.byte_len); + + let mut writer = Heap::functor_writer(stub); + writer(self).unwrap(); + }); + } + + #[inline] + pub(crate) fn compare_pstr_segments( + &self, + pstr_loc1: usize, + pstr_loc2: usize, + ) -> PStrSegmentCmpResult { + let slice1 = &self.as_slice()[pstr_loc1..]; + let slice2 = &self.as_slice()[pstr_loc2..]; + + compare_pstr_slices(slice1, slice2) + } + + #[inline] + pub(crate) fn slice_to_str(&self, slice_loc: usize, slice_len: usize) -> &str { + unsafe { + let slice = std::slice::from_raw_parts(self.inner.ptr.add(slice_loc), slice_len); + std::str::from_utf8_unchecked(slice) + } + } + + #[inline] + pub(crate) fn byte_len(&self) -> usize { + self.inner.byte_len + } + + #[inline] + pub(crate) fn cell_len(&self) -> usize { + cell_index!(self.inner.byte_len) + } + + // free space in bytes. + #[inline] + fn free_space(&self) -> usize { + self.inner.byte_cap - self.inner.byte_len + } + + pub(crate) fn char_iter<'a>(&'a self, pstr_loc: usize) -> PStrSegmentIter<'a> { + PStrSegmentIter::from(self, pstr_loc) + } + + // either succeed & return nothing or fail & return an offset into + // the heap to a pre-allocated resource error + pub(crate) fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), usize> { + unsafe { + if self.inner.byte_len == self.inner.byte_cap && !self.grow() { + return Err(self.resource_error_offset()); } - (HeapCellValueTag::Cons, cons_ptr) => { - match_untyped_arena_ptr!(cons_ptr, - (ArenaHeaderTag::Integer, n) => { - Ok(Literal::Integer(n)) - } - (ArenaHeaderTag::Rational, n) => { - Ok(Literal::Rational(n)) - } - (ArenaHeaderTag::IndexPtr, ip) => { - Ok(Literal::CodeIndex(CodeIndex::from(ip))) - } - _ => { - Err(()) - } - ) + + // SAFETY: + // - Postcondition: from `self.grow()`, `self.inner.byte_len + size_of::()` + // is strictly less than `self.inner.byte_cap`. + // - Asserted: `self.cell_len() * size_of::() <= self.inner.byte_cap`. + // - Invariant: from `InnerHeap`, `self.inner.byte_cap < isize::MAX`. + let cell_ptr = self.inner.ptr.cast::().add(self.cell_len()); + cell_ptr.write(cell); + // self.pstr_vec.push(false); + self.inner.byte_len += heap_index!(1); + } + + Ok(()) + } + + fn slice_range>(&self, range: R) -> Range { + let start = match range.start_bound() { + Bound::Included(lower_bound) => *lower_bound, + Bound::Excluded(lower_bound) => *lower_bound + 1, + Bound::Unbounded => 0, + }; + + let end = match range.end_bound() { + Bound::Included(upper_bound) => *upper_bound + 1, + Bound::Excluded(0) => 0, + Bound::Excluded(upper_bound) => *upper_bound, + Bound::Unbounded => self.cell_len(), + }; + + Range { start, end } + } + + pub fn allocate_pstr(&mut self, src: &str) -> Result { + let size_in_heap = Self::compute_pstr_size(src); + let mut writer = self.reserve(size_in_heap)?; + let HeapSectionWriteResult { result, .. } = + writer.write_with(|section| match section.push_pstr(src) { + None => empty_list_as_cell!(), + Some(cell) => cell, + }); + + Ok(result) + } + + // note that allocate_cstr emits a tail cell to the string (completing it with the empty list) + // unlike any version of allocate_pstr. + + pub fn allocate_cstr(&mut self, src: &str) -> Result { + let size_in_heap = Self::compute_pstr_size(src); + let mut writer = self.reserve(size_in_heap + 1)?; + let HeapSectionWriteResult { result, .. } = + writer.write_with(|section| match section.push_pstr(src) { + None => empty_list_as_cell!(), + Some(cell) => { + section.push_cell(empty_list_as_cell!()); + cell + } + }); + + Ok(result) + } + + pub const fn heap_cell_alignment() -> usize { + // yes, size_of, not align_of. the alignment of HeapCellValue + // is 1 byte. In the heap, though, its alignment must be its + // size. + size_of::() + } + + #[inline] + pub(crate) fn char_at(&self, byte_idx: usize) -> char { + let s = unsafe { + let char_ptr = self.inner.ptr.add(byte_idx); + let slice = std::slice::from_raw_parts(char_ptr, size_of::()); + std::str::from_utf8_unchecked(slice) + }; + + s.chars().next().unwrap() + } + + pub(crate) fn last_str_char_and_tail(&self, loc: usize) -> (char, HeapCellValue) { + unsafe { + let char_ptr = self.inner.ptr.add(loc); + let slice = std::slice::from_raw_parts(char_ptr, self.inner.byte_len - loc); + + let s = std::str::from_utf8_unchecked(slice); + let mut chars_iter = s.chars(); + let c = chars_iter.next().unwrap(); + let next_char_opt = chars_iter.next(); + + if next_char_opt.is_none() || next_char_opt == Some('\u{0}') { + let tail_idx = scan_slice_to_str(slice).tail_idx + cell_index!(loc); + (c, heap_loc_as_cell!(tail_idx)) + } else { + let succ_len = loc + c.len_utf8(); + (c, pstr_loc_as_cell!(succ_len)) } - (HeapCellValueTag::CStr, cstr_atom) => { - Ok(Literal::String(cstr_atom)) + } + } + + // copies only the string, not its tail. returns the cell index of + // the tail location + pub(crate) fn copy_pstr_within(&mut self, pstr_loc: usize) -> Result { + let HeapStringScan { string, tail_idx } = self.scan_slice_to_str(pstr_loc); + let s_len = string.len(); + + let align_offset = pstr_sentinel_length(s_len); + let copy_size = s_len + align_offset; + + unsafe { + loop { + if self.free_space() >= copy_size { + let slice = + std::slice::from_raw_parts_mut(self.inner.ptr, self.inner.byte_len + s_len); + + slice.copy_within(pstr_loc..pstr_loc + s_len, self.inner.byte_len); + + ptr::write_bytes( + self.inner.ptr.add(self.inner.byte_len + s_len), + 0u8, + align_offset, + ); + + if align_offset == 1 { + ptr::write_bytes( + self.inner.ptr.add(self.inner.byte_len + copy_size), + 0u8, + size_of::(), + ); + + self.inner.byte_len += copy_size + heap_index!(1); + } else { + self.inner.byte_len += copy_size; + } + + break; + } else if !self.grow() { + return Err(self.resource_error_offset()); + } } - _ => { - Err(()) + } + + Ok(tail_idx) + } + + // src is a cell-indexed range. + pub(crate) fn copy_slice_to_end>(&mut self, src: R) -> Result<(), usize> { + let range = self.slice_range(src); + let len = range.end - range.start; + + unsafe { + loop { + if self.free_space() >= heap_index!(len) { + ptr::copy_nonoverlapping( + self.inner.ptr.add(heap_index!(range.start)), + self.inner.ptr.add(self.inner.byte_len), + heap_index!(len), + ); + + // self.pstr_vec.resize(self.cell_len() + len, false); + self.inner.byte_len += heap_index!(len); + + break; + } else if !self.grow() { + return Err(self.resource_error_offset()); + } } - ) + } + + Ok(()) + } + + /// Returns the number of bytes needed to store `src` as a `PStr`. + /// Assumes the string will be allocated on a ALIGN-byte boundary. + pub(crate) fn compute_pstr_size(src: &str) -> usize { + let mut byte_size = 0; + let mut src_bytes = src.as_bytes(); + + while !src_bytes.is_empty() { + if src_bytes[0] == 0 { + // push a list_loc_as_cell! and null char atom to the heap and continue. + byte_size += heap_index!(2); + src_bytes = &src_bytes[1..]; + continue; + } + + let HeapStringScan { string, tail_idx } = + unsafe { scan_slice_to_str_from_start(src_bytes) }; + + src_bytes = &src_bytes[string.len()..]; + byte_size += heap_index!(tail_idx); + } + + // add 1 cell to make up for the final tail cell. if src == "" it's written to the heap as + // empty_list_as_cell!() and the pstr_size is 0 + heap_index!(1). + byte_size + heap_index!(1) + } + + pub(crate) const fn compute_functor_byte_size(functor: &[FunctorElement]) -> usize { + let mut byte_size = 0; + let mut idx = 0; + + while idx < functor.len() { + match &functor[idx] { + &FunctorElement::InnerFunctor(inner_cell_size, ref _inner_functor) => { + byte_size += inner_cell_size as usize * size_of::(); + } + FunctorElement::AbsoluteCell(_cell) | FunctorElement::Cell(_cell) => { + byte_size += size_of::(); + } + &FunctorElement::String(cell_len, _) => { + byte_size += cell_len as usize * size_of::(); + } + } + + idx += 1; + } + + byte_size + } + + pub(crate) fn functor_writer( + functor: Vec, + ) -> impl FnMut(&mut Heap) -> Result { + let size = Heap::compute_functor_byte_size(&functor); + let mut functor_writer = ReservedHeapSection::functor_writer(functor); + + move |heap| { + let mut writer = heap.reserve(size)?; + let heap_byte_len = *writer.heap_byte_len; + let HeapSectionWriteResult { bytes_written, .. } = + writer.write_with(&mut functor_writer); + + Ok(if cell_index!(bytes_written) > 1 { + str_loc_as_cell!(cell_index!(heap_byte_len)) + } else { + heap_loc_as_cell!(cell_index!(heap_byte_len)) + }) + } + } + + #[inline] + pub(crate) fn truncate(&mut self, cell_offset: usize) { + self.inner.byte_len = heap_index!(cell_offset); + // self.pstr_vec.truncate(cell_offset); + } +} + +pub(crate) struct PStrSegmentIter<'a> { + string_buf: &'a str, +} + +impl<'a> PStrSegmentIter<'a> { + fn from(heap: &'a Heap, pstr_loc: usize) -> Self { + debug_assert!(pstr_loc <= heap.inner.byte_len); + + let string_buf = unsafe { + let char_ptr = heap.inner.ptr.add(pstr_loc); + let slice = std::slice::from_raw_parts(char_ptr, heap.inner.byte_len - pstr_loc); + std::str::from_utf8_unchecked(slice) + }; + + PStrSegmentIter { string_buf } + } +} + +impl<'a> Iterator for PStrSegmentIter<'a> { + type Item = char; + + #[inline] + fn next(&mut self) -> Option { + self.string_buf.chars().next().and_then(|c| { + if c == '\u{0}' { + None + } else { + self.string_buf = &self.string_buf[c.len_utf8()..]; + Some(c) + } + }) + } +} + +pub trait SizedHeap: Index { + // return the size of the instance in cells + fn cell_len(&self) -> usize; + + // return a pointer to the heap string and the cell index of its tail + fn scan_slice_to_str<'a>(&'a self, slice_loc: usize) -> HeapStringScan<'a>; + + fn as_slice(&self) -> &[u8]; + + // return true iff a partial string is stored at cell_offset. + // fn pstr_at(&self, cell_offset: usize) -> bool; +} + +impl Index for Heap { + type Output = HeapCellValue; + + #[inline] + fn index(&self, idx: usize) -> &Self::Output { + unsafe { &*self.inner.ptr.cast::().add(idx) } + } +} + +impl IndexMut for Heap { + #[inline] + fn index_mut(&mut self, idx: usize) -> &mut Self::Output { + unsafe { &mut *self.inner.ptr.cast::().add(idx) } + } +} + +impl SizedHeap for Heap { + #[inline] + fn cell_len(&self) -> usize { + self.cell_len() + } + + fn scan_slice_to_str(&self, slice_loc: usize) -> HeapStringScan { + let HeapStringScan { string, tail_idx } = unsafe { + let slice = std::slice::from_raw_parts( + self.inner.ptr.add(slice_loc), + self.inner.byte_len - slice_loc, + ); + + scan_slice_to_str(slice) + }; + + HeapStringScan { + string, + tail_idx: cell_index!(slice_loc) + tail_idx, + } + } + + #[inline] + fn as_slice(&self) -> &[u8] { + unsafe { std::slice::from_raw_parts(self.inner.ptr, self.inner.byte_len) } } } @@ -91,7 +1090,7 @@ impl TryFrom for Literal { // the heap without access to the full WAM (e.g., while detecting // cycles in terms), and which therefore may only point other cells in // the heap (thanks to the design of the WAM). -pub fn heap_bound_deref(heap: &[HeapCellValue], mut value: HeapCellValue) -> HeapCellValue { +pub fn heap_bound_deref(heap: &impl SizedHeap, mut value: HeapCellValue) -> HeapCellValue { loop { let new_value = read_heap_cell!(value, (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { @@ -111,7 +1110,7 @@ pub fn heap_bound_deref(heap: &[HeapCellValue], mut value: HeapCellValue) -> Hea } } -pub fn heap_bound_store(heap: &[HeapCellValue], value: HeapCellValue) -> HeapCellValue { +pub fn heap_bound_store(heap: &impl SizedHeap, value: HeapCellValue) -> HeapCellValue { read_heap_cell!(value, (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { heap[h] @@ -123,135 +1122,57 @@ pub fn heap_bound_store(heap: &[HeapCellValue], value: HeapCellValue) -> HeapCel } #[allow(dead_code)] -pub fn print_heap_terms<'a, I: Iterator>(heap: I, h: usize) { - for (index, term) in heap.enumerate() { - println!("{} : {:?}", h + index, term); +pub fn print_heap_terms(heap: &impl SizedHeap, h: usize) { + for idx in 0..heap.cell_len() { + let term = heap[idx]; + println!("{} : {:?}", h + idx, term); } } -#[inline] -pub(crate) fn put_complete_string(heap: &mut Heap, s: &str, atom_tbl: &AtomTable) -> HeapCellValue { - match allocate_pstr(heap, s, atom_tbl) { - Some(h) => { - heap.pop(); // pop the trailing variable cell from the heap planted by allocate_pstr. - - if heap.len() == h + 1 { - let pstr_atom = cell_as_atom!(heap[h]); - heap[h] = atom_as_cstr_cell!(pstr_atom); - heap_loc_as_cell!(h) - } else { - heap.push(empty_list_as_cell!()); - pstr_loc_as_cell!(h) - } - } - None => { - let h = heap.len(); - heap.push(empty_list_as_cell!()); - heap_loc_as_cell!(h) - } - } -} - -#[inline] -pub(crate) fn put_partial_string(heap: &mut Heap, s: &str, atom_tbl: &AtomTable) -> HeapCellValue { - match allocate_pstr(heap, s, atom_tbl) { - Some(h) => { - pstr_loc_as_cell!(h) - } - None => { - empty_list_as_cell!() - } - } -} - -#[inline] -pub(crate) fn allocate_pstr(heap: &mut Heap, mut src: &str, atom_tbl: &AtomTable) -> Option { - let orig_h = heap.len(); - - loop { - if src.is_empty() { - return if orig_h == heap.len() { - None - } else { - let tail_h = heap.len() - 1; - heap[tail_h] = heap_loc_as_cell!(tail_h); - - Some(orig_h) - }; - } - - let h = heap.len(); - - let (pstr, rest_src) = match PartialString::new(src, atom_tbl) { - Some(tuple) => tuple, - None => { - if src.len() > '\u{0}'.len_utf8() { - src = &src['\u{0}'.len_utf8()..]; - continue; - } else if orig_h == h { - return None; - } else { - heap[h - 1] = heap_loc_as_cell!(h - 1); - return Some(orig_h); - } - } - }; - - heap.push(string_as_pstr_cell!(pstr)); - - if !rest_src.is_empty() { - heap.push(pstr_loc_as_cell!(h + 2)); - src = rest_src; - } else { - heap.push(heap_loc_as_cell!(h + 1)); - return Some(orig_h); - } - } -} - -pub fn filtered_iter_to_heap_list>( +pub fn sized_iter_to_heap_list>( heap: &mut Heap, + size: usize, values: impl Iterator, - filter_fn: impl Fn(&Heap, HeapCellValue) -> bool, -) -> usize { - let head_addr = heap.len(); - let mut h = head_addr; +) -> Result { + if size > 0 { + let h = heap.cell_len(); + let mut writer = heap.reserve(1 + 2 * size)?; - for value in values { - let value = value.into(); + writer.write_with(|section| { + for (idx, value) in values.enumerate() { + section.push_cell(list_loc_as_cell!(h + 1 + 2 * idx)); + section.push_cell(value.into()); + } - if filter_fn(heap, value) { - heap.push(list_loc_as_cell!(h + 1)); - heap.push(value); + section.push_cell(empty_list_as_cell!()); + }); - h += 2; - } + Ok(heap_loc_as_cell!(h)) + } else { + Ok(empty_list_as_cell!()) } - - heap.push(empty_list_as_cell!()); - - head_addr -} - -#[inline(always)] -pub fn iter_to_heap_list(heap: &mut Heap, values: Iter) -> usize -where - Iter: Iterator, - SrcT: Into, -{ - filtered_iter_to_heap_list(heap, values, |_, _| true) } pub(crate) fn to_local_code_ptr(heap: &Heap, addr: HeapCellValue) -> Option { let extract_integer = |s: usize| -> Option { - match Number::try_from(heap[s]) { - Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).ok(), - Ok(Number::Integer(n)) => { - let value: usize = (&*n).try_into().unwrap(); - Some(value) + read_heap_cell!(heap[s], + (HeapCellValueTag::Cons, c) => { + match_untyped_arena_ptr!(c, + (ArenaHeaderTag::Integer, n) => { + (&*n).try_into().ok() + } + _ => { + None + } + ) } - _ => None, - } + (HeapCellValueTag::Fixnum, n) => { + usize::try_from(n.get_num()).ok() + } + _ => { + None + } + ) }; read_heap_cell!(addr, diff --git a/src/machine/lib_machine/mod.rs b/src/machine/lib_machine/mod.rs index 887d2a23..a1ba3399 100644 --- a/src/machine/lib_machine/mod.rs +++ b/src/machine/lib_machine/mod.rs @@ -1,14 +1,15 @@ use std::cmp::Ordering; use std::collections::BTreeMap; +use std::rc::Rc; use crate::atom_table; use crate::heap_iter::{stackful_post_order_iter, NonListElider}; use crate::machine::machine_indices::VarKey; use crate::machine::mock_wam::CompositeOpDir; use crate::machine::{ - ArenaHeaderTag, F64Offset, F64Ptr, Fixnum, Number, BREAK_FROM_DISPATCH_LOOP_LOC, - LIB_QUERY_SUCCESS, + ArenaHeaderTag, Fixnum, Number, BREAK_FROM_DISPATCH_LOOP_LOC, LIB_QUERY_SUCCESS, }; +use crate::offset_table::*; use crate::parser::ast::{Var, VarPtr}; use crate::parser::parser::{Parser, Tokens}; use crate::read::{write_term_to_heap, TermWriteResult}; @@ -175,10 +176,13 @@ impl Term { ) -> Self { // Adapted from MachineState::read_term_from_heap let mut term_stack = vec![]; - let iter = stackful_post_order_iter::( + + machine.machine_st.heap[0] = heap_cell; + + let mut iter = stackful_post_order_iter::( &mut machine.machine_st.heap, &mut machine.machine_st.stack, - heap_cell, + 0, ); let mut anon_count: usize = 0; @@ -193,7 +197,7 @@ impl Term { }, }; - for addr in iter { + while let Some(addr) = iter.next() { let addr = unmark_cell_bits!(addr); read_heap_cell!(addr, @@ -244,11 +248,11 @@ impl Term { (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { let var = var_names.get(&addr).map(|x| x.borrow().clone()); match var { - Some(Var::Named(name)) => term_stack.push(Term::Var(name)), + Some(Var::Named(name)) => term_stack.push(Term::Var(name.as_ref().to_owned())), _ => { let anon_name = loop { // Generate a name for the anonymous variable - let anon_name = count_to_letter_code(anon_count); + let anon_name = Rc::new(count_to_letter_code(anon_count)); // Find if this name is already being used var_names.sort_by(|_, a, _, b| { @@ -269,21 +273,19 @@ impl Term { }, } }; - term_stack.push(Term::Var(anon_name)); + term_stack.push(Term::Var(anon_name.as_ref().to_owned())); }, } } - (HeapCellValueTag::F64, f) => { - term_stack.push(Term::Float((*f).into())); - } - (HeapCellValueTag::Char, c) => { - term_stack.push(Term::Atom(c.into())); + (HeapCellValueTag::F64Offset, offset) => { + let f = machine.machine_st.arena.f64_tbl.get_entry(offset); + term_stack.push(Term::Float(f.into())); } (HeapCellValueTag::Fixnum, n) => { term_stack.push(Term::Integer(n.into())); } (HeapCellValueTag::Cons, ptr) => { - if let Ok(n) = Number::try_from(addr) { + if let Ok(n) = Number::try_from((addr, &machine.machine_st.arena.f64_tbl)) { match n { Number::Integer(i) => term_stack.push(Term::Integer((*i).clone())), Number::Rational(r) => term_stack.push(Term::Rational((*r).clone())), @@ -296,7 +298,7 @@ impl Term { Term::atom(alias.as_str().to_string()) } else { Term::compound("$stream", [ - Term::integer(stream.as_ptr() as usize) + Term::integer(stream.as_ptr().addr()) ]) }; term_stack.push(stream_term); @@ -310,35 +312,7 @@ impl Term { ); } } - (HeapCellValueTag::CStr, s) => { - term_stack.push(Term::String(s.as_str().to_string())); - } (HeapCellValueTag::Atom, (name, arity)) => { - //let h = iter.focus().value() as usize; - //let mut arity = arity; - - // Not sure why/if this is needed. - // Might find out with better testing later. - /* - if iter.heap.len() > h + arity + 1 { - let value = iter.heap[h + arity + 1]; - - if let Some(idx) = get_structure_index(value) { - // in the second condition, arity == 0, - // meaning idx cannot pertain to this atom - // if it is the direct subterm of a larger - // structure. - if arity > 0 || !iter.direct_subterm_of_str(h) { - term_stack.push( - Term::Literal(Cell::default(), Literal::CodeIndex(idx)) - ); - - arity += 1; - } - } - } - */ - if arity == 0 { let atom_name = name.as_str().to_string(); if atom_name == "[]" { @@ -354,8 +328,9 @@ impl Term { term_stack.push(Term::Compound(name.as_str().to_string(), subterms)); } } - (HeapCellValueTag::PStr, atom) => { + (HeapCellValueTag::PStrLoc, pstr_loc) => { let tail = term_stack.pop().unwrap(); + let char_iter = iter.base_iter.heap.char_iter(pstr_loc); match tail { Term::Atom(atom) => { @@ -363,21 +338,18 @@ impl Term { term_stack.push(Term::String(atom.as_str().to_string())); } }, + Term::List(l) if l.is_empty() => { + term_stack.push(Term::String(char_iter.collect())); + } Term::List(l) => { - let mut list: Vec = atom - .as_str() - .to_string() - .chars() + let mut list: Vec = char_iter .map(|x| Term::Atom(x.to_string())) .collect(); list.extend(l.into_iter()); term_stack.push(Term::List(list)); }, _ => { - let mut list: Vec = atom - .as_str() - .to_string() - .chars() + let mut list: Vec = char_iter .map(|x| Term::Atom(x.to_string())) .collect(); @@ -403,19 +375,6 @@ impl Term { } } } - // I dont know if this is needed here. - /* - (HeapCellValueTag::PStrLoc, h) => { - let atom = cell_as_atom_cell!(iter.heap[h]).get_name(); - let tail = term_stack.pop().unwrap(); - - term_stack.push(Term::PartialString( - Cell::default(), - atom.as_str().to_owned(), - Box::new(tail), - )); - } - */ _ => { unreachable!(); } @@ -467,11 +426,20 @@ impl Iterator for QueryState<'_> { // this should halt the search for solutions as it // does in the Scryer top-level. the exception term is // contained in self.machine_st.ball. - let h = machine.machine_st.heap.len(); - machine + let h = machine.machine_st.heap.cell_len(); + + if let Err(resource_err_loc) = machine .machine_st .heap - .extend(machine.machine_st.ball.stub.clone()); + .append(&machine.machine_st.ball.stub) + { + return Some(Err(Term::from_heapcell( + machine, + machine.machine_st.heap[resource_err_loc], + &mut IndexMap::new(), + ))); + } + let exception_term = Term::from_heapcell(machine, machine.machine_st.heap[h], &mut var_names.clone()); @@ -503,7 +471,7 @@ impl Iterator for QueryState<'_> { let mut var_name = var_key.to_string(); if var_name.starts_with('_') { let should_print = var_names.values().any(|x| match x.borrow().clone() { - Var::Named(v) => v == var_name, + Var::Named(v) => *v == *var_name, _ => false, }); if !should_print { @@ -522,10 +490,10 @@ impl Iterator for QueryState<'_> { // Var dict is in the order things appear in the query. If var_name appears // after term in the query, switch their places. let var_name_idx = var_dict - .get_index_of(&VarKey::VarPtr(Var::Named(var_name.clone()).into())) + .get_index_of(&VarKey::VarPtr(Var::from(var_name.clone()).into())) .unwrap(); let term_idx = - var_dict.get_index_of(&VarKey::VarPtr(Var::Named(term_str.clone()).into())); + var_dict.get_index_of(&VarKey::VarPtr(Var::from(term_str.clone()).into())); if let Some(idx) = term_idx { if idx < var_name_idx { let new_term = Term::Var(var_name); @@ -559,7 +527,7 @@ impl Machine { /// Consults a module into the [`Machine`] from a string. pub fn consult_module_string(&mut self, module_name: &str, program: impl Into) { let stream = Stream::from_owned_string(program.into(), &mut self.machine_st.arena); - self.machine_st.registers[1] = stream.into(); + self.machine_st.registers[1] = stream_as_cell!(stream); self.machine_st.registers[2] = atom_as_cell!(&atom_table::AtomTable::build_with( &self.machine_st.atom_tbl, module_name @@ -588,7 +556,7 @@ impl Machine { or_frame.prelude.attr_var_queue_len = 0; self.machine_st.b = stub_b; - self.machine_st.hb = self.machine_st.heap.len(); + self.machine_st.hb = self.machine_st.heap.cell_len(); self.machine_st.block = stub_b; } @@ -606,9 +574,8 @@ impl Machine { self.allocate_stub_choice_point(); // Write parsed term to heap - let term_write_result = - write_term_to_heap(&term, &mut self.machine_st.heap, &self.machine_st.atom_tbl) - .expect("couldn't write term to heap"); + let term_write_result = write_term_to_heap(&term, &mut self.machine_st.heap) + .expect("couldn't write term to heap"); let var_names: IndexMap<_, _> = term_write_result .var_dict @@ -630,9 +597,15 @@ impl Machine { .indices .code_dir .get(&(atom!("call"), 1)) - .expect("couldn't get code index") - .local() - .unwrap(); + .cloned() + .map(|offset| { + self.machine_st + .arena + .code_index_tbl + .get_entry(offset.into()) + .p() as usize + }) + .expect("couldn't get code index"); self.machine_st.execute_at_index(1, call_index_p); diff --git a/src/machine/load_state.rs b/src/machine/load_state.rs index 86c1b681..89c50153 100644 --- a/src/machine/load_state.rs +++ b/src/machine/load_state.rs @@ -2,7 +2,6 @@ use crate::forms::*; use crate::machine::loader::*; use crate::machine::machine_errors::*; use crate::machine::machine_indices::*; -use crate::machine::preprocessor::*; use crate::machine::term_stream::*; use crate::machine::*; use crate::parser::ast::*; @@ -16,35 +15,38 @@ use std::mem; pub(super) type ModuleOpExports = Vec<(OpDecl, Option)>; -pub(super) fn set_code_index( - retraction_info: &mut RetractionInfo, +pub(super) fn set_code_index<'a, LS: LoadState<'a>>( + payload: &mut >::LoaderFieldType, compilation_target: &CompilationTarget, key: PredicateKey, - mut code_index: CodeIndex, + code_idx: CodeIndex, code_ptr: IndexPtr, ) { - let record = match compilation_target { - CompilationTarget::User => { - if IndexPtrTag::Undefined == code_index.get().tag() { - code_index.set(code_ptr); - RetractionRecord::AddedUserPredicate(key) - } else { - let replaced = code_index.replace(code_ptr); - RetractionRecord::ReplacedUserPredicate(key, replaced) + let record = LS::machine_st(payload).arena.code_index_tbl.with_entry_mut( + code_idx.into(), + |code_idx_ptr| match compilation_target { + CompilationTarget::User => { + if IndexPtrTag::Undefined == code_idx_ptr.tag() { + *code_idx_ptr = code_ptr; + RetractionRecord::AddedUserPredicate(key) + } else { + let replaced = mem::replace(code_idx_ptr, code_ptr); + RetractionRecord::ReplacedUserPredicate(key, replaced) + } } - } - CompilationTarget::Module(ref module_name) => { - if IndexPtrTag::Undefined == code_index.get().tag() { - code_index.set(code_ptr); - RetractionRecord::AddedModulePredicate(*module_name, key) - } else { - let replaced = code_index.replace(code_ptr); - RetractionRecord::ReplacedModulePredicate(*module_name, key, replaced) + CompilationTarget::Module(ref module_name) => { + if IndexPtrTag::Undefined == code_idx_ptr.tag() { + *code_idx_ptr = code_ptr; + RetractionRecord::AddedModulePredicate(*module_name, key) + } else { + let replaced = mem::replace(code_idx_ptr, code_ptr); + RetractionRecord::ReplacedModulePredicate(*module_name, key, replaced) + } } - } - }; + }, + ); - retraction_info.push_record(record); + payload.retraction_info.push_record(record); } fn add_op_decl_as_module_export<'a, LS: LoadState<'a>>( @@ -134,21 +136,28 @@ pub(super) fn import_module_exports<'a, LS: LoadState<'a>>( } if let Some(src_code_index) = imported_module.code_dir.get(&key).cloned() { - let arena = &mut LS::machine_st(payload).arena; + let code_idx_tbl = &mut LS::machine_st(payload).arena.code_index_tbl; let target_code_index = *code_dir .entry(key) - .or_insert_with(|| CodeIndex::default(arena)); + .or_insert_with(|| CodeIndex::default(code_idx_tbl)); - set_code_index( - &mut payload.retraction_info, + let src_code_index_ptr = code_idx_tbl.get_entry(src_code_index.into()); + + set_code_index::( + payload, compilation_target, key, target_code_index, - src_code_index.get(), + src_code_index_ptr, ); - if src_code_index.is_dynamic_undefined() { + if LS::machine_st(payload) + .arena + .code_index_tbl + .get_entry(src_code_index.into()) + .is_dynamic_undefined() + { code_dir.insert(key, src_code_index); } } else { @@ -190,19 +199,20 @@ fn import_module_exports_into_module<'a, LS: LoadState<'a>>( meta_predicates.insert(key, meta_specs.clone()); } - if let Some(src_code_index) = imported_module.code_dir.get(&key) { - let arena = &mut LS::machine_st(payload).arena; + if let Some(src_code_index) = imported_module.code_dir.get(&key).cloned() { + let code_index_tbl = &mut LS::machine_st(payload).arena.code_index_tbl; + let src_code_ptr = code_index_tbl.get_entry(src_code_index.into()); let target_code_index = *code_dir .entry(key) - .or_insert_with(|| CodeIndex::default(arena)); + .or_insert_with(|| CodeIndex::default(code_index_tbl)); - set_code_index( - &mut payload.retraction_info, + set_code_index::( + payload, compilation_target, key, target_code_index, - src_code_index.get(), + src_code_ptr, ); } else { return Err(SessionError::ModuleDoesNotContainExport( @@ -243,21 +253,21 @@ fn import_qualified_module_exports<'a, LS: LoadState<'a>>( .insert(key, meta_specs.clone()); } - if let Some(src_code_index) = imported_module.code_dir.get(&key) { - let arena = &mut LS::machine_st(payload).arena; + if let Some(src_code_index) = imported_module.code_dir.get(&key).cloned() { + let code_index_tbl = &mut LS::machine_st(payload).arena.code_index_tbl; - let target_code_index = *wam_prelude - .indices - .code_dir - .entry(key) - .or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena)); + let src_code_ptr = code_index_tbl.get_entry(src_code_index.into()); + let target_code_index = + *wam_prelude.indices.code_dir.entry(key).or_insert_with(|| { + CodeIndex::new(IndexPtr::undefined(), code_index_tbl) + }); - set_code_index( - &mut payload.retraction_info, + set_code_index::( + payload, compilation_target, key, target_code_index, - src_code_index.get(), + src_code_ptr, ); } else { return Err(SessionError::ModuleDoesNotContainExport( @@ -304,19 +314,20 @@ fn import_qualified_module_exports_into_module<'a, LS: LoadState<'a>>( meta_predicates.insert(key, meta_specs.clone()); } - if let Some(src_code_index) = imported_module.code_dir.get(&key) { - let arena = &mut LS::machine_st(payload).arena; + if let Some(src_code_index) = imported_module.code_dir.get(&key).cloned() { + let code_index_tbl = &mut LS::machine_st(payload).arena.code_index_tbl; + let src_code_ptr = code_index_tbl.get_entry(src_code_index.into()); let target_code_index = *code_dir .entry(key) - .or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena)); + .or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), code_index_tbl)); - set_code_index( - &mut payload.retraction_info, + set_code_index::( + payload, &payload_compilation_target, key, target_code_index, - src_code_index.get(), + src_code_ptr, ); } else { return Err(SessionError::ModuleDoesNotContainExport( @@ -434,19 +445,6 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { self.retract_local_clauses_impl(clause_clause_compilation_target, key, clause_locs); } - pub(super) fn try_term_to_tl( - &mut self, - term: Term, - preprocessor: &mut Preprocessor, - ) -> Result { - let tl = preprocessor.try_term_to_tl(self, term)?; - - Ok(match tl { - TopLevel::Fact(fact, var_data) => PredicateClause::Fact(fact, var_data), - TopLevel::Rule(rule, var_data) => PredicateClause::Rule(rule, var_data), - }) - } - #[inline] pub(super) fn remove_module_op_exports(&mut self) { for (mut op_decl, record) in self.payload.module_op_exports.drain(0..) { @@ -482,11 +480,14 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { .indices .get_predicate_skeleton(local_compilation_target, key) { - let old_index_ptr = code_index.replace(if global_skeleton.core.is_dynamic { - IndexPtr::dynamic_undefined() - } else { - IndexPtr::undefined() - }); + let old_index_ptr = code_index.replace( + &mut LS::machine_st(&mut self.payload).arena.code_index_tbl, + if global_skeleton.core.is_dynamic { + IndexPtr::dynamic_undefined() + } else { + IndexPtr::undefined() + }, + ); self.payload.retraction_info.push_record( RetractionRecord::ReplacedModulePredicate(module_name, *key, old_index_ptr), @@ -500,8 +501,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { continue; } - if !code_index.is_undefined() && !code_index.is_dynamic_undefined() { - let old_index_ptr = code_index.replace(IndexPtr::undefined()); + let code_index_tbl = &mut LS::machine_st(&mut self.payload).arena.code_index_tbl; + let code_ptr = code_index_tbl.get_entry((*code_index).into()); + + if !code_ptr.is_undefined() && !code_ptr.is_dynamic_undefined() { + let old_index_ptr = code_index.replace(code_index_tbl, IndexPtr::undefined()); self.payload.retraction_info.push_record( RetractionRecord::ReplacedModulePredicate(module_name, *key, old_index_ptr), @@ -531,27 +535,32 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { None => return, }; - fn remove_module_exports( + fn remove_module_exports<'b, LS: LoadState<'b>>( + payload: &mut >::LoaderFieldType, removed_module: &Module, code_dir: &mut CodeDir, op_dir: &mut OpDir, - retraction_info: &mut RetractionInfo, predicate_retractor: impl Fn(PredicateKey, IndexPtr) -> RetractionRecord, op_retractor: impl Fn(OpDecl, OpDesc) -> RetractionRecord, ) { for export in removed_module.module_decl.exports.iter() { match export { ModuleExport::PredicateKey(ref key) => { - match (removed_module.code_dir.get(key), code_dir.get_mut(key)) { - (Some(module_code_index), Some(target_code_index)) - if module_code_index.get() == target_code_index.get() => - { + if let (Some(module_code_idx), Some(target_code_idx)) = ( + removed_module.code_dir.get(key).cloned(), + code_dir.get_mut(key).cloned(), + ) { + let code_index_tbl = &mut LS::machine_st(payload).arena.code_index_tbl; + let module_code_ptr = code_index_tbl.get_entry(module_code_idx.into()); + let target_code_ptr = code_index_tbl.get_entry(target_code_idx.into()); + + if module_code_ptr == target_code_ptr { let old_index_ptr = - target_code_index.replace(IndexPtr::undefined()); - retraction_info + target_code_idx.replace(code_index_tbl, IndexPtr::undefined()); + payload + .retraction_info .push_record(predicate_retractor(*key, old_index_ptr)); } - _ => {} } } ModuleExport::OpDecl(op_decl) => { @@ -559,7 +568,9 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { .swap_remove(&(op_decl.name, op_decl.op_desc.get_spec().fixity())); if let Some(op_desc) = op_dir_value_opt { - retraction_info.push_record(op_retractor(*op_decl, op_desc)); + payload + .retraction_info + .push_record(op_retractor(*op_decl, op_desc)); } } } @@ -568,11 +579,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { match self.payload.compilation_target { CompilationTarget::User => { - remove_module_exports( + remove_module_exports::( + &mut self.payload, &removed_module, &mut self.wam_prelude.indices.code_dir, &mut self.wam_prelude.indices.op_dir, - &mut self.payload.retraction_info, RetractionRecord::ReplacedUserPredicate, RetractionRecord::ReplacedUserOp, ); @@ -592,11 +603,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { .modules .get_mut(&target_module_name) { - remove_module_exports( + remove_module_exports::( + &mut self.payload, &removed_module, &mut module.code_dir, &mut module.op_dir, - &mut self.payload.retraction_info, predicate_retractor, op_retractor, ); @@ -622,7 +633,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { Some(ref mut module) => *module.code_dir.entry(key).or_insert_with(|| { CodeIndex::new( IndexPtr::undefined(), - &mut LS::machine_st(&mut self.payload).arena, + &mut LS::machine_st(&mut self.payload).arena.code_index_tbl, ) }), None => { @@ -632,7 +643,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { Some(ref mut module) => *module.code_dir.entry(key).or_insert_with(|| { CodeIndex::new( IndexPtr::undefined(), - &mut LS::machine_st(&mut self.payload).arena, + &mut LS::machine_st(&mut self.payload).arena.code_index_tbl, ) }), None => { @@ -648,7 +659,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { key: PredicateKey, compilation_target: CompilationTarget, ) -> CodeIndex { - let arena = &mut LS::machine_st(&mut self.payload).arena; + let code_index_tbl = &mut LS::machine_st(&mut self.payload).arena.code_index_tbl; match compilation_target { CompilationTarget::User => *self @@ -656,7 +667,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { .indices .code_dir .entry(key) - .or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena)), + .or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), code_index_tbl)), CompilationTarget::Module(module_name) => { self.get_or_insert_local_code_index(module_name, key) } @@ -668,15 +679,15 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { module_name: Atom, key: PredicateKey, ) -> CodeIndex { - let arena = &mut LS::machine_st(&mut self.payload).arena; + let code_index_tbl = &mut LS::machine_st(&mut self.payload).arena.code_index_tbl; if module_name == atom!("user") { - return *self + *self .wam_prelude .indices .code_dir .entry(key) - .or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena)); + .or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), code_index_tbl)) } else { self.get_or_insert_local_code_index(module_name, key) } diff --git a/src/machine/loader.rs b/src/machine/loader.rs index b7e7c4b5..08282f68 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -20,6 +20,7 @@ use std::collections::VecDeque; use std::convert::TryFrom; use std::fmt; use std::ops::{Deref, DerefMut}; +use std::rc::Rc; /* * The loader compiles Prolog terms read from a TermStream instance, @@ -382,7 +383,6 @@ impl<'a> LoadState<'a> for BootstrappingLoadState<'a> { let repo_len = loader.wam_prelude.code.len(); loader.payload.retraction_info.reset(repo_len); - loader.remove_module_op_exports(); Ok(loader.payload.compilation_target) @@ -720,7 +720,9 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { self.wam_prelude.indices.modules.get_mut(&module_name) { if let Some(code_idx) = module.code_dir.get_mut(&key) { - code_idx.set(old_code_idx) + let code_index_tbl = + &mut LS::machine_st(&mut self.payload).arena.code_index_tbl; + code_idx.set(code_index_tbl, old_code_idx); } } } @@ -741,7 +743,9 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { } RetractionRecord::ReplacedUserPredicate(key, old_code_idx) => { if let Some(code_idx) = self.wam_prelude.indices.code_dir.get_mut(&key) { - code_idx.set(old_code_idx) + let code_index_tbl = + &mut LS::machine_st(&mut self.payload).arena.code_index_tbl; + code_idx.set(code_index_tbl, old_code_idx) } } RetractionRecord::AddedIndex(index_key, clause_loc) => { @@ -762,7 +766,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { ) => { remove_constant_indices( constant, - &overlapping_constants, + overlapping_constants, indexing_code, clause_loc - index_loc, // WAS: &inner_index_locs, ); @@ -1046,8 +1050,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { let cell = machine_st[r]; let export_list = machine_st.read_term_from_heap(cell); - let atom_tbl = &mut LS::machine_st(&mut self.payload).atom_tbl; - let export_list = setup_module_export_list(export_list, atom_tbl)?; + let export_list = setup_module_export_list(export_list)?; Ok(export_list.into_iter().collect()) } @@ -1060,7 +1063,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { self.payload.clause_clauses.push((head, body)); } - head @ Term::Literal(_, Literal::Atom(..)) | head @ Term::Clause(..) => { + head @ (Term::Clause(..) | Term::Literal(_, Literal::Atom(_))) => { let body = Term::Literal(Cell::default(), Literal::Atom(atom!("true"))); self.payload.clause_clauses.push((head, body)); } @@ -1218,14 +1221,18 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { * but to multifile and discontiguous predicates as well. */ - let code_index = self.get_or_insert_code_index(key, compilation_target); + let offset = self.get_or_insert_code_index(key, compilation_target); + let code_idx_ptr = LS::machine_st(&mut self.payload) + .arena + .code_index_tbl + .get_entry(offset.into()); - if code_index.is_undefined() { - set_code_index( - &mut self.payload.retraction_info, + if code_idx_ptr.is_undefined() { + set_code_index::( + &mut self.payload, &compilation_target, key, - code_index, + offset, IndexPtr::dynamic_undefined(), ); } @@ -1289,13 +1296,16 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { fn add_clause_clause_if_dynamic(&mut self, term: &Term) -> Result<(), SessionError> { if let Some(predicate_name) = ClauseInfo::name(term) { - let arity = ClauseInfo::arity(term); + let predicate_arity = ClauseInfo::arity(term); let predicates_compilation_target = self.payload.predicates.compilation_target; let is_dynamic = self .wam_prelude .indices - .get_predicate_skeleton(&predicates_compilation_target, &(predicate_name, arity)) + .get_predicate_skeleton( + &predicates_compilation_target, + &(predicate_name, predicate_arity), + ) .map(|skeleton| skeleton.core.is_dynamic) .unwrap_or(false); @@ -1369,8 +1379,9 @@ impl<'a> MachinePreludeView<'a> { impl MachineState { pub(super) fn read_term_from_heap(&mut self, term_addr: HeapCellValue) -> Term { let mut term_stack = vec![]; + self.heap[0] = term_addr; let mut iter = - stackful_post_order_iter::(&mut self.heap, &mut self.stack, term_addr); + stackful_post_order_iter::(&mut self.heap, &mut self.stack, 0); while let Some(addr) = iter.next() { let addr = unmark_cell_bits!(addr); @@ -1384,45 +1395,31 @@ impl MachineState { match as_partial_string(head, tail) { Ok((string, Some(tail))) => { - term_stack.push(Term::PartialString(Cell::default(), string, tail)); + term_stack.push(Term::PartialString(Cell::default(), Rc::new(string), tail)); } Ok((string, None)) => { - let atom = AtomTable::build_with(&self.atom_tbl, &string); - term_stack.push(Term::CompleteString(Cell::default(), atom)); + term_stack.push(Term::CompleteString(Cell::default(), Rc::new(string))); } Err(cons_term) => term_stack.push(cons_term), } } + (HeapCellValueTag::Cons | HeapCellValueTag::Fixnum | HeapCellValueTag::F64Offset) => { + term_stack.push(Term::Literal(Cell::default(), Literal::try_from(addr).unwrap())); + } (HeapCellValueTag::StackVar, h) => { - term_stack.push(Term::Var(Cell::default(), VarPtr::from(format!("s_{}", h)))); + term_stack.push(Term::Var(Cell::default(), VarPtr::from(format!("s_{h}")))); } (HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => { - term_stack.push(Term::Var(Cell::default(), VarPtr::from(format!("_{}", h)))); - } - (HeapCellValueTag::Cons | HeapCellValueTag::CStr | HeapCellValueTag::Fixnum | - HeapCellValueTag::Char | HeapCellValueTag::F64) => { - term_stack.push(Term::Literal(Cell::default(), Literal::try_from(addr).unwrap())); + term_stack.push(Term::Var(Cell::default(), VarPtr::from(format!("_{h}")))); } (HeapCellValueTag::Atom, (name, arity)) => { let h = iter.focus().value() as usize; let mut arity = arity; + let value = iter.heap[h.saturating_sub(1)]; - if iter.heap.len() > h + arity + 1 { - let value = iter.heap[h + arity + 1]; - - if let Some(idx) = get_structure_index(value) { - // in the second condition, arity == 0, - // meaning idx cannot pertain to this atom - // if it is the direct subterm of a larger - // structure. - if arity > 0 || !iter.direct_subterm_of_str(h) { - term_stack.push( - Term::Literal(Cell::default(), Literal::CodeIndex(idx)) - ); - - arity += 1; - } - } + if let Some(idx) = get_structure_index(value) { + term_stack.push(Term::Literal(Cell::default(), Literal::CodeIndexOffset(idx.into()))); + arity += 1; } if arity == 0 { @@ -1435,28 +1432,22 @@ impl MachineState { term_stack.push(Term::Clause(Cell::default(), name, subterms)); } } - (HeapCellValueTag::PStr, atom) => { - let tail = term_stack.pop().unwrap(); - - if let Term::Literal(_, Literal::Atom(atom!("[]"))) = &tail { - term_stack.push(Term::CompleteString(Cell::default(), atom)); - } else { - term_stack.push(Term::PartialString( - Cell::default(), - atom.as_str().to_owned(), - Box::new(tail), - )); - } - } (HeapCellValueTag::PStrLoc, h) => { - let atom = cell_as_atom_cell!(iter.heap[h]).get_name(); + let HeapStringScan { string, .. } = iter.heap.scan_slice_to_str(h); let tail = term_stack.pop().unwrap(); - term_stack.push(Term::PartialString( - Cell::default(), - atom.as_str().to_owned(), - Box::new(tail), - )); + term_stack.push(if matches!(tail, Term::Literal(_, Literal::Atom(atom!("[]")))) { + Term::CompleteString( + Cell::default(), + Rc::new(string.to_owned()), + ) + } else { + Term::PartialString( + Cell::default(), + Rc::new(string.to_owned()), + Box::new(tail), + ) + }); } _ => { } @@ -1605,7 +1596,7 @@ impl Machine { let predicate_name = cell_as_atom!(self.deref_register(2)); let arity = self.deref_register(3); - let arity = match Number::try_from(arity) { + let arity = match Number::try_from((arity, &self.machine_st.arena.f64_tbl)) { Ok(Number::Integer(n)) if *n >= Integer::ZERO && *n <= Integer::from(MAX_ARITY) => { let value: usize = (&*n).try_into().unwrap(); Ok(value) @@ -1981,7 +1972,6 @@ impl Machine { }; let stub_gen = || functor_stub(key.0, key.1); - let head = self.deref_register(2); if head.is_var() { @@ -2017,7 +2007,14 @@ impl Machine { .wam_prelude .indices .get_predicate_code_index(name, arity, module_name) - .map(|code_idx| code_idx.get_tag()) + .map(|offset| { + loader + .payload + .machine_st + .arena + .code_index_tbl + .with_entry(offset.into(), |idx| idx.tag()) + }) .unwrap_or(IndexPtrTag::DynamicUndefined); if idx_tag == IndexPtrTag::Index { @@ -2123,14 +2120,16 @@ impl Machine { .indices .remove_predicate_skeleton(&compilation_target, &key) .map(|skeleton| { - let mut clause_clause_skeleton = loader - .wam_prelude - .indices - .remove_predicate_skeleton( + let mut clause_clause_skeleton = + match loader.wam_prelude.indices.remove_predicate_skeleton( &clause_clause_compilation_target, &(atom!("$clause"), 2), - ) - .unwrap(); + ) { + Some(skeleton) => skeleton, + None => { + return vec![]; + } + }; let result = skeleton .core @@ -2160,9 +2159,16 @@ impl Machine { .indices .remove_predicate_skeleton(&compilation_target, &key); - let mut code_index = loader.get_or_insert_code_index(key, compilation_target); + let offset = loader.get_or_insert_code_index(key, compilation_target); - code_index.set(IndexPtr::undefined()); + loader + .payload + .machine_st + .arena + .code_index_tbl + .with_entry_mut(offset.into(), |code_idx| { + *code_idx = IndexPtr::undefined(); + }); loader.payload.compilation_target = clause_clause_compilation_target; @@ -2192,7 +2198,7 @@ impl Machine { .machine_st .store(self.machine_st.deref(self.machine_st[temp_v!(3)])); - let target_pos = match Number::try_from(target_pos) { + let target_pos = match Number::try_from((target_pos, &self.machine_st.arena.f64_tbl)) { Ok(Number::Integer(n)) => { let value: usize = (&*n).try_into().unwrap(); value @@ -2342,29 +2348,38 @@ impl Machine { .get_meta_predicate_spec(predicate_name, arity, &compilation_target) { Some(meta_specs) => { - let term_loc = self.machine_st.heap.len(); + let term_loc = self.machine_st.heap.cell_len(); - self.machine_st - .heap - .push(atom_as_cell!(predicate_name, arity)); - self.machine_st - .heap - .extend(meta_specs.iter().map(|meta_spec| match meta_spec { - MetaSpec::Minus => atom_as_cell!(atom!("+")), - MetaSpec::Plus => atom_as_cell!(atom!("-")), - MetaSpec::Either => atom_as_cell!(atom!("?")), - MetaSpec::Colon => atom_as_cell!(atom!(":")), - MetaSpec::RequiresExpansionWithArgument(ref arg_num) => { - fixnum_as_cell!(Fixnum::build_with(*arg_num as i64)) - } - })); + let mut writer = match self.machine_st.heap.reserve(3 + meta_specs.len()) { + Ok(writer) => writer, + Err(err_loc) => { + self.machine_st.throw_resource_error(err_loc); + return; + } + }; - let heap_loc = self.machine_st.heap.len(); + writer.write_with(|section| { + section.push_cell(atom_as_cell!(predicate_name, arity)); - self.machine_st - .heap - .push(atom_as_cell!(atom!("meta_predicate"), 1)); - self.machine_st.heap.push(str_loc_as_cell!(term_loc)); + for meta_spec in meta_specs.iter() { + section.push_cell(match meta_spec { + MetaSpec::Minus => atom_as_cell!(atom!("+")), + MetaSpec::Plus => atom_as_cell!(atom!("-")), + MetaSpec::Either => atom_as_cell!(atom!("?")), + MetaSpec::Colon => atom_as_cell!(atom!(":")), + MetaSpec::RequiresExpansionWithArgument(ref arg_num) => { + fixnum_as_cell!(/* FIXME this is not safe */ unsafe { + Fixnum::build_with_unchecked(*arg_num as i64) + }) + } + }); + } + + section.push_cell(atom_as_cell!(atom!("meta_predicate"), 1)); + section.push_cell(str_loc_as_cell!(term_loc)); + }); + + let heap_loc = self.machine_st.heap.cell_len() - 2; unify!( self.machine_st, diff --git a/src/machine/machine_errors.rs b/src/machine/machine_errors.rs index 82056f3a..c6836709 100644 --- a/src/machine/machine_errors.rs +++ b/src/machine/machine_errors.rs @@ -5,6 +5,7 @@ use crate::parser::ast::*; #[cfg(feature = "ffi")] use crate::ffi::FFIError; use crate::forms::*; +use crate::functor_macro::*; use crate::machine::heap::*; use crate::machine::loader::CompilationTarget; use crate::machine::machine_state::*; @@ -12,20 +13,13 @@ use crate::machine::streams::*; use crate::machine::system_calls::BrentAlgState; use crate::types::*; -pub type MachineStub = Vec; +pub type MachineStub = Vec; pub type MachineStubGen = Box MachineStub>; -#[derive(Debug, Clone, Copy)] -enum ErrorProvenance { - Constructed, // if constructed, offset the addresses. - Received, // otherwise, preserve the addresses. -} - #[derive(Debug)] pub(crate) struct MachineError { stub: MachineStub, location: Option<(usize, usize)>, // line_num, col_num - from: ErrorProvenance, } // from 7.12.2 b) of 13211-1:1995 @@ -50,6 +44,7 @@ pub(crate) enum ValidType { // PredicateIndicator, // Variable TcpListener, + Process, } impl ValidType { @@ -73,6 +68,7 @@ impl ValidType { // ValidType::PredicateIndicator => atom!("predicate_indicator"), // ValidType::Variable => atom!("variable") ValidType::TcpListener => atom!("tcp_listener"), + ValidType::Process => atom!("process"), } } } @@ -91,45 +87,26 @@ impl TypeError for HeapCellValue { fn type_error(self, _machine_st: &mut MachineState, valid_type: ValidType) -> MachineError { let stub = functor!( atom!("type_error"), - [atom(valid_type.as_atom()), cell(self)] + [atom_as_cell((valid_type.as_atom())), cell(self)] ); MachineError { stub, location: None, - from: ErrorProvenance::Received, } } } impl TypeError for MachineStub { - fn type_error(self, machine_st: &mut MachineState, valid_type: ValidType) -> MachineError { + fn type_error(self, _machine_st: &mut MachineState, valid_type: ValidType) -> MachineError { let stub = functor!( atom!("type_error"), - [atom(valid_type.as_atom()), str(machine_st.heap.len(), 0)], - [self] + [atom_as_cell((valid_type.as_atom())), functor(self)] ); MachineError { stub, location: None, - from: ErrorProvenance::Constructed, - } - } -} - -impl TypeError for FunctorStub { - fn type_error(self, machine_st: &mut MachineState, valid_type: ValidType) -> MachineError { - let stub = functor!( - atom!("type_error"), - [atom(valid_type.as_atom()), str(machine_st.heap.len(), 0)], - [self] - ); - - MachineError { - stub, - location: None, - from: ErrorProvenance::Constructed, } } } @@ -139,15 +116,14 @@ impl TypeError for Number { let stub = functor!( atom!("type_error"), [ - atom(valid_type.as_atom()), - number(&mut machine_st.arena, self) + atom_as_cell((valid_type.as_atom())), + number(self, (&mut machine_st.arena)) ] ); MachineError { stub, location: None, - from: ErrorProvenance::Received, } } } @@ -171,16 +147,15 @@ impl PermissionError for Atom { let stub = functor!( atom!("permission_error"), [ - atom(perm.as_atom()), - atom(index_atom), - cell(atom_as_cell!(self)) + atom_as_cell((perm.as_atom())), + atom_as_cell(index_atom), + atom_as_cell(self) ] ); MachineError { stub, location: None, - from: ErrorProvenance::Received, } } } @@ -214,13 +189,16 @@ impl PermissionError for HeapCellValue { let stub = functor!( atom!("permission_error"), - [atom(perm.as_atom()), atom(index_atom), cell(cell)] + [ + atom_as_cell((perm.as_atom())), + atom_as_cell(index_atom), + cell(cell) + ] ); MachineError { stub, location: None, - from: ErrorProvenance::Received, } } } @@ -228,24 +206,22 @@ impl PermissionError for HeapCellValue { impl PermissionError for MachineStub { fn permission_error( self, - machine_st: &mut MachineState, + _machine_st: &mut MachineState, index_atom: Atom, perm: Permission, ) -> MachineError { let stub = functor!( atom!("permission_error"), [ - atom(perm.as_atom()), - atom(index_atom), - str(machine_st.heap.len(), 0) - ], - [self] + atom_as_cell((perm.as_atom())), + atom_as_cell(index_atom), + functor(self) + ] ); MachineError { stub, location: None, - from: ErrorProvenance::Constructed, } } } @@ -256,32 +232,14 @@ pub(super) trait DomainError { impl DomainError for HeapCellValue { fn domain_error(self, _machine_st: &mut MachineState, error: DomainErrorType) -> MachineError { - let stub = functor!(atom!("domain_error"), [atom(error.as_atom()), cell(self)]); - - MachineError { - stub, - location: None, - from: ErrorProvenance::Received, - } - } -} - -impl DomainError for FunctorStub { - fn domain_error( - self, - machine_st: &mut MachineState, - valid_type: DomainErrorType, - ) -> MachineError { let stub = functor!( atom!("domain_error"), - [atom(valid_type.as_atom()), str(machine_st.heap.len(), 0)], - [self] + [atom_as_cell((error.as_atom())), cell(self)] ); MachineError { stub, location: None, - from: ErrorProvenance::Constructed, } } } @@ -290,26 +248,36 @@ impl DomainError for Number { fn domain_error(self, machine_st: &mut MachineState, error: DomainErrorType) -> MachineError { let stub = functor!( atom!("domain_error"), - [atom(error.as_atom()), number(&mut machine_st.arena, self)] + [ + atom_as_cell((error.as_atom())), + number(self, (&mut machine_st.arena)) + ] ); MachineError { stub, location: None, - from: ErrorProvenance::Received, } } } -pub(super) type FunctorStub = [HeapCellValue; 3]; +impl DomainError for MachineStub { + fn domain_error(self, _machine_st: &mut MachineState, error: DomainErrorType) -> MachineError { + let stub = functor!( + atom!("domain_error"), + [atom_as_cell((error.as_atom())), functor(self)] + ); + + MachineError { + stub, + location: None, + } + } +} #[inline(always)] -pub(super) fn functor_stub(name: Atom, arity: usize) -> FunctorStub { - [ - atom_as_cell!(atom!("/"), 2), - atom_as_cell!(name), - fixnum_as_cell!(Fixnum::build_with(arity as i64)), - ] +pub(super) fn functor_stub(name: Atom, arity: usize) -> MachineStub { + functor!(atom!("/"), [atom_as_cell(name), fixnum(arity)]) } impl MachineState { @@ -320,37 +288,40 @@ impl MachineState { MachineError { stub, location: None, - from: ErrorProvenance::Received, } } pub(super) fn evaluation_error(&mut self, eval_error: EvalError) -> MachineError { - let stub = functor!(atom!("evaluation_error"), [atom(eval_error.as_atom())]); + let stub = functor!( + atom!("evaluation_error"), + [atom_as_cell((eval_error.as_atom()))] + ); MachineError { stub, location: None, - from: ErrorProvenance::Received, } } - pub(super) fn resource_error(&mut self, err: ResourceError) -> MachineError { + pub(super) fn resource_error(err: ResourceError) -> MachineError { let stub = match err { ResourceError::FiniteMemory(size_requested) => { functor!( atom!("resource_error"), - [atom(atom!("finite_memory")), cell(size_requested)] + [atom_as_cell((atom!("finite_memory"))), cell(size_requested)] ) } ResourceError::OutOfFiles => { - functor!(atom!("resource_error"), [atom(atom!("file_descriptors"))]) + functor!( + atom!("resource_error"), + [atom_as_cell((atom!("file_descriptors")))] + ) } }; MachineError { stub, location: None, - from: ErrorProvenance::Received, } } @@ -367,13 +338,12 @@ impl MachineState { ExistenceError::Module(name) => { let stub = functor!( atom!("existence_error"), - [atom(atom!("source_sink")), atom(name)] + [atom_as_cell((atom!("source_sink"))), atom_as_cell(name)] ); MachineError { stub, location: None, - from: ErrorProvenance::Received, } } ExistenceError::QualifiedProcedure { @@ -381,36 +351,30 @@ impl MachineState { name, arity, } => { - let h = self.heap.len(); - - let ind_stub = functor!(atom!("/"), [atom(name), fixnum(arity)]); - let res_stub = functor!(atom!(":"), [atom(module_name), str(h + 3, 0)], [ind_stub]); + let ind_stub = functor!(atom!("/"), [atom_as_cell(name), fixnum(arity)]); + let res_stub = functor!(atom!(":"), [atom_as_cell(module_name), functor(ind_stub)]); let stub = functor!( atom!("existence_error"), - [atom(atom!("procedure")), str(h, 0)], - [res_stub] + [atom_as_cell((atom!("procedure"))), functor(res_stub)] ); MachineError { stub, location: None, - from: ErrorProvenance::Constructed, } } ExistenceError::Procedure(name, arity) => { - let culprit = functor!(atom!("/"), [atom(name), fixnum(arity)]); + let culprit = functor!(atom!("/"), [atom_as_cell(name), fixnum(arity)]); let stub = functor!( atom!("existence_error"), - [atom(atom!("procedure")), str(self.heap.len(), 0)], - [culprit] + [atom_as_cell((atom!("procedure"))), functor(culprit)] ); MachineError { stub, location: None, - from: ErrorProvenance::Constructed, } } ExistenceError::ModuleSource(source) => { @@ -418,40 +382,83 @@ impl MachineState { let stub = functor!( atom!("existence_error"), - [atom(atom!("source_sink")), str(self.heap.len(), 0)], - [source_stub] + [atom_as_cell((atom!("source_sink"))), functor(source_stub)] ); MachineError { stub, location: None, - from: ErrorProvenance::Constructed, } } ExistenceError::SourceSink(culprit) => { let stub = functor!( atom!("existence_error"), - [atom(atom!("source_sink")), cell(culprit)] + [atom_as_cell((atom!("source_sink"))), cell(culprit)] ); MachineError { stub, location: None, - from: ErrorProvenance::Received, } } ExistenceError::Stream(culprit) => { let stub = functor!( atom!("existence_error"), - [atom(atom!("stream")), cell(culprit)] + [atom_as_cell((atom!("stream"))), cell(culprit)] ); MachineError { stub, location: None, - from: ErrorProvenance::Received, } } + ExistenceError::Process(culprit) => { + let stub = functor!( + atom!("existence_error"), + [atom_as_cell((atom!("process"))), cell(culprit)] + ); + + MachineError { + stub, + location: None, + } + } + } + } + + pub(crate) fn directive_error(&mut self, err: DirectiveError) -> MachineError { + match err { + DirectiveError::ExpectedDirective(_term) => self.domain_error( + DomainErrorType::Directive, + atom_as_cell!(atom!("todo_insert_invalid_term_here")), + ), + DirectiveError::InvalidDirective(name, arity) => { + self.domain_error(DomainErrorType::Directive, functor_stub(name, arity)) + } + DirectiveError::InvalidOpDeclNameType(_term) => self.type_error( + ValidType::List, + atom_as_cell!(atom!("todo_insert_invalid_term_here")), + ), + DirectiveError::InvalidOpDeclSpecDomain(_term) => self.domain_error( + DomainErrorType::OperatorSpecifier, + atom_as_cell!(atom!("todo_insert_invalid_term_here")), + ), + DirectiveError::InvalidOpDeclSpecValue(atom) => { + self.domain_error(DomainErrorType::OperatorSpecifier, atom_as_cell!(atom)) + } + DirectiveError::InvalidOpDeclPrecType(_term) => self.type_error( + ValidType::Integer, + atom_as_cell!(atom!("todo_insert_invalid_term_here")), + ), + DirectiveError::InvalidOpDeclPrecDomain(num) => { + self.domain_error(DomainErrorType::OperatorPriority, fixnum_as_cell!(num)) + } + DirectiveError::ShallNotCreate(atom) => { + self.permission_error(Permission::Create, atom!("operator"), atom) + } + DirectiveError::ShallNotModify(atom) => { + self.permission_error(Permission::Modify, atom!("operator"), atom) + } } } @@ -471,12 +478,11 @@ impl MachineState { fn arithmetic_error(&mut self, err: ArithmeticError) -> MachineError { match err { - ArithmeticError::UninstantiatedVar => self.instantiation_error(), - ArithmeticError::NonEvaluableFunctor(literal, arity) => { - let culprit = functor!(atom!("/"), [literal(literal), fixnum(arity)]); - + ArithmeticError::NonEvaluableFunctor(cell, arity) => { + let culprit = functor!(atom!("/"), [literal(cell), fixnum(arity)]); self.type_error(ValidType::Evaluable, culprit) } + ArithmeticError::UninstantiatedVar => self.instantiation_error(), } } @@ -495,7 +501,6 @@ impl MachineState { MachineError { stub, location: None, - from: ErrorProvenance::Received, } } @@ -504,16 +509,12 @@ impl MachineState { SessionError::CannotOverwriteBuiltIn(key) => self.permission_error( Permission::Modify, atom!("static_procedure"), - functor_stub(key.0, key.1) - .into_iter() - .collect::(), + functor_stub(key.0, key.1), ), SessionError::CannotOverwriteStaticProcedure(key) => self.permission_error( Permission::Modify, atom!("static_procedure"), - functor_stub(key.0, key.1) - .into_iter() - .collect::(), + functor_stub(key.0, key.1), ), SessionError::CannotOverwriteBuiltInModule(module) => { self.permission_error(Permission::Modify, atom!("static_module"), module) @@ -524,8 +525,7 @@ impl MachineState { let stub = functor!( atom!("module_does_not_contain_claimed_export"), - [atom(module_name), str(self.heap.len() + 4, 0)], - [functor_stub] + [atom_as_cell(module_name), functor(functor_stub)] ); self.permission_error(Permission::Access, atom!("private_procedure"), stub) @@ -536,7 +536,7 @@ impl MachineState { self.permission_error( Permission::Modify, atom!("module"), - functor!(error_atom, [atom(module_name)]), + functor!(error_atom, [atom_as_cell(module_name)]), ) } SessionError::NamelessEntry => { @@ -555,15 +555,12 @@ impl MachineState { } SessionError::CompilationError(err) => self.syntax_error(err), SessionError::PredicateNotMultifileOrDiscontiguous(compilation_target, key) => { - let functor_stub = functor_stub(key.0, key.1); - let stub = functor!( atom!(":"), [ - atom(compilation_target.module_name()), - str(self.heap.len() + 4, 0) - ], - [functor_stub] + atom_as_cell((compilation_target.module_name())), + functor((key.0), [fixnum((key.1))]) + ] ); self.permission_error( @@ -587,30 +584,36 @@ impl MachineState { } let location = err.line_and_col_num(); - let len = self.heap.len(); let stub = err.as_functor(); - let stub = functor!(atom!("syntax_error"), [str(len, 0)], [stub]); + let stub = functor!(atom!("syntax_error"), [functor(stub)]); - MachineError { - stub, - location, - from: ErrorProvenance::Constructed, - } + MachineError { stub, location } } - pub(super) fn representation_error(&mut self, flag: RepFlag) -> MachineError { - let stub = functor!(atom!("representation_error"), [atom(flag.as_atom())]); + pub(super) fn representation_error(&self, flag: RepFlag) -> MachineError { + let stub = functor!( + atom!("representation_error"), + [atom_as_cell((flag.as_atom()))] + ); + + MachineError { + stub, + location: None, + } + } + + pub(super) fn unreachable_error(&self) -> MachineError { + let stub = functor!(atom!("system_error")); MachineError { stub, location: None, - from: ErrorProvenance::Received, } } #[cfg(feature = "ffi")] - pub(super) fn ffi_error(&mut self, err: FFIError) -> MachineError { + pub(super) fn ffi_error(&self, err: FFIError) -> MachineError { let error_atom = match err { FFIError::ValueCast => atom!("value_cast"), FFIError::ValueDontFit => atom!("value_dont_fit"), @@ -619,62 +622,50 @@ impl MachineState { FFIError::FunctionNotFound => atom!("function_not_found"), FFIError::StructNotFound => atom!("struct_not_found"), }; - let stub = functor!(atom!("ffi_error"), [atom(error_atom)]); + let stub = functor!(atom!("ffi_error"), [atom_as_cell(error_atom)]); MachineError { stub, location: None, - from: ErrorProvenance::Constructed, } } - pub(super) fn error_form(&mut self, err: MachineError, src: FunctorStub) -> MachineStub { - let h = self.heap.len(); - let location = err.location; - let stub_addition_len = if err.len() == 1 { - 0 // if err contains 1 cell, it can be inlined at stub[1]. + pub(super) fn error_form(&mut self, err: MachineError, src: MachineStub) -> MachineStub { + if let Some((line_num, _col_num)) = err.location { + functor!( + atom!("error"), + [ + functor((err.stub)), + functor( + (atom!(":")), + [functor(src), number(line_num, (&mut self.arena))] + ) + ] + ) } else { - err.len() - }; - - let mut stub = vec![ - atom_as_cell!(atom!("error"), 2), - str_loc_as_cell!(h + 3), - str_loc_as_cell!(h + 3 + stub_addition_len), - ]; - - if stub_addition_len > 0 { - stub.extend(err.into_iter(3)); - } else { - stub[1] = err.stub[0]; + functor!(atom!("error"), [functor((err.stub)), functor(src)]) } + } - if let Some((line_num, _)) = location { - stub.push(atom_as_cell!(atom!(":"), 2)); - stub.push(str_loc_as_cell!(h + 6 + stub_addition_len)); - stub.push(integer_as_cell!(Number::arena_from( - line_num, - &mut self.arena - ))); - } - - stub.extend(src.iter()); - stub + // throw an error pre-allocated in the heap + pub(super) fn throw_resource_error(&mut self, err_loc: usize) { + self.registers[1] = str_loc_as_cell!(err_loc); + self.set_ball(); + self.unwind_stack(); } pub(super) fn throw_exception(&mut self, err: MachineStub) { - let h = self.heap.len(); - let err_len = err.len(); - self.ball.boundary = 0; self.ball.stub.truncate(0); - self.heap.extend(err); + let mut writer = Heap::functor_writer(err); - self.registers[1] = if err_len == 1 { - heap_loc_as_cell!(h) - } else { - str_loc_as_cell!(h) + self.registers[1] = match writer(&mut self.heap) { + Ok(loc) => loc, + Err(resource_err_loc) => { + self.throw_resource_error(resource_err_loc); + return; + } }; self.set_ball(); @@ -682,21 +673,6 @@ impl MachineState { } } -impl MachineError { - fn into_iter(self, offset: usize) -> Box> { - match self.from { - ErrorProvenance::Constructed => { - Box::new(self.stub.into_iter().map(move |hcv| hcv + offset)) - } - ErrorProvenance::Received => Box::new(self.stub.into_iter()), - } - } - - fn len(&self) -> usize { - self.stub.len() - } -} - #[derive(Debug)] pub enum CompilationError { Arithmetic(ArithmeticError), @@ -711,6 +687,7 @@ pub enum CompilationError { InvalidRuleHead, InvalidUseModuleDecl, InvalidModuleResolution(Atom), + FiniteMemoryInHeap(usize), } #[derive(Debug)] @@ -757,11 +734,9 @@ impl CompilationError { functor!(atom!("exceeded_max_arity")) } CompilationError::InadmissibleFact => { - // TODO: type_error(callable, _). functor!(atom!("inadmissible_fact")) } CompilationError::InadmissibleQueryTerm => { - // TODO: type_error(callable, _). functor!(atom!("inadmissible_query_term")) } CompilationError::InvalidDirective(_) => { @@ -776,8 +751,8 @@ impl CompilationError { CompilationError::InvalidModuleExport => { functor!(atom!("invalid_module_export")) } - CompilationError::InvalidModuleResolution(ref module_name) => { - functor!(atom!("no_such_module"), [atom(module_name)]) + &CompilationError::InvalidModuleResolution(module_name) => { + functor!(atom!("no_such_module"), [atom_as_cell(module_name)]) } CompilationError::InvalidRuleHead => { functor!(atom!("invalid_head_of_rule")) // TODO: type_error(callable, _). @@ -788,6 +763,9 @@ impl CompilationError { CompilationError::ParserError(ref err) => { functor!(err.as_atom()) } + CompilationError::FiniteMemoryInHeap(h) => { + vec![FunctorElement::AbsoluteCell(str_loc_as_cell!(*h))] + } } } } @@ -896,14 +874,29 @@ impl EvalError { // used by '$skip_max_list'. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CycleSearchResult { - Cyclic(usize), + Cyclic { + lambda: usize, + }, // number of steps EmptyList, - NotList(usize, HeapCellValue), // the list length until the second argument in the heap - PartialList(usize, Ref), // the list length (up to max), and an offset into the heap. - ProperList(usize), // the list length. - PStrLocation(usize, usize, usize), // list length (up to max), the heap address of the PStr, the offset - UntouchedList(usize, usize), // list length (up to max), the address of an uniterated Addr::Lis(address). - UntouchedCStr(Atom, usize), + NotList { + num_steps: usize, + heap_loc: HeapCellValue, + }, + PartialList { + num_steps: usize, + heap_loc: HeapCellValue, + }, + ProperList { + num_steps: usize, + }, + PStrLocation { + num_steps: usize, + pstr_loc: HeapCellValue, + }, + UntouchedList { + num_steps: usize, + list_loc: usize, + }, } impl MachineState { @@ -915,11 +908,11 @@ impl MachineState { let sorted = self.store(self.deref(self.registers[2])); match BrentAlgState::detect_cycles(&self.heap, list) { - CycleSearchResult::PartialList(..) => { + CycleSearchResult::PartialList { .. } => { let err = self.instantiation_error(); return Err(self.error_form(err, stub_gen())); } - CycleSearchResult::NotList(..) | CycleSearchResult::Cyclic(_) => { + CycleSearchResult::NotList { .. } | CycleSearchResult::Cyclic { .. } => { let err = self.type_error(ValidType::List, list); return Err(self.error_form(err, stub_gen())); } @@ -927,7 +920,9 @@ impl MachineState { }; match BrentAlgState::detect_cycles(&self.heap, sorted) { - CycleSearchResult::NotList(..) | CycleSearchResult::Cyclic(_) if !sorted.is_var() => { + CycleSearchResult::NotList { .. } | CycleSearchResult::Cyclic { .. } + if !sorted.is_var() => + { let err = self.type_error(ValidType::List, sorted); Err(self.error_form(err, stub_gen())) } @@ -939,7 +934,9 @@ impl MachineState { let stub_gen = || functor_stub(atom!("keysort"), 2); match BrentAlgState::detect_cycles(&self.heap, list) { - CycleSearchResult::NotList(..) | CycleSearchResult::Cyclic(_) if !list.is_var() => { + CycleSearchResult::NotList { .. } | CycleSearchResult::Cyclic { .. } + if !list.is_var() => + { let err = self.type_error(ValidType::List, list); Err(self.error_form(err, stub_gen())) } @@ -1001,11 +998,11 @@ impl MachineState { let sorted = self.store(self.deref(self[temp_v!(2)])); match BrentAlgState::detect_cycles(&self.heap, pairs) { - CycleSearchResult::PartialList(..) => { + CycleSearchResult::PartialList { .. } => { let err = self.instantiation_error(); Err(self.error_form(err, stub_gen())) } - CycleSearchResult::NotList(..) | CycleSearchResult::Cyclic(_) => { + CycleSearchResult::NotList { .. } | CycleSearchResult::Cyclic { .. } => { let err = self.type_error(ValidType::List, pairs); Err(self.error_form(err, stub_gen())) } @@ -1028,6 +1025,7 @@ pub enum ExistenceError { }, SourceSink(HeapCellValue), Stream(HeapCellValue), + Process(HeapCellValue), } #[derive(Debug)] diff --git a/src/machine/machine_indices.rs b/src/machine/machine_indices.rs index e2d7b248..7f7bfe35 100644 --- a/src/machine/machine_indices.rs +++ b/src/machine/machine_indices.rs @@ -2,7 +2,6 @@ use crate::parser::ast::*; -use crate::arena::*; use crate::atom_table::*; use crate::forms::*; use crate::machine::loader::*; @@ -10,6 +9,7 @@ use crate::machine::machine_state::*; use crate::machine::streams::{Stream, StreamOptions}; use crate::machine::ClauseType; use crate::machine::MachineStubGen; +use crate::offset_table::*; use fxhash::FxBuildHasher; use indexmap::{IndexMap, IndexSet}; @@ -18,7 +18,6 @@ use scryer_modular_bitfield::{bitfield, BitfieldSpecifier}; use std::cmp::Ordering; use std::collections::BTreeSet; -use std::ops::{Deref, DerefMut}; use crate::types::*; @@ -126,10 +125,19 @@ impl IndexPtr { pub(crate) fn is_dynamic_undefined(&self) -> bool { matches!(self.tag(), IndexPtrTag::DynamicUndefined) } + + #[inline] + pub(crate) fn local(&self) -> Option { + match self.tag() { + IndexPtrTag::Index => Some(self.p() as usize), + IndexPtrTag::DynamicIndex => Some(self.p() as usize), + _ => None, + } + } } -#[derive(Debug, Clone, Copy, Ord, Hash, PartialOrd, Eq, PartialEq)] -pub struct CodeIndex(TypedArenaPtr); +#[derive(Debug, Clone, Copy)] // , Ord, Hash, PartialOrd, Eq, PartialEq)] +pub struct CodeIndex(CodeIndexOffset); #[cfg(target_pointer_width = "32")] const_assert!(std::mem::align_of::() == 4); @@ -137,78 +145,53 @@ const_assert!(std::mem::align_of::() == 4); #[cfg(target_pointer_width = "64")] const_assert!(std::mem::align_of::() == 8); -impl Deref for CodeIndex { - type Target = TypedArenaPtr; - +impl From for HeapCellValue { #[inline(always)] - fn deref(&self) -> &TypedArenaPtr { - &self.0 + fn from(idx: CodeIndex) -> HeapCellValue { + HeapCellValue::from(idx.0) } } -impl DerefMut for CodeIndex { +impl From for CodeIndex { #[inline(always)] - fn deref_mut(&mut self) -> &mut TypedArenaPtr { - &mut self.0 + fn from(offset: CodeIndexOffset) -> CodeIndex { + CodeIndex(offset) } } -impl From for UntypedArenaPtr { +impl From for CodeIndexOffset { #[inline(always)] - fn from(ptr: CodeIndex) -> UntypedArenaPtr { - UntypedArenaPtr::build_with(ptr.0.as_ptr() as usize) + fn from(value: CodeIndex) -> CodeIndexOffset { + value.0 } } -impl From> for CodeIndex { +impl From<&'_ CodeIndex> for CodeIndexOffset { #[inline(always)] - fn from(ptr: TypedArenaPtr) -> CodeIndex { - CodeIndex(ptr) + fn from(value: &'_ CodeIndex) -> CodeIndexOffset { + value.0 } } impl CodeIndex { #[inline] - pub(crate) fn new(ptr: IndexPtr, arena: &mut Arena) -> Self { - CodeIndex(arena_alloc!(ptr, arena)) + pub(crate) fn new(ptr: IndexPtr, code_index_tbl: &mut CodeIndexTable) -> Self { + CodeIndex(code_index_tbl.build_with(ptr)) } #[inline(always)] - pub(crate) fn default(arena: &mut Arena) -> Self { - CodeIndex::new(IndexPtr::undefined(), arena) - } - - pub(crate) fn local(&self) -> Option { - match self.0.tag() { - IndexPtrTag::Index => Some(self.0.p() as usize), - IndexPtrTag::DynamicIndex => Some(self.0.p() as usize), - _ => None, - } + pub(crate) fn default(code_index_tbl: &mut CodeIndexTable) -> Self { + CodeIndex::new(IndexPtr::undefined(), code_index_tbl) } #[inline(always)] - pub(crate) fn get(&self) -> IndexPtr { - *self.0.deref() + pub(crate) fn set(&self, code_index_tbl: &mut CodeIndexTable, value: IndexPtr) { + code_index_tbl.with_entry_mut(self.0, |idx| *idx = value); } #[inline(always)] - pub(crate) fn set(&mut self, value: IndexPtr) { - *self.0.deref_mut() = value; - } - - #[inline(always)] - pub(crate) fn get_tag(self) -> IndexPtrTag { - self.0.tag() - } - - #[inline(always)] - pub(crate) fn replace(&mut self, value: IndexPtr) -> IndexPtr { - std::mem::replace(self.0.deref_mut(), value) - } - - #[inline(always)] - pub(crate) fn as_ptr(&self) -> *const IndexPtr { - self.0.as_ptr() + pub(crate) fn replace(&self, code_index_tbl: &mut CodeIndexTable, value: IndexPtr) -> IndexPtr { + code_index_tbl.with_entry_mut(self.0, |idx| std::mem::replace(idx, value)) } } @@ -223,7 +206,7 @@ impl VarKey { #[inline] pub(crate) fn to_string(&self) -> String { match self { - VarKey::AnonVar(h) => format!("_{}", h), + VarKey::AnonVar(h) => format!("_{h}"), VarKey::VarPtr(var) => var.borrow().to_string(), } } @@ -543,7 +526,7 @@ impl IndexStore { &'a self, range: R, ) -> impl Iterator + 'a { - self.streams.range(range).into_iter().copied() + self.streams.range(range).copied() } /// Forcibly sets `alias` to `stream`. diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 5376042c..7663c952 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -1,6 +1,7 @@ use crate::arena::*; use crate::atom_table::*; use crate::forms::*; +use crate::functor_macro::*; use crate::heap_iter::*; use crate::heap_print::*; use crate::machine::attributed_variables::*; @@ -21,7 +22,7 @@ use indexmap::IndexMap; use std::convert::TryFrom; use std::fmt; -use std::ops::{Index, IndexMut}; +use std::ops::{Index, IndexMut, Range}; use std::sync::Arc; pub(crate) type Registers = [HeapCellValue; MAX_ARITY + 1]; @@ -35,8 +36,8 @@ pub(super) enum MachineMode { #[derive(Debug, Clone)] pub(super) enum HeapPtr { HeapCell(usize), - PStrChar(usize, usize), - PStrLocation(usize, usize), + PStr(usize), // Char(usize), + // PStrLocation(usize), } impl Default for HeapPtr { @@ -183,47 +184,62 @@ impl IndexMut for MachineState { } } -pub type CallResult = Result<(), Vec>; - -#[inline(always)] -pub fn pstr_loc_and_offset(heap: &[HeapCellValue], index: usize) -> (usize, Fixnum) { - read_heap_cell!(heap[index], - (HeapCellValueTag::PStr | HeapCellValueTag::CStr) => { - (index, Fixnum::build_with(0)) - } - (HeapCellValueTag::PStrOffset, h) => { - (h, cell_as_fixnum!(heap[index+1])) - } - _ => { - unreachable!() - } - ) -} +pub type CallResult = Result<(), Vec>; +// size may be an upper bound. +// true_size is calculated to compute the exact offset. fn push_var_eq_functors<'a>( heap: &mut Heap, + size: usize, iter: impl Iterator, atom_tbl: &AtomTable, -) -> Vec { - let mut list_of_var_eqs = vec![]; +) -> Result { + let src_h = heap.cell_len(); - for (var, binding) in iter { - let var_atom = AtomTable::build_with(atom_tbl, &var.to_string()); - let h = heap.len(); + let true_size = if size > 0 { + let mut writer = heap.reserve(2 + 5 * size)?; - heap.push(atom_as_cell!(atom!("="), 2)); - heap.push(atom_as_cell!(var_atom)); - heap.push(*binding); + writer + .write_with(|section| { + let mut size = 0; - list_of_var_eqs.push(str_loc_as_cell!(h)); - } + for (var, binding) in iter { + let var_atom = AtomTable::build_with(atom_tbl, &var.to_string()); - list_of_var_eqs + section.push_cell(atom_as_cell!(atom!("="), 2)); + section.push_cell(atom_as_cell!(var_atom)); + section.push_cell(*binding); + + size += 1; + } + + for idx in 0..size { + section.push_cell(list_loc_as_cell!(section.cell_len() + 1)); + section.push_cell(str_loc_as_cell!(src_h + 3 * idx)); + } + + if size > 0 { + section.push_cell(empty_list_as_cell!()); + } + + size + }) + .result + } else { + size + }; + + Ok(if true_size > 0 { + heap_loc_as_cell!(src_h + 3 * true_size) + } else { + empty_list_as_cell!() + }) } #[derive(Debug)] pub struct Ball { pub(super) boundary: usize, + pub(super) pstr_boundary: usize, pub(super) stub: Heap, } @@ -231,23 +247,44 @@ impl Ball { pub(super) fn new() -> Self { Ball { boundary: 0, + pstr_boundary: 0, stub: Heap::new(), } } pub(super) fn reset(&mut self) { self.boundary = 0; + self.pstr_boundary = 0; self.stub.clear(); } - pub(super) fn copy_and_align(&self, h: usize) -> Heap { + pub(super) fn copy_and_align_to(&self, dest: &mut Heap) -> Result { + let h = dest.cell_len(); let diff = self.boundary as i64 - h as i64; - self.stub - .iter() - .cloned() - .map(|heap_value| heap_value - diff) - .collect() + let mut dest_writer = dest.reserve(self.stub.cell_len())?; + + dest_writer.write_with(|section| { + for idx in 0..self.pstr_boundary { + section.push_cell(self.stub[idx] - diff); + } + }); + + let mut pstr_threshold = heap_index!(self.pstr_boundary); + + while pstr_threshold < heap_index!(self.stub.cell_len()) { + let HeapStringScan { string, tail_idx } = self.stub.scan_slice_to_str(pstr_threshold); + + pstr_threshold += dest_writer + .write_with(|section| { + if section.push_pstr(string).is_some() { + section.push_cell(self.stub[tail_idx] - diff); + } + }) + .bytes_written; + } + + Ok(h) } } @@ -279,21 +316,6 @@ impl<'a> IndexMut for CopyTerm<'a> { } impl<'a> CopierTarget for CopyTerm<'a> { - #[inline(always)] - fn threshold(&self) -> usize { - self.state.heap.len() - } - - #[inline(always)] - fn push(&mut self, hcv: HeapCellValue) { - self.state.heap.push(hcv); - } - - #[inline(always)] - fn push_attr_var_queue(&mut self, attr_var_loc: usize) { - self.state.attr_var_init.attr_var_queue.push(attr_var_loc); - } - #[inline(always)] fn store(&self, value: HeapCellValue) -> HeapCellValue { self.state.store(value) @@ -304,10 +326,40 @@ impl<'a> CopierTarget for CopyTerm<'a> { self.state.deref(value) } + #[inline(always)] + fn push_attr_var_queue(&mut self, attr_var_loc: usize) { + self.state.attr_var_init.attr_var_queue.push(attr_var_loc); + } + #[inline(always)] fn stack(&mut self) -> &mut Stack { &mut self.state.stack } + + #[inline(always)] + fn threshold(&self) -> usize { + self.state.heap.cell_len() + } + + #[inline(always)] + fn as_slice_from<'b>(&'b self, from: usize) -> Box + 'b> { + Box::new(self.state.heap.as_slice()[from..].iter().cloned()) + } + + #[inline(always)] + fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result { + self.state.heap.copy_pstr_within(pstr_loc) + } + + #[inline(always)] + fn reserve(&mut self, num_cells: usize) -> Result { + self.state.heap.reserve(num_cells) + } + + #[inline(always)] + fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), usize> { + self.state.heap.copy_slice_to_end(bounds) + } } #[derive(Debug)] @@ -315,7 +367,6 @@ pub(super) struct CopyBallTerm<'a> { attr_var_queue: &'a mut Vec, stack: &'a mut Stack, heap: &'a mut Heap, - heap_boundary: usize, stub: &'a mut Heap, } @@ -326,13 +377,10 @@ impl<'a> CopyBallTerm<'a> { heap: &'a mut Heap, stub: &'a mut Heap, ) -> Self { - let hb = heap.len(); - CopyBallTerm { attr_var_queue, stack, heap, - heap_boundary: hb, stub, } } @@ -342,10 +390,10 @@ impl<'a> Index for CopyBallTerm<'a> { type Output = HeapCellValue; fn index(&self, index: usize) -> &Self::Output { - if index < self.heap_boundary { + if index < self.heap.cell_len() { &self.heap[index] } else { - let index = index - self.heap_boundary; + let index = index - self.heap.cell_len(); &self.stub[index] } } @@ -353,10 +401,10 @@ impl<'a> Index for CopyBallTerm<'a> { impl<'a> IndexMut for CopyBallTerm<'a> { fn index_mut(&mut self, index: usize) -> &mut Self::Output { - if index < self.heap_boundary { + if index < self.heap.cell_len() { &mut self.heap[index] } else { - let index = index - self.heap_boundary; + let index = index - self.heap.cell_len(); &mut self.stub[index] } } @@ -364,11 +412,7 @@ impl<'a> IndexMut for CopyBallTerm<'a> { impl<'a> CopierTarget for CopyBallTerm<'a> { fn threshold(&self) -> usize { - self.heap_boundary + self.stub.len() - } - - fn push(&mut self, value: HeapCellValue) { - self.stub.push(value); + self.heap.cell_len() + self.stub.cell_len() } #[inline(always)] @@ -379,10 +423,10 @@ impl<'a> CopierTarget for CopyBallTerm<'a> { fn store(&self, value: HeapCellValue) -> HeapCellValue { read_heap_cell!(value, (HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => { - if h < self.heap_boundary { + if h < self.heap.cell_len() { self.heap[h] } else { - let index = h - self.heap_boundary; + let index = h - self.heap.cell_len(); self.stub[index] } } @@ -411,6 +455,45 @@ impl<'a> CopierTarget for CopyBallTerm<'a> { fn stack(&mut self) -> &mut Stack { self.stack } + + fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result { + debug_assert!(pstr_loc < self.heap.byte_len()); + + let HeapStringScan { string, tail_idx } = self.heap.scan_slice_to_str(pstr_loc); + self.stub.allocate_pstr(string)?; + Ok(tail_idx) + } + + fn as_slice_from<'b>(&'b self, from: usize) -> Box + 'b> { + if from < self.heap.byte_len() { + Box::new( + self.heap.as_slice()[from..] + .iter() + .cloned() + .chain(self.stub.as_slice().iter().cloned()), + ) + } else { + Box::new(self.stub.as_slice()[from..].iter().cloned()) + } + } + + #[inline] + fn reserve(&mut self, num_cells: usize) -> Result { + self.stub.reserve(num_cells) + } + + fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), usize> { + let len = bounds.end - bounds.start; + let mut stub_writer = self.stub.reserve(len)?; + + stub_writer.write_with(|section| { + for idx in bounds { + section.push_cell(self.heap[idx]); + } + }); + + Ok(()) + } } impl MachineState { @@ -461,10 +544,6 @@ impl MachineState { let addr = self.store(self.deref(addr)); read_heap_cell!(addr, - (HeapCellValueTag::Char, c) => { - chars.push(c); - continue; - } (HeapCellValueTag::Atom, (name, arity)) => { if arity == 0 { if let Some(c) = name.as_char() { @@ -537,39 +616,26 @@ impl MachineState { pub fn write_read_term_options( &mut self, mut var_list: Vec<(VarKey, HeapCellValue, usize)>, - singleton_var_list: Vec, + singleton_heap_list: HeapCellValue, ) -> CallResult { var_list.sort_by(|(_, _, idx_1), (_, _, idx_2)| idx_1.cmp(idx_2)); - let list_of_var_eqs = push_var_eq_functors( - &mut self.heap, - var_list.iter().filter_map(|(var_name, var, _)| { - if var_name.is_anon() { - None - } else { - Some((var_name, var)) - } - }), - &self.atom_tbl, - ); - let singleton_addr = self.registers[3]; - let singletons_offset = heap_loc_as_cell!(iter_to_heap_list( - &mut self.heap, - singleton_var_list.into_iter() - )); - - unify_fn!(*self, singletons_offset, singleton_addr); + unify_fn!(*self, singleton_heap_list, singleton_addr); if self.fail { return Ok(()); } let vars_addr = self.registers[4]; - let vars_offset = heap_loc_as_cell!(iter_to_heap_list( - &mut self.heap, - var_list.into_iter().map(|(_, cell, _)| cell) - )); + let vars_offset = resource_error_call_result!( + self, + sized_iter_to_heap_list( + &mut self.heap, + var_list.len(), + var_list.iter().map(|(_, cell, _)| *cell), + ) + ); unify_fn!(*self, vars_offset, vars_addr); @@ -578,24 +644,27 @@ impl MachineState { } let var_names_addr = self.registers[5]; - let var_names_offset = heap_loc_as_cell!(iter_to_heap_list( - &mut self.heap, - list_of_var_eqs.into_iter() - )); - - Ok(unify_fn!(*self, var_names_offset, var_names_addr)) + let var_names_offset = resource_error_call_result!( + self, + push_var_eq_functors( + &mut self.heap, + var_list.len(), + var_list.iter().filter_map(|(var_name, var, _)| { + if var_name.is_anon() { + None + } else { + Some((var_name, var)) + } + }), + &self.atom_tbl, + ) + ); + unify_fn!(*self, var_names_offset, var_names_addr); + Ok(()) } pub fn read_term_body(&mut self, mut term_write_result: TermWriteResult) -> CallResult { - let heap_loc = read_heap_cell!(self.heap[term_write_result.heap_loc], - (HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset) => { - pstr_loc_as_cell!(term_write_result.heap_loc) - } - _ => { - heap_loc_as_cell!(term_write_result.heap_loc) - } - ); - + let heap_loc = heap_loc_as_cell!(term_write_result.heap_loc); unify_fn!(*self, heap_loc, self.registers[2]); if self.fail { @@ -607,10 +676,9 @@ impl MachineState { } let mut singleton_var_set: IndexMap = IndexMap::new(); + self.heap[0] = heap_loc; - for cell in - stackful_preorder_iter::(&mut self.heap, &mut self.stack, heap_loc) - { + for cell in stackful_preorder_iter::(&mut self.heap, &mut self.stack, 0) { let cell = unmark_cell_bits!(cell); if let Some(var) = cell.as_var() { @@ -622,29 +690,29 @@ impl MachineState { } } - let singleton_var_list = push_var_eq_functors( - &mut self.heap, - term_write_result - .var_dict - .iter() - .filter(|(var_name, binding)| { - if var_name.is_anon() { - return false; - } + let singleton_var_list = resource_error_call_result!( + self, + push_var_eq_functors( + &mut self.heap, + term_write_result.var_dict.len(), + term_write_result + .var_dict + .iter() + .filter(|(var_name, binding)| { + if var_name.is_anon() { + return false; + } - if let Some(r) = binding.as_var() { - *singleton_var_set.get(&r).unwrap_or(&false) - } else { - false - } - }), - &self.atom_tbl, + if let Some(r) = binding.as_var() { + *singleton_var_set.get(&r).unwrap_or(&false) + } else { + false + } + }), + &self.atom_tbl, + ) ); - for var in term_write_result.var_dict.values_mut() { - *var = heap_bound_deref(&self.heap, *var); - } - let mut var_list = Vec::with_capacity(singleton_var_set.len()); for (var_name, addr) in term_write_result.var_dict { @@ -741,7 +809,8 @@ impl MachineState { CompilationError::ParserError(e) if e.is_unexpected_eof() => { match eof_handler(self, stream)? { OnEOF::Return => { - return self.write_read_term_options(vec![], vec![]) + return self + .write_read_term_options(vec![], empty_list_as_cell!()); } OnEOF::Continue => continue, } @@ -790,19 +859,16 @@ impl MachineState { } read_heap_cell!(atom, - (HeapCellValueTag::Char, c) => { - var_names.insert(var, VarPtr::from(c.to_string())); - } (HeapCellValueTag::Atom, (name, _arity)) => { debug_assert_eq!(_arity, 0); - var_names.insert(var, VarPtr::from(&*name.as_str())); + var_names.insert(var, VarPtr::from(name.as_str().to_owned())); } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.heap[s]) .get_name_and_arity(); debug_assert_eq!(arity, 0); - var_names.insert(var, VarPtr::from(&*name.as_str())); + var_names.insert(var, VarPtr::from(name.as_str().to_owned())); } _ => { unreachable!(); @@ -881,13 +947,15 @@ impl MachineState { } ); + self.heap[0] = term_to_be_printed; + let mut printer = HCPrinter::new( &mut self.heap, - Arc::clone(&self.atom_tbl), &mut self.stack, + &self.arena, op_dir, PrinterOutputter::new(), - term_to_be_printed, + 0, ); printer.ignore_ops = ignore_ops; @@ -895,7 +963,7 @@ impl MachineState { printer.quoted = quoted; printer.double_quotes = double_quotes; - match Number::try_from(max_depth) { + match Number::try_from((max_depth, &self.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => { if let Ok(n) = usize::try_from(n.get_num()) { printer.max_depth = n; @@ -920,7 +988,6 @@ impl MachineState { } printer.var_names = var_names; - printer } Err(err) => { @@ -937,7 +1004,10 @@ impl MachineState { arity: HeapCellValue, ) -> (Atom, usize) { let name = cell_as_atom!(self.store(self.deref(name))); - let arity = cell_as_fixnum!(self.store(self.deref(arity))); + let arity = unsafe { + self.store(self.deref(arity)) + .to_fixnum_or_cut_point_unchecked() + }; (name, usize::try_from(arity.get_num()).unwrap()) } @@ -978,42 +1048,6 @@ impl MachineState { } ); } - - pub(crate) fn directive_error(&mut self, err: DirectiveError) -> MachineError { - match err { - DirectiveError::ExpectedDirective(_term) => self.domain_error( - DomainErrorType::Directive, - atom_as_cell!(atom!("todo_insert_invalid_term_here")), - ), - DirectiveError::InvalidDirective(name, arity) => { - self.domain_error(DomainErrorType::Directive, functor_stub(name, arity)) - } - DirectiveError::InvalidOpDeclNameType(_term) => self.type_error( - ValidType::List, - atom_as_cell!(atom!("todo_insert_invalid_term_here")), - ), - DirectiveError::InvalidOpDeclSpecDomain(_term) => self.domain_error( - DomainErrorType::OperatorSpecifier, - atom_as_cell!(atom!("todo_insert_invalid_term_here")), - ), - DirectiveError::InvalidOpDeclSpecValue(atom) => { - self.domain_error(DomainErrorType::OperatorSpecifier, atom_as_cell!(atom)) - } - DirectiveError::InvalidOpDeclPrecType(_term) => self.type_error( - ValidType::Integer, - atom_as_cell!(atom!("todo_insert_invalid_term_here")), - ), - DirectiveError::InvalidOpDeclPrecDomain(num) => { - self.domain_error(DomainErrorType::OperatorPriority, fixnum_as_cell!(num)) - } - DirectiveError::ShallNotCreate(atom) => { - self.permission_error(Permission::Create, atom!("operator"), atom) - } - DirectiveError::ShallNotModify(atom) => { - self.permission_error(Permission::Modify, atom!("operator"), atom) - } - } - } } #[allow(clippy::upper_case_acronyms)] diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index e93356fd..3f14253d 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -11,6 +11,7 @@ use crate::machine::machine_state::*; use crate::machine::partial_string::*; use crate::machine::stack::*; use crate::machine::unify::*; +use crate::offset_table::*; use crate::parser::ast::*; use crate::parser::dashu::{Integer, Rational}; use crate::types::*; @@ -22,6 +23,12 @@ use std::convert::TryFrom; impl MachineState { pub(crate) fn new() -> Self { + let mut heap = Heap::with_cell_capacity(256 * 256).unwrap(); + + // the cell at index 0 is an interstitial cell reserved for use by the runtime. + heap.push_cell(empty_list_as_cell!()).unwrap(); + heap.store_resource_error(); + MachineState { arena: Arena::new(), atom_tbl: AtomTable::new(), @@ -38,7 +45,7 @@ impl MachineState { cp: 0, attr_var_init: AttrVarInitializer::new(0), fail: false, - heap: Heap::with_capacity(256 * 256), + heap, mode: MachineMode::Write, stack: Stack::new(), registers: [heap_loc_as_cell!(0); MAX_ARITY + 1], // self.registers[0] is never used. @@ -261,11 +268,6 @@ impl MachineState { unifier.unify_atom(atom, value); } - pub fn unify_complete_string(&mut self, atom: Atom, value: HeapCellValue) { - let mut unifier = DefaultUnifier::from(self); - unifier.unify_complete_string(atom, value); - } - pub fn unify_char(&mut self, c: char, value: HeapCellValue) { let mut unifier = DefaultUnifier::from(self); unifier.unify_char(c, value); @@ -286,7 +288,7 @@ impl MachineState { unifier.unify_big_rational(n1, value); } - pub fn unify_f64(&mut self, f1: F64Ptr, value: HeapCellValue) { + pub fn unify_f64(&mut self, f1: F64Offset, value: HeapCellValue) { let mut unifier = DefaultUnifier::from(self); unifier.unify_f64(f1, value); } @@ -316,17 +318,20 @@ impl MachineState { self.ball.reset(); let addr = self.registers[1]; - self.ball.boundary = self.heap.len(); - copy_term( - CopyBallTerm::new( - &mut self.attr_var_init.attr_var_queue, - &mut self.stack, - &mut self.heap, - &mut self.ball.stub, - ), - addr, - AttrVarPolicy::DeepCopy, + self.ball.boundary = self.heap.cell_len(); + self.ball.pstr_boundary = step_or_resource_error!( + self, + copy_term( + CopyBallTerm::new( + &mut self.attr_var_init.attr_var_queue, + &mut self.stack, + &mut self.heap, + &mut self.ball.stub, + ), + addr, + AttrVarPolicy::DeepCopy, + ) ); } @@ -336,84 +341,33 @@ impl MachineState { self.fail = true; } - pub(crate) fn read_s(&mut self) -> HeapCellValue { - match &mut self.s { - &mut HeapPtr::HeapCell(h) => self.deref(self.heap[h + self.s_offset]), - &mut HeapPtr::PStrChar(h, n) if self.s_offset == 0 => { - read_heap_cell!(self.heap[h], - (HeapCellValueTag::PStr, pstr_atom) => { - let pstr = PartialString::from(pstr_atom); + // return the read value and the succeeding HeapPtr + pub(crate) fn read_s(&mut self) -> (HeapCellValue, usize) { + match self.s { + HeapPtr::HeapCell(h) => (self.deref(self.heap[h + self.s_offset]), 1), + HeapPtr::PStr(byte_index) => { + let mut char_iter = self.heap.char_iter(byte_index); - if let Some(c) = pstr.as_str_from(n).chars().next() { - char_as_cell!(c) - } else { - self.deref(self.heap[h+1]) - } + if self.s_offset == 0 { + // read the car of the list + let c = char_iter.next().unwrap(); + (char_as_cell!(c), c.len_utf8()) + } else { + // read the (self.s_offset)^{th} cdr of the list + // self.s_offset is the number of bytes offset into the PStr + // in this context, *not* the number of heap cells. + let new_h = byte_index + self.s_offset; + self.s_offset = 0; + + if self.heap.char_iter(new_h).next().is_some() { + self.s = HeapPtr::PStr(new_h); + (pstr_loc_as_cell!(new_h), 0) + } else { + let h = Heap::pstr_tail_idx(new_h); + self.s = HeapPtr::HeapCell(h); + (self.deref(heap_loc_as_cell!(h)), 0) } - (HeapCellValueTag::CStr, cstr_atom) => { - let pstr = PartialString::from(cstr_atom); - - if let Some(c) = pstr.as_str_from(n).chars().next() { - char_as_cell!(c) - } else { - empty_list_as_cell!() - } - } - _ => { - unreachable!() - } - ) - } - &mut HeapPtr::PStrChar(h, ref mut n) | &mut HeapPtr::PStrLocation(h, ref mut n) => { - read_heap_cell!(self.heap[h], - (HeapCellValueTag::PStr, pstr_atom) => { - let pstr = PartialString::from(pstr_atom); - let n_offset: usize = pstr.as_str_from(*n) - .chars() - .take(self.s_offset) - .map(|c| c.len_utf8()) - .sum(); - - self.s_offset = 0; - *n += n_offset; - - if *n < pstr_atom.len() { - let h_len = self.heap.len(); - - self.heap.push(pstr_offset_as_cell!(h)); - self.heap.push(fixnum_as_cell!(Fixnum::build_with(*n as i64))); - - pstr_loc_as_cell!(h_len) - } else { - self.deref(self.heap[h+1]) - } - } - (HeapCellValueTag::CStr, cstr_atom) => { - let pstr = PartialString::from(cstr_atom); - let n_offset: usize = pstr.as_str_from(*n) - .chars() - .take(self.s_offset) - .map(|c| c.len_utf8()) - .sum(); - - self.s_offset = 0; - *n += n_offset; - - if *n < cstr_atom.len() { - let h_len = self.heap.len(); - - self.heap.push(pstr_offset_as_cell!(h)); - self.heap.push(fixnum_as_cell!(Fixnum::build_with(*n as i64))); - - pstr_loc_as_cell!(h_len) - } else { - empty_list_as_cell!() - } - } - _ => { - unreachable!() - } - ) + } } } } @@ -455,8 +409,11 @@ impl MachineState { } } Some(TermOrderCategory::FloatingPoint) => { - let v1 = cell_as_f64_ptr!(v1); - let v2 = cell_as_f64_ptr!(v2); + let v1 = cell_as_f64_offset!(v1); + let v2 = cell_as_f64_offset!(v2); + + let v1 = self.arena.f64_tbl.get_entry(v1); + let v2 = self.arena.f64_tbl.get_entry(v2); if v1 != v2 { self.pdl.clear(); @@ -464,8 +421,8 @@ impl MachineState { } } Some(TermOrderCategory::Integer) => { - let v1 = Number::try_from(v1).unwrap(); - let v2 = Number::try_from(v2).unwrap(); + let v1 = Number::try_from((v1, &self.arena.f64_tbl)).unwrap(); + let v2 = Number::try_from((v2, &self.arena.f64_tbl)).unwrap(); if v1 != v2 { self.pdl.clear(); @@ -482,20 +439,6 @@ impl MachineState { return Some(n1.cmp(&n2)); } } - (HeapCellValueTag::Char, c2) => { - if let Some(c1) = n1.as_char() { - if c1 != c2 { - self.pdl.clear(); - return Some(c1.cmp(&c2)); - } - } else { - self.pdl.clear(); - return Some( - n1.as_str().chars().next().cmp(&Some(c2)) - .then(Ordering::Greater) - ); - } - } (HeapCellValueTag::Str, s) => { let n2 = cell_as_atom_cell!(self.heap[s]) .get_name(); @@ -510,50 +453,6 @@ impl MachineState { } ) } - (HeapCellValueTag::Char, c1) => { - read_heap_cell!(v2, - (HeapCellValueTag::Atom, (n2, _a2)) => { - if let Some(c2) = n2.as_char() { - if c1 != c2 { - self.pdl.clear(); - return Some(c1.cmp(&c2)); - } - } else { - self.pdl.clear(); - return Some( - Some(c1).cmp(&n2.as_str().chars().next()) - .then(Ordering::Less) - ); - } - } - (HeapCellValueTag::Char, c2) => { - if c1 != c2 { - self.pdl.clear(); - return Some(c1.cmp(&c2)); - } - } - (HeapCellValueTag::Str, s) => { - let n2 = cell_as_atom_cell!(self.heap[s]) - .get_name(); - - if let Some(c2) = n2.as_char() { - if c1 != c2 { - self.pdl.clear(); - return Some(c1.cmp(&c2)); - } - } else { - self.pdl.clear(); - return Some( - Some(c1).cmp(&n2.as_str().chars().next()) - .then(Ordering::Less) - ); - } - } - _ => { - unreachable!() - } - ) - } (HeapCellValueTag::Str, s) => { let n1 = cell_as_atom_cell!(self.heap[s]) .get_name(); @@ -565,20 +464,6 @@ impl MachineState { return Some(n1.cmp(&n2)); } } - (HeapCellValueTag::Char, c2) => { - if let Some(c1) = n1.as_char() { - if c1 != c2 { - self.pdl.clear(); - return Some(c1.cmp(&c2)); - } - } else { - self.pdl.clear(); - return Some( - n1.as_str().chars().next().cmp(&Some(c2)) - .then(Ordering::Greater) - ); - } - } (HeapCellValueTag::Str, s) => { let n2 = cell_as_atom_cell!(self.heap[s]) .get_name(); @@ -599,121 +484,28 @@ impl MachineState { ) } Some(TermOrderCategory::Compound) => { - fn stalled_pstr_iter_comparator( - iteratee: PStrIteratee, - iter2: HeapPStrIter, - pdl: &mut Vec, - ) -> Option { - let compound = Some(TermOrderCategory::Compound); - - if iter2.focus.order_category(iter2.heap) != compound { - Some(compound.cmp(&iter2.focus.order_category(iter2.heap))) - } else { - let c1 = match iteratee { - PStrIteratee::Char(_, c) => c, - PStrIteratee::PStrSegment(focus, pstr_atom, n) => { - let pstr = PartialString::from(pstr_atom); - - match pstr.as_str_from(n).chars().next() { - Some(c) => c, - None => { - pdl.push(iter2.focus); - // iter2 is continuable, so it - // has a tail in the heap at - // focus+1. - pdl.push(iter2.heap[focus + 1]); - - return None; - } - } - } - }; - - read_heap_cell!(iter2.focus, - (HeapCellValueTag::Lis, l) => { - pdl.push(iter2.heap[l]); - pdl.push(char_as_cell!(c1)); - - None - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(iter2.heap[s]) - .get_name_and_arity(); - - if name == atom!(".") && arity == 2 { - pdl.push(iter2.heap[s+1]); - pdl.push(char_as_cell!(c1)); - - None - } else { - Some((2, atom!(".")).cmp(&(arity, name))) - } - } - _ => { - unreachable!() - } - ) - } - } - - fn pstr_comparator( - heap: &[HeapCellValue], - pdl: &mut Vec, - s1: usize, - s2: usize, - ) -> Option { - let mut iter1 = HeapPStrIter::new(heap, s1); - let mut iter2 = HeapPStrIter::new(heap, s2); - - match compare_pstr_prefixes(&mut iter1, &mut iter2) { - PStrCmpResult::Ordered(ordering) => Some(ordering), - PStrCmpResult::FirstIterContinuable(iteratee) => { - stalled_pstr_iter_comparator(iteratee, iter2, pdl) - } - PStrCmpResult::SecondIterContinuable(iteratee) => { - let result = stalled_pstr_iter_comparator(iteratee, iter1, pdl); - - if let Some(ordering) = result { - Some(ordering.reverse()) - } else { - let pdl_len = pdl.len(); - pdl.swap(pdl_len - 2, pdl_len - 1); - result - } - } - PStrCmpResult::Unordered => { - pdl.push(iter2.focus); - pdl.push(iter1.focus); - - None - } - } - } - read_heap_cell!(v1, (HeapCellValueTag::Lis, l1) => { read_heap_cell!(v2, - (HeapCellValueTag::CStr | HeapCellValueTag::PStrLoc) => { - let h = self.heap.len(); - - self.heap.push(v1); - self.heap.push(v2); - - if let Some(ordering) = pstr_comparator( - &self.heap, &mut self.pdl, h, h+1 - ) { - if ordering != Ordering::Equal { - self.heap.pop(); - self.heap.pop(); - - self.pdl.clear(); - - return Some(ordering); - } + (HeapCellValueTag::PStrLoc, l2) => { + if tabu_list.contains(&(l1, l2)) { + continue; } - self.heap.pop(); - self.heap.pop(); + tabu_list.insert((l1, l2)); + + // like the action of + // partial_string_to_pdl here but + // the ordering of PDL pushes is + // (crucially for comparison + // correctness) different. + let (c, succ_cell) = self.heap.last_str_char_and_tail(l2); + + self.pdl.push(succ_cell); + self.pdl.push(heap_loc_as_cell!(l1 + 1)); + + self.pdl.push(char_as_cell!(c)); + self.pdl.push(heap_loc_as_cell!(l1)); } (HeapCellValueTag::Lis, l2) => { if tabu_list.contains(&(l1, l2)) { @@ -757,27 +549,69 @@ impl MachineState { } ) } - (HeapCellValueTag::CStr | HeapCellValueTag::PStrLoc) => { - let h = self.heap.len(); + (HeapCellValueTag::PStrLoc, l1) => { + read_heap_cell!(v2, + (HeapCellValueTag::PStrLoc, l2) => { + if tabu_list.contains(&(l1, l2)) { + continue; + } - self.heap.push(v1); - self.heap.push(v2); + tabu_list.insert((l1, l2)); - if let Some(ordering) = pstr_comparator( - &self.heap, &mut self.pdl, h, h+1, - ) { - if ordering != Ordering::Equal { - self.heap.pop(); - self.heap.pop(); - - self.pdl.clear(); - - return Some(ordering); + match self.heap.compare_pstr_segments(l1, l2) { + PStrSegmentCmpResult::Continue(v1, v2) => { + self.pdl.push(v1.offset_by(l1)); + self.pdl.push(v2.offset_by(l2)); + } + PStrSegmentCmpResult::Less => { + return Some(Ordering::Less); + } + PStrSegmentCmpResult::Greater => { + return Some(Ordering::Greater); + } + } } - } + (HeapCellValueTag::Lis, l2) => { + if tabu_list.contains(&(l1, l2)) { + continue; + } - self.heap.pop(); - self.heap.pop(); + tabu_list.insert((l1, l2)); + + let (c, succ_cell) = self.heap.last_str_char_and_tail(l1); + + self.pdl.push(succ_cell); + self.pdl.push(heap_loc_as_cell!(l2 + 1)); + + self.pdl.push(char_as_cell!(c)); + self.pdl.push(heap_loc_as_cell!(l2)); + } + (HeapCellValueTag::Str, s) => { + if tabu_list.contains(&(l1, s)) { + continue; + } + + let (name, arity) = cell_as_atom_cell!(self.heap[s]) + .get_name_and_arity(); + + if name == atom!(".") && arity == 2 { + tabu_list.insert((l1, s)); + + let (c, succ_cell) = self.heap.last_str_char_and_tail(l1); + + self.pdl.push(heap_loc_as_cell!(s+2)); + self.pdl.push(succ_cell); + + self.pdl.push(heap_loc_as_cell!(s+1)); + self.pdl.push(char_as_cell!(c)); + } else { + self.fail = true; + } + } + _ => { + unreachable!() + } + ); } (HeapCellValueTag::Str, s1) => { read_heap_cell!(v2, @@ -831,27 +665,21 @@ impl MachineState { } } } - (HeapCellValueTag::CStr | HeapCellValueTag::PStrLoc) => { - let h = self.heap.len(); + (HeapCellValueTag::PStrLoc, l2) => { + let (name, arity) = cell_as_atom_cell!(self.heap[s1]) + .get_name_and_arity(); - self.heap.push(v1); - self.heap.push(v2); + if name == atom!(".") && arity == 2 { + let (c, succ_cell) = self.heap.last_str_char_and_tail(l2); - if let Some(ordering) = pstr_comparator( - &self.heap, &mut self.pdl, h, h+1, - ) { - if ordering != Ordering::Equal { - self.heap.pop(); - self.heap.pop(); + self.pdl.push(succ_cell); + self.pdl.push(heap_loc_as_cell!(s1+2)); - self.pdl.clear(); - - return Some(ordering); - } + self.pdl.push(char_as_cell!(c)); + self.pdl.push(heap_loc_as_cell!(s1+1)); + } else { + self.fail = true; } - - self.heap.pop(); - self.heap.pop(); } _ => { unreachable!() @@ -875,186 +703,6 @@ impl MachineState { Some(Ordering::Equal) } - pub fn match_partial_string(&mut self, value: HeapCellValue, string: Atom, has_tail: bool) { - let h = self.heap.len(); - self.heap.push(value); - - let prefix_len; - let mut heap_pstr_iter = HeapPStrIter::new(&self.heap, h); - - let s = string.as_str(); - - match heap_pstr_iter.compare_pstr_to_string(&s) { - Some(PStrPrefixCmpResult { - focus, - offset, - prefix_len, - }) if prefix_len == s.len() => { - let focus_addr = self.heap[focus]; - - read_heap_cell!(focus_addr, - (HeapCellValueTag::PStr | HeapCellValueTag::CStr, pstr_atom) => { - if has_tail { - self.s = HeapPtr::PStrLocation(focus, offset); - self.s_offset = 0; - self.mode = MachineMode::Read; - } else if offset == pstr_atom.len() { - let focus = heap_pstr_iter.focus; - unify!(self, focus, empty_list_as_cell!()); - } else { - self.fail = true; - } - } - (HeapCellValueTag::PStrLoc | HeapCellValueTag::PStrOffset, h) => { - let (focus, _) = pstr_loc_and_offset(&self.heap, h); - let pstr_atom = cell_as_atom!(self.heap[focus]); - - if has_tail { - self.s = HeapPtr::PStrLocation(focus, offset); - self.s_offset = 0; - self.mode = MachineMode::Read; - } else if offset == pstr_atom.len() { - let focus = heap_pstr_iter.focus; - unify!(self, focus, empty_list_as_cell!()); - } else { - self.fail = true; - } - } - _ => { - let focus = heap_pstr_iter.focus(); - - if has_tail { - self.s = HeapPtr::HeapCell(focus); - self.s_offset = 0; - self.mode = MachineMode::Read; - } else { - let focus = heap_pstr_iter.focus; - unify!(self, focus, empty_list_as_cell!()); - } - } - ); - - return; - } - Some(PStrPrefixCmpResult { - prefix_len: inner_prefix_len, - .. - }) => { - prefix_len = inner_prefix_len; - } - None => { - read_heap_cell!(value, - (HeapCellValueTag::Str, s) => { - let cell = heap_loc_as_cell!(s + 1); - let is_list = self.heap[s] == atom_as_cell!(atom!("."), 2); - - if !(is_list && self.store(self.deref(cell)).is_var()) { - self.fail = true; - return; - } - } - (HeapCellValueTag::Lis, l) => { - let cell = heap_loc_as_cell!(l); - - if !self.store(self.deref(cell)).is_var() { - self.fail = true; - return; - } - } - (HeapCellValueTag::AttrVar | - HeapCellValueTag::StackVar | - HeapCellValueTag::Var) => { - } - _ => { - self.fail = true; - return; - } - ); - - prefix_len = 0; - } - } - - let focus = heap_pstr_iter.focus(); - let tail_addr = self.heap[focus]; - let target_cell = self.push_str_to_heap(&string.as_str()[prefix_len..], has_tail); - - unify!(self, tail_addr, target_cell); - } - - #[inline(always)] - pub(super) fn push_str_to_heap(&mut self, pstr: &str, has_tail: bool) -> HeapCellValue { - let h = self.heap.len(); - - if has_tail { - self.s = HeapPtr::HeapCell(h + 1); - self.s_offset = 0; - self.mode = MachineMode::Read; - - put_partial_string(&mut self.heap, pstr, &self.atom_tbl) - } else { - put_complete_string(&mut self.heap, pstr, &self.atom_tbl) - } - } - - pub(super) fn write_literal_to_var(&mut self, deref_v: HeapCellValue, lit: HeapCellValue) { - let store_v = self.store(deref_v); - - read_heap_cell!(lit, - (HeapCellValueTag::Atom, (atom, arity)) => { - if arity == 0 { - self.unify_atom(atom, store_v); - } else { - self.fail = true; - } - } - (HeapCellValueTag::Char, c) => { - self.unify_char(c, store_v); - } - (HeapCellValueTag::Fixnum, n) => { - self.unify_fixnum(n, store_v); - } - (HeapCellValueTag::F64, f64_ptr) => { - self.unify_f64(f64_ptr, store_v); - } - (HeapCellValueTag::Cons, ptr) => { - match_untyped_arena_ptr!(ptr, - (ArenaHeaderTag::Integer, n) => { - self.unify_big_int(n, store_v); - } - (ArenaHeaderTag::Rational, r) => { - self.unify_rational(r, store_v); - } - _ => { - self.fail = true; - } - ) - } - (HeapCellValueTag::CStr, cstr_atom) => { - read_heap_cell!(store_v, - (HeapCellValueTag::PStrLoc | - HeapCellValueTag::Lis | - HeapCellValueTag::Str) => { - self.match_partial_string(store_v, cstr_atom, false); - } - (HeapCellValueTag::AttrVar | HeapCellValueTag::Var) => { - let r = store_v.as_var().unwrap(); - self.bind(r, lit); - } - (HeapCellValueTag::CStr, cstr2_atom) => { - self.fail = cstr_atom != cstr2_atom; - } - _ => { - self.fail = true; - } - ); - } - _ => { - unreachable!() - } - ) - } - pub(crate) fn setup_call_n_init_goal_info( &mut self, goal: HeapCellValue, @@ -1084,9 +732,11 @@ impl MachineState { (name, 0, 0) } + /* (HeapCellValueTag::Char, c) => { (AtomTable::build_with(&self.atom_tbl, &c.to_string()), 0, 0) } + */ (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { let stub = functor_stub(atom!("call"), arity + 1); let err = self.instantiation_error(); @@ -1118,24 +768,14 @@ impl MachineState { } #[inline] - pub fn is_cyclic_term(&mut self, value: HeapCellValue) -> bool { - let value = self.store(self.deref(value)); - - if value.is_stack_var() || value.is_constant() { + pub fn is_cyclic_term(&mut self, term_loc: usize) -> bool { + if self.heap[term_loc].is_stack_var() { return false; } - let h = self.heap.len(); - self.heap.push(value); - - let cycle_found = { - let mut iter = cycle_detecting_stackless_preorder_iter(&mut self.heap, h); - for _ in iter.by_ref() {} - iter.cycle_found() - }; - - self.heap.pop(); - cycle_found + let mut iter = cycle_detecting_stackless_preorder_iter(&mut self.heap, term_loc); + for _ in iter.by_ref() {} + iter.cycle_found() } // arg(+N, +Term, ?Arg) @@ -1150,7 +790,7 @@ impl MachineState { return Err(self.error_form(err, stub_gen())); } _ => { - let n = match Number::try_from(n) { + let n = match Number::try_from((n, &self.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => Number::Fixnum(n), Ok(Number::Integer(n)) => Number::Integer(n), _ => { @@ -1202,37 +842,16 @@ impl MachineState { (HeapCellValueTag::PStrLoc, pstr_loc) => { if n == 1 || n == 2 { let a3 = self.registers[3]; - let (h, offset) = pstr_loc_and_offset(&self.heap, pstr_loc); + let mut char_iter = self.heap.char_iter(pstr_loc); - let pstr = cell_as_string!(self.heap[h]); - let offset = offset.get_num() as usize; - - if let Some(c) = pstr.as_str_from(offset).chars().next() { + if let Some(c) = char_iter.next() { if n == 1 { self.unify_char(c, a3); + } else if char_iter.next().is_some() { + unify_fn!(*self, pstr_loc_as_cell!(pstr_loc + c.len_utf8()), a3); } else { - let offset = (offset + c.len_utf8()) as i64; - let h_len = self.heap.len(); - let pstr_atom: Atom = pstr.into(); - - if pstr_atom.len() > offset as usize { - self.heap.push(pstr_offset_as_cell!(h)); - self.heap.push(fixnum_as_cell!(Fixnum::build_with(offset))); - - unify_fn!(*self, pstr_loc_as_cell!(h_len), a3); - } else { - match self.heap[h].get_tag() { - HeapCellValueTag::CStr => { - self.unify_atom(atom!("[]"), self.store(self.deref(a3))); - } - HeapCellValueTag::PStr => { - unify_fn!(*self, self.heap[h+1], a3); - } - _ => { - unreachable!(); - } - } - } + let tail_idx = Heap::pstr_tail_idx(pstr_loc + c.len_utf8()); + unify_fn!(*self, self.heap[tail_idx], a3); } } else { unreachable!() @@ -1241,32 +860,6 @@ impl MachineState { self.fail = true; } } - (HeapCellValueTag::CStr, cstr_atom) => { - let cstr = PartialString::from(cstr_atom); - - if let Some(c) = cstr.as_str_from(0).chars().next() { - if n == 1 { - self.unify_char(c, self.store(self.deref(self.registers[3]))); - } else if n == 2 { - let offset = c.len_utf8() as i64; - let h_len = self.heap.len(); - - if cstr_atom.len() > offset as usize { - self.heap.push(atom_as_cstr_cell!(cstr_atom)); - self.heap.push(pstr_offset_as_cell!(h_len)); - self.heap.push(fixnum_as_cell!(Fixnum::build_with(offset))); - - unify_fn!(*self, pstr_loc_as_cell!(h_len+1), self.registers[3]); - } else { - self.unify_atom(atom!("[]"), self.store(self.deref(self.registers[3]))); - } - } else { - self.fail = true; - } - } else { - unreachable!() - } - } _ => { // 8.5.2.3 d) let err = self.type_error(ValidType::Compound, term); @@ -1297,28 +890,42 @@ impl MachineState { fn try_functor_unify_components(&mut self, name: HeapCellValue, arity: usize) { let a2 = self.deref(self.registers[2]); - self.write_literal_to_var(a2, name); + unify!(self, a2, name); if !self.fail { let a3 = self.store(self.deref(self.registers[3])); - self.unify_fixnum(Fixnum::build_with(arity as i64), a3); + self.unify_fixnum( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(arity as i64) }, + a3, + ); } } - fn try_functor_fabricate_struct(&mut self, name: Atom, arity: usize, r: Ref) { - let h = self.heap.len(); + fn try_functor_fabricate_struct( + &mut self, + name: Atom, + arity: usize, + r: Ref, + ) -> Result<(), usize> { + let h = self.heap.cell_len(); + let mut writer = self.heap.reserve(arity + 1)?; let f_a = if name == atom!(".") && arity == 2 { - self.heap.push(heap_loc_as_cell!(h)); - self.heap.push(heap_loc_as_cell!(h + 1)); + writer.write_with(|section| { + section.push_cell(heap_loc_as_cell!(h)); + section.push_cell(heap_loc_as_cell!(h + 1)); + }); list_loc_as_cell!(h) } else { - self.heap.push(atom_as_cell!(name, arity)); + writer.write_with(|section| { + section.push_cell(atom_as_cell!(name, arity)); - for i in 0..arity { - self.heap.push(heap_loc_as_cell!(h + i + 1)); - } + for i in 0..arity { + section.push_cell(heap_loc_as_cell!(h + i + 1)); + } + }); if arity == 0 { heap_loc_as_cell!(h) @@ -1328,6 +935,7 @@ impl MachineState { }; (self.bind_fn)(self, r, f_a); + Ok(()) } pub fn try_functor(&mut self) -> CallResult { @@ -1335,8 +943,8 @@ impl MachineState { let a1 = self.store(self.deref(self.registers[1])); read_heap_cell!(a1, - (HeapCellValueTag::Cons | HeapCellValueTag::Char | HeapCellValueTag::Fixnum | - HeapCellValueTag::F64) => { + (HeapCellValueTag::Cons | HeapCellValueTag::Fixnum | // | HeapCellValueTag::Char + HeapCellValueTag::F64Offset) => { self.try_functor_unify_components(a1, 0); } (HeapCellValueTag::Atom, (_name, arity)) => { @@ -1347,7 +955,7 @@ impl MachineState { let (name, arity) = cell_as_atom_cell!(self.heap[s]).get_name_and_arity(); self.try_functor_compound_case(name, arity); } - (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc | HeapCellValueTag::CStr) => { + (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => { // | HeapCellValueTag::CStr) => { self.try_functor_compound_case(atom!("."), 2); } (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { @@ -1362,17 +970,17 @@ impl MachineState { return Err(self.error_form(err, stub_gen())); } - let mut type_error = |arity| { - let err = self.type_error(ValidType::Integer, arity); - Err(self.error_form(err, stub_gen())) + let type_error = |machine_st: &mut Self, arity| { + let err = machine_st.type_error(ValidType::Integer, arity); + Err(machine_st.error_form(err, stub_gen())) }; - let arity = match Number::try_from(arity) { + let arity = match Number::try_from((arity, &self.arena.f64_tbl)) { Ok(Number::Float(_)) => { - return type_error(arity); + return type_error(self, arity); } Ok(Number::Rational(n)) if !n.denominator().is_one() => { - return type_error(arity); + return type_error(self, arity); } Ok(n) if n > MAX_ARITY => { // 8.5.1.3 f) @@ -1394,21 +1002,24 @@ impl MachineState { value }, Err(_) => { - return type_error(arity); + return type_error(self, arity); } }; read_heap_cell!(store_name, - (HeapCellValueTag::Cons | HeapCellValueTag::Char | HeapCellValueTag::Fixnum | - HeapCellValueTag::F64) if arity == 0 => { - self.bind(a1.as_var().unwrap(), deref_name); - } + (HeapCellValueTag::Cons | HeapCellValueTag::Fixnum | HeapCellValueTag::F64Offset) + if arity == 0 => { + self.bind(a1.as_var().unwrap(), deref_name); + } (HeapCellValueTag::Atom, (name, atom_arity)) => { debug_assert_eq!(atom_arity, 0); - self.try_functor_fabricate_struct( - name, - arity as usize, - a1.as_var().unwrap(), + resource_error_call_result!( + self, + self.try_functor_fabricate_struct( + name, + arity as usize, + a1.as_var().unwrap(), + ) ); } (HeapCellValueTag::Str, s) => { @@ -1416,27 +1027,21 @@ impl MachineState { .get_name_and_arity(); if atom_arity == 0 { - self.try_functor_fabricate_struct( - name, - arity as usize, - a1.as_var().unwrap(), + resource_error_call_result!( + self, + self.try_functor_fabricate_struct( + name, + arity as usize, + a1.as_var().unwrap(), + ) ); } else { let err = self.type_error(ValidType::Atomic, store_name); return Err(self.error_form(err, stub_gen())); } } - (HeapCellValueTag::Char, c) => { - let c = AtomTable::build_with(&self.atom_tbl, &c.to_string()); - - self.try_functor_fabricate_struct( - c, - arity as usize, - a1.as_var().unwrap(), - ); - } (HeapCellValueTag::Cons | HeapCellValueTag::Fixnum | - HeapCellValueTag::F64) if arity != 0 => { + HeapCellValueTag::F64Offset) if arity != 0 => { let err = self.type_error(ValidType::Atom, store_name); return Err(self.error_form(err, stub_gen())); // 8.5.1.3 e) } @@ -1457,7 +1062,7 @@ impl MachineState { pub fn try_from_list( &mut self, value: HeapCellValue, - stub_gen: impl Fn() -> FunctorStub, + stub_gen: impl Fn() -> MachineStub, ) -> Result, MachineStub> { let value = self.store(self.deref(value)); @@ -1465,8 +1070,8 @@ impl MachineState { (HeapCellValueTag::Lis, l) => { self.try_from_inner_list(vec![], l, stub_gen, value) } - (HeapCellValueTag::PStrLoc, h) => { - self.try_from_partial_string(vec![], h, stub_gen, value) + (HeapCellValueTag::PStrLoc, pstr_loc) => { + self.try_from_partial_string(vec![], pstr_loc, stub_gen, value) } (HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar | HeapCellValueTag::Var) => { let err = self.instantiation_error(); @@ -1491,10 +1096,6 @@ impl MachineState { Err(self.error_form(err, stub_gen())) } } - (HeapCellValueTag::CStr, cstr_atom) => { - let cstr = cstr_atom.as_str(); - Ok(cstr.chars().map(|c| char_as_cell!(c)).collect()) - } _ => { let err = self.type_error(ValidType::List, value); Err(self.error_form(err, stub_gen())) @@ -1506,7 +1107,7 @@ impl MachineState { &mut self, mut result: Vec, mut l: usize, - stub_gen: impl Fn() -> FunctorStub, + stub_gen: impl Fn() -> MachineStub, a1: HeapCellValue, ) -> Result, MachineStub> { result.push(self.heap[l]); @@ -1520,8 +1121,8 @@ impl MachineState { result.push(self.heap[hcp]); l = hcp + 1; } - (HeapCellValueTag::PStrLoc, l) => { - return self.try_from_partial_string(result, l, stub_gen, a1); + (HeapCellValueTag::PStrLoc, pstr_loc) => { + return self.try_from_partial_string(result, pstr_loc, stub_gen, a1); } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.heap[s]) @@ -1560,52 +1161,34 @@ impl MachineState { fn try_from_partial_string( &mut self, mut chars: Vec, - h: usize, - stub_gen: impl Fn() -> FunctorStub, + pstr_loc: usize, + stub_gen: impl Fn() -> MachineStub, a1: HeapCellValue, ) -> Result, MachineStub> { - let mut heap_pstr_iter = HeapPStrIter::new(&self.heap, h); + self.heap[0] = pstr_loc_as_cell!(pstr_loc); + let mut heap_pstr_iter = HeapPStrIter::new(&self.heap, 0); - for iteratee in heap_pstr_iter.by_ref() { + while let Some(iteratee) = heap_pstr_iter.next() { match iteratee { - PStrIteratee::Char(_, c) => chars.push(char_as_cell!(c)), - PStrIteratee::PStrSegment(_, pstr_atom, n) => { - let pstr = PartialString::from(pstr_atom); - chars.extend(pstr.as_str_from(n).chars().map(|c| char_as_cell!(c))); + PStrIteratee::Char { value: c, .. } => chars.push(char_as_cell!(c)), + PStrIteratee::PStrSlice { + slice_loc, + slice_len, + } => { + let pstr = heap_pstr_iter.heap.slice_to_str(slice_loc, slice_len); + chars.extend(pstr.chars().map(|c| char_as_cell!(c))); } } } - match self.heap[h].get_tag() { - HeapCellValueTag::PStr => { - if heap_pstr_iter.at_string_terminator() { - Ok(chars) - } else { - read_heap_cell!(self.heap[heap_pstr_iter.focus()], - (HeapCellValueTag::Lis, l) => { - self.try_from_inner_list(chars, l, stub_gen, a1) - } - (HeapCellValueTag::Atom, (name, arity)) => { - if name == atom!(".") && arity == 2 { - let l = heap_pstr_iter.focus() + 1; - self.try_from_inner_list(chars, l, stub_gen, a1) - } else { - let err = self.type_error(ValidType::List, a1); - Err(self.error_form(err, stub_gen())) - } - } - _ => { - let err = self.type_error(ValidType::List, a1); - Err(self.error_form(err, stub_gen())) - } - ) - } - } - HeapCellValueTag::CStr => Ok(chars), - _ => { - unreachable!() - } + let end_cell = heap_pstr_iter.heap[heap_pstr_iter.focus()]; + + if heap_pstr_iter.is_cyclic() || end_cell != empty_list_as_cell!() { + let err = self.type_error(ValidType::List, a1); + return Err(self.error_form(err, stub_gen())); } + + Ok(chars) } // returns true on failure. @@ -1624,7 +1207,7 @@ impl MachineState { pub fn integers_to_bytevec( &mut self, value: HeapCellValue, - stub_gen: impl Fn() -> FunctorStub, + stub_gen: impl Fn() -> MachineStub, ) -> Vec { let mut bytes: Vec = Vec::new(); @@ -1636,7 +1219,7 @@ impl MachineState { for addr in addrs { let addr = self.store(self.deref(addr)); - match Number::try_from(addr) { + match Number::try_from((addr, &self.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => { if let Ok(b) = u8::try_from(n.get_num()) { bytes.push(b) diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index 457afacf..0fcebd41 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -4,16 +4,13 @@ pub use crate::machine::machine_state::*; pub use crate::machine::streams::*; pub use crate::machine::*; pub use crate::parser::ast::*; -use crate::read::*; -pub use crate::types::*; - -use std::sync::Arc; #[cfg(test)] use crate::machine::copier::CopierTarget; +use crate::read::TermWriteResult; #[cfg(test)] -use std::ops::{Deref, DerefMut, Index, IndexMut}; +use std::ops::{Deref, DerefMut, Index, IndexMut, Range}; // a mini-WAM for test purposes. @@ -31,7 +28,6 @@ impl MockWAM { Self { machine_st: MachineState::new(), op_dir, - //flags: MachineFlags::default(), } } @@ -55,16 +51,15 @@ impl MockWAM { term_string: &'static str, ) -> Result { let term_write_result = self.parse_and_write_parsed_term_to_heap(term_string)?; - - print_heap_terms(self.machine_st.heap.iter(), term_write_result.heap_loc); + print_heap_terms(&self.machine_st.heap, term_write_result.heap_loc); let mut printer = HCPrinter::new( &mut self.machine_st.heap, - Arc::clone(&self.machine_st.atom_tbl), &mut self.machine_st.stack, + &self.machine_st.arena, &self.op_dir, PrinterOutputter::new(), - heap_loc_as_cell!(term_write_result.heap_loc), + term_write_result.heap_loc, ); printer.var_names = term_write_result @@ -95,6 +90,7 @@ pub struct TermCopyingMockWAM<'a> { impl<'a> Index for TermCopyingMockWAM<'a> { type Output = HeapCellValue; + #[inline] fn index(&self, index: usize) -> &HeapCellValue { &self.wam.machine_st.heap[index] } @@ -112,6 +108,7 @@ impl<'a> IndexMut for TermCopyingMockWAM<'a> { impl<'a> Deref for TermCopyingMockWAM<'a> { type Target = MockWAM; + #[inline] fn deref(&self) -> &Self::Target { self.wam } @@ -119,6 +116,7 @@ impl<'a> Deref for TermCopyingMockWAM<'a> { #[cfg(test)] impl<'a> DerefMut for TermCopyingMockWAM<'a> { + #[inline] fn deref_mut(&mut self) -> &mut Self::Target { self.wam } @@ -153,10 +151,6 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> { } } - fn push(&mut self, val: HeapCellValue) { - self.wam.machine_st.heap.push(val); - } - fn push_attr_var_queue(&mut self, attr_var_loc: usize) { self.wam .machine_st @@ -170,43 +164,66 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> { } fn threshold(&self) -> usize { - self.wam.machine_st.heap.len() + self.wam.machine_st.heap.cell_len() + } + + #[inline(always)] + fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result { + self.wam.machine_st.heap.copy_pstr_within(pstr_loc) + } + + #[inline(always)] + fn as_slice_from<'b>(&'b self, from: usize) -> Box + 'b> { + Box::new(self.wam.machine_st.heap.as_slice()[from..].iter().cloned()) + } + + #[inline(always)] + fn reserve(&mut self, num_cells: usize) -> Result { + self.wam.machine_st.heap.reserve(num_cells) + } + + #[inline(always)] + fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), usize> { + self.wam.machine_st.heap.copy_slice_to_end(bounds) } } #[cfg(test)] -pub fn all_cells_marked_and_unforwarded(heap: &[HeapCellValue]) { - for (idx, cell) in heap.iter().enumerate() { +pub fn all_cells_marked_and_unforwarded(heap: &Heap, offset: usize) { + for curr_idx in offset..heap.cell_len() { + let cell = heap[curr_idx]; + assert!( cell.get_mark_bit(), - "cell {:?} at index {} is not marked", - cell, - idx + "cell {cell:?} at index {curr_idx} is not marked" ); assert!( !cell.get_forwarding_bit(), - "cell {:?} at index {} is forwarded", - cell, - idx + "cell {cell:?} at index {curr_idx} is forwarded" ); } } #[cfg(test)] -pub fn all_cells_unmarked(heap: &Heap) { - for (idx, cell) in heap.iter().enumerate() { - assert!( - !cell.get_mark_bit(), - "cell {:?} at index {} is still marked", - cell, - idx - ); +pub fn unmark_all_cells(heap: &mut Heap, offset: usize) { + for idx in offset..heap.cell_len() { + heap[idx].set_mark_bit(false); + } +} + +#[cfg(test)] +pub fn all_cells_unmarked(iter: &impl SizedHeap) { + let mut idx = 0; + let cell_len = iter.cell_len(); + + while idx < cell_len { + let curr_idx = idx; + idx += 1; + let cell = iter[curr_idx]; assert!( - !cell.get_forwarding_bit(), - "cell {:?} at index {} is still forwarded", - cell, - idx + !cell.get_mark_bit(), + "cell {cell:?} at index {curr_idx} is still marked" ); } } @@ -232,6 +249,7 @@ pub(crate) fn parse_and_write_parsed_term_to_heap( impl Machine { /// For use in tests. + #[allow(clippy::unbuffered_bytes)] pub fn test_load_file(&mut self, file: &str) -> Vec { let stream = Stream::from_owned_string( std::fs::read_to_string(AsRef::::as_ref(file)).unwrap(), @@ -243,6 +261,7 @@ impl Machine { } /// For use in tests. + #[allow(clippy::unbuffered_bytes)] pub fn test_load_string(&mut self, code: &str) -> Vec { let stream = Stream::from_owned_string(code.to_owned(), &mut self.machine_st.arena); @@ -255,6 +274,8 @@ impl Machine { mod tests { use super::*; + use crate::functor_macro::FunctorElement; + #[test] fn unify_tests() { let mut wam = MachineState::new(); @@ -387,36 +408,66 @@ mod tests { wam.heap.clear(); - wam.heap.push(pstr_as_cell!(atom!("this is a string"))); - wam.heap.push(heap_loc_as_cell!(1)); + let mut writer = wam.heap.reserve(96).unwrap(); - wam.heap.push(pstr_as_cell!(atom!("this is a string"))); - wam.heap.push(pstr_loc_as_cell!(4)); + writer.write_with(|section| { + section.push_pstr("this is a string"); // 0 - wam.heap.push(pstr_offset_as_cell!(0)); - wam.heap.push(fixnum_as_cell!(Fixnum::build_with(6))); + let h = section.cell_len(); + assert_eq!(h, 3); - unify!(wam, pstr_loc_as_cell!(0), pstr_loc_as_cell!(2)); + section.push_cell(heap_loc_as_cell!(h)); // 3 + section.push_pstr("this is a string"); // 4 + + let h = section.cell_len(); + assert_eq!(h + 1, 8); + + section.push_cell(pstr_loc_as_cell!(heap_index!(h + 1))); // 7 + section.push_pstr("this is a string"); // 8 + + section.push_cell(pstr_loc_as_cell!(heap_index!(h + 1))); + }); + + unify!(wam, pstr_loc_as_cell!(0), pstr_loc_as_cell!(heap_index!(4))); assert!(!wam.fail); - assert_eq!(wam.heap[1], pstr_loc_as_cell!(4)); - - all_cells_unmarked(&wam.heap); + assert_eq!( + wam.heap + .slice_to_str(heap_index!(0), "this is a string".len()), + "this is a string" + ); + assert_eq!(wam.heap[3], pstr_loc_as_cell!(heap_index!(8))); + assert_eq!( + wam.heap + .slice_to_str(heap_index!(4), "this is a string".len()), + "this is a string" + ); + assert_eq!(wam.heap[7], pstr_loc_as_cell!(heap_index!(8))); + assert_eq!( + wam.heap + .slice_to_str(heap_index!(8), "this is a string".len()), + "this is a string" + ); + assert_eq!(wam.heap[11], pstr_loc_as_cell!(heap_index!(8))); wam.heap.clear(); - wam.heap.push(list_loc_as_cell!(1)); - wam.heap.push(atom_as_cell!(atom!("a"))); - wam.heap.push(list_loc_as_cell!(3)); - wam.heap.push(atom_as_cell!(atom!("b"))); - wam.heap.push(heap_loc_as_cell!(0)); + let mut writer = wam.heap.reserve(96).unwrap(); - wam.heap.push(list_loc_as_cell!(6)); - wam.heap.push(atom_as_cell!(atom!("a"))); - wam.heap.push(list_loc_as_cell!(8)); - wam.heap.push(atom_as_cell!(atom!("b"))); - wam.heap.push(heap_loc_as_cell!(5)); + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(atom_as_cell!(atom!("a"))); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(atom_as_cell!(atom!("b"))); + section.push_cell(heap_loc_as_cell!(0)); + + section.push_cell(list_loc_as_cell!(6)); + section.push_cell(atom_as_cell!(atom!("a"))); + section.push_cell(list_loc_as_cell!(8)); + section.push_cell(atom_as_cell!(atom!("b"))); + section.push_cell(heap_loc_as_cell!(5)); + }); unify!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(5)); @@ -426,17 +477,21 @@ mod tests { wam.heap.clear(); - wam.heap.push(list_loc_as_cell!(1)); - wam.heap.push(atom_as_cell!(atom!("a"))); - wam.heap.push(list_loc_as_cell!(3)); - wam.heap.push(atom_as_cell!(atom!("b"))); - wam.heap.push(heap_loc_as_cell!(0)); + let mut writer = wam.heap.reserve(96).unwrap(); - wam.heap.push(list_loc_as_cell!(6)); - wam.heap.push(atom_as_cell!(atom!("a"))); - wam.heap.push(list_loc_as_cell!(8)); - wam.heap.push(atom_as_cell!(atom!("c"))); - wam.heap.push(heap_loc_as_cell!(5)); + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(atom_as_cell!(atom!("a"))); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(atom_as_cell!(atom!("b"))); + section.push_cell(heap_loc_as_cell!(0)); + + section.push_cell(list_loc_as_cell!(6)); + section.push_cell(atom_as_cell!(atom!("a"))); + section.push_cell(list_loc_as_cell!(8)); + section.push_cell(atom_as_cell!(atom!("c"))); + section.push_cell(heap_loc_as_cell!(5)); + }); unify!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(5)); @@ -444,19 +499,24 @@ mod tests { wam.fail = false; all_cells_unmarked(&wam.heap); + wam.heap.clear(); - wam.heap.push(list_loc_as_cell!(1)); - wam.heap.push(atom_as_cell!(atom!("a"))); - wam.heap.push(list_loc_as_cell!(3)); - wam.heap.push(atom_as_cell!(atom!("b"))); - wam.heap.push(heap_loc_as_cell!(5)); + let mut writer = wam.heap.reserve(96).unwrap(); - wam.heap.push(list_loc_as_cell!(6)); - wam.heap.push(atom_as_cell!(atom!("a"))); - wam.heap.push(list_loc_as_cell!(8)); - wam.heap.push(atom_as_cell!(atom!("b"))); - wam.heap.push(heap_loc_as_cell!(0)); + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(atom_as_cell!(atom!("a"))); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(atom_as_cell!(atom!("b"))); + section.push_cell(heap_loc_as_cell!(5)); + + section.push_cell(list_loc_as_cell!(6)); + section.push_cell(atom_as_cell!(atom!("a"))); + section.push_cell(list_loc_as_cell!(8)); + section.push_cell(atom_as_cell!(atom!("b"))); + section.push_cell(heap_loc_as_cell!(0)); + }); unify!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(5)); @@ -468,7 +528,7 @@ mod tests { let term_write_result_1 = parse_and_write_parsed_term_to_heap(&mut wam, "X = g(X,y).", &op_dir).unwrap(); - print_heap_terms(wam.heap.iter(), term_write_result_1.heap_loc); + print_heap_terms(&wam.heap, term_write_result_1.heap_loc); unify!(wam, heap_loc_as_cell!(2), str_loc_as_cell!(4)); @@ -510,8 +570,15 @@ mod tests { let mut wam = MachineState::new(); - wam.heap.push(heap_loc_as_cell!(0)); - wam.heap.push(heap_loc_as_cell!(1)); + // clear the heap of resource error data etc + wam.heap.clear(); + + let mut writer = wam.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(heap_loc_as_cell!(0)); + section.push_cell(heap_loc_as_cell!(1)); + }); assert_eq!( compare_term_test!(wam, wam.heap[0], wam.heap[1]), @@ -533,12 +600,10 @@ mod tests { Some(Ordering::Equal) ); + let cstr_cell = wam.heap.allocate_cstr("string").unwrap(); + assert_eq!( - compare_term_test!( - wam, - atom_as_cell!(atom!("atom")), - atom_as_cstr_cell!(atom!("string")) - ), + compare_term_test!(wam, atom_as_cell!(atom!("atom")), cstr_cell), Some(Ordering::Less) ); @@ -571,8 +636,12 @@ mod tests { wam.heap.clear(); - wam.heap.push(atom_as_cell!(atom!("f"), 1)); - wam.heap.push(heap_loc_as_cell!(1)); + let mut writer = wam.heap.reserve(96).unwrap(); + + writer.write_with(|section| { + section.push_cell(atom_as_cell!(atom!("f"), 1)); + section.push_cell(heap_loc_as_cell!(1)); + }); assert_eq!( compare_term_test!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(0)), @@ -586,21 +655,25 @@ mod tests { wam.heap.clear(); - // [1,2,3] - wam.heap.push(list_loc_as_cell!(1)); - wam.heap.push(fixnum_as_cell!(Fixnum::build_with(1))); - wam.heap.push(list_loc_as_cell!(3)); - wam.heap.push(fixnum_as_cell!(Fixnum::build_with(2))); - wam.heap.push(list_loc_as_cell!(5)); - wam.heap.push(fixnum_as_cell!(Fixnum::build_with(3))); - wam.heap.push(empty_list_as_cell!()); + let mut writer = wam.heap.reserve(96).unwrap(); - // [1,2] - wam.heap.push(list_loc_as_cell!(8)); - wam.heap.push(fixnum_as_cell!(Fixnum::build_with(1))); - wam.heap.push(list_loc_as_cell!(10)); - wam.heap.push(fixnum_as_cell!(Fixnum::build_with(2))); - wam.heap.push(empty_list_as_cell!()); + writer.write_with(|section| { + // [1,2,3] + section.push_cell(list_loc_as_cell!(1)); + section.push_cell(fixnum_as_cell!(Fixnum::build_with(1))); + section.push_cell(list_loc_as_cell!(3)); + section.push_cell(fixnum_as_cell!(Fixnum::build_with(2))); + section.push_cell(list_loc_as_cell!(5)); + section.push_cell(fixnum_as_cell!(Fixnum::build_with(3))); + section.push_cell(empty_list_as_cell!()); + + // [1,2] + section.push_cell(list_loc_as_cell!(8)); + section.push_cell(fixnum_as_cell!(Fixnum::build_with(1))); + section.push_cell(list_loc_as_cell!(10)); + section.push_cell(fixnum_as_cell!(Fixnum::build_with(2))); + section.push_cell(empty_list_as_cell!()); + }); assert_eq!( compare_term_test!(wam, heap_loc_as_cell!(7), heap_loc_as_cell!(7)), @@ -626,12 +699,10 @@ mod tests { Some(Ordering::Greater) ); + let cstr_cell = wam.heap.allocate_cstr("string").unwrap(); + assert_eq!( - compare_term_test!( - wam, - empty_list_as_cell!(), - atom_as_cstr_cell!(atom!("string")) - ), + compare_term_test!(wam, empty_list_as_cell!(), cstr_cell), Some(Ordering::Less) ); @@ -662,55 +733,63 @@ mod tests { fn is_cyclic_term_tests() { let mut wam = MachineState::new(); - assert!(!wam.is_cyclic_term(atom_as_cell!(atom!("f")))); - assert!(!wam.is_cyclic_term(fixnum_as_cell!(Fixnum::build_with(555)))); + let mut writer = wam.heap.reserve(96).unwrap(); - wam.heap.push(heap_loc_as_cell!(0)); + writer.write_with(|section| { + section.push_cell(atom_as_cell!(atom!("f"))); + section.push_cell(fixnum_as_cell!(Fixnum::build_with(555))); + section.push_cell(heap_loc_as_cell!(0)); + }); - assert!(!wam.is_cyclic_term(heap_loc_as_cell!(0))); + assert!(!wam.is_cyclic_term(0)); + assert!(!wam.is_cyclic_term(1)); + assert!(!wam.is_cyclic_term(2)); all_cells_unmarked(&wam.heap); wam.heap.clear(); - wam.heap - .extend(functor!(atom!("f"), [atom(atom!("a")), atom(atom!("b"))])); + let mut functor_writer = Heap::functor_writer(functor!( + atom!("f"), + [atom_as_cell((atom!("a"))), atom_as_cell((atom!("b")))] + )); - assert!(!wam.is_cyclic_term(str_loc_as_cell!(0))); + functor_writer(&mut wam.heap).unwrap(); + + let h = wam.heap.cell_len(); + wam.heap.push_cell(str_loc_as_cell!(0)).unwrap(); + + assert!(!wam.is_cyclic_term(h)); all_cells_unmarked(&wam.heap); - assert!(!wam.is_cyclic_term(heap_loc_as_cell!(1))); + assert!(!wam.is_cyclic_term(1)); all_cells_unmarked(&wam.heap); - assert!(!wam.is_cyclic_term(heap_loc_as_cell!(2))); + assert!(!wam.is_cyclic_term(2)); all_cells_unmarked(&wam.heap); wam.heap[2] = str_loc_as_cell!(0); - print_heap_terms(wam.heap.iter(), 0); + print_heap_terms(&wam.heap, 0); - assert!(wam.is_cyclic_term(str_loc_as_cell!(0))); + assert!(wam.is_cyclic_term(2)); all_cells_unmarked(&wam.heap); wam.heap[2] = atom_as_cell!(atom!("b")); wam.heap[1] = str_loc_as_cell!(0); - assert!(wam.is_cyclic_term(str_loc_as_cell!(0))); - - all_cells_unmarked(&wam.heap); - - assert!(wam.is_cyclic_term(heap_loc_as_cell!(1))); + assert!(wam.is_cyclic_term(1)); all_cells_unmarked(&wam.heap); wam.heap.clear(); - wam.heap.push(pstr_as_cell!(atom!("a string"))); - wam.heap.push(empty_list_as_cell!()); + let h = wam.heap.cell_len(); + wam.heap.allocate_cstr("a string").unwrap(); - assert!(!wam.is_cyclic_term(pstr_loc_as_cell!(0))); + assert!(!wam.is_cyclic_term(h)); } } diff --git a/src/machine/mod.rs b/src/machine/mod.rs index d180f02d..51ae44e4 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -45,6 +45,7 @@ use crate::machine::machine_indices::*; use crate::machine::machine_state::*; use crate::machine::stack::*; use crate::machine::streams::*; +use crate::offset_table::*; use crate::parser::ast::*; use crate::parser::dashu::{Integer, Rational}; use crate::types::*; @@ -207,13 +208,8 @@ pub(crate) fn import_builtin_impls(code_dir: &CodeDir, builtins: &mut Module) { #[inline] pub(crate) fn get_structure_index(value: HeapCellValue) -> Option { read_heap_cell!(value, - (HeapCellValueTag::Cons, cons_ptr) => { - match_untyped_arena_ptr!(cons_ptr, - (ArenaHeaderTag::IndexPtr, ip) => { - return Some(CodeIndex::from(ip)); - } - _ => {} - ); + (HeapCellValueTag::CodeIndexOffset, offset) => { + return Some(CodeIndex::from(offset)); } _ => { } @@ -246,7 +242,7 @@ impl Machine { } /// Runs the predicate `key` in `module_name` until completion. - /// Siltently ignores failure, thrown errors and choice points. + /// Silently ignores failure, thrown errors and choice points. /// /// Consider using [`Machine::run_query`] if you wish to handle /// predicates that may fail, leave a choice point or throw. @@ -256,8 +252,14 @@ impl Machine { key: PredicateKey, ) -> std::process::ExitCode { if let Some(module) = self.indices.modules.get(&module_name) { - if let Some(code_index) = module.code_dir.get(&key) { - let p = code_index.local().unwrap(); + if let Some(code_idx) = module.code_dir.get(&key) { + let index_ptr = self + .machine_st + .arena + .code_index_tbl + .get_entry(code_idx.into()); + let p = index_ptr.local().unwrap(); + // Leave a halting choice point to backtrack to in case the predicate fails or throws. self.allocate_stub_choice_point(); @@ -328,8 +330,13 @@ impl Machine { self.load_file(path_buf.to_str().unwrap(), stream); if let Some(module) = self.indices.modules.get(&atom!("$atts")) { - if let Some(code_index) = module.code_dir.get(&(atom!("driver"), 2)) { - self.machine_st.attr_var_init.verify_attrs_loc = code_index.local().unwrap(); + if let Some(code_idx) = module.code_dir.get(&(atom!("driver"), 2)) { + let index_ptr = self + .machine_st + .arena + .code_index_tbl + .get_entry(code_idx.into()); + self.machine_st.attr_var_init.verify_attrs_loc = index_ptr.local().unwrap(); } } } @@ -343,13 +350,16 @@ impl Machine { for arity in 1..66 { let key = (atom!("call"), arity); - match loader.code_dir.get(&key) { + match loader.code_dir.get(&key).cloned() { Some(src_code_index) => { - let target_code_index = target_code_dir - .entry(key) - .or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena)); + let code_index_tbl = &mut arena.code_index_tbl; - target_code_index.set(src_code_index.get()); + let target_code_index = target_code_dir.entry(key).or_insert_with(|| { + CodeIndex::new(IndexPtr::undefined(), code_index_tbl) + }); + + let src_code_ptr = code_index_tbl.get_entry(src_code_index.into()); + target_code_index.set(code_index_tbl, src_code_ptr); } None => { unreachable!(); @@ -458,7 +468,7 @@ impl Machine { key, CodeIndex::new( IndexPtr::index(p + impls_offset), - &mut self.machine_st.arena, + &mut self.machine_st.arena.code_index_tbl, ), ); } @@ -485,7 +495,10 @@ impl Machine { #[inline(always)] pub(crate) fn run_verify_attr_interrupt(&mut self, arity: usize) { let p = self.machine_st.attr_var_init.verify_attrs_loc; - self.machine_st.verify_attr_interrupt(p, arity); + step_or_resource_error!( + self.machine_st, + self.machine_st.verify_attr_interrupt(p, arity) + ); } fn next_clause_applicable(&mut self, mut offset: usize) -> bool { @@ -509,8 +522,8 @@ impl Machine { .select_switch_on_term_index(cell, v, c, l, s) } IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(hm)) => { - let lit = self.machine_st.constant_to_literal(cell); - hm.get(&lit).cloned().unwrap_or(IndexingCodePtr::Fail) + // let lit = self.machine_st.constant_to_literal(cell); + hm.get(&cell).cloned().unwrap_or(IndexingCodePtr::Fail) } IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(hm)) => { self.machine_st.select_switch_on_structure_index(cell, hm) @@ -536,34 +549,8 @@ impl Machine { if cell.is_var() { offset += 1; - } else if lit.get_tag() == HeapCellValueTag::CStr { - read_heap_cell!(cell, - (HeapCellValueTag::CStr) => { - if cell == lit { - offset += 1; - } else { - return false; - } - } - (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => { - offset += 1; - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]) - .get_name_and_arity(); - - if name == atom!(".") && arity == 2 { - offset += 1; - } else { - return false; - } - } - _ => { - return false; - } - ); } else { - self.machine_st.write_literal_to_var(cell, lit); + unify!(self.machine_st, cell, lit); if self.machine_st.fail { self.machine_st.fail = false; @@ -577,7 +564,7 @@ impl Machine { let cell = self.deref_register(t); read_heap_cell!(cell, - (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc | HeapCellValueTag::CStr) => { + (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => {// | HeapCellValueTag::CStr) => { offset += 1; } (HeapCellValueTag::Str, s) => { @@ -616,27 +603,24 @@ impl Machine { } ); } - &Instruction::GetPartialString( - Level::Shallow, - string, - RegType::Temp(t), - has_tail, - ) => { + &Instruction::GetPartialString(Level::Shallow, ref string, RegType::Temp(t)) => { let cell = self.deref_register(t); read_heap_cell!(cell, - (HeapCellValueTag::CStr, cstr) => { - if !has_tail && string != cstr { - return false; - } + (HeapCellValueTag::PStrLoc, pstr_loc) => { + let heap_slice = &self.machine_st.heap.as_slice()[pstr_loc ..]; - offset += 1; + match compare_pstr_slices(heap_slice, string.as_bytes()) { + PStrSegmentCmpResult::Continue(..) => offset += 1, + _ => return false, + } } - (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => { + (HeapCellValueTag::Lis) => { offset += 1; } (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity(); + let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]) + .get_name_and_arity(); if name == atom!(".") && arity == 2 { offset += 1; @@ -759,7 +743,7 @@ impl Machine { or_frame.prelude.boip = 0; or_frame.prelude.biip = 0; or_frame.prelude.tr = self.machine_st.tr; - or_frame.prelude.h = self.machine_st.heap.len(); + or_frame.prelude.h = self.machine_st.heap.cell_len(); or_frame.prelude.b0 = self.machine_st.b0; or_frame.prelude.attr_var_queue_len = self.machine_st.attr_var_init.attr_var_queue.len(); @@ -770,7 +754,7 @@ impl Machine { or_frame[i] = self.machine_st.registers[i + 1]; } - self.machine_st.hb = self.machine_st.heap.len(); + self.machine_st.hb = self.machine_st.heap.cell_len(); } self.machine_st.p += 1; @@ -791,7 +775,7 @@ impl Machine { or_frame.prelude.boip = self.machine_st.oip; or_frame.prelude.biip = self.machine_st.iip + iip_offset; // 1 or_frame.prelude.tr = self.machine_st.tr; - or_frame.prelude.h = self.machine_st.heap.len(); + or_frame.prelude.h = self.machine_st.heap.cell_len(); or_frame.prelude.b0 = self.machine_st.b0; or_frame.prelude.attr_var_queue_len = self.machine_st.attr_var_init.attr_var_queue.len(); @@ -802,7 +786,7 @@ impl Machine { or_frame[i] = self.machine_st.registers[i + 1]; } - self.machine_st.hb = self.machine_st.heap.len(); + self.machine_st.hb = self.machine_st.heap.cell_len(); // self.machine_st.oip = 0; // self.machine_st.iip = 0; @@ -1069,13 +1053,15 @@ impl Machine { if module_name == atom!("user") { if let Some(idx) = self.indices.code_dir.get(&(name, arity)).cloned() { - self.try_call(name, arity, idx.get()) + let index_ptr = self.machine_st.arena.code_index_tbl.get_entry(idx.into()); + self.try_call(name, arity, index_ptr) } else { Err(self.machine_st.throw_undefined_error(name, arity)) } } else if let Some(module) = self.indices.modules.get(&module_name) { if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() { - self.try_call(name, arity, idx.get()) + let index_ptr = self.machine_st.arena.code_index_tbl.get_entry(idx.into()); + self.try_call(name, arity, index_ptr) } else { self.undefined_procedure(name, arity) } @@ -1098,14 +1084,24 @@ impl Machine { let (name, arity) = key; if module_name == atom!("user") { - if let Some(idx) = self.indices.code_dir.get(&(name, arity)).cloned() { - self.try_execute(name, arity, idx.get()) + if let Some(offset) = self.indices.code_dir.get(&(name, arity)).cloned() { + let index_ptr = self + .machine_st + .arena + .code_index_tbl + .get_entry(offset.into()); + self.try_execute(name, arity, index_ptr) } else { self.undefined_procedure(name, arity) } } else if let Some(module) = self.indices.modules.get(&module_name) { - if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() { - self.try_execute(name, arity, idx.get()) + if let Some(offset) = module.code_dir.get(&(name, arity)).cloned() { + let index_ptr = self + .machine_st + .arena + .code_index_tbl + .get_entry(offset.into()); + self.try_execute(name, arity, index_ptr) } else { self.undefined_procedure(name, arity) } @@ -1147,12 +1143,24 @@ impl Machine { let r_c_w_h = self .indices .get_predicate_code_index(r_c_w_h_atom, 0, iso_ext) - .and_then(|item| item.local()) + .and_then(|code_idx| { + self.machine_st + .arena + .code_index_tbl + .get_entry(code_idx.into()) + .local() + }) .unwrap(); let r_c_wo_h = self .indices .get_predicate_code_index(r_c_wo_h_atom, 1, iso_ext) - .and_then(|item| item.local()) + .and_then(|code_idx| { + self.machine_st + .arena + .code_index_tbl + .get_entry(code_idx.into()) + .local() + }) .unwrap(); (r_c_w_h, r_c_wo_h) }); @@ -1162,8 +1170,10 @@ impl Machine { let (idx, arity) = if self.machine_st.effective_block() > prev_block { (r_c_w_h, 0) } else { - self.machine_st.registers[1] = - fixnum_as_cell!(Fixnum::build_with(b_cutoff as i64)); + self.machine_st.registers[1] = fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(b_cutoff as i64) } + ); (r_c_wo_h, 1) }; diff --git a/src/machine/partial_string.rs b/src/machine/partial_string.rs index 6a6ba96d..ee0c2be9 100644 --- a/src/machine/partial_string.rs +++ b/src/machine/partial_string.rs @@ -1,94 +1,40 @@ use crate::atom_table::*; -use crate::parser::ast::*; use crate::machine::heap::*; use crate::machine::machine_errors::CycleSearchResult; use crate::machine::system_calls::BrentAlgState; use crate::types::*; -use std::cmp::Ordering; use std::ops::Deref; -use std::str; - -#[derive(Copy, Clone, Debug)] -pub struct PartialString(Atom); - -fn scan_for_terminator>(iter: Iter) -> usize { - let mut terminator_idx = 0; - - for c in iter { - if c == '\u{0}' && terminator_idx != 0 { - return terminator_idx; - } - - terminator_idx += c.len_utf8(); - } - - terminator_idx -} - -impl From for PartialString { - #[inline] - fn from(buf: Atom) -> PartialString { - PartialString(buf) - } -} - -impl From for Atom { - #[inline] - fn from(val: PartialString) -> Self { - val.0 - } -} - -impl PartialString { - #[inline] - pub(super) fn new<'a>(src: &'a str, atom_tbl: &AtomTable) -> Option<(Self, &'a str)> { - let terminator_idx = scan_for_terminator(src.chars()); - let pstr = PartialString(AtomTable::build_with(atom_tbl, &src[..terminator_idx])); - Some(if terminator_idx < src.as_bytes().len() { - (pstr, &src[terminator_idx + 1..]) - } else { - (pstr, "") - }) - } - - #[inline(always)] - pub(crate) fn as_str_from(&self, n: usize) -> AtomString { - self.0.as_str().map(|str| &str[n..]) - } -} #[derive(Clone, Copy)] pub struct HeapPStrIter<'a> { - pub heap: &'a [HeapCellValue], - pub focus: HeapCellValue, + pub heap: &'a Heap, + // pub focus: HeapCellValue, orig_focus: usize, brent_st: BrentAlgState, stepper: fn(&mut HeapPStrIter<'a>) -> Option, } -#[derive(Debug, Clone, Copy)] -pub struct PStrPrefixCmpResult { - pub focus: usize, - pub offset: usize, - pub prefix_len: usize, -} - struct PStrIterStep { iteratee: PStrIteratee, next_hare: usize, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PStrIteratee { + Char { heap_loc: usize, value: char }, + PStrSlice { slice_loc: usize, slice_len: usize }, +} + impl<'a> HeapPStrIter<'a> { - pub fn new(heap: &'a [HeapCellValue], h: usize) -> Self { - let value = heap[h]; + pub fn new(heap: &'a Heap, orig_focus: usize) -> Self { + debug_assert!(heap[orig_focus].is_ref()); Self { heap, - focus: value, - orig_focus: h, - brent_st: BrentAlgState::new(h), + orig_focus, + brent_st: BrentAlgState::new(orig_focus), stepper: HeapPStrIter::pre_cycle_discovery_stepper, } } @@ -98,99 +44,6 @@ impl<'a> HeapPStrIter<'a> { self.brent_st.hare } - #[inline(always)] - pub fn at_string_terminator(&self) -> bool { - self.focus.is_string_terminator(self.heap) - } - - #[inline(always)] - pub fn chars(mut self) -> PStrCharsIter<'a> { - let item = self.next(); - PStrCharsIter { iter: self, item } - } - - pub fn compare_pstr_to_string(&mut self, s: &str) -> Option { - let mut result = PStrPrefixCmpResult { - focus: self.brent_st.hare, - offset: 0, - prefix_len: 0, - }; - - let mut final_result = None; - - while let Some(PStrIterStep { - iteratee, - next_hare, - }) = self.step(self.brent_st.hare) - { - self.brent_st.hare = next_hare; - self.focus = self.heap[iteratee.focus()]; - - result.focus = iteratee.focus(); - result.offset = iteratee.offset(); - - match iteratee { - PStrIteratee::Char(_, c1) => { - if let Some(c2) = s[result.prefix_len..].chars().next() { - if c1 != c2 { - return None; - } else { - result.prefix_len += c1.len_utf8(); - result.offset += c1.len_utf8(); - } - } else { - final_result = Some(result); - break; - } - } - PStrIteratee::PStrSegment(_, pstr_atom, n) => { - let pstr = PartialString::from(pstr_atom); - let t = pstr.as_str_from(n); - let s = &s[result.prefix_len..]; - - if s.len() >= t.len() { - if s.starts_with(&*t) { - result.prefix_len += t.len(); - result.offset += t.len(); - } else { - return None; - } - } else if t.starts_with(s) { - result.prefix_len += s.len(); - result.offset += s.len(); - - final_result = Some(result); - break; - } else { - return None; - } - } - } - - if s.len() == result.prefix_len { - final_result = Some(result); - break; - } - } - - if let Some(result) = &final_result { - if self.at_string_terminator() { - self.focus = empty_list_as_cell!(); - self.brent_st.hare = result.focus; - } else { - read_heap_cell!(self.heap[result.focus], - (HeapCellValueTag::Lis | HeapCellValueTag::Str | HeapCellValueTag::PStr) => { - self.focus = self.heap[self.brent_st.hare]; - } - _ => { - } - ); - } - } - - Some(result) - } - fn walk_hare_to_cycle_end(&mut self) { // walk_hare_to_cycle_end assumes a cycle has been found, // so it is always safe to unwrap self.step() @@ -209,21 +62,23 @@ impl<'a> HeapPStrIter<'a> { self.brent_st.hare = self.step(self.brent_st.hare).unwrap().next_hare; } - self.focus = self.heap[orig_hare]; + // self.focus = self.heap[orig_hare]; self.brent_st.hare = orig_hare; } pub fn to_string_mut(&mut self) -> String { let mut buf = String::with_capacity(32); - for iteratee in self.by_ref() { + while let Some(iteratee) = self.next() { match iteratee { - PStrIteratee::Char(_, c) => { + PStrIteratee::Char { value: c, .. } => { buf.push(c); } - PStrIteratee::PStrSegment(_, pstr_atom, n) => { - let pstr = PartialString::from(pstr_atom); - buf += &*pstr.as_str_from(n); + PStrIteratee::PStrSlice { + slice_loc, + slice_len, + } => { + buf += self.heap.slice_to_str(slice_loc, slice_len); } } } @@ -231,98 +86,19 @@ impl<'a> HeapPStrIter<'a> { buf } - #[inline] - pub fn is_continuable(&self) -> bool { - let mut focus = self.focus; - - loop { - read_heap_cell!(focus, - (HeapCellValueTag::CStr | HeapCellValueTag::PStrLoc) => { - return true; - } - (HeapCellValueTag::Atom, (name, arity)) => { // TODO: use Str here? - return name == atom!(".") && arity == 2; - } - (HeapCellValueTag::Lis, h) => { - let value = self.heap[h]; - let value = heap_bound_store( - self.heap, - heap_bound_deref(self.heap, value), - ); - - return read_heap_cell!(value, - (HeapCellValueTag::Atom, (name, arity)) => { - arity == 0 && name.as_char().is_some() - } - (HeapCellValueTag::Char) => { - true - } - _ => { - false - } - ); - } - (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { - if focus == self.heap[h] { - return false; - } - - focus = self.heap[h]; - } - _ => { - return false; - } - ); - } - } - - #[inline(always)] - pub fn cycle_detected(&self) -> bool { - self.stepper as usize == HeapPStrIter::post_cycle_discovery_stepper as usize - } - - fn step(&self, mut curr_hare: usize) -> Option { + // return the next step in the iteration or the updated curr_hare + // for the sake of pointing to the pstr tail + fn step(&self, mut curr_hare: usize) -> Result { loop { read_heap_cell!(self.heap[curr_hare], - (HeapCellValueTag::CStr, cstr_atom) => { - return if self.focus == empty_list_as_cell!() { - None - } else { - Some(PStrIterStep { - iteratee: PStrIteratee::PStrSegment(curr_hare, cstr_atom, 0), - next_hare: curr_hare, - }) - } - } (HeapCellValueTag::PStrLoc, h) => { - curr_hare = h; - } - (HeapCellValueTag::PStr, pstr_atom) => { - return Some(PStrIterStep { - iteratee: PStrIteratee::PStrSegment(curr_hare, pstr_atom, 0), - next_hare: curr_hare+1, + let HeapStringScan { string, tail_idx } = self.heap.scan_slice_to_str(h); + + return Ok(PStrIterStep { + iteratee: PStrIteratee::PStrSlice { slice_loc: h, slice_len: string.len() }, + next_hare: tail_idx, }); } - (HeapCellValueTag::PStrOffset, pstr_offset) => { - if self.focus == empty_list_as_cell!() { - return None; - } - - let pstr_atom = cell_as_atom!(self.heap[pstr_offset]); - let n = cell_as_fixnum!(self.heap[curr_hare+1]).get_num() as usize; - - return if self.heap[pstr_offset].get_tag() == HeapCellValueTag::CStr { - Some(PStrIterStep { - iteratee: PStrIteratee::PStrSegment(curr_hare, pstr_atom, n), - next_hare: pstr_offset, - }) - } else { - Some(PStrIterStep { - iteratee: PStrIteratee::PStrSegment(curr_hare, pstr_atom, n), - next_hare: pstr_offset+1, - }) - }; - } (HeapCellValueTag::Lis, h) => { let value = heap_bound_store( self.heap, @@ -330,9 +106,9 @@ impl<'a> HeapPStrIter<'a> { ); return value.as_char().map(|c| PStrIterStep { - iteratee: PStrIteratee::Char(curr_hare, c), + iteratee: PStrIteratee::Char { heap_loc: curr_hare, value: c }, next_hare: h+1, - }); + }).ok_or(curr_hare) } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.heap[s]) @@ -345,26 +121,26 @@ impl<'a> HeapPStrIter<'a> { ); value.as_char().map(|c| PStrIterStep { - iteratee: PStrIteratee::Char(curr_hare, c), + iteratee: PStrIteratee::Char { heap_loc: curr_hare, value: c }, next_hare: s+2, - }) + }).ok_or(curr_hare) } else { - None + Err(curr_hare) }; } (HeapCellValueTag::Atom, (_name, arity)) => { debug_assert!(arity == 0); - return None; + return Err(curr_hare); } (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { if h == curr_hare { - return None; + return Err(curr_hare); } curr_hare = h; } _ => { - return None; + return Err(curr_hare); } ); } @@ -375,30 +151,22 @@ impl<'a> HeapPStrIter<'a> { iteratee, next_hare, } = match self.step(self.brent_st.hare) { - Some(results) => results, - None => { + Ok(results) => results, + Err(next_hare) => { + self.brent_st.hare = next_hare; return None; } }; - self.focus = self.heap[iteratee.focus()]; - - if self.at_string_terminator() { - self.focus = empty_list_as_cell!(); - self.brent_st.hare = iteratee.focus(); - - return Some(iteratee); - } - match self.brent_st.step(next_hare) { Some(cycle_result) => { - debug_assert!(matches!(cycle_result, CycleSearchResult::Cyclic(..))); + debug_assert!(matches!(cycle_result, CycleSearchResult::Cyclic { .. })); self.walk_hare_to_cycle_end(); self.stepper = HeapPStrIter::post_cycle_discovery_stepper; } None => { - self.focus = self.heap[next_hare]; + // self.focus = self.heap[next_hare]; } } @@ -414,40 +182,21 @@ impl<'a> HeapPStrIter<'a> { iteratee, next_hare, } = match self.step(self.brent_st.hare) { - Some(results) => results, - None => { + Ok(results) => results, + Err(next_hare) => { + self.brent_st.hare = next_hare; return None; } }; - self.focus = self.heap[next_hare]; + // self.focus = self.heap[next_hare]; self.brent_st.hare = next_hare; Some(iteratee) } -} -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PStrIteratee { - Char(usize, char), - PStrSegment(usize, Atom, usize), -} - -impl PStrIteratee { - #[inline] - fn offset(&self) -> usize { - match self { - PStrIteratee::Char(_, _) => 0, - PStrIteratee::PStrSegment(_, _, n) => *n, - } - } - - #[inline] - fn focus(&self) -> usize { - match self { - PStrIteratee::Char(focus, _) => *focus, - PStrIteratee::PStrSegment(focus, _, _) => *focus, - } + pub(crate) fn is_cyclic(&self) -> bool { + self.stepper as usize == Self::post_cycle_discovery_stepper as usize } } @@ -465,24 +214,6 @@ pub struct PStrCharsIter<'a> { pub item: Option, } -impl<'a> PStrCharsIter<'a> { - pub fn peek(&self) -> Option { - if let Some(iteratee) = self.item { - match iteratee { - PStrIteratee::Char(_, c) => { - return Some(c); - } - PStrIteratee::PStrSegment(_, pstr_atom, n) => { - let pstr = PartialString::from(pstr_atom); - return pstr.as_str_from(n).chars().next(); - } - } - } - - None - } -} - impl<'a> Deref for PStrCharsIter<'a> { type Target = HeapPStrIter<'a>; @@ -497,18 +228,22 @@ impl<'a> Iterator for PStrCharsIter<'a> { fn next(&mut self) -> Option { while let Some(item) = self.item { match item { - PStrIteratee::Char(_, c) => { + PStrIteratee::Char { value, .. } => { self.item = self.iter.next(); - return Some(c); + return Some(value); } - PStrIteratee::PStrSegment(f1, pstr_atom, n) => { - let pstr = PartialString::from(pstr_atom); + PStrIteratee::PStrSlice { + slice_loc, + slice_len, + } => { + let s = self.iter.heap.slice_to_str(slice_loc, slice_len); - match pstr.as_str_from(n).chars().next() { + match s.chars().next() { Some(c) => { - self.item = - Some(PStrIteratee::PStrSegment(f1, pstr_atom, n + c.len_utf8())); - + self.item = Some(PStrIteratee::PStrSlice { + slice_loc: slice_loc + c.len_utf8(), + slice_len: slice_len - c.len_utf8(), + }); return Some(c); } None => { @@ -523,272 +258,6 @@ impl<'a> Iterator for PStrCharsIter<'a> { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PStrCmpResult { - Ordered(Ordering), - FirstIterContinuable(PStrIteratee), - SecondIterContinuable(PStrIteratee), - Unordered, -} - -impl PStrCmpResult { - #[inline] - pub fn is_second_iter(&self) -> bool { - matches!(self, PStrCmpResult::SecondIterContinuable(_)) - } -} - -#[inline] -pub fn compare_pstr_prefixes<'a>( - i1: &mut HeapPStrIter<'a>, - i2: &mut HeapPStrIter<'a>, -) -> PStrCmpResult { - #[inline(always)] - fn step(iter: &mut HeapPStrIter, hare: usize) -> Option { - let result = iter.step(hare); - iter.focus = iter.heap[hare]; - - if iter.focus.is_string_terminator(iter.heap) { - iter.focus = empty_list_as_cell!(); - } - - result - } - - #[inline(always)] - fn cycle_detection_step(i1: &mut HeapPStrIter, i2: &HeapPStrIter, step: &PStrIterStep) -> bool { - if i1.cycle_detected() { - i1.brent_st.hare = step.next_hare; - i2.cycle_detected() - } else if i1.brent_st.step(step.next_hare).is_some() { - i1.stepper = HeapPStrIter::post_cycle_discovery_stepper; - i2.cycle_detected() - } else { - false - } - } - - let mut r1 = step(i1, i1.brent_st.hare); - let mut r2 = step(i2, i2.brent_st.hare); - - loop { - if let Some(step_1) = r1.as_mut() { - if let Some(step_2) = r2.as_mut() { - match (step_1.iteratee, step_2.iteratee) { - (PStrIteratee::Char(_, c1), PStrIteratee::Char(_, c2)) => { - if c1 != c2 { - return PStrCmpResult::Ordered(c1.cmp(&c2)); - } - - cycle_detection_step(i1, i2, step_1); - let both_cyclic = cycle_detection_step(i2, i1, step_2); - - r1 = step(i1, i1.brent_st.hare); - r2 = step(i2, i2.brent_st.hare); - - if !both_cyclic { - continue; - } - } - (PStrIteratee::Char(_, c1), PStrIteratee::PStrSegment(f2, pstr_atom, n)) => { - let pstr = PartialString::from(pstr_atom); - - if let Some(c2) = pstr.as_str_from(n).chars().next() { - if c1 != c2 { - return PStrCmpResult::Ordered(c1.cmp(&c2)); - } - - let n1 = n + c2.len_utf8(); - - if n1 < pstr_atom.len() { - step_2.iteratee = PStrIteratee::PStrSegment(f2, pstr_atom, n1); - - let c1_result = cycle_detection_step(i1, i2, step_1); - r1 = step(i1, i1.brent_st.hare); - - if !c1_result { - continue; - } - } else { - cycle_detection_step(i1, i2, step_1); - let both_cyclic = cycle_detection_step(i2, i1, step_2); - - r1 = step(i1, i1.brent_st.hare); - r2 = step(i2, i2.brent_st.hare); - - if !both_cyclic { - continue; - } - } - } else { - let c2_result = cycle_detection_step(i2, i1, step_2); - r2 = step(i2, i2.brent_st.hare); - - if !c2_result { - continue; - } - } - } - (PStrIteratee::PStrSegment(f1, pstr_atom, n), PStrIteratee::Char(_, c2)) => { - let pstr = PartialString::from(pstr_atom); - - if let Some(c1) = pstr.as_str_from(n).chars().next() { - if c1 != c2 { - return PStrCmpResult::Ordered(c1.cmp(&c2)); - } - - let n1 = n + c1.len_utf8(); - - if n1 < pstr_atom.len() { - step_1.iteratee = PStrIteratee::PStrSegment(f1, pstr_atom, n1); - - let c2_result = cycle_detection_step(i2, i1, step_2); - r2 = step(i2, step_2.next_hare); - - if !c2_result { - continue; - } - } else { - cycle_detection_step(i1, i2, step_1); - let both_cyclic = cycle_detection_step(i2, i1, step_2); - - r1 = step(i1, i1.brent_st.hare); - r2 = step(i2, i2.brent_st.hare); - - if !both_cyclic { - continue; - } - } - } else { - let c1_result = cycle_detection_step(i1, i2, step_1); - r1 = step(i1, i1.brent_st.hare); - - if !c1_result { - continue; - } - } - } - ( - PStrIteratee::PStrSegment(f1, pstr1_atom, n1), - PStrIteratee::PStrSegment(f2, pstr2_atom, n2), - ) => { - if pstr1_atom == pstr2_atom && n1 == n2 { - cycle_detection_step(i1, i2, step_1); - let both_cyclic = cycle_detection_step(i2, i1, step_2); - - r1 = step(i1, i1.brent_st.hare); - r2 = step(i2, i2.brent_st.hare); - - if !both_cyclic { - continue; - } - - break; - } - - let pstr1 = PartialString::from(pstr1_atom); - let pstr2 = PartialString::from(pstr2_atom); - - let str1 = pstr1.as_str_from(n1); - let str2 = pstr2.as_str_from(n2); - - match str1.len().cmp(&str2.len()) { - Ordering::Equal if *str1 == *str2 => { - cycle_detection_step(i1, i2, step_1); - let both_cyclic = cycle_detection_step(i2, i1, step_2); - - r1 = step(i1, i1.brent_st.hare); - r2 = step(i2, i2.brent_st.hare); - - if !both_cyclic { - continue; - } - } - Ordering::Less if str2.starts_with(&*str1) => { - step_2.iteratee = - PStrIteratee::PStrSegment(f2, pstr2_atom, n2 + str1.len()); - let c1_result = cycle_detection_step(i1, i2, step_1); - r1 = step(i1, i1.brent_st.hare); - - if !c1_result { - continue; - } - } - Ordering::Greater if str1.starts_with(&*str2) => { - step_1.iteratee = - PStrIteratee::PStrSegment(f1, pstr1_atom, n1 + str2.len()); - let c2_result = cycle_detection_step(i2, i1, step_2); - r2 = step(i2, i2.brent_st.hare); - - if !c2_result { - continue; - } - } - _ => { - return PStrCmpResult::Ordered(str1.cmp(&*str2)); - } - } - } - } - } - } - - break; - } - - // to have a cyclic term, the cell at i1.focus must be: - // - // 1) 'continuable' as a cell in a string traversal, and, - // 2) matchable by compare_pstr_prefixes to the cell at i2.focus. - // - // If both cells are continuable they must have been encountered - // and thus matched by the compare_pstr_prefixes loop previously, - // so here it suffices to check if they are both continuable. - - let r1_at_end = r1.is_none(); - let r2_at_end = r2.is_none(); - - if r1_at_end && r2_at_end { - if i1.focus == i2.focus { - PStrCmpResult::Ordered(Ordering::Equal) - } else { - PStrCmpResult::Unordered - } - } else if r1_at_end { - if i1.focus == empty_list_as_cell!() { - PStrCmpResult::Ordered(Ordering::Less) - } else { - let r2_step = r2.unwrap(); - - // advance i2 to the next character so the same character - // isn't repeated - if matches!(r2_step.iteratee, PStrIteratee::Char(..)) { - cycle_detection_step(i2, i1, &r2_step); - } - - PStrCmpResult::SecondIterContinuable(r2_step.iteratee) - } - } else if r2_at_end { - if i2.focus == empty_list_as_cell!() { - PStrCmpResult::Ordered(Ordering::Greater) - } else { - let r1_step = r1.unwrap(); - - // advance i1 to the next character so the same character - // isn't repeated - if matches!(r1_step.iteratee, PStrIteratee::Char(..)) { - cycle_detection_step(i1, i2, &r1_step); - } - - PStrCmpResult::FirstIterContinuable(r1_step.iteratee) - } - } else if i1.is_continuable() && i2.is_continuable() { - PStrCmpResult::Ordered(Ordering::Equal) - } else { - PStrCmpResult::Unordered - } -} - #[cfg(test)] mod test { use super::*; @@ -799,233 +268,170 @@ mod test { fn pstr_iter_tests() { let mut wam = MockWAM::new(); - let pstr_var_cell = - put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); - - let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; - - { - let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0); - - assert_eq!( - iter.next(), - Some(PStrIteratee::PStrSegment(0, cell_as_atom!(pstr_cell), 0)) - ); - assert_eq!(iter.next(), None); - - assert!(!iter.at_string_terminator()); - } - - wam.machine_st.heap.pop(); - wam.machine_st.heap.push(pstr_loc_as_cell!(2)); - - let pstr_second_var_cell = - put_partial_string(&mut wam.machine_st.heap, "def", &wam.machine_st.atom_tbl); - - let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; - - { - let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0); - - assert_eq!( - iter.next(), - Some(PStrIteratee::PStrSegment(0, cell_as_atom!(pstr_cell), 0)) - ); - assert_eq!( - iter.next(), - Some(PStrIteratee::PStrSegment( - 2, - cell_as_atom!(pstr_second_cell), - 0 - )) - ); - - assert_eq!(iter.next(), None); - assert!(!iter.at_string_terminator()); - } - - wam.machine_st.heap.pop(); - wam.machine_st.heap.push(empty_list_as_cell!()); - - { - let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0); - - assert_eq!( - iter.next(), - Some(PStrIteratee::PStrSegment(0, cell_as_atom!(pstr_cell), 0)) - ); - assert_eq!( - iter.next(), - Some(PStrIteratee::PStrSegment( - 2, - cell_as_atom!(pstr_second_cell), - 0 - )) - ); - - assert_eq!(iter.next(), None); - assert!(iter.at_string_terminator()); - } - - wam.machine_st.heap.pop(); + let pstr_cell = wam.machine_st.heap.allocate_pstr("abc ").unwrap(); wam.machine_st .heap - .push(pstr_loc_as_cell!(wam.machine_st.heap.len() + 1)); + .push_cell(empty_list_as_cell!()) + .unwrap(); - wam.machine_st.heap.push(pstr_offset_as_cell!(0)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(0))); + // not overwriting anything! 0 is an interstitial cell + // reserved for use by the runtime + wam.machine_st.heap[0] = pstr_cell; { let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0); + assert_eq!( + iter.next(), + Some(PStrIteratee::PStrSlice { + slice_loc: heap_index!(1), + slice_len: "abc ".len() + }), + ); + assert_eq!(iter.next(), None); + assert!(!iter.is_cyclic()); + } + + assert_eq!(wam.machine_st.heap[2], empty_list_as_cell!()); + + wam.machine_st.heap[2] = pstr_loc_as_cell!(heap_index!(3)); + + wam.machine_st.heap.allocate_pstr("def").unwrap(); + let h = wam.machine_st.heap.cell_len(); + + wam.machine_st.heap.push_cell(heap_loc_as_cell!(h)).unwrap(); + + { + let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0); + + assert_eq!( + iter.next(), + Some(PStrIteratee::PStrSlice { + slice_loc: heap_index!(1), + slice_len: "abc ".len() + }) + ); + assert_eq!( + iter.next(), + Some(PStrIteratee::PStrSlice { + slice_loc: heap_index!(3), + slice_len: "def".len(), + }) + ); + + assert_eq!(iter.next(), None); + assert!(!iter.is_cyclic()); + } + + assert_eq!(wam.machine_st.heap[h], heap_loc_as_cell!(h)); + + wam.machine_st.heap[h] = empty_list_as_cell!(); + + { + let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0); + + assert_eq!( + iter.next(), + Some(PStrIteratee::PStrSlice { + slice_loc: heap_index!(1), + slice_len: "abc ".len() + }) + ); + assert_eq!( + iter.next(), + Some(PStrIteratee::PStrSlice { + slice_loc: heap_index!(3), + slice_len: "def".len(), + }) + ); + + assert_eq!(iter.next(), None); + assert!(!iter.is_cyclic()); + } + + wam.machine_st.heap[h] = pstr_loc_as_cell!(heap_index!(3)); + + { + let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0); for _ in iter.by_ref() {} - - assert!(!iter.at_string_terminator()); - } - - { - let mut iter1 = HeapPStrIter::new(&wam.machine_st.heap, 0); - let mut iter2 = HeapPStrIter::new(&wam.machine_st.heap, 0); - - assert_eq!( - compare_pstr_prefixes(&mut iter1, &mut iter2), - PStrCmpResult::Ordered(Ordering::Equal) - ); - } - - { - let second_h = wam.machine_st.heap.len(); - - // construct a structurally similar but different cyclic partial string - // matching the one beginning at wam.machine_st.heap[0]. - - put_partial_string(&mut wam.machine_st.heap, "ab", &wam.machine_st.atom_tbl); - - wam.machine_st.heap.pop(); - - wam.machine_st.heap.push(pstr_loc_as_cell!(second_h + 2)); - - put_partial_string(&mut wam.machine_st.heap, "c ", &wam.machine_st.atom_tbl); - - wam.machine_st.heap.pop(); - - wam.machine_st.heap.push(pstr_loc_as_cell!(second_h + 4)); - - wam.machine_st.heap.push(pstr_second_cell); - wam.machine_st.heap.push(pstr_loc_as_cell!(second_h + 6)); - - wam.machine_st.heap.push(pstr_offset_as_cell!(second_h)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(0))); - - let mut iter1 = HeapPStrIter::new(&wam.machine_st.heap, 0); - let mut iter2 = HeapPStrIter::new(&wam.machine_st.heap, second_h); - - assert_eq!( - compare_pstr_prefixes(&mut iter1, &mut iter2), - PStrCmpResult::Ordered(Ordering::Equal) - ); - } - - wam.machine_st.heap.clear(); - - put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); - - let pstr_cell = wam.machine_st.heap[0]; - - wam.machine_st.heap[1] = list_loc_as_cell!(2); - - wam.machine_st.heap.push(char_as_cell!('a')); - wam.machine_st.heap.push(list_loc_as_cell!(4)); - wam.machine_st.heap.push(char_as_cell!('b')); - wam.machine_st.heap.push(empty_list_as_cell!()); - - wam.machine_st.heap.push(pstr_cell); - wam.machine_st.heap.push(heap_loc_as_cell!(7)); - - { - let mut iter1 = HeapPStrIter::new(&wam.machine_st.heap, 0); - let mut iter2 = HeapPStrIter::new(&wam.machine_st.heap, 6); - - assert_eq!( - compare_pstr_prefixes(&mut iter1, &mut iter2), - PStrCmpResult::FirstIterContinuable(PStrIteratee::Char(1, 'a')), - ); - - assert_eq!(iter2.focus, heap_loc_as_cell!(7)); + assert!(iter.is_cyclic()); } // test "abc" = [X,Y,Z]. wam.machine_st.heap.clear(); - let cstr_var_cell = - put_complete_string(&mut wam.machine_st.heap, "abc", &wam.machine_st.atom_tbl); + let pstr_cell = wam.machine_st.heap.allocate_cstr("abc").unwrap(); + let start = wam.machine_st.heap.cell_len(); - wam.machine_st.heap.push(list_loc_as_cell!(2)); - wam.machine_st.heap.push(heap_loc_as_cell!(2)); + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); - wam.machine_st.heap.push(list_loc_as_cell!(4)); - wam.machine_st.heap.push(heap_loc_as_cell!(4)); + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1 + start)); + section.push_cell(heap_loc_as_cell!(1 + start)); - wam.machine_st.heap.push(list_loc_as_cell!(6)); - wam.machine_st.heap.push(heap_loc_as_cell!(6)); + section.push_cell(list_loc_as_cell!(3 + start)); + section.push_cell(heap_loc_as_cell!(3 + start)); - wam.machine_st.heap.push(empty_list_as_cell!()); + section.push_cell(list_loc_as_cell!(5 + start)); + section.push_cell(heap_loc_as_cell!(5 + start)); - unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1)); + section.push_cell(empty_list_as_cell!()); + }); - assert_eq!(wam.machine_st.heap[2], char_as_cell!('a'),); + unify!(wam.machine_st, pstr_cell, heap_loc_as_cell!(2)); - assert_eq!(wam.machine_st.heap[4], char_as_cell!('b'),); - - assert_eq!(wam.machine_st.heap[6], char_as_cell!('c'),); + assert_eq!(wam.machine_st.heap[1 + start], char_as_cell!('a')); + assert_eq!(wam.machine_st.heap[3 + start], char_as_cell!('b')); + assert_eq!(wam.machine_st.heap[5 + start], char_as_cell!('c')); // test "abc" = [X,Y,Z|D]. wam.machine_st.heap.clear(); - let cstr_var_cell = - put_complete_string(&mut wam.machine_st.heap, "abc", &wam.machine_st.atom_tbl); + let pstr_cell = wam.machine_st.heap.allocate_cstr("abc").unwrap(); + let start = wam.machine_st.heap.cell_len(); - wam.machine_st.heap.push(list_loc_as_cell!(2)); - wam.machine_st.heap.push(heap_loc_as_cell!(2)); // X + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); - wam.machine_st.heap.push(list_loc_as_cell!(4)); - wam.machine_st.heap.push(heap_loc_as_cell!(4)); // Y + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1 + start)); + section.push_cell(heap_loc_as_cell!(1 + start)); // X - wam.machine_st.heap.push(list_loc_as_cell!(6)); - wam.machine_st.heap.push(heap_loc_as_cell!(6)); // Z + section.push_cell(list_loc_as_cell!(3 + start)); + section.push_cell(heap_loc_as_cell!(3 + start)); // Y - wam.machine_st.heap.push(heap_loc_as_cell!(7)); // D + section.push_cell(list_loc_as_cell!(5 + start)); + section.push_cell(heap_loc_as_cell!(5 + start)); // Z - unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1)); + section.push_cell(heap_loc_as_cell!(6 + start)); // D + }); + + unify!(wam.machine_st, pstr_cell, heap_loc_as_cell!(2)); assert!(!wam.machine_st.fail); - assert_eq!(wam.machine_st.heap[2], char_as_cell!('a'),); - - assert_eq!(wam.machine_st.heap[4], char_as_cell!('b'),); - - assert_eq!(wam.machine_st.heap[6], char_as_cell!('c'),); - - assert_eq!(wam.machine_st.heap[7], empty_list_as_cell!(),); + assert_eq!(wam.machine_st.heap[3], char_as_cell!('a'),); + assert_eq!(wam.machine_st.heap[5], char_as_cell!('b'),); + assert_eq!(wam.machine_st.heap[7], char_as_cell!('c'),); + assert_eq!(wam.machine_st.heap[8], empty_list_as_cell!(),); // test "d" = [d]. wam.machine_st.heap.clear(); - let cstr_var_cell = - put_complete_string(&mut wam.machine_st.heap, "d", &wam.machine_st.atom_tbl); + let pstr_cell = wam.machine_st.heap.allocate_cstr("d").unwrap(); + let start = wam.machine_st.heap.cell_len(); - wam.machine_st.heap.push(list_loc_as_cell!(2)); - wam.machine_st.heap.push(char_as_cell!('d')); - wam.machine_st.heap.push(empty_list_as_cell!()); + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); - unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1)); + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1 + start)); + section.push_cell(char_as_cell!('d')); + section.push_cell(empty_list_as_cell!()); + }); + + unify!(wam.machine_st, pstr_cell, heap_loc_as_cell!(start)); assert!(!wam.machine_st.fail); @@ -1033,71 +439,92 @@ mod test { wam.machine_st.heap.clear(); - let cstr_var_cell = - put_complete_string(&mut wam.machine_st.heap, "abc", &wam.machine_st.atom_tbl); + let pstr_cell = wam.machine_st.heap.allocate_cstr("abc").unwrap(); + let start = wam.machine_st.heap.cell_len(); - wam.machine_st.heap.push(list_loc_as_cell!(2)); - wam.machine_st.heap.push(heap_loc_as_cell!(2)); + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); - wam.machine_st.heap.push(list_loc_as_cell!(4)); - wam.machine_st.heap.push(char_as_cell!('b')); + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(1 + start)); + section.push_cell(heap_loc_as_cell!(1 + start)); - wam.machine_st.heap.push(list_loc_as_cell!(6)); - wam.machine_st.heap.push(heap_loc_as_cell!(6)); + section.push_cell(list_loc_as_cell!(3 + start)); + section.push_cell(char_as_cell!('b')); - wam.machine_st.heap.push(empty_list_as_cell!()); + section.push_cell(list_loc_as_cell!(5 + start)); + section.push_cell(heap_loc_as_cell!(5 + start)); - unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1)); + section.push_cell(empty_list_as_cell!()); + }); + + unify!(wam.machine_st, pstr_cell, heap_loc_as_cell!(start)); assert!(!wam.machine_st.fail); - assert_eq!(wam.machine_st.heap[2], char_as_cell!('a'),); - - assert_eq!(wam.machine_st.heap[4], char_as_cell!('b'),); - - assert_eq!(wam.machine_st.heap[6], char_as_cell!('c'),); + assert_eq!(wam.machine_st.heap[1 + start], char_as_cell!('a')); + assert_eq!(wam.machine_st.heap[3 + start], char_as_cell!('b')); + assert_eq!(wam.machine_st.heap[5 + start], char_as_cell!('c')); // test "abcdef" = [a,b,c|X]. wam.machine_st.heap.clear(); - put_complete_string(&mut wam.machine_st.heap, "abcdef", &wam.machine_st.atom_tbl); + let pstr_cell = wam.machine_st.heap.allocate_cstr("abcdef").unwrap(); + let start = wam.machine_st.heap.cell_len(); - wam.machine_st.heap.push(pstr_as_cell!(atom!("abc"))); - wam.machine_st.heap.push(heap_loc_as_cell!(2)); + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); - unify!(wam.machine_st, heap_loc_as_cell!(0), pstr_loc_as_cell!(1)); + writer.write_with(|section| { + section.push_pstr("abc"); + let h = section.cell_len(); // h == 3 + section.push_cell(heap_loc_as_cell!(h)); + }); - print_heap_terms(wam.machine_st.heap.iter(), 0); + unify!( + wam.machine_st, + pstr_cell, + pstr_loc_as_cell!(heap_index!(start)) + ); assert!(!wam.machine_st.fail); - assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(5)); - assert_eq!(wam.machine_st.heap[3], pstr_loc_as_cell!(1)); - assert_eq!(wam.machine_st.heap[4], atom_as_cstr_cell!(atom!("abcdef"))); - assert_eq!(wam.machine_st.heap[5], pstr_offset_as_cell!(4)); assert_eq!( - wam.machine_st.heap[6], - fixnum_as_cell!(Fixnum::build_with("abc".len() as i64)) + wam.machine_st.heap.slice_to_str(0, "abcdef".len()), + "abcdef" + ); + assert_eq!( + wam.machine_st + .heap + .slice_to_str(heap_index!(start), "abc".len()), + "abc" + ); + assert_eq!( + wam.machine_st.heap[3], + pstr_loc_as_cell!(heap_index!(0) + 3) ); // test iteration on X = [b,c,b,c,b,c,b,c|...] as an offset. wam.machine_st.heap.clear(); - wam.machine_st.heap.push(pstr_as_cell!(atom!("abc"))); - wam.machine_st.heap.push(pstr_loc_as_cell!(2)); - wam.machine_st.heap.push(pstr_offset_as_cell!(0)); - wam.machine_st - .heap - .push(fixnum_as_cell!(Fixnum::build_with(1))); + wam.machine_st.heap.allocate_cstr("abc").unwrap(); + let start = wam.machine_st.heap.cell_len(); + + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); + + writer.write_with(|section| { + section.push_cell(pstr_loc_as_cell!('a'.len_utf8())); + }); { - let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 2); + let mut iter = HeapPStrIter::new(&wam.machine_st.heap, start); assert_eq!( iter.next(), - Some(PStrIteratee::PStrSegment(2, atom!("abc"), 1)) + Some(PStrIteratee::PStrSlice { + slice_loc: 'a'.len_utf8(), + slice_len: "bc".len() + }) ); for _ in iter {} @@ -1107,13 +534,23 @@ mod test { wam.machine_st.heap.clear(); - wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("a "))); - wam.machine_st.heap.push(heap_loc_as_cell!(1)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(char_as_cell!(' ')); - wam.machine_st.heap.push(empty_list_as_cell!()); + wam.machine_st.heap.allocate_cstr("a ").unwrap(); + let start = wam.machine_st.heap.cell_len(); - unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0)); + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); + + writer.write_with(|section| { + section.push_cell(heap_loc_as_cell!(start)); + section.push_cell(list_loc_as_cell!(2 + start)); + section.push_cell(char_as_cell!(' ')); + section.push_cell(empty_list_as_cell!()); + }); + + unify!( + wam.machine_st, + list_loc_as_cell!(start), + pstr_loc_as_cell!(0) + ); assert!(!wam.machine_st.fail); @@ -1121,98 +558,170 @@ mod test { wam.machine_st.heap.clear(); - wam.machine_st.heap.push(atom_as_cstr_cell!(atom!(" a"))); - wam.machine_st.heap.push(char_as_cell!(' ')); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(heap_loc_as_cell!(3)); - wam.machine_st.heap.push(empty_list_as_cell!()); + wam.machine_st.heap.allocate_cstr(" a").unwrap(); + let start = wam.machine_st.heap.cell_len(); - unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0)); + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); + writer.write_with(|section| { + section.push_cell(char_as_cell!(' ')); + section.push_cell(list_loc_as_cell!(2 + start)); + section.push_cell(heap_loc_as_cell!(2 + start)); + section.push_cell(empty_list_as_cell!()); + }); + + unify!( + wam.machine_st, + list_loc_as_cell!(start), + pstr_loc_as_cell!(0) + ); + + assert_eq!(wam.machine_st.heap[2 + start], char_as_cell!('a')); assert!(!wam.machine_st.fail); // #2293, test3. wam.machine_st.heap.clear(); - wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("a b"))); - wam.machine_st.heap.push(heap_loc_as_cell!(1)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(char_as_cell!(' ')); - wam.machine_st.heap.push(list_loc_as_cell!(5)); - wam.machine_st.heap.push(heap_loc_as_cell!(5)); - wam.machine_st.heap.push(empty_list_as_cell!()); + wam.machine_st.heap.allocate_cstr("a b").unwrap(); + let start = wam.machine_st.heap.cell_len(); - unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0)); + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); + writer.write_with(|section| { + section.push_cell(heap_loc_as_cell!(start)); + section.push_cell(list_loc_as_cell!(2 + start)); + section.push_cell(char_as_cell!(' ')); + section.push_cell(list_loc_as_cell!(4 + start)); + section.push_cell(heap_loc_as_cell!(4 + start)); + section.push_cell(empty_list_as_cell!()); + }); + + unify!( + wam.machine_st, + list_loc_as_cell!(start), + pstr_loc_as_cell!(0) + ); + + assert_eq!(wam.machine_st.heap[start], char_as_cell!('a')); + assert_eq!(wam.machine_st.heap[4 + start], char_as_cell!('b')); assert!(!wam.machine_st.fail); // #2293, test4. wam.machine_st.heap.clear(); - wam.machine_st.heap.push(atom_as_cstr_cell!(atom!(" a "))); - wam.machine_st.heap.push(char_as_cell!(' ')); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(heap_loc_as_cell!(3)); - wam.machine_st.heap.push(list_loc_as_cell!(5)); - wam.machine_st.heap.push(char_as_cell!(' ')); - wam.machine_st.heap.push(empty_list_as_cell!()); + wam.machine_st.heap.allocate_cstr(" a ").unwrap(); + let start = wam.machine_st.heap.cell_len(); - unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0)); + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); + writer.write_with(|section| { + section.push_cell(char_as_cell!(' ')); + section.push_cell(list_loc_as_cell!(2 + start)); + section.push_cell(heap_loc_as_cell!(2 + start)); + section.push_cell(list_loc_as_cell!(4 + start)); + section.push_cell(char_as_cell!(' ')); + section.push_cell(empty_list_as_cell!()); + }); + + unify!( + wam.machine_st, + list_loc_as_cell!(start), + pstr_loc_as_cell!(0) + ); + + assert_eq!(wam.machine_st.heap[2 + start], char_as_cell!('a')); assert!(!wam.machine_st.fail); // #2293, test5. wam.machine_st.heap.clear(); - wam.machine_st.heap.push(atom_as_cstr_cell!(atom!(" a bc"))); - wam.machine_st.heap.push(char_as_cell!(' ')); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(heap_loc_as_cell!(3)); - wam.machine_st.heap.push(list_loc_as_cell!(5)); - wam.machine_st.heap.push(char_as_cell!(' ')); - wam.machine_st.heap.push(heap_loc_as_cell!(6)); + wam.machine_st.heap.allocate_cstr(" a bc").unwrap(); + let start = wam.machine_st.heap.cell_len(); - unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0)); + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); + writer.write_with(|section| { + section.push_cell(char_as_cell!(' ')); + section.push_cell(list_loc_as_cell!(2 + start)); + section.push_cell(heap_loc_as_cell!(2 + start)); + section.push_cell(list_loc_as_cell!(4 + start)); + section.push_cell(char_as_cell!(' ')); + section.push_cell(heap_loc_as_cell!(5 + start)); + }); + + unify!( + wam.machine_st, + list_loc_as_cell!(start), + pstr_loc_as_cell!(0) + ); + + assert_eq!(wam.machine_st.heap[2 + start], char_as_cell!('a')); + assert_eq!( + wam.machine_st.heap[5 + start], + pstr_loc_as_cell!(heap_index!(0) + 3) + ); assert!(!wam.machine_st.fail); // #2293, test6. wam.machine_st.heap.clear(); - wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("abc"))); - wam.machine_st.heap.push(heap_loc_as_cell!(1)); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(char_as_cell!('b')); - wam.machine_st.heap.push(list_loc_as_cell!(5)); - wam.machine_st.heap.push(heap_loc_as_cell!(5)); - wam.machine_st.heap.push(empty_list_as_cell!()); + wam.machine_st.heap.allocate_cstr("abc").unwrap(); + let start = wam.machine_st.heap.cell_len(); - unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0)); + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); + writer.write_with(|section| { + section.push_cell(heap_loc_as_cell!(start)); + section.push_cell(list_loc_as_cell!(2 + start)); + section.push_cell(char_as_cell!('b')); + section.push_cell(list_loc_as_cell!(4 + start)); + section.push_cell(heap_loc_as_cell!(4 + start)); + section.push_cell(empty_list_as_cell!()); + }); + + unify!( + wam.machine_st, + list_loc_as_cell!(start), + pstr_loc_as_cell!(0) + ); + + assert_eq!(wam.machine_st.heap[start], char_as_cell!('a')); + assert_eq!(wam.machine_st.heap[4 + start], char_as_cell!('c')); assert!(!wam.machine_st.fail); // #2293, test7. wam.machine_st.heap.clear(); + wam.machine_st.heap.allocate_cstr("abcde").unwrap(); - wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("abcde"))); - wam.machine_st.heap.push(char_as_cell!('a')); - wam.machine_st.heap.push(list_loc_as_cell!(3)); - wam.machine_st.heap.push(heap_loc_as_cell!(3)); - wam.machine_st.heap.push(list_loc_as_cell!(5)); - wam.machine_st.heap.push(char_as_cell!('c')); - wam.machine_st.heap.push(list_loc_as_cell!(7)); - wam.machine_st.heap.push(heap_loc_as_cell!(7)); - wam.machine_st.heap.push(list_loc_as_cell!(9)); - wam.machine_st.heap.push(char_as_cell!('e')); - wam.machine_st.heap.push(empty_list_as_cell!()); + let start = wam.machine_st.heap.cell_len(); + let mut writer = wam.machine_st.heap.reserve(16).unwrap(); - unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0)); + writer.write_with(|section| { + section.push_cell(char_as_cell!('a')); + section.push_cell(list_loc_as_cell!(2 + start)); + section.push_cell(heap_loc_as_cell!(2 + start)); + section.push_cell(list_loc_as_cell!(4 + start)); + section.push_cell(char_as_cell!('c')); + section.push_cell(list_loc_as_cell!(6 + start)); + section.push_cell(heap_loc_as_cell!(6 + start)); + section.push_cell(list_loc_as_cell!(8 + start)); + section.push_cell(char_as_cell!('e')); + section.push_cell(empty_list_as_cell!()); + }); + unify!( + wam.machine_st, + list_loc_as_cell!(start), + pstr_loc_as_cell!(0) + ); + + assert_eq!(wam.machine_st.heap[2 + start], char_as_cell!('b')); + assert_eq!(wam.machine_st.heap[6 + start], char_as_cell!('d')); assert!(!wam.machine_st.fail); } } diff --git a/src/machine/preprocessor.rs b/src/machine/preprocessor.rs index 9fcd1825..3a043402 100644 --- a/src/machine/preprocessor.rs +++ b/src/machine/preprocessor.rs @@ -21,11 +21,10 @@ pub(crate) fn to_op_decl_spec(spec: Atom) -> Result, atom_tbl: &AtomTable) -> Result { +fn setup_op_decl(mut terms: Vec) -> Result { // should allow non-partial lists? let name = match terms.pop().unwrap() { Term::Literal(_, Literal::Atom(name)) => name, - Term::Literal(_, Literal::Char(c)) => AtomTable::build_with(atom_tbl, &c.to_string()), other => { return Err(CompilationError::InvalidDirective( DirectiveError::InvalidOpDeclNameType(other), @@ -112,16 +111,13 @@ fn setup_predicate_indicator(term: &mut Term) -> Result Result { +fn setup_module_export(mut term: Term) -> Result { setup_predicate_indicator(&mut term) .map(ModuleExport::PredicateKey) .or_else(|_| { if let Term::Clause(_, name, terms) = term { if terms.len() == 3 && name == atom!("op") { - Ok(ModuleExport::OpDecl(setup_op_decl(terms, atom_tbl)?)) + Ok(ModuleExport::OpDecl(setup_op_decl(terms)?)) } else { Err(CompilationError::InvalidModuleDecl) } @@ -140,12 +136,11 @@ pub(crate) fn build_rule_body(vars: &[Term], body_term: Term) -> Term { pub(super) fn setup_module_export_list( mut export_list: Term, - atom_tbl: &AtomTable, ) -> Result, CompilationError> { let mut exports = vec![]; while let Term::Cons(_, t1, t2) = export_list { - let module_export = setup_module_export(*t1, atom_tbl)?; + let module_export = setup_module_export(*t1)?; exports.push(module_export); export_list = *t2; @@ -158,10 +153,7 @@ pub(super) fn setup_module_export_list( } } -fn setup_module_decl( - mut terms: Vec, - atom_tbl: &AtomTable, -) -> Result { +fn setup_module_decl(mut terms: Vec) -> Result { let export_list = terms.pop().unwrap(); let name = terms.pop().unwrap(); @@ -171,8 +163,7 @@ fn setup_module_decl( } .ok_or(CompilationError::InvalidModuleDecl)?; - let exports = setup_module_export_list(export_list, atom_tbl)?; - + let exports = setup_module_export_list(export_list)?; Ok(ModuleDecl { name, exports }) } @@ -191,10 +182,7 @@ fn setup_use_module_decl(mut terms: Vec) -> Result); -fn setup_qualified_import( - mut terms: Vec, - atom_tbl: &AtomTable, -) -> Result { +fn setup_qualified_import(mut terms: Vec) -> Result { let mut export_list = terms.pop().unwrap(); let module_src = match terms.pop().unwrap() { Term::Clause(_, name, mut terms) if name == atom!("library") && terms.len() == 1 => { @@ -210,7 +198,7 @@ fn setup_qualified_import( let mut exports = IndexSet::new(); while let Term::Cons(_, t1, t2) = export_list { - exports.insert(setup_module_export(*t1, atom_tbl)?); + exports.insert(setup_module_export(*t1)?); export_list = *t2; } @@ -252,7 +240,7 @@ fn setup_qualified_import( * contained in src/lib/ops_and_meta_predicates.pl, which is loaded before * src/lib/builtins.pl. * - * Meta-specs have three forms: + * Meta-specs have four forms: * * (:) (the argument should be expanded with (:)/2 as described above) * + (mode declarations under the mode syntax, which currently have no effect) @@ -340,23 +328,15 @@ pub(super) fn setup_declaration<'a, LS: LoadState<'a>>( let (name, arity) = setup_predicate_indicator(&mut terms.pop().unwrap())?; Ok(Declaration::Dynamic(name, arity)) } - (atom!("module"), 2) => { - let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl; - Ok(Declaration::Module(setup_module_decl(terms, atom_tbl)?)) - } - (atom!("op"), 3) => { - let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl; - Ok(Declaration::Op(setup_op_decl(terms, atom_tbl)?)) - } + (atom!("module"), 2) => Ok(Declaration::Module(setup_module_decl(terms)?)), + (atom!("op"), 3) => Ok(Declaration::Op(setup_op_decl(terms)?)), (atom!("non_counted_backtracking"), 1) => { let (name, arity) = setup_predicate_indicator(&mut terms.pop().unwrap())?; Ok(Declaration::NonCountedBacktracking(name, arity)) } (atom!("use_module"), 1) => Ok(Declaration::UseModule(setup_use_module_decl(terms)?)), (atom!("use_module"), 2) => { - let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl; - let (name, exports) = setup_qualified_import(terms, atom_tbl)?; - + let (name, exports) = setup_qualified_import(terms)?; Ok(Declaration::UseQualifiedModule(name, exports)) } (atom!("meta_predicate"), 1) => { @@ -444,7 +424,7 @@ fn build_meta_predicate_clause<'a, LS: LoadState<'a>>( let term = match term { Term::Clause(cell, name, mut terms) => { - if let Some(Term::Literal(_, Literal::CodeIndex(_))) = terms.last() { + if let Some(Term::Literal(_, Literal::CodeIndexOffset(_))) = terms.last() { arg_terms .push(process_term(module_name, Term::Clause(cell, name, terms))); @@ -453,7 +433,10 @@ fn build_meta_predicate_clause<'a, LS: LoadState<'a>>( let idx = loader.get_or_insert_qualified_code_index(module_name, key); - terms.push(Term::Literal(Cell::default(), Literal::CodeIndex(idx))); + terms.push(Term::Literal( + Cell::default(), + Literal::CodeIndexOffset(idx.into()), + )); process_term(module_name, Term::Clause(cell, name, terms)) } Term::Literal(cell, Literal::Atom(name)) => { @@ -464,7 +447,10 @@ fn build_meta_predicate_clause<'a, LS: LoadState<'a>>( Term::Clause( cell, name, - vec![Term::Literal(Cell::default(), Literal::CodeIndex(idx))], + vec![Term::Literal( + Cell::default(), + Literal::CodeIndexOffset(idx.into()), + )], ), ) } @@ -489,7 +475,7 @@ pub(super) fn clause_to_query_term<'a, LS: LoadState<'a>>( mut terms: Vec, call_policy: CallPolicy, ) -> QueryTerm { - if let Some(Term::Literal(_, Literal::CodeIndex(_))) = terms.last() { + if let Some(Term::Literal(_, Literal::CodeIndexOffset(_))) = terms.last() { // supplementary code vector indices are unnecessary for // root-level clauses. terms.pop(); @@ -524,7 +510,7 @@ pub(super) fn qualified_clause_to_query_term<'a, LS: LoadState<'a>>( mut terms: Vec, call_policy: CallPolicy, ) -> QueryTerm { - if let Some(Term::Literal(_, Literal::CodeIndex(_))) = terms.last() { + if let Some(Term::Literal(_, Literal::CodeIndexOffset(_))) = terms.last() { // supplementary code vector indices are unnecessary for // root-level clauses. terms.pop(); @@ -605,7 +591,7 @@ impl Preprocessor { &mut self, loader: &mut Loader<'a, LS>, term: Term, - ) -> Result { + ) -> Result { match term { Term::Clause(r, name, mut terms) => { let is_rule = name == atom!(":-") && terms.len() == 2; @@ -615,16 +601,16 @@ impl Preprocessor { let head = terms.pop().unwrap(); let (rule, var_data) = self.setup_rule(loader, head, tail)?; - Ok(TopLevel::Rule(rule, var_data)) + Ok(PredicateClause::Rule(rule, var_data)) } else { let term = Term::Clause(r, name, terms); let (fact, var_data) = self.setup_fact(term)?; - Ok(TopLevel::Fact(fact, var_data)) + Ok(PredicateClause::Fact(fact, var_data)) } } term => { let (fact, var_data) = self.setup_fact(term)?; - Ok(TopLevel::Fact(fact, var_data)) + Ok(PredicateClause::Fact(fact, var_data)) } } } diff --git a/src/machine/stack.rs b/src/machine/stack.rs index 210a7cac..4247149f 100644 --- a/src/machine/stack.rs +++ b/src/machine/stack.rs @@ -59,9 +59,10 @@ impl Index for AndFrame { unsafe { let ptr = self as *const crate::machine::stack::AndFrame as *const u8; - let ptr = ptr as usize + prelude_offset + index_offset; - &*(ptr as *const HeapCellValue) + // This address falls outside the provenance for self, therefore we have to get it + // from exposed provenance. + &*std::ptr::with_exposed_provenance(ptr.addr() + prelude_offset + index_offset) } } } @@ -72,10 +73,11 @@ impl IndexMut for AndFrame { let index_offset = (index - 1) * mem::size_of::(); unsafe { - let ptr = self as *mut crate::machine::stack::AndFrame as *const u8; - let ptr = ptr as usize + prelude_offset + index_offset; + let ptr = self as *mut crate::machine::stack::AndFrame as *mut u8; - &mut *(ptr as *mut HeapCellValue) + // This address falls outside the provenance for self, therefore we have to get it + // from exposed provenance. + &mut *std::ptr::with_exposed_provenance_mut(ptr.addr() + prelude_offset + index_offset) } } } @@ -85,20 +87,14 @@ impl Index for Stack { #[inline] fn index(&self, index: usize) -> &Self::Output { - unsafe { - let ptr = self.buf.base as usize + index; - &*(ptr as *const HeapCellValue) - } + unsafe { &*self.buf.base.add(index).cast() } } } impl IndexMut for Stack { #[inline] fn index_mut(&mut self, index: usize) -> &mut Self::Output { - unsafe { - let ptr = self.buf.base as usize + index; - &mut *(ptr as *mut HeapCellValue) - } + unsafe { &mut *self.buf.base.add(index).cast_mut().cast() } } } @@ -132,9 +128,10 @@ impl Index for OrFrame { unsafe { let ptr = self as *const crate::machine::stack::OrFrame as *const u8; - let ptr = ptr as usize + prelude_offset + index_offset; - &*(ptr as *const HeapCellValue) + // This address falls outside the provenance for self, therefore we have to get it + // from exposed provenance. + &*std::ptr::with_exposed_provenance(ptr.addr() + prelude_offset + index_offset) } } } @@ -146,10 +143,11 @@ impl IndexMut for OrFrame { let index_offset = index * mem::size_of::(); unsafe { - let ptr = self as *mut crate::machine::stack::OrFrame as *const u8; - let ptr = ptr as usize + prelude_offset + index_offset; + let ptr = self as *mut crate::machine::stack::OrFrame as *mut u8; - &mut *(ptr as *mut HeapCellValue) + // This address falls outside the provenance for self, therefore we have to get it + // from exposed provenance. + &mut *std::ptr::with_exposed_provenance_mut(ptr.addr() + prelude_offset + index_offset) } } } @@ -187,15 +185,19 @@ impl Stack { let frame_size = AndFrame::size_of(num_cells); unsafe { - let e = (*self.buf.ptr.get_mut()) as usize - self.buf.base as usize; + let e = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr(); let new_ptr = self.alloc(frame_size); let mut offset = prelude_size::(); for idx in 0..num_cells { - ptr::write( - new_ptr.add(offset) as *mut HeapCellValue, - stack_loc_as_cell!(AndFrame, e, idx + 1), - ); + let cell_ptr = new_ptr.add(offset) as *mut HeapCellValue; + ptr::write(cell_ptr, stack_loc_as_cell!(AndFrame, e, idx + 1)); + + // Because in the Index and IndexMut inplementations we need to get this from + // exposed provenance, we need to expose the provenance here, even though we don't + // actually use the value for anything. This is a reminder that `expose_provenance` + // isn't just a cast from a pointer to an integer but has actual side effects. + cell_ptr.expose_provenance(); offset += mem::size_of::(); } @@ -208,22 +210,26 @@ impl Stack { } pub(crate) fn top(&self) -> usize { - unsafe { (*self.buf.ptr.get()) as usize - self.buf.base as usize } + unsafe { (*self.buf.ptr.get()).addr() - self.buf.base.addr() } } pub(crate) fn allocate_or_frame(&mut self, num_cells: usize) -> usize { let frame_size = OrFrame::size_of(num_cells); unsafe { - let b = (*self.buf.ptr.get_mut()) as usize - self.buf.base as usize; + let b = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr(); let new_ptr = self.alloc(frame_size); let mut offset = prelude_size::(); for idx in 0..num_cells { - ptr::write( - (new_ptr as usize + offset) as *mut HeapCellValue, - stack_loc_as_cell!(OrFrame, b, idx), - ); + let cell_ptr = new_ptr.byte_add(offset) as *mut HeapCellValue; + ptr::write(cell_ptr, stack_loc_as_cell!(OrFrame, b, idx)); + + // Because in the Index and IndexMut inplementations we need to get this from + // exposed provenance, we need to expose the provenance here, even though we don't + // actually use the value for anything. This is a reminder that `expose_provenance` + // isn't just a cast from a pointer to an integer but has actual side effects. + cell_ptr.expose_provenance(); offset += mem::size_of::(); } @@ -237,10 +243,7 @@ impl Stack { #[inline(always)] pub(crate) fn index_and_frame(&self, e: usize) -> &AndFrame { - unsafe { - let ptr = self.buf.base as usize + e; - &*(ptr as *const AndFrame) - } + unsafe { &*self.buf.base.add(e).cast() } } #[inline(always)] @@ -254,26 +257,20 @@ impl Stack { #[inline(always)] pub(crate) fn index_or_frame(&self, b: usize) -> &OrFrame { - unsafe { - let ptr = self.buf.base as usize + b; - &*(ptr as *const OrFrame) - } + unsafe { &*self.buf.base.add(b).cast() } } #[inline(always)] pub(crate) fn index_or_frame_mut(&mut self, b: usize) -> &mut OrFrame { - unsafe { - let ptr = self.buf.base as usize + b; - &mut *(ptr as *mut OrFrame) - } + unsafe { &mut *self.buf.base.add(b).cast_mut().cast() } } #[inline(always)] pub(crate) fn truncate(&mut self, b: usize) { - let base = self.buf.base as usize + b; + let base = unsafe { self.buf.base.add(b) }; - if base < (*self.buf.ptr.get_mut()) as usize { - *self.buf.ptr.get_mut() = base as *mut _; + if base < (*self.buf.ptr.get_mut()) { + *self.buf.ptr.get_mut() = base.cast_mut(); } } } diff --git a/src/machine/streams.rs b/src/machine/streams.rs index 678b2297..596b09de 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -1,12 +1,12 @@ use crate::arena::*; use crate::atom_table::*; +use crate::functor_macro::*; use crate::parser::ast::*; use crate::parser::char_reader::*; use crate::read::*; #[cfg(feature = "http")] use crate::http::HttpResponse; -use crate::machine::heap::*; use crate::machine::machine_errors::*; use crate::machine::machine_indices::*; use crate::machine::machine_state::*; @@ -23,6 +23,8 @@ use std::fmt::Debug; use std::fs::{File, OpenOptions}; use std::hash::Hash; use std::io; +use std::io::PipeReader; +use std::io::PipeWriter; use std::io::{Cursor, ErrorKind, Read, Seek, SeekFrom, Write}; use std::mem::ManuallyDrop; use std::net::{Shutdown, TcpStream}; @@ -476,7 +478,7 @@ impl StreamOptions { #[inline] pub fn get_alias(self) -> Option { if self.has_alias() { - Some(Atom::from(self.alias() << 3)) + Some(Atom::from(self.alias())) } else { None } @@ -487,7 +489,7 @@ impl StreamOptions { self.set_has_alias(alias.is_some()); if let Some(alias) = alias { - self.set_alias(alias.flat_index()); + self.set_alias(alias.index); } } } @@ -588,6 +590,8 @@ arena_allocated_impl_for_stream!(StandardOutputStream, StandardOutputStream); arena_allocated_impl_for_stream!(StandardErrorStream, StandardErrorStream); arena_allocated_impl_for_stream!(CharReader, CallbackStream); arena_allocated_impl_for_stream!(CharReader, InputChannelStream); +arena_allocated_impl_for_stream!(CharReader, PipeReader); +arena_allocated_impl_for_stream!(CharReader, PipeWriter); #[derive(Debug, Copy, Clone)] pub enum Stream { @@ -608,6 +612,8 @@ pub enum Stream { StandardError(TypedArenaPtr), Callback(TypedArenaPtr), InputChannel(TypedArenaPtr), + PipeReader(TypedArenaPtr), + PipeWriter(TypedArenaPtr), } impl From> for Stream { @@ -688,6 +694,8 @@ impl Stream { ArenaHeaderTag::InputChannelStream => { Stream::InputChannel(unsafe { ptr.as_typed_ptr() }) } + ArenaHeaderTag::PipeReader => Stream::PipeReader(unsafe { ptr.as_typed_ptr() }), + ArenaHeaderTag::PipeWriter => Stream::PipeWriter(unsafe { ptr.as_typed_ptr() }), _ => unreachable!(), } } @@ -726,6 +734,8 @@ impl Stream { Stream::StandardError(ptr) => ptr.header_ptr(), Stream::Callback(ptr) => ptr.header_ptr(), Stream::InputChannel(ptr) => ptr.header_ptr(), + Stream::PipeReader(ptr) => ptr.header_ptr(), + Stream::PipeWriter(ptr) => ptr.header_ptr(), } } @@ -748,6 +758,8 @@ impl Stream { Stream::StandardError(ref ptr) => &ptr.options, Stream::Callback(ref ptr) => &ptr.options, Stream::InputChannel(ref ptr) => &ptr.options, + Stream::PipeReader(ref ptr) => &ptr.options, + Stream::PipeWriter(ref ptr) => &ptr.options, } } @@ -770,6 +782,8 @@ impl Stream { Stream::StandardError(ref mut ptr) => &mut ptr.options, Stream::Callback(ref mut ptr) => &mut ptr.options, Stream::InputChannel(ref mut ptr) => &mut ptr.options, + Stream::PipeReader(ref mut ptr) => &mut ptr.options, + Stream::PipeWriter(ref mut ptr) => &mut ptr.options, } } @@ -793,6 +807,8 @@ impl Stream { Stream::StandardError(ptr) => ptr.lines_read += incr_num_lines_read, Stream::Callback(ptr) => ptr.lines_read += incr_num_lines_read, Stream::InputChannel(ptr) => ptr.lines_read += incr_num_lines_read, + Stream::PipeReader(ptr) => ptr.lines_read += incr_num_lines_read, + Stream::PipeWriter(_) => {} } } @@ -816,6 +832,8 @@ impl Stream { Stream::StandardError(ptr) => ptr.lines_read = value, Stream::Callback(ptr) => ptr.lines_read = value, Stream::InputChannel(ptr) => ptr.lines_read = value, + Stream::PipeReader(ptr) => ptr.lines_read = value, + Stream::PipeWriter(_) => {} } } @@ -839,6 +857,8 @@ impl Stream { Stream::StandardError(ptr) => ptr.lines_read, Stream::Callback(ptr) => ptr.lines_read, Stream::InputChannel(ptr) => ptr.lines_read, + Stream::PipeReader(ptr) => ptr.lines_read, + Stream::PipeWriter(_) => 0, } } } @@ -856,6 +876,8 @@ impl CharRead for Stream { Stream::StaticString(src) => (*src).peek_char(), Stream::Byte(cursor) => (*cursor).peek_char(), Stream::InputChannel(cursor) => (*cursor).peek_char(), + Stream::PipeReader(cursor) => (*cursor).peek_char(), + #[cfg(feature = "http")] Stream::HttpWrite(_) => Some(Err(std::io::Error::new( ErrorKind::PermissionDenied, @@ -865,7 +887,8 @@ impl CharRead for Stream { | Stream::StandardError(_) | Stream::StandardOutput(_) | Stream::Null(_) - | Stream::Callback(_) => Some(Err(std::io::Error::new( + | Stream::Callback(_) + | Stream::PipeWriter(_) => Some(Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::ReadFromOutputStream, ))), @@ -884,6 +907,7 @@ impl CharRead for Stream { Stream::StaticString(src) => (*src).read_char(), Stream::Byte(cursor) => (*cursor).read_char(), Stream::InputChannel(cursor) => (*cursor).read_char(), + Stream::PipeReader(cursor) => (*cursor).read_char(), #[cfg(feature = "http")] Stream::HttpWrite(_) => Some(Err(std::io::Error::new( ErrorKind::PermissionDenied, @@ -893,7 +917,8 @@ impl CharRead for Stream { | Stream::StandardError(_) | Stream::StandardOutput(_) | Stream::Null(_) - | Stream::Callback(_) => Some(Err(std::io::Error::new( + | Stream::Callback(_) + | Stream::PipeWriter(_) => Some(Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::ReadFromOutputStream, ))), @@ -911,13 +936,15 @@ impl CharRead for Stream { Stream::Readline(rl_stream) => rl_stream.put_back_char(c), Stream::StaticString(src) => src.put_back_char(c), Stream::Byte(cursor) => cursor.put_back_char(c), + Stream::PipeReader(cursor) => cursor.put_back_char(c), #[cfg(feature = "http")] Stream::HttpWrite(_) => {} Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) | Stream::Null(_) - | Stream::Callback(_) => {} + | Stream::Callback(_) + | Stream::PipeWriter(_) => {} Stream::InputChannel(_) => {} } } @@ -934,13 +961,15 @@ impl CharRead for Stream { Stream::StaticString(ref mut src) => src.consume(nread), Stream::Byte(ref mut cursor) => cursor.consume(nread), Stream::InputChannel(ref mut cursor) => cursor.consume(nread), + Stream::PipeReader(ref mut cursor) => cursor.consume(nread), #[cfg(feature = "http")] Stream::HttpWrite(_) => {} Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) | Stream::Null(_) - | Stream::Callback(_) => {} + | Stream::Callback(_) + | Stream::PipeWriter(_) => {} } } } @@ -959,6 +988,7 @@ impl Read for Stream { Stream::StaticString(src) => (*src).read(buf), Stream::Byte(cursor) => (*cursor).read(buf), Stream::InputChannel(cursor) => (*cursor).read(buf), + Stream::PipeReader(cursor) => (*cursor).read(buf), #[cfg(feature = "http")] Stream::HttpWrite(_) => Err(std::io::Error::new( ErrorKind::PermissionDenied, @@ -967,7 +997,8 @@ impl Read for Stream { Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) - | Stream::Callback(_) => Err(std::io::Error::new( + | Stream::Callback(_) + | Stream::PipeWriter(_) => Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::ReadFromOutputStream, )), @@ -989,6 +1020,7 @@ impl Write for Stream { Stream::StandardError(stream) => stream.write(buf), #[cfg(feature = "http")] Stream::HttpWrite(ref mut stream) => stream.get_mut().write(buf), + Stream::PipeWriter(ref mut stream) => stream.get_mut().write(buf), #[cfg(feature = "http")] Stream::HttpRead(_) => Err(std::io::Error::new( ErrorKind::PermissionDenied, @@ -998,7 +1030,8 @@ impl Write for Stream { Stream::StaticString(_) | Stream::InputChannel(_) | Stream::Readline(_) - | Stream::InputFile(..) => Err(std::io::Error::new( + | Stream::InputFile(..) + | Stream::PipeReader(_) => Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::WriteToInputStream, )), @@ -1015,6 +1048,7 @@ impl Write for Stream { Stream::Callback(ref mut callback_stream) => callback_stream.stream.get_mut().flush(), Stream::StandardError(stream) => stream.stream.flush(), Stream::StandardOutput(stream) => stream.stream.flush(), + Stream::PipeWriter(ref mut stream) => stream.stream.get_mut().flush(), #[cfg(feature = "http")] Stream::HttpWrite(ref mut stream) => stream.stream.get_mut().flush(), #[cfg(feature = "http")] @@ -1026,7 +1060,8 @@ impl Write for Stream { Stream::StaticString(_) | Stream::InputChannel(_) | Stream::Readline(_) - | Stream::InputFile(_) => Err(std::io::Error::new( + | Stream::InputFile(_) + | Stream::PipeReader(_) => Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::FlushToInputStream, )), @@ -1192,6 +1227,8 @@ impl Stream { Stream::StandardError(stream) => stream.past_end_of_stream, Stream::Callback(stream) => stream.past_end_of_stream, Stream::InputChannel(stream) => stream.past_end_of_stream, + Stream::PipeReader(stream) => stream.past_end_of_stream, + Stream::PipeWriter(stream) => stream.past_end_of_stream, } } @@ -1220,6 +1257,8 @@ impl Stream { Stream::StandardError(stream) => stream.past_end_of_stream = value, Stream::Callback(stream) => stream.past_end_of_stream = value, Stream::InputChannel(stream) => stream.past_end_of_stream = value, + Stream::PipeReader(stream) => stream.past_end_of_stream = value, + Stream::PipeWriter(stream) => stream.past_end_of_stream = value, } } @@ -1330,7 +1369,8 @@ impl Stream { | Stream::InputChannel(_) | Stream::Readline(_) | Stream::StaticString(_) - | Stream::InputFile(..) => atom!("read"), + | Stream::InputFile(..) + | Stream::PipeReader(_) => atom!("read"), Stream::NamedTcp(..) => atom!("read_append"), Stream::OutputFile(file) if file.is_append => atom!("append"), #[cfg(feature = "http")] @@ -1338,7 +1378,8 @@ impl Stream { Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) - | Stream::Callback(_) => { + | Stream::Callback(_) + | Stream::PipeWriter(_) => { atom!("write") } Stream::Null(_) => atom!(""), @@ -1372,6 +1413,20 @@ impl Stream { )) } + pub(crate) fn from_pipe_writer(writer: io::PipeWriter, arena: &mut Arena) -> Stream { + Stream::PipeWriter(arena_alloc!( + ManuallyDrop::new(StreamLayout::new(CharReader::new(writer))), + arena + )) + } + + pub(crate) fn from_pipe_reader(reader: io::PipeReader, arena: &mut Arena) -> Stream { + Stream::PipeReader(arena_alloc!( + ManuallyDrop::new(StreamLayout::new(CharReader::new(reader))), + arena + )) + } + #[inline] pub(crate) fn from_tcp_stream(address: Atom, tcp_stream: TcpStream, arena: &mut Arena) -> Self { tcp_stream.set_read_timeout(None).unwrap(); @@ -1512,6 +1567,16 @@ impl Stream { Ok(()) } + Stream::PipeReader(mut stream) => { + stream.drop_payload(); + Ok(()) + } + + Stream::PipeWriter(mut stream) => { + stream.drop_payload(); + Ok(()) + } + Stream::Null(_) => Ok(()), Stream::Readline(_) | Stream::StandardOutput(_) | Stream::StandardError(_) => { @@ -1538,6 +1603,7 @@ impl Stream { | Stream::Readline(_) | Stream::StaticString(_) | Stream::InputFile(..) + | Stream::PipeReader(_) | Stream::Null(_) => true, _ => false, } @@ -1556,6 +1622,7 @@ impl Stream { | Stream::Byte(_) | Stream::OutputFile(..) | Stream::Callback(_) + | Stream::PipeWriter(_) | Stream::Null(_) => true, _ => false, } @@ -1653,7 +1720,8 @@ impl MachineState { }; stream.set_past_end_of_stream(true); - Ok(unify!(self, result, end_of_stream)) + unify!(self, result, end_of_stream); + Ok(()) } EOFAction::Reset => { if !stream.reset() { @@ -1808,59 +1876,60 @@ impl MachineState { let addr = self.store(MachineState::deref(self, addr)); read_heap_cell!(addr, - (HeapCellValueTag::Atom, (name, arity)) => { - debug_assert_eq!(arity, 0); + (HeapCellValueTag::Atom, (name, arity)) => { + debug_assert_eq!(arity, 0); - return match indices.get_stream(name) { - Some(stream) => Ok(stream), - _ => { - let stub = functor_stub(caller, arity); - let addr = atom_as_cell!(name); + return match indices.get_stream(name) { + Some(stream) => Ok(stream), + _ => { + let stub = functor_stub(caller, arity); + let addr = atom_as_cell!(name); - let existence_error = self.existence_error(ExistenceError::Stream(addr)); + let existence_error = self.existence_error(ExistenceError::Stream(addr)); - Err(self.error_form(existence_error, stub)) - } - }; - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(self.heap[s]) - .get_name_and_arity(); + Err(self.error_form(existence_error, stub)) + } + }; + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.heap[s]) + .get_name_and_arity(); - debug_assert_eq!(arity, 0); + debug_assert_eq!(arity, 0); - return match indices.get_stream(name) { - Some(stream) => Ok(stream), - _ => { - let stub = functor_stub(caller, arity); - let addr = atom_as_cell!(name); + return match indices.get_stream(name) { + Some(stream) => Ok(stream), + _ => { + let stub = functor_stub(caller, arity); + let addr = atom_as_cell!(name); - let existence_error = self.existence_error(ExistenceError::Stream(addr)); + let existence_error = self.existence_error(ExistenceError::Stream(addr)); - Err(self.error_form(existence_error, stub)) - } - }; - } - (HeapCellValueTag::Cons, ptr) => { - match_untyped_arena_ptr!(ptr, - (ArenaHeaderTag::Stream, stream) => { - if stream.is_null_stream() { - unreachable!("Null streams have no Cons representation"); - } - return Ok(stream); - } - (ArenaHeaderTag::Dropped, _value) => { - let stub = functor_stub(caller, arity); - let err = self.existence_error(ExistenceError::Stream(addr)); + Err(self.error_form(existence_error, stub)) + } + }; + } + (HeapCellValueTag::Cons, ptr) => { + match_untyped_arena_ptr!(ptr, + (ArenaHeaderTag::Stream, stream) => { + return if stream.is_null_stream() { + Err(self.open_permission_error(stream_as_cell!(stream), caller, arity)) + } else { + Ok(stream) + }; + } + (ArenaHeaderTag::Dropped, _value) => { + let stub = functor_stub(caller, arity); + let err = self.existence_error(ExistenceError::Stream(addr)); - return Err(self.error_form(err, stub)); - } - _ => { - } - ); - } - _ => { - } + return Err(self.error_form(err, stub)); + } + _ => { + } + ); + } + _ => { + } ); let stub = functor_stub(caller, arity); @@ -1953,7 +2022,7 @@ impl MachineState { let err = self.permission_error( Permission::Open, atom!("source_sink"), - functor!(atom!("alias"), [atom(alias)]), + functor!(atom!("alias"), [atom_as_cell(alias)]), ); self.error_form(err, stub) @@ -1961,7 +2030,7 @@ impl MachineState { pub(crate) fn reposition_error(&mut self, stub_name: Atom, stub_arity: usize) -> MachineStub { let stub = functor_stub(stub_name, stub_arity); - let rep_stub = functor!(atom!("reposition"), [atom(atom!("true"))]); + let rep_stub = functor!(atom!("reposition"), [atom_as_cell((atom!("true")))]); let err = self.permission_error(Permission::Open, atom!("source_sink"), rep_stub); self.error_form(err, stub) @@ -2086,7 +2155,7 @@ impl MachineState { _ => { // assume the OS is out of file descriptors. let stub = functor_stub(atom!("open"), 4); - let err = self.resource_error(ResourceError::OutOfFiles); + let err = Self::resource_error(ResourceError::OutOfFiles); return Err(self.error_form(err, stub)); } diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index cc39cba5..3f367a39 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1,6 +1,3 @@ -use crate::parser::ast::*; -use crate::parser::parser::*; - use base64::Engine; use dashu::integer::{Sign, UBig}; use lazy_static::lazy_static; @@ -11,6 +8,7 @@ use crate::atom_table::*; #[cfg(feature = "ffi")] use crate::ffi::*; use crate::forms::*; +use crate::functor_macro::*; use crate::heap_iter::*; use crate::heap_print::*; #[cfg(feature = "http")] @@ -27,8 +25,11 @@ use crate::machine::partial_string::*; use crate::machine::stack::*; use crate::machine::streams::*; use crate::machine::{get_structure_index, Machine, VERIFY_ATTR_INTERRUPT_LOC}; +use crate::offset_table::*; +use crate::parser::ast::*; use crate::parser::char_reader::*; use crate::parser::dashu::Integer; +use crate::parser::parser::*; use crate::read::*; use crate::types::*; use rand::rngs::StdRng; @@ -55,6 +56,8 @@ use std::net::{SocketAddr, ToSocketAddrs}; use std::net::{TcpListener, TcpStream}; use std::num::NonZeroU32; use std::process; +use std::process::Child; +use std::process::Stdio; #[cfg(feature = "http")] use std::str::FromStr; #[cfg(feature = "http")] @@ -127,15 +130,10 @@ pub(crate) enum ModuleQuantification { } impl ModuleQuantification { - fn to_functor(&self) -> (Vec, HeapCellValueTag) { + fn to_functor(&self) -> Vec { match self { - &ModuleQuantification::Specified(cell) => ( - functor!(atom!("specified"), [cell(cell)]), - HeapCellValueTag::Str, - ), - ModuleQuantification::Unspecified => { - (functor!(atom!("unspecified")), HeapCellValueTag::Var) - } + &ModuleQuantification::Specified(cell) => functor!(atom!("specified"), [cell(cell)]), + ModuleQuantification::Unspecified => functor!(atom!("unspecified")), } } @@ -170,6 +168,61 @@ pub(crate) fn get_key() -> KeyEvent { key } +fn pstr_segment_char_count_and_tail(heap: &Heap, pstr_loc: usize) -> (usize, usize) { + let char_iter = heap.char_iter(pstr_loc); + + let mut char_count = 0; + let mut byte_offset = 0; + + for c in char_iter { + if c == '\u{0}' { + break; + } + + char_count += 1; + byte_offset += c.len_utf8(); + } + + (char_count, Heap::pstr_tail_idx(pstr_loc + byte_offset)) +} + +fn pstr_segment_char_count_up_to( + heap: &Heap, + pstr_loc: usize, + max_chars: usize, +) -> PStrSegmentCountResult { + let mut char_iter = heap.char_iter(pstr_loc); + let mut char_count = 0; + let mut byte_offset = 0; + + if max_chars > 0 { + for c in &mut char_iter { + if c == '\u{0}' { + break; + } + + char_count += 1; + byte_offset += c.len_utf8(); + + if char_count >= max_chars { + break; + } + } + } + + if char_iter.next().is_some() { + PStrSegmentCountResult::Mid { + char_count, + pstr_loc: pstr_loc + byte_offset, + } + } else { + PStrSegmentCountResult::End { + char_count, + tail_loc: Heap::pstr_tail_idx(pstr_loc + byte_offset), + } + } +} + #[derive(Debug, Clone, Copy)] pub struct BrentAlgState { pub hare: usize, @@ -207,7 +260,7 @@ impl BrentAlgState { self.lam += 1; if self.tortoise == self.hare { - return Some(CycleSearchResult::Cyclic(self.lam)); + return Some(CycleSearchResult::Cyclic { lambda: self.lam }); } else { self.teleport_tortoise(); } @@ -225,28 +278,26 @@ impl BrentAlgState { self.max_steps > -1 && self.num_steps() as i64 >= self.max_steps } - pub fn to_result(mut self, heap: &[HeapCellValue]) -> CycleSearchResult { + pub fn to_result(mut self, heap: &Heap) -> CycleSearchResult { loop { read_heap_cell!(heap[self.hare], - (HeapCellValueTag::PStrOffset) => { - let (pstr_loc, offset) = pstr_loc_and_offset(heap, self.hare); - let offset = offset.get_num() as usize; - - let pstr = cell_as_string!(heap[self.hare]); - self.pstr_chars += pstr.as_str_from(offset).chars().count(); - - return CycleSearchResult::PStrLocation(self.num_steps(), pstr_loc, offset); - } - (HeapCellValueTag::PStrLoc, l) => { - let (_pstr_loc, offset) = pstr_loc_and_offset(heap, l); - let offset = offset.get_num() as usize; - return CycleSearchResult::PStrLocation(self.num_steps(), l, offset); + (HeapCellValueTag::PStrLoc) => { + // let (_pstr_loc, offset) = pstr_loc_and_offset(heap, l); + // let offset = offset.get_num() as usize; + let num_steps = self.num_steps(); + return CycleSearchResult::PStrLocation { num_steps, pstr_loc: heap[self.hare] }; } (HeapCellValueTag::Atom, (name, arity)) => { return if name == atom!("[]") && arity == 0 { - CycleSearchResult::ProperList(self.num_steps()) + CycleSearchResult::ProperList { num_steps: self.num_steps() } } else { - CycleSearchResult::NotList(self.num_steps(), heap[self.hare]) + let heap_loc = if arity > 0 { + str_loc_as_cell!(self.hare) + } else { + heap_loc_as_cell!(self.hare) + }; + + CycleSearchResult::NotList { num_steps: self.num_steps(), heap_loc } }; } (HeapCellValueTag::Str, s) => { @@ -254,96 +305,78 @@ impl BrentAlgState { .get_name_and_arity(); return if name == atom!("[]") && arity == 0 { - CycleSearchResult::ProperList(self.num_steps()) + CycleSearchResult::ProperList { num_steps: self.num_steps() } } else { - CycleSearchResult::NotList(self.num_steps(), heap[self.hare]) + CycleSearchResult::NotList { + num_steps: self.num_steps(), + heap_loc: heap[self.hare], + } }; } (HeapCellValueTag::Lis, l) => { - return CycleSearchResult::UntouchedList(self.num_steps(), l); + return CycleSearchResult::UntouchedList { num_steps: self.num_steps(), list_loc: l }; } (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { if h == self.hare { - let var = heap[self.hare].as_var().unwrap(); - return CycleSearchResult::PartialList(self.num_steps(), var); + // let var = heap[self.hare].as_var().unwrap(); + return CycleSearchResult::PartialList { + num_steps: self.num_steps(), + heap_loc: heap[self.hare], + }; } else { self.hare = h; } } _ => { - return CycleSearchResult::NotList(self.num_steps(), heap[self.hare]); + let heap_loc = heap_loc_as_cell!(self.hare); + return CycleSearchResult::NotList { num_steps: self.num_steps(), heap_loc }; } ); } } - fn add_pstr_offset_chars( - &mut self, - heap: &[HeapCellValue], - h: usize, - offset: usize, - ) -> Option { - read_heap_cell!(heap[h], - (HeapCellValueTag::CStr, cstr_atom) => { - let cstr = PartialString::from(cstr_atom); - let num_chars = cstr.as_str_from(offset).chars().count(); + fn add_pstr_chars(&mut self, heap: &Heap, pstr_loc: usize) -> Option { + let next_cell_loc; - if self.max_steps == -1 || self.num_steps() + num_chars <= self.max_steps as usize { - self.pstr_chars += num_chars; - Some(CycleSearchResult::ProperList(self.num_steps())) - } else { - let char_offset = self.max_steps as usize - self.num_steps(); - self.pstr_chars += char_offset; - Some(CycleSearchResult::PStrLocation(self.max_steps as usize, h, char_offset + offset)) + if self.max_steps == -1 { + let num_chars; + (num_chars, next_cell_loc) = pstr_segment_char_count_and_tail(heap, pstr_loc); + self.pstr_chars += num_chars - 1; + } else { + let max_chars = self.max_steps as usize - self.num_steps(); + + match pstr_segment_char_count_up_to(heap, pstr_loc, max_chars) { + PStrSegmentCountResult::Mid { + char_count, + pstr_loc, + } => { + self.pstr_chars += char_count; + return Some(CycleSearchResult::PStrLocation { + num_steps: self.num_steps(), + pstr_loc: pstr_loc_as_cell!(pstr_loc), + }); + } + PStrSegmentCountResult::End { + char_count, + tail_loc, + } => { + self.pstr_chars += char_count.saturating_sub(1); + next_cell_loc = tail_loc; } } - (HeapCellValueTag::PStr, pstr_atom) => { - let pstr = PartialString::from(pstr_atom); - let num_chars = pstr.as_str_from(offset).chars().count(); + } - if self.max_steps == -1 || self.num_steps() + num_chars <= self.max_steps as usize { - self.pstr_chars += num_chars - 1; - self.step(h+1) - } else { - let char_offset = self.max_steps as usize - self.num_steps(); - self.pstr_chars += char_offset; - Some(CycleSearchResult::PStrLocation(self.max_steps as usize, h, char_offset + offset)) - } - } - _ => { - unreachable!() - } - ) - } - - fn add_pstr_chars_and_step( - &mut self, - heap: &[HeapCellValue], - h: usize, - ) -> Option { - read_heap_cell!(heap[h], - (HeapCellValueTag::PStrOffset, l) => { - let (pstr_loc, _) = pstr_loc_and_offset(heap, l); - let offset = cell_as_fixnum!(heap[h+1]); - self.add_pstr_offset_chars(heap, pstr_loc, offset.get_num() as usize) - } - _ => { - self.add_pstr_offset_chars(heap, h, 0) - } - ) + self.step(next_cell_loc) } #[inline(always)] - fn cycle_step(&mut self, heap: &[HeapCellValue]) -> Option { + fn cycle_step(&mut self, heap: &Heap) -> Option { loop { let value = heap[self.hare]; read_heap_cell!(value, (HeapCellValueTag::PStrLoc, h) => { - return self.add_pstr_chars_and_step(heap, h); - } - (HeapCellValueTag::CStr | HeapCellValueTag::PStrOffset) => { - return self.add_pstr_chars_and_step(heap, self.hare); + return self.add_pstr_chars(heap, h); } (HeapCellValueTag::Lis, h) => { return self.step(h+1); @@ -354,63 +387,49 @@ impl BrentAlgState { return if name == atom!(".") && arity == 2 { self.step(s+2) } else { - Some(CycleSearchResult::NotList(self.num_steps(), value)) + Some(CycleSearchResult::NotList { num_steps: self.num_steps(), heap_loc: value }) }; } (HeapCellValueTag::Atom, (name, arity)) => { debug_assert!(arity == 0); return if name == atom!("[]") { - Some(CycleSearchResult::ProperList(self.num_steps())) + Some(CycleSearchResult::ProperList { num_steps: self.num_steps() }) } else { - Some(CycleSearchResult::NotList(self.num_steps(), value)) + Some(CycleSearchResult::NotList { num_steps: self.num_steps(), heap_loc: value }) }; } (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { if self.hare == h { - let r = value.as_var().unwrap(); - return Some(CycleSearchResult::PartialList(self.num_steps(), r)); + return Some(CycleSearchResult::PartialList { num_steps: self.num_steps(), heap_loc: value }); } self.hare = h; } _ => { - return Some(CycleSearchResult::NotList(self.num_steps(), value)); + return Some(CycleSearchResult::NotList { num_steps: self.num_steps(), heap_loc: value }); } ); } } - pub fn detect_cycles(heap: &[HeapCellValue], value: HeapCellValue) -> CycleSearchResult { - let mut pstr_chars = 0; + pub fn detect_cycles(heap: &Heap, value: HeapCellValue) -> CycleSearchResult { + let mut char_count = 0; let hare = read_heap_cell!(value, (HeapCellValueTag::Lis, offset) => { offset+1 } (HeapCellValueTag::PStrLoc, h) => { - let (h_offset, n) = pstr_loc_and_offset(heap, h); - let n = n.get_num() as usize; - let pstr = cell_as_string!(heap[h_offset]); + let tail_idx; + (char_count, tail_idx) = pstr_segment_char_count_and_tail(heap, h); - pstr_chars = pstr.as_str_from(n).chars().count() - 1; - - if heap[h].get_tag() == HeapCellValueTag::PStrOffset { - debug_assert!(heap[h].get_tag() == HeapCellValueTag::PStrOffset); - - if heap[h_offset].get_tag() == HeapCellValueTag::CStr { - return CycleSearchResult::ProperList(pstr_chars + 1); - } + if heap[tail_idx] == empty_list_as_cell!() { + return CycleSearchResult::ProperList { num_steps: char_count }; } - h_offset+1 - } - (HeapCellValueTag::PStrOffset) => { - unreachable!() - } - (HeapCellValueTag::CStr, cstr_atom) => { - let cstr = PartialString::from(cstr_atom); - return CycleSearchResult::ProperList(cstr.as_str_from(0).chars().count()); + char_count = char_count.saturating_sub(1); + tail_idx } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(heap[s]) @@ -421,28 +440,29 @@ impl BrentAlgState { } else if name == atom!(".") && arity == 2 { s + 2 } else { - return CycleSearchResult::NotList(0, value); + return CycleSearchResult::NotList { num_steps: 0, heap_loc: value }; } } (HeapCellValueTag::Atom, (name, arity)) => { return if name == atom!("[]") && arity == 0 { CycleSearchResult::EmptyList } else { - CycleSearchResult::NotList(0, value) + debug_assert_eq!(arity, 0); + CycleSearchResult::NotList { num_steps: 0, heap_loc: value } }; } (HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar | HeapCellValueTag::Var) => { - return CycleSearchResult::PartialList(0, value.as_var().unwrap()); + return CycleSearchResult::PartialList { num_steps: 0, heap_loc: value }; } _ => { - return CycleSearchResult::NotList(0, value); + return CycleSearchResult::NotList { num_steps: 0, heap_loc: value }; } ); let mut brent_st = BrentAlgState::new(hare); brent_st.power += 1; // advance a step. - brent_st.pstr_chars = pstr_chars; + brent_st.pstr_chars = char_count; loop { if let Some(result) = brent_st.cycle_step(heap) { @@ -452,58 +472,31 @@ impl BrentAlgState { } pub fn detect_cycles_with_max( - heap: &[HeapCellValue], + heap: &Heap, max_steps: usize, value: HeapCellValue, ) -> CycleSearchResult { - let mut pstr_chars = 0; + let mut char_count = 0; let hare = read_heap_cell!(value, (HeapCellValueTag::Lis, offset) => { if max_steps > 0 { offset+1 } else { - return CycleSearchResult::UntouchedList(0, offset); + return CycleSearchResult::UntouchedList { num_steps: 0, list_loc: offset }; } } (HeapCellValueTag::PStrLoc, h) => { - let (h_offset, n) = pstr_loc_and_offset(heap, h); - let n = n.get_num() as usize; - let pstr = cell_as_string!(heap[h_offset]); - - pstr_chars = pstr.as_str_from(n).chars().count() - 1; - - if heap[h].get_tag() == HeapCellValueTag::PStrOffset && heap[h_offset].get_tag() == HeapCellValueTag::CStr { - return if pstr_chars < max_steps { - CycleSearchResult::ProperList(pstr_chars + 1) - } else { - let offset = max_steps + n; - CycleSearchResult::PStrLocation(max_steps, h_offset, offset) + match pstr_segment_char_count_up_to(heap, h, max_steps) { + PStrSegmentCountResult::Mid { char_count, pstr_loc } => { + let pstr_loc = pstr_loc_as_cell!(pstr_loc); + return CycleSearchResult::PStrLocation { num_steps: char_count, pstr_loc }; + } + PStrSegmentCountResult::End { char_count: num_chars, tail_loc } => { + char_count = num_chars - 1; + tail_loc } } - - if pstr_chars + 1 > max_steps { - return CycleSearchResult::PStrLocation(max_steps, h_offset, max_steps); - } - - h_offset+1 - } - (HeapCellValueTag::PStrOffset) => { - unreachable!() - } - (HeapCellValueTag::CStr, cstr_atom) => { - return if max_steps > 0 { - let cstr = PartialString::from(cstr_atom); - let pstr_chars = cstr.as_str_from(0).chars().count(); - - if pstr_chars <= max_steps { - CycleSearchResult::ProperList(pstr_chars) - } else { - CycleSearchResult::UntouchedCStr(cstr_atom, max_steps) - } - } else { - CycleSearchResult::UntouchedCStr(cstr_atom, 0) - }; } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(heap[s]).get_name_and_arity(); @@ -514,31 +507,32 @@ impl BrentAlgState { if max_steps > 0 { s + 2 } else { - return CycleSearchResult::UntouchedList(0, s + 1); + return CycleSearchResult::UntouchedList { num_steps: 0, list_loc: s + 1 }; } } else { - return CycleSearchResult::NotList(0, value); + return CycleSearchResult::NotList { num_steps: 0, heap_loc: value }; } } (HeapCellValueTag::Atom, (name, arity)) => { return if name == atom!("[]") && arity == 0 { CycleSearchResult::EmptyList } else { - CycleSearchResult::NotList(0, value) + debug_assert_eq!(arity, 0); + CycleSearchResult::NotList { num_steps: 0, heap_loc: value } }; } (HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar | HeapCellValueTag::Var) => { - return CycleSearchResult::PartialList(0, value.as_var().unwrap()); + return CycleSearchResult::PartialList { num_steps: 0, heap_loc: value }; } _ => { - return CycleSearchResult::NotList(0, value); + return CycleSearchResult::NotList { num_steps: 0, heap_loc: value }; } ); let mut brent_st = BrentAlgState::new(hare); brent_st.power += 1; // advance a step. - brent_st.pstr_chars = pstr_chars; + brent_st.pstr_chars = char_count; brent_st.max_steps = max_steps as i64; loop { @@ -559,13 +553,65 @@ enum MatchSite { Match(usize), // a match } +#[derive(Debug)] +enum PStrSegmentCountResult { + Mid { char_count: usize, pstr_loc: usize }, + End { char_count: usize, tail_loc: usize }, +} + #[derive(Debug)] struct AttrListMatch { match_site: MatchSite, prev_tail: Option, } +#[derive(Debug)] +pub(crate) struct FindallCopyInfo { + offset: usize, + pstr_threshold: usize, +} + impl MachineState { + fn copy_lifted_heap_from_offset(&mut self, offset: usize, lh_offset: usize) { + let reserve_size = self.lifted_heap.cell_len() - lh_offset; + let mut writer = step_or_resource_error!(self, self.heap.reserve(reserve_size)); + + writer.write_with(|section| { + let mut lh_offset = lh_offset; + + while lh_offset + 4 < self.lifted_heap.cell_len() { + let cell_threshold = + unsafe { self.lifted_heap[lh_offset + 3].to_fixnum_or_cut_point_unchecked() } + .get_num() as usize; + let pstr_upper_threshold = + unsafe { self.lifted_heap[lh_offset + 4].to_fixnum_or_cut_point_unchecked() } + .get_num() as usize; + + for idx in lh_offset..cell_threshold { + section.push_cell(self.lifted_heap[idx] + offset); + } + + let mut pstr_threshold = heap_index!(cell_threshold); + + while pstr_threshold < heap_index!(pstr_upper_threshold) { + let HeapStringScan { string, tail_idx } = + self.lifted_heap.scan_slice_to_str(pstr_threshold); + + section.push_pstr(string); + section.push_cell(self.lifted_heap[tail_idx] + offset); + + pstr_threshold = heap_index!(tail_idx + 1); + } + + lh_offset = pstr_upper_threshold; + } + + for idx in lh_offset..self.lifted_heap.cell_len() { + section.push_cell(self.lifted_heap[idx] + offset); + } + }); + } + #[inline(always)] pub(crate) fn unattributed_var(&mut self) { let attr_var = self.store(self.deref(self.registers[1])); @@ -585,24 +631,29 @@ impl MachineState { ); } - pub(crate) fn get_attr_var_list(&mut self, attr_var: HeapCellValue) -> Option { + pub(crate) fn get_attr_var_list( + &mut self, + attr_var: HeapCellValue, + ) -> Result, usize> { read_heap_cell!(attr_var, (HeapCellValueTag::AttrVar, h) => { - Some(h + 1) + Ok(Some(h + 1)) } (HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { // create an AttrVar in the heap. - let h = self.heap.len(); + let h = self.heap.cell_len(); + let mut writer = self.heap.reserve(2)?; - self.heap.push(attr_var_as_cell!(h)); - self.heap.push(heap_loc_as_cell!(h+1)); + writer.write_with(|section| { + section.push_cell(attr_var_as_cell!(h)); + section.push_cell(heap_loc_as_cell!(h+1)); + }); self.bind(Ref::attr_var(h), attr_var); - - Some(h + 1) + Ok(Some(h + 1)) } _ => { - None + Ok(None) } ) } @@ -637,12 +688,12 @@ impl MachineState { } fn skip_max_list_cycle(&mut self, lam: usize) { - fn step(heap: &[HeapCellValue], mut value: HeapCellValue) -> usize { + fn step(heap: &Heap, mut value: HeapCellValue) -> usize { loop { read_heap_cell!(value, (HeapCellValueTag::PStrLoc, h) => { - let (h_offset, _) = pstr_loc_and_offset(heap, h); - return h_offset+1; + let HeapStringScan { tail_idx, .. } = heap.scan_slice_to_str(h); + return tail_idx; } (HeapCellValueTag::Lis, h) => { return h+1; @@ -660,13 +711,14 @@ impl MachineState { } } - let h = self.heap.len(); - self.heap.push(self.registers[3]); + // let h = self.heap.cell_len(); + // self.heap.push(self.registers[3]); - let mut hare = h; + let orig_hare = step(&self.heap, self.registers[3]); + let mut hare = orig_hare; let mut tortoise = hare; - for _ in 0..lam { + for _ in 1..lam { hare = step(&self.heap, self.heap[hare]); } @@ -682,7 +734,7 @@ impl MachineState { // reached in the fashion of a C do-while loop since hare // may point to the beginning of a cycle. - let mut brent_st = BrentAlgState::new(h); + let mut brent_st = BrentAlgState::new(orig_hare); brent_st.cycle_step(&self.heap); @@ -690,10 +742,14 @@ impl MachineState { brent_st.cycle_step(&self.heap); } - self.heap.pop(); + // self.heap.pop_cell(); let target_n = self.store(self.deref(self.registers[1])); - self.unify_fixnum(Fixnum::build_with(brent_st.num_steps() as i64), target_n); + self.unify_fixnum( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(brent_st.num_steps() as i64) }, + target_n, + ); if !self.fail { unify!(self, self.registers[4], self.heap[prev_hare]); @@ -702,7 +758,10 @@ impl MachineState { fn finalize_skip_max_list(&mut self, n: i64, value: HeapCellValue) { let target_n = self.store(self.deref(self.registers[1])); - self.unify_fixnum(Fixnum::build_with(n), target_n); + self.unify_fixnum( + /* FIXME this is not safe */ unsafe { Fixnum::build_with_unchecked(n) }, + target_n, + ); if !self.fail { let xs = self.registers[4]; @@ -711,72 +770,51 @@ impl MachineState { } fn skip_max_list_result(&mut self, max_steps: i64) { + let cell = self.store(self.deref(self.registers[3])); + let search_result = if max_steps == -1 { - BrentAlgState::detect_cycles(&self.heap, self.store(self.deref(self.registers[3]))) + BrentAlgState::detect_cycles(&self.heap, cell) } else { - BrentAlgState::detect_cycles_with_max( - &self.heap, - max_steps as usize, - self.store(self.deref(self.registers[3])), - ) + BrentAlgState::detect_cycles_with_max(&self.heap, max_steps as usize, cell) }; match search_result { - CycleSearchResult::PStrLocation(steps, pstr_loc, offset) => { + CycleSearchResult::PStrLocation { + num_steps, + pstr_loc, + } => { let steps = if max_steps > -1 { - std::cmp::min(max_steps, steps as i64) + std::cmp::min(max_steps, num_steps as i64) } else { - steps as i64 + max_steps }; - let cell = if offset > 0 { - let h = self.heap.len(); - let (pstr_loc, _) = pstr_loc_and_offset(&self.heap, pstr_loc); - - self.heap.push(pstr_offset_as_cell!(pstr_loc)); - self.heap - .push(fixnum_as_cell!(Fixnum::build_with(offset as i64))); - - pstr_loc_as_cell!(h) - } else { - pstr_loc_as_cell!(pstr_loc) - }; - - self.finalize_skip_max_list(steps, cell); + self.finalize_skip_max_list(steps, pstr_loc); // cell); } - CycleSearchResult::UntouchedList(n, l) => { - self.finalize_skip_max_list(n as i64, list_loc_as_cell!(l)); - } - CycleSearchResult::UntouchedCStr(cstr_atom, n) => { - let cell = if n > 0 { - let h = self.heap.len(); - - self.heap.push(string_as_cstr_cell!(cstr_atom)); - self.heap.push(pstr_offset_as_cell!(h)); - self.heap - .push(fixnum_as_cell!(Fixnum::build_with(n as i64))); - - pstr_loc_as_cell!(h + 1) - } else { - string_as_cstr_cell!(cstr_atom) - }; - - self.finalize_skip_max_list(n as i64, cell); + CycleSearchResult::UntouchedList { + num_steps, + list_loc: l, + } => { + self.finalize_skip_max_list(num_steps as i64, list_loc_as_cell!(l)); } CycleSearchResult::EmptyList => { self.finalize_skip_max_list(0, empty_list_as_cell!()); } - CycleSearchResult::PartialList(n, r) => { - self.finalize_skip_max_list(n as i64, r.as_heap_cell_value()); + CycleSearchResult::PartialList { + num_steps, + heap_loc, + } => self.finalize_skip_max_list(num_steps as i64, heap_loc), + CycleSearchResult::ProperList { num_steps } => { + self.finalize_skip_max_list(num_steps as i64, empty_list_as_cell!()) } - CycleSearchResult::ProperList(steps) => { - self.finalize_skip_max_list(steps as i64, empty_list_as_cell!()) + CycleSearchResult::NotList { + num_steps, + heap_loc, + } => { + self.finalize_skip_max_list(num_steps as i64, heap_loc); } - CycleSearchResult::NotList(n, value) => { - self.finalize_skip_max_list(n as i64, value); - } - CycleSearchResult::Cyclic(lam) => { - self.skip_max_list_cycle(lam); + CycleSearchResult::Cyclic { lambda } => { + self.skip_max_list_cycle(lambda); } }; } @@ -786,7 +824,7 @@ impl MachineState { let mut max_old = -1i64; if !max_steps.is_var() { - let max_steps = Number::try_from(max_steps); + let max_steps = Number::try_from((max_steps, &self.arena.f64_tbl)); let max_steps_n = match max_steps { Ok(Number::Fixnum(n)) => Some(n.get_num()), @@ -825,8 +863,9 @@ impl MachineState { let mut seen_set = IndexSet::new(); { + self.heap[0] = term; let mut iter = - stackful_post_order_iter::(&mut self.heap, &mut self.stack, term); + stackful_post_order_iter::(&mut self.heap, &mut self.stack, 0); while let Some(value) = iter.next() { if iter.parent_stack_len() >= max_depth { @@ -842,8 +881,10 @@ impl MachineState { } } - let outcome = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, seen_set.into_iter(),)); - + let outcome = step_or_resource_error!( + self, + sized_iter_to_heap_list(&mut self.heap, seen_set.len(), seen_set.into_iter()) + ); unify_fn!(*self, list_of_vars, outcome); } @@ -852,7 +893,11 @@ impl MachineState { let value = self.store(self.deref(value)); self.block = self.b; - self.unify_fixnum(Fixnum::build_with(self.block as i64), value); + self.unify_fixnum( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(self.block as i64) }, + value, + ); self.block } @@ -861,23 +906,33 @@ impl MachineState { &mut self, lh_offset: usize, copy_target: HeapCellValue, - ) -> usize { - let threshold = self.lifted_heap.len() - lh_offset; + ) -> Result { + let threshold = self.lifted_heap.cell_len() - lh_offset; + let mut writer = self.lifted_heap.reserve(5)?; - let mut copy_ball_term = CopyBallTerm::new( + writer.write_with(|section| { + section.push_cell(list_loc_as_cell!(threshold + 1)); + section.push_cell(heap_loc_as_cell!(threshold + 5)); + section.push_cell(heap_loc_as_cell!(threshold + 2)); + section.push_cell(fixnum_as_cell!(Fixnum::build_with(0))); + section.push_cell(fixnum_as_cell!(Fixnum::build_with(0))); + }); + + let old_lifted_cell_len = self.lifted_heap.cell_len(); + + let copy_ball_term = CopyBallTerm::new( &mut self.attr_var_init.attr_var_queue, &mut self.stack, &mut self.heap, &mut self.lifted_heap, ); - copy_ball_term.push(list_loc_as_cell!(threshold + 1)); - copy_ball_term.push(heap_loc_as_cell!(threshold + 3)); - copy_ball_term.push(heap_loc_as_cell!(threshold + 2)); + let pstr_boundary = copy_term(copy_ball_term, copy_target, AttrVarPolicy::DeepCopy)?; - copy_term(copy_ball_term, copy_target, AttrVarPolicy::DeepCopy); - - threshold + lh_offset + 2 + Ok(FindallCopyInfo { + offset: threshold + lh_offset + 2, + pstr_threshold: pstr_boundary + old_lifted_cell_len, + }) } #[inline(always)] @@ -889,11 +944,14 @@ impl MachineState { (HeapCellValueTag::Fixnum, n) => { let lh_offset = n.get_num() as usize; - if lh_offset >= self.lifted_heap.len() { + if lh_offset >= self.lifted_heap.cell_len() { self.lifted_heap.truncate(lh_offset); } else { - let threshold = self.lifted_heap.len() - lh_offset; - self.lifted_heap.push(addr_constr(threshold)); + let threshold = self.lifted_heap.cell_len() - lh_offset; + step_or_resource_error!( + self, + self.lifted_heap.push_cell(addr_constr(threshold)) + ); } } _ => { @@ -906,7 +964,7 @@ impl MachineState { &mut self, string: &str, indices: &IndexStore, - stub_gen: impl Fn() -> FunctorStub, + stub_gen: impl Fn() -> MachineStub, ) -> CallResult { use crate::parser::lexer::*; @@ -917,7 +975,7 @@ impl MachineState { let mut tokens = vec![]; match lexer.next_number_token() { - Ok(token @ Token::Literal(Literal::Atom(atom!("-")) | Literal::Char('-'))) => { + Ok(token @ Token::Literal(Literal::Atom(atom!("-")))) => { tokens.push(token); if let Ok(token) = lexer.next_number_token() { @@ -933,6 +991,7 @@ impl MachineState { } } + #[allow(clippy::never_loop)] // TODO why is there a loop here that never loops? loop { match lexer.lookahead_char() { Err(e) if e.is_unexpected_eof() => { @@ -949,8 +1008,8 @@ impl MachineState { Ok(Term::Literal(_, Literal::Rational(n))) => { self.unify_rational(n, nx); } - Ok(Term::Literal(_, Literal::Float(n))) => { - self.unify_f64(n.as_ptr(), nx); + Ok(Term::Literal(_, Literal::F64Offset(n))) => { + self.unify_f64(n, nx); } Ok(Term::Literal(_, Literal::Integer(n))) => { self.unify_big_int(n, nx); @@ -966,7 +1025,7 @@ impl MachineState { } } - break; + return Ok(()); } Ok(c) => { let (line_num, col_num) = (lexer.line_num, lexer.col_num); @@ -979,8 +1038,6 @@ impl MachineState { Err(_) => unreachable!(), } } - - Ok(()) } pub(crate) fn call_continuation_chunk( @@ -1020,7 +1077,10 @@ impl MachineState { for index in s + 2..s + 2 + num_cells { if let HeapCellValueTag::CutPoint = self.heap[index].get_tag() { // adjust cut point to occur after call_continuation. - and_frame[index - (s + 1)] = fixnum_as_cell!(Fixnum::as_cutpoint(self.b as i64)); + and_frame[index - (s + 1)] = fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(self.b as i64) }.as_cutpoint() + ); } else { and_frame[index - (s + 1)] = self.heap[index]; } @@ -1032,16 +1092,6 @@ impl MachineState { pub fn value_to_str_like(&mut self, value: HeapCellValue) -> Option { read_heap_cell!(value, - (HeapCellValueTag::CStr, cstr_atom) => { - // avoid allocating a String if possible: - // We must be careful to preserve the string "[]" as is, - // instead of turning it into the atom [], i.e., "". - if cstr_atom == atom!("[]") { - Some(AtomOrString::String("[]".to_string())) - } else { - Some(AtomOrString::Atom(cstr_atom)) - } - } (HeapCellValueTag::Atom, (atom, arity)) => { if arity == 0 { // ... likewise. @@ -1060,27 +1110,23 @@ impl MachineState { None } } - (HeapCellValueTag::Char, c) => { - Some(AtomOrString::String(c.to_string())) - } _ => { if value.is_constant() { return None; } - let h = self.heap.len(); - self.heap.push(value); + // 0 is reserved for use by the machine. See + // MachineState::new. + self.heap[0] = value; - let mut iter = HeapPStrIter::new(&self.heap, h); + let mut iter = HeapPStrIter::new(&self.heap, 0); let string = iter.to_string_mut(); - let at_terminator = iter.at_string_terminator(); - - self.heap.pop(); + let end_cell = iter.heap[iter.focus()]; // if the iteration doesn't terminate like a string // (i.e. with the [] atom or a CStr), it is not // "str_like" so return None. - if at_terminator { + if end_cell.is_string_terminator(iter.heap) { Some(AtomOrString::String(string)) } else { None @@ -1092,14 +1138,14 @@ impl MachineState { pub(crate) fn codes_to_string( &mut self, addrs: impl Iterator, - stub_gen: impl Fn() -> FunctorStub, + stub_gen: impl Fn() -> MachineStub, ) -> Result { let mut string = String::new(); for addr in addrs { let addr = self.store(self.deref(addr)); - match Number::try_from(addr) { + match Number::try_from((addr, &self.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => { if let Ok(n) = u32::try_from(n.get_num()) { if let Some(c) = std::char::from_u32(n) { @@ -1208,7 +1254,13 @@ impl Machine { let mut bp = self .indices .get_predicate_code_index(atom!("$clause"), 2, module_name) - .and_then(|idx| idx.local()) + .and_then(|idx| { + self.machine_st + .arena + .code_index_tbl + .get_entry(idx.into()) + .local() + }) .unwrap(); macro_rules! extract_ptr { @@ -1255,7 +1307,7 @@ impl Machine { boip + extract_ptr!(hm.get(&key).cloned().unwrap()) } IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref hm)) => { - boip + extract_ptr!(hm.get(&Literal::Atom(key.0)).cloned().unwrap()) + boip + extract_ptr!(hm.get(&atom_as_cell!(key.0)).cloned().unwrap()) } _ => boip, }; @@ -1378,11 +1430,7 @@ impl Machine { let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]) .get_name_and_arity(); - (name, arity, if self.machine_st.heap.len() > s + arity + 1 { - get_structure_index(self.machine_st.heap[s + arity + 1]) - } else { - None - }) + (name, arity, get_structure_index(self.machine_st.heap[s.saturating_sub(1)])) } (HeapCellValueTag::Atom, (name, arity)) => { debug_assert_eq!(arity, 0); @@ -1402,7 +1450,7 @@ impl Machine { self.machine_st.error_form(err, stub) })?; - let index_cell = if index_cell_opt.is_some() { + let index_cell_opt = if index_cell_opt.is_some() { index_cell_opt } else { let is_internal_call = name == atom!("$call") && goal_arity > 0; @@ -1440,11 +1488,17 @@ impl Machine { } }; - if let Some(code_index) = index_cell { - if !code_index.is_undefined() { + if let Some(code_idx) = index_cell_opt { + let index_ptr = self + .machine_st + .arena + .code_index_tbl + .get_entry(code_idx.into()); + + if !index_ptr.is_undefined() { load_registers(&mut self.machine_st, goal, goal_arity); self.machine_st.neck_cut(); - return call_at_index(self, name, arity, code_index.get()); + return call_at_index(self, name, arity, index_ptr); } } @@ -1465,6 +1519,7 @@ impl Machine { .variable_set(&mut supp_vars, self.machine_st.registers[2]); struct GoalAnalysisResult { + index_ptr_loc: usize, is_simple_goal: bool, goal: HeapCellValue, key: PredicateKey, @@ -1480,7 +1535,7 @@ impl Machine { // fill expanded_vars with variables of the partial // goal pre-completion by complete_partial_goal. - for idx in s + 1 .. s + arity - supp_vars.len() + 1 { + for idx in s + 1 ..= s + arity - supp_vars.len() { self.machine_st.variable_set(&mut expanded_vars, self.machine_st.heap[idx]); } @@ -1494,9 +1549,8 @@ impl Machine { // disjoint from them. if they are not, the // expanded goal is not simple. - let post_supp_args = self.machine_st.heap[s+arity-supp_vars.len()+1 .. s+arity+1] - .iter() - .cloned(); + let post_supp_args = (s+arity-supp_vars.len()+1 ..= s+arity) + .map(|idx| self.machine_st.heap[idx]); post_supp_args .zip(supp_vars.iter()) @@ -1521,23 +1575,33 @@ impl Machine { false }; - let goal = if is_simple_goal { - let h = self.machine_st.heap.len(); + let (index_ptr_loc, goal) = if is_simple_goal { + let h = self.machine_st.heap.cell_len(); let arity = arity - supp_vars.len(); - for idx in 0 .. arity + 1 { - let value = self.machine_st.heap[s + idx]; - self.machine_st.heap.push(value); - } + resource_error_call_result!( + self.machine_st, + self.machine_st.heap.push_cell(empty_list_as_cell!()) + ); - self.machine_st.heap[h] = atom_as_cell!(name, arity); + resource_error_call_result!( + self.machine_st, + self.machine_st.heap.copy_slice_to_end( + s ..= s + arity, + ) + ); - str_loc_as_cell!(h) + self.machine_st.heap[h+1] = atom_as_cell!(name, arity); + + // even if arity == 0, goal must be a Str cell, + // since an index is about to appended to it. + (h, str_loc_as_cell!(h+1)) } else { - goal + (0, goal) }; GoalAnalysisResult { + index_ptr_loc, is_simple_goal, goal, key: (name, arity), @@ -1547,25 +1611,22 @@ impl Machine { (HeapCellValueTag::Atom, (name, arity)) => { debug_assert_eq!(arity, 0); - let h = self.machine_st.heap.len(); - self.machine_st.heap.push(goal); + let h = self.machine_st.heap.cell_len(); + + let mut writer = resource_error_call_result!( + self.machine_st, + self.machine_st.heap.reserve(2) + ); + + writer.write_with(|section| { + section.push_cell(empty_list_as_cell!()); + section.push_cell(goal); + }); GoalAnalysisResult { + index_ptr_loc: h, is_simple_goal: true, - goal: str_loc_as_cell!(h), - key: (name, 0), - supp_vars, - } - } - (HeapCellValueTag::Char, c) => { - let name = AtomTable::build_with(&self.machine_st.atom_tbl,&c.to_string()); - - let h = self.machine_st.heap.len(); - self.machine_st.heap.push(atom_as_cell!(name)); - - GoalAnalysisResult { - is_simple_goal: true, - goal: str_loc_as_cell!(h), + goal: str_loc_as_cell!(h+1), key: (name, 0), supp_vars, } @@ -1583,9 +1644,7 @@ impl Machine { let expanded_term = if result.is_simple_goal { let idx = self.get_or_insert_qualified_code_index(module_name, result.key); - self.machine_st - .heap - .push(untyped_arena_ptr_as_cell!(UntypedArenaPtr::from(idx))); + self.machine_st.heap[result.index_ptr_loc] = HeapCellValue::from(idx); result.goal } else { let mut unexpanded_vars = IndexSet::with_hasher(FxBuildHasher::default()); @@ -1608,32 +1667,33 @@ impl Machine { Err(e) => { let err = self.machine_st.session_error(e); let stub = functor_stub(atom!("call"), result.key.1); - return Err(self.machine_st.error_form(err, stub)); } Ok(()) => { - let h = self.machine_st.heap.len(); - - self.machine_st.heap.push(atom_as_cell!(atom!("$aux"), 0)); - - for value in unexpanded_vars.difference(&result.supp_vars).cloned() { - self.machine_st.heap.push(value); - } - - let anon_str_arity = self.machine_st.heap.len() - h - 1; - - self.machine_st.heap[h] = atom_as_cell!(atom!("$aux"), anon_str_arity); + let h = self.machine_st.heap.cell_len(); + let mut writer = resource_error_call_result!( + self.machine_st, + self.machine_st.heap.reserve(unexpanded_vars.len() + 2) + ); let idx = CodeIndex::new( IndexPtr::index(helper_clause_loc), - &mut self.machine_st.arena, + &mut self.machine_st.arena.code_index_tbl, ); - self.machine_st - .heap - .push(untyped_arena_ptr_as_cell!(UntypedArenaPtr::from(idx))); + writer.write_with(|section| { + section.push_cell(HeapCellValue::from(idx)); + section.push_cell(atom_as_cell!(atom!("$aux"), 0)); - str_loc_as_cell!(h) + for value in unexpanded_vars.difference(&result.supp_vars).cloned() { + section.push_cell(value); + } + }); + + let anon_str_arity = self.machine_st.heap.cell_len() - h - 2; + self.machine_st.heap[h + 1] = atom_as_cell!(atom!("$aux"), anon_str_arity); + + str_loc_as_cell!(h + 1) } } }; @@ -1651,24 +1711,16 @@ impl Machine { if HeapCellValueTag::Str == qualified_goal.get_tag() { let s = qualified_goal.get_value() as usize; - let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity(); + let name = cell_as_atom_cell!(self.machine_st.heap[s]).get_name(); if name == atom!("$call") { return false; } - if self.machine_st.heap.len() > s + 1 + arity { - let idx_cell = self.machine_st.heap[s + 1 + arity]; + let idx_cell = self.machine_st.heap[s.saturating_sub(1)]; - if HeapCellValueTag::Cons == idx_cell.get_tag() { - match_untyped_arena_ptr!(cell_as_untyped_arena_ptr!(idx_cell), - (ArenaHeaderTag::IndexPtr, _ip) => { - return true; - } - _ => { - } - ); - } + if HeapCellValueTag::CodeIndexOffset == idx_cell.get_tag() { + return true; } } @@ -1682,18 +1734,16 @@ impl Machine { let target_module_loc = self.machine_st.registers[2]; - let (functor_stub, ref_cell_tag) = module_quantification.to_functor(); + let functor_stub = module_quantification.to_functor(); + let mut functor_writer = Heap::functor_writer(functor_stub); - let h = self.machine_st.heap.len(); - let ref_cell = HeapCellValue::build_with(ref_cell_tag, h as u64); + let cell = functor_writer(&mut self.machine_st.heap).unwrap(); + unify_fn!(&mut self.machine_st, cell, target_module_loc); - self.machine_st.heap.extend(functor_stub); - - unify_fn!(&mut self.machine_st, ref_cell, target_module_loc); - - let target_qualified_goal = self.machine_st.registers[3]; - - unify_fn!(&mut self.machine_st, qualified_goal, target_qualified_goal); + if !self.machine_st.fail { + let target_qualified_goal = self.machine_st.registers[3]; + unify_fn!(&mut self.machine_st, qualified_goal, target_qualified_goal); + } } #[inline(always)] @@ -1713,21 +1763,24 @@ impl Machine { let target_goal = if arity == 0 { qualified_goal } else { - // if narity + arity > 0 { - let h = self.machine_st.heap.len(); - self.machine_st - .heap - .push(atom_as_cell!(name, narity + arity)); + let h = self.machine_st.heap.cell_len(); - for idx in 1..narity + 1 { - self.machine_st.heap.push(self.machine_st.heap[s + idx]); - } + let mut writer = resource_error_call_result!( + self.machine_st, + self.machine_st.heap.reserve(1 + narity + arity) + ); - for idx in 1..arity + 1 { - self.machine_st - .heap - .push(self.machine_st.registers[2 + idx]); - } + writer.write_with(|section| { + section.push_cell(atom_as_cell!(name, narity + arity)); + + for idx in 1..narity + 1 { + section.push_cell(section[s + idx]); + } + + for idx in 1..arity + 1 { + section.push_cell(self.machine_st.registers[2 + idx]); + } + }); if narity + arity > 0 { str_loc_as_cell!(h) @@ -1737,9 +1790,7 @@ impl Machine { }; let target_qualified_goal = self.machine_st.registers[1]; - unify_fn!(&mut self.machine_st, target_goal, target_qualified_goal); - Ok(()) } @@ -1763,12 +1814,17 @@ impl Machine { module_name } _ => { - let h = self.machine_st.heap.len(); - let call_form = functor!(atom!(":"), [cell(module_name), cell(self.machine_st.registers[2])]); + let goal = self.machine_st.registers[2]; + let mut functor_writer = Heap::functor_writer( + functor!(atom!(":"), [cell(module_name), cell(goal)]), + ); - self.machine_st.heap.extend(call_form); + let goal = resource_error_call_result!( + self.machine_st, + functor_writer(&mut self.machine_st.heap) + ); - let err = self.machine_st.type_error(ValidType::Callable, str_loc_as_cell!(h)); + let err = self.machine_st.type_error(ValidType::Callable, goal); let stub = functor_stub(atom!("call"), narity + 1); return Err(self.machine_st.error_form(err, stub)); @@ -1815,7 +1871,7 @@ impl Machine { #[inline(always)] pub(crate) fn bind_from_register(&mut self) { let reg = self.deref_register(2); - let n = match Number::try_from(reg) { + let n = match Number::try_from((reg, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).ok(), Ok(Number::Integer(n)) => { let value: usize = (&*n).try_into().unwrap(); @@ -1945,9 +2001,12 @@ impl Machine { for entry in entries { if let Ok(entry) = entry { if let Some(name) = entry.file_name().to_str() { - let name = AtomTable::build_with(&self.machine_st.atom_tbl, name); - files.push(atom_as_cstr_cell!(name)); + let file_string_cell = resource_error_call_result!( + self.machine_st, + self.machine_st.heap.allocate_cstr(name) + ); + files.push(file_string_cell); continue; } } @@ -1959,12 +2018,20 @@ impl Machine { return Err(err); } - let files_list = heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - files.into_iter() - )); + let files_list_cell = resource_error_call_result!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + files.len(), + files.into_iter() + ) + ); - unify!(self.machine_st, self.machine_st.registers[2], files_list); + unify!( + self.machine_st, + self.machine_st.registers[2], + files_list_cell + ); return Ok(()); } } @@ -2051,11 +2118,14 @@ impl Machine { unreachable!() } } { - let chars_atom = self.systemtime_to_timestamp(time); + let chars_string = self.systemtime_to_timestamp(time); - self.machine_st - .unify_complete_string(chars_atom, self.machine_st.registers[3]); + let cstr_cell = step_or_resource_error!( + self.machine_st, + self.machine_st.heap.allocate_cstr(&chars_string) + ); + unify!(self.machine_st, cstr_cell, self.machine_st.registers[3]); return; } } @@ -2186,10 +2256,16 @@ impl Machine { } }; - let current_atom = AtomTable::build_with(&self.machine_st.atom_tbl, current); + let current_string = resource_error_call_result!( + self.machine_st, + self.machine_st.heap.allocate_cstr(current) + ); - let a1 = self.deref_register(1); - self.machine_st.unify_complete_string(current_atom, a1); + unify!( + self.machine_st, + current_string, + self.machine_st.registers[1] + ); if self.machine_st.fail { return Ok(()); @@ -2226,11 +2302,16 @@ impl Machine { } }; - let canonical_atom = AtomTable::build_with(&self.machine_st.atom_tbl, cs); - - let a2 = self.deref_register(2); - self.machine_st.unify_complete_string(canonical_atom, a2); + let canonical_string = resource_error_call_result!( + self.machine_st, + self.machine_st.heap.allocate_cstr(cs) + ); + unify!( + self.machine_st, + canonical_string, + self.machine_st.registers[2] + ); return Ok(()); } } @@ -2242,39 +2323,17 @@ impl Machine { #[inline(always)] pub(crate) fn atom_chars(&mut self) { let a1 = self.deref_register(1); - let a2 = self.deref_register(2); read_heap_cell!(a1, - (HeapCellValueTag::Char) => { - let h = self.machine_st.heap.len(); - - self.machine_st.heap.push(a1); - self.machine_st.heap.push(empty_list_as_cell!()); - - unify!(self.machine_st, self.machine_st.registers[2], list_loc_as_cell!(h)); - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]) - .get_name_and_arity(); - - if arity == 0 { - self.machine_st.unify_complete_string( - name, - a2, - ); - } else { - self.machine_st.fail = true; - } - } (HeapCellValueTag::Atom, (name, arity)) => { - if arity == 0 { - self.machine_st.unify_complete_string( - name, - a2, - ); - } else { - self.machine_st.fail = true; - } + debug_assert_eq!(arity, 0); + + let cell = step_or_resource_error!( + self.machine_st, + self.machine_st.heap.allocate_cstr(&name.as_str()) + ); + + unify!(self.machine_st, self.machine_st.registers[2], cell); } (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { let a2 = self.deref_register(2); @@ -2300,7 +2359,7 @@ impl Machine { self.machine_st.fail = true; } _ => { - unreachable!(); + self.machine_st.fail = true; } ); } @@ -2310,41 +2369,59 @@ impl Machine { let a1 = self.deref_register(1); read_heap_cell!(a1, + /* (HeapCellValueTag::Char, c) => { let h = self.machine_st.heap.len(); - self.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(c as i64))); + self.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(u32::from(c)))); self.machine_st.heap.push(empty_list_as_cell!()); unify!(self.machine_st, list_loc_as_cell!(h), self.machine_st.registers[2]); } + */ (HeapCellValueTag::Atom, (name, arity)) => { - if arity == 0 { - let name = name.as_str(); - let iter = name.chars() - .map(|c| fixnum_as_cell!(Fixnum::build_with(c as i64))); + debug_assert_eq!(arity, 0); - let h = iter_to_heap_list(&mut self.machine_st.heap, iter); - unify!(self.machine_st, heap_loc_as_cell!(h), self.machine_st.registers[2]); - } else { - self.machine_st.fail = true; - } + let name = name.as_str(); + let iter = name.chars().map(|c| fixnum_as_cell!(Fixnum::build_with(c))); + + let list_cell = resource_error_call_result!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + name.chars().count(), + iter, + ) + ); + + unify!(self.machine_st, list_cell, self.machine_st.registers[2]); } + /* (HeapCellValueTag::Str, s) => { + /* let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]) .get_name_and_arity(); if arity == 0 { let name = name.as_str(); - let iter = name.chars() - .map(|c| fixnum_as_cell!(Fixnum::build_with(c as i64))); + let iter = name.chars().map(|c| fixnum_as_cell!(Fixnum::build_with(c as i64))); - let h = iter_to_heap_list(&mut self.machine_st.heap, iter); - unify!(self.machine_st, heap_loc_as_cell!(h), self.machine_st.registers[2]); + let list_cell = resource_error_call_result!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + name.as_str().chars().count(), + iter, + ) + ); + + unify!(self.machine_st, list_cell, self.machine_st.registers[2]); } else { - self.machine_st.fail = true; - } + */ + self.machine_st.fail = true; + // } } + */ (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { let stub_gen = || functor_stub(atom!("atom_codes"), 2); @@ -2392,16 +2469,21 @@ impl Machine { return; } } + /* (HeapCellValueTag::Char) => { 1 } + */ _ => { unreachable!() } ); let a2 = self.deref_register(2); - self.machine_st.unify_fixnum(Fixnum::build_with(len), a2); + self.machine_st.unify_fixnum( + /* FIXME this is not safe */ unsafe { Fixnum::build_with_unchecked(len) }, + a2, + ); } #[inline(always)] @@ -2441,27 +2523,32 @@ impl Machine { #[inline(always)] pub(crate) fn create_partial_string(&mut self) { - let atom = cell_as_atom!(self.deref_register(1)); + let a1 = self.deref_register(1); - if atom == atom!("") { - self.machine_st.fail = true; - return; - } + if let Some(str_like) = self.machine_st.value_to_str_like(a1) { + let str = match str_like { + AtomOrString::String(string) => string, + _ => { + unreachable!() + } + }; - let pstr_h = self.machine_st.heap.len(); + let pstr_loc_cell = + step_or_resource_error!(self.machine_st, self.machine_st.heap.allocate_pstr(&str)); - self.machine_st.heap.push(pstr_as_cell!(atom)); - self.machine_st.heap.push(heap_loc_as_cell!(pstr_h + 1)); + let tail_loc = self.machine_st.heap.cell_len(); - unify!( - self.machine_st, - self.machine_st.registers[2], - pstr_loc_as_cell!(pstr_h) - ); + step_or_resource_error!( + self.machine_st, + self.machine_st.heap.push_cell(heap_loc_as_cell!(tail_loc)) + ); - if !self.machine_st.fail { - let tail = self.machine_st.registers[3]; - unify!(self.machine_st, tail, heap_loc_as_cell!(pstr_h + 1)); + unify!(self.machine_st, self.machine_st.registers[2], pstr_loc_cell); + + if !self.machine_st.fail { + let tail = self.machine_st.registers[3]; + unify!(self.machine_st, tail, heap_loc_as_cell!(tail_loc)); + } } } @@ -2469,17 +2556,21 @@ impl Machine { pub(crate) fn is_partial_string(&mut self) { let value = self.deref_register(1); - let h = self.machine_st.heap.len(); - self.machine_st.heap.push(value); + if value.is_constant() { + self.machine_st.fail = empty_list_as_cell!() != value; + } else { + self.machine_st.heap[0] = value; + let mut iter = HeapPStrIter::new(&self.machine_st.heap, 0); - let mut iter = HeapPStrIter::new(&self.machine_st.heap, h); + for _ in iter.by_ref() {} - for _ in iter.by_ref() {} + let focus = iter.focus(); + let end_cell = self.machine_st.heap[focus]; + let at_end_of_pstr = + end_cell.is_var() || end_cell.is_string_terminator(&self.machine_st.heap); - let at_end_of_pstr = iter.focus.is_var() || iter.at_string_terminator(); - self.machine_st.fail = !at_end_of_pstr; - - self.machine_st.heap.pop(); + self.machine_st.fail = !at_end_of_pstr; + } } #[inline(always)] @@ -2489,26 +2580,8 @@ impl Machine { read_heap_cell!(pstr, (HeapCellValueTag::PStrLoc, h) => { - let (h, _) = pstr_loc_and_offset(&self.machine_st.heap, h); - - if HeapCellValueTag::CStr == self.machine_st.heap[h].get_tag() { - self.machine_st.unify_atom( - atom!("[]"), - a2 - ); - } else { - unify_fn!( - self.machine_st, - heap_loc_as_cell!(h+1), - a2 - ); - } - } - (HeapCellValueTag::CStr) => { - self.machine_st.unify_atom( - atom!("[]"), - a2 - ); + let HeapStringScan { tail_idx, .. } = self.machine_st.heap.scan_slice_to_str(h); + unify_fn!(self.machine_st, heap_loc_as_cell!(tail_idx), a2); } (HeapCellValueTag::Lis, h) => { unify_fn!( @@ -2560,11 +2633,11 @@ impl Machine { let addr = match addr { addr if addr.is_var() => addr, - addr => match Number::try_from(addr) { + addr => match Number::try_from((addr, &self.machine_st.arena.f64_tbl)) { Ok(Number::Integer(n)) => { let result: Result = (&*n).try_into(); if let Ok(value) = result { - fixnum_as_cell!(Fixnum::build_with(value as i64)) + fixnum_as_cell!(Fixnum::build_with(value)) } else { let err = self.machine_st.type_error(ValidType::InByte, addr); return Err(self.machine_st.error_form(err, stub_gen())); @@ -2572,7 +2645,7 @@ impl Machine { } Ok(Number::Fixnum(n)) => { if let Ok(nb) = u8::try_from(n.get_num()) { - fixnum_as_cell!(Fixnum::build_with(nb as i64)) + fixnum_as_cell!(Fixnum::build_with(nb)) } else { let err = self.machine_st.type_error(ValidType::InByte, addr); return Err(self.machine_st.error_form(err, stub_gen())); @@ -2588,8 +2661,7 @@ impl Machine { loop { match stream.peek_byte().map_err(|e| e.kind()) { Ok(b) => { - self.machine_st - .unify_fixnum(Fixnum::build_with(b as i64), addr); + self.machine_st.unify_fixnum(Fixnum::build_with(b), addr); break; } Err(ErrorKind::PermissionDenied) => { @@ -2650,9 +2722,11 @@ impl Machine { } let a2 = read_heap_cell!(a2, + /* (HeapCellValueTag::Char) => { a2 } + */ (HeapCellValueTag::Atom, (name, arity)) => { if arity == 0 { if let Some(c) = name.as_char() { @@ -2748,14 +2822,12 @@ impl Machine { a2 } _ => { - match Number::try_from(a2) { + match Number::try_from((a2, &self.machine_st.arena.f64_tbl)) { Ok(Number::Integer(n)) => { let n: u32 = (&*n).try_into().unwrap(); - let n = std::char::from_u32(n).map(|_| n); - - if let Some(n) = n { - fixnum_as_cell!(Fixnum::build_with(n as i64)) + if std::char::from_u32(n).is_some() { + fixnum_as_cell!(Fixnum::build_with(n)) } else { let err = self.machine_st.representation_error(RepFlag::InCharacterCode); return Err(self.machine_st.error_form(err, stub_gen())); @@ -2767,7 +2839,7 @@ impl Machine { .and_then(|n| std::char::from_u32(n).map(|_| n)); if let Some(n) = n { - fixnum_as_cell!(Fixnum::build_with(n as i64)) + fixnum_as_cell!(Fixnum::build_with(n)) } else { let err = self.machine_st.representation_error(RepFlag::InCharacterCode); return Err(self.machine_st.error_form(err, stub_gen())); @@ -2787,7 +2859,7 @@ impl Machine { match result.map(|result| result.map_err(|e| e.kind())) { Some(Ok(c)) => { self.machine_st - .unify_fixnum(Fixnum::build_with(c as i64), addr); + .unify_fixnum(Fixnum::build_with(u32::from(c)), addr); break; } Some(Err(ErrorKind::PermissionDenied)) => { @@ -2817,7 +2889,7 @@ impl Machine { let n = self.deref_register(1); let chs = self.deref_register(2); - let string = match Number::try_from(n) { + let string = match Number::try_from((n, &self.machine_st.arena.f64_tbl)) { Ok(Number::Float(OrderedFloat(n))) => fmt_float(n), Ok(Number::Fixnum(n)) => n.get_num().to_string(), Ok(Number::Integer(n)) => n.to_string(), @@ -2832,8 +2904,12 @@ impl Machine { } }; - let chars_atom = AtomTable::build_with(&self.machine_st.atom_tbl, string.trim()); - self.machine_st.unify_complete_string(chars_atom, chs); + let cstr_cell = step_or_resource_error!( + self.machine_st, + self.machine_st.heap.allocate_cstr(string.trim()) + ); + + unify!(self.machine_st, cstr_cell, chs); } #[inline(always)] @@ -2841,9 +2917,9 @@ impl Machine { let n = self.deref_register(1); let chs = self.machine_st.registers[2]; - let string = match Number::try_from(n) { + let string = match Number::try_from((n, &self.machine_st.arena.f64_tbl)) { Ok(Number::Float(OrderedFloat(n))) => { - format!("{0:<20?}", n) + format!("{n:<20?}") } Ok(Number::Fixnum(n)) => n.get_num().to_string(), Ok(Number::Integer(n)) => n.to_string(), @@ -2861,10 +2937,18 @@ impl Machine { let codes = string .trim() .chars() - .map(|c| fixnum_as_cell!(Fixnum::build_with(c as i64))); + .map(|c| fixnum_as_cell!(Fixnum::build_with(u32::from(c)))); - let h = iter_to_heap_list(&mut self.machine_st.heap, codes); - unify!(self.machine_st, heap_loc_as_cell!(h), chs); + let list_cell = step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + string.trim().chars().count(), + codes, + ) + ); + + unify!(self.machine_st, list_cell, chs); } #[inline(always)] @@ -2896,7 +2980,9 @@ impl Machine { #[inline(always)] pub(crate) fn lifted_heap_length(&mut self) { let a1 = self.machine_st.registers[1]; - let lh_len = Fixnum::build_with(self.machine_st.lifted_heap.len() as i64); + /* FIXME this is not safe */ + let lh_len = + unsafe { Fixnum::build_with_unchecked(self.machine_st.lifted_heap.cell_len() as i64) }; self.machine_st.unify_fixnum(lh_len, a1); } @@ -2918,11 +3004,8 @@ impl Machine { debug_assert_eq!(arity, 0); name.as_char().unwrap() } - (HeapCellValueTag::Char, c) => { - c - } _ => { - match Number::try_from(a2) { + match Number::try_from((a2, &self.machine_st.arena.f64_tbl)) { Ok(Number::Integer(n)) => { let n: u32 = (&*n).try_into().unwrap(); let n = std::char::from_u32(n); @@ -2957,7 +3040,7 @@ impl Machine { ); self.machine_st - .unify_fixnum(Fixnum::build_with(c as i64), a2); + .unify_fixnum(Fixnum::build_with(u32::from(c)), a2); Ok(()) } @@ -2968,9 +3051,11 @@ impl Machine { let a2 = self.deref_register(2); let c = read_heap_cell!(a1, + /* (HeapCellValueTag::Char, c) => { c } + */ (HeapCellValueTag::Atom, (name, _arity)) => { name.as_char().unwrap() } @@ -3056,14 +3141,19 @@ impl Machine { match (name, arity) { (atom!("upper"), 1) => { let reg = self.machine_st.deref(self.machine_st.heap[s+1]); - let atom = AtomTable::build_with(&self.machine_st.atom_tbl, &c.to_uppercase().to_string()); - let upper_str = string_as_cstr_cell!(atom); + let upper_str = step_or_resource_error!( + self.machine_st, + self.machine_st.heap.allocate_cstr(&c.to_uppercase().to_string()) + ); unify!(self.machine_st, reg, upper_str); } (atom!("lower"), 1) => { let reg = self.machine_st.deref(self.machine_st.heap[s+1]); - let atom = AtomTable::build_with(&self.machine_st.atom_tbl, &c.to_lowercase().to_string()); - let lower_str = string_as_cstr_cell!(atom); + let lower_str = step_or_resource_error!( + self.machine_st, + self.machine_st.heap.allocate_cstr(&c.to_uppercase().to_string()) + ); + unify!(self.machine_st, reg, lower_str); } _ => { @@ -3080,7 +3170,7 @@ impl Machine { #[inline(always)] pub(crate) fn check_cut_point(&mut self) { let addr = self.deref_register(1); - let old_b = cell_as_fixnum!(addr).get_num() as usize; + let old_b = unsafe { addr.to_fixnum_or_cut_point_unchecked() }.get_num() as usize; let prev_b = self .machine_st @@ -3111,10 +3201,10 @@ impl Machine { unify_fn!(self.machine_st, addr, *value_loc); } None if !ball.stub.is_empty() => { - let h = self.machine_st.heap.len(); - let stub = ball.copy_and_align(h); - - self.machine_st.heap.extend(stub); + let h = step_or_resource_error!( + self.machine_st, + ball.copy_and_align_to(&mut self.machine_st.heap) + ); unify_fn!(self.machine_st, addr, heap_loc_as_cell!(h)); @@ -3153,12 +3243,12 @@ impl Machine { let err = self.machine_st.instantiation_error(); Err(self.machine_st.error_form(err, stub_gen())) } else { - match Number::try_from(addr) { + match Number::try_from((addr, &self.machine_st.arena.f64_tbl)) { Ok(Number::Integer(n)) => { let n: u32 = (&*n).try_into().unwrap(); let n = char::try_from(n); if let Ok(c) = n { - write!(&mut stream, "{}", c).unwrap(); + write!(&mut stream, "{c}").unwrap(); return Ok(()); } } @@ -3166,7 +3256,7 @@ impl Machine { let n = n.get_num(); if let Some(c) = u32::try_from(n).ok().and_then(char::from_u32) { - write!(&mut stream, "{}", c).unwrap(); + write!(&mut stream, "{c}").unwrap(); return Ok(()); } } @@ -3208,14 +3298,16 @@ impl Machine { read_heap_cell!(addr, (HeapCellValueTag::Atom, (name, _arity)) => { if let Some(c) = name.as_char() { - write!(&mut stream, "{}", c).unwrap(); + write!(&mut stream, "{c}").unwrap(); return Ok(()); } } + /* (HeapCellValueTag::Char, c) => { - write!(&mut stream, "{}", c).unwrap(); + write!(&mut stream, "{c}").unwrap(); return Ok(()); } + */ _ => { } ); @@ -3298,7 +3390,7 @@ impl Machine { let err = self.machine_st.instantiation_error(); return Err(self.machine_st.error_form(err, stub_gen())); } else { - match Number::try_from(addr) { + match Number::try_from((addr, &self.machine_st.arena.f64_tbl)) { Ok(Number::Integer(n)) => { let n: u8 = (&*n).try_into().unwrap(); @@ -3377,7 +3469,7 @@ impl Machine { let addr = if addr.is_var() { addr } else { - match Number::try_from(addr) { + match Number::try_from((addr, &self.machine_st.arena.f64_tbl)) { Ok(Number::Integer(ref n)) if (**n).num_eq(&1_i64) => { fixnum_as_cell!(Fixnum::build_with(-1)) } @@ -3388,7 +3480,7 @@ impl Machine { let n: Result = (&*n).try_into(); if let Ok(value) = n { - fixnum_as_cell!(Fixnum::build_with(value as i64)) + fixnum_as_cell!(Fixnum::build_with(value)) } else { let err = self.machine_st.type_error(ValidType::InByte, addr); return Err(self.machine_st.error_form(err, stub_gen())); @@ -3396,7 +3488,7 @@ impl Machine { } Ok(Number::Fixnum(n)) => { if let Ok(nb) = u8::try_from(n.get_num()) { - fixnum_as_cell!(Fixnum::build_with(nb as i64)) + fixnum_as_cell!(Fixnum::build_with(nb)) } else { let err = self.machine_st.type_error(ValidType::InByte, addr); return Err(self.machine_st.error_form(err, stub_gen())); @@ -3413,8 +3505,7 @@ impl Machine { match stream.read(&mut b) { Ok(1) => { - self.machine_st - .unify_fixnum(Fixnum::build_with(b[0] as i64), addr); + self.machine_st.unify_fixnum(Fixnum::build_with(b[0]), addr); } _ => { stream.set_past_end_of_stream(true); @@ -3457,20 +3548,20 @@ impl Machine { self.machine_st.unify_atom(end_of_file, addr); + return Ok(()); + } else if addr == atom_as_cell!(atom!("end_of_file")) { + self.machine_st.fail = true; return Ok(()); } let stub_gen = || functor_stub(atom!("get_char"), 2); - let result = self.machine_st.open_parsing_stream(stream); let addr = if addr.is_var() { addr } else { read_heap_cell!(addr, (HeapCellValueTag::Atom, (atom, _arity)) => { - char_as_cell!(atom.as_char().unwrap()) - } - (HeapCellValueTag::Char) => { + debug_assert!(atom.as_char().is_some()); addr } _ => { @@ -3480,7 +3571,7 @@ impl Machine { ) }; - let mut iter = match result { + let mut iter = match self.machine_st.open_parsing_stream(stream) { Ok(iter) => iter, Err(e) => { if e.is_unexpected_eof() { @@ -3530,7 +3621,7 @@ impl Machine { 3, )?; - let num = match Number::try_from(self.deref_register(2)) { + let num = match Number::try_from((self.deref_register(2), &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).unwrap(), Ok(Number::Integer(n)) => match (&*n).try_into() as Result { Ok(u) => u, @@ -3584,9 +3675,12 @@ impl Machine { }; let output = self.deref_register(3); - let atom = AtomTable::build_with(&self.machine_st.atom_tbl, &string); + let cstr_cell = resource_error_call_result!( + self.machine_st, + self.machine_st.heap.allocate_cstr(&string) + ); - self.machine_st.unify_complete_string(atom, output); + unify!(self.machine_st, cstr_cell, output); Ok(()) } @@ -3628,13 +3722,13 @@ impl Machine { let addr = if addr.is_var() { addr } else { - match Number::try_from(addr) { + match Number::try_from((addr, &self.machine_st.arena.f64_tbl)) { Ok(Number::Integer(n)) => { let n: u32 = (&*n).try_into().unwrap(); let n = std::char::from_u32(n); if let Some(n) = n { - fixnum_as_cell!(Fixnum::build_with(n as i64)) + fixnum_as_cell!(Fixnum::build_with(u32::from(n))) } else { let err = self .machine_st @@ -3678,7 +3772,7 @@ impl Machine { match result { Some(Ok(c)) => { self.machine_st - .unify_fixnum(Fixnum::build_with(c as i64), addr); + .unify_fixnum(Fixnum::build_with(u32::from(c)), addr); break; } _ => { @@ -3701,11 +3795,7 @@ impl Machine { #[inline(always)] pub(crate) fn first_stream(&mut self) { - let first_stream = self - .indices - .iter_streams(..) - .filter(|s| !s.is_null_stream()) - .next(); + let first_stream = self.indices.iter_streams(..).find(|s| !s.is_null_stream()); if let Some(first_stream) = first_stream { let stream = first_stream.into(); @@ -3725,8 +3815,7 @@ impl Machine { .indices .iter_streams(prev_stream..) .filter(|s| !s.is_null_stream()) - .skip(1) - .next(); + .nth(1); if let Some(next_stream) = next_stream { let var = self.deref_register(2).as_var().unwrap(); @@ -3844,32 +3933,58 @@ impl Machine { self.indices.remove_stream(stream); - stream.close().or_else(|_| { + stream.close().map_err(|_| { let stub = functor_stub(atom!("close"), 1); let addr = stream.into(); let err = self .machine_st .existence_error(ExistenceError::Stream(addr)); - Err(self.machine_st.error_form(err, stub)) + self.machine_st.error_form(err, stub) }) } #[inline(always)] pub(crate) fn copy_to_lifted_heap(&mut self) { - let lh_offset = cell_as_fixnum!(self.deref_register(1)).get_num() as usize; - + let lh_offset = + unsafe { self.deref_register(1).to_fixnum_or_cut_point_unchecked() }.get_num() as usize; let copy_target = self.machine_st.registers[2]; + let FindallCopyInfo { + offset: old_threshold, + pstr_threshold, + } = step_or_resource_error!( + self.machine_st, + self.machine_st + .copy_findall_solution(lh_offset, copy_target) + ); - let old_threshold = self - .machine_st - .copy_findall_solution(lh_offset, copy_target); - let new_threshold = self.machine_st.lifted_heap.len() - lh_offset; + let new_threshold = self.machine_st.lifted_heap.cell_len() - lh_offset; self.machine_st.lifted_heap[old_threshold] = heap_loc_as_cell!(new_threshold); - for addr in self.machine_st.lifted_heap[old_threshold + 1..].iter_mut() { - *addr -= self.machine_st.heap.len() + lh_offset; + for idx in old_threshold + 1..pstr_threshold { + self.machine_st.lifted_heap[idx] -= self.machine_st.heap.cell_len() + lh_offset; + } + + self.machine_st.lifted_heap[old_threshold + 1] = fixnum_as_cell!( + /* FIXME this is not safe */ + unsafe { Fixnum::build_with_unchecked(pstr_threshold as i64) } + ); + self.machine_st.lifted_heap[old_threshold + 2] = + fixnum_as_cell!(/* FIXME this is not safe */ unsafe { + Fixnum::build_with_unchecked(self.machine_st.lifted_heap.cell_len() as i64) + }); + + let mut pstr_threshold = heap_index!(pstr_threshold); + + while pstr_threshold < heap_index!(self.machine_st.lifted_heap.cell_len()) { + let HeapStringScan { tail_idx, .. } = self + .machine_st + .lifted_heap + .scan_slice_to_str(pstr_threshold); + + self.machine_st.lifted_heap[tail_idx] -= self.machine_st.heap.cell_len() + lh_offset; + pstr_threshold = heap_index!(tail_idx + 1); } } @@ -3877,7 +3992,8 @@ impl Machine { pub(crate) fn lookup_db_ref(&mut self) { let module_name = self.deref_register(1); let name = cell_as_atom!(self.deref_register(2)); - let arity = cell_as_fixnum!(self.deref_register(3)).get_num() as usize; + let arity = + unsafe { self.deref_register(3).to_fixnum_or_cut_point_unchecked() }.get_num() as usize; let module_name = read_heap_cell!(module_name, (HeapCellValueTag::Atom, (module_name, _arity)) => { @@ -3937,7 +4053,7 @@ impl Machine { } else { arity_match = |arity_1, arity_2| arity_1 == arity_2; - let arity = match Number::try_from(arity) { + let arity = match Number::try_from((arity, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => Some(n.get_num() as usize), Ok(Number::Integer(n)) => { let value: usize = (&*n).try_into().unwrap(); @@ -3954,7 +4070,7 @@ impl Machine { } }; - let h = self.machine_st.heap.len(); + let h = self.machine_st.heap.cell_len(); let mut num_functors = 0; let code_dir = if module_name == atom!("user") { @@ -3974,51 +4090,47 @@ impl Machine { } }; - for (name, arity) in code_dir.keys() { - if self.indices.builtin_property((*name, *arity)) { + for (name, arity) in code_dir.keys().cloned() { + if self.indices.builtin_property((name, arity)) { continue; } - if name_match(pred_atom, *name) && arity_match(pred_arity, *arity) { - self.machine_st.heap.extend(functor!( - atom!("/"), - [cell(atom_as_cell!(name)), fixnum(*arity)] - )); + if name_match(pred_atom, name) && arity_match(pred_arity, arity) { + let functor = functor!(atom!("/"), [atom_as_cell(name), fixnum(arity)]); // self.machine_st.heap.extend( + + let mut functor_writer = Heap::functor_writer(functor); + + step_or_resource_error!(self.machine_st, functor_writer(&mut self.machine_st.heap)); num_functors += 1; } } - if num_functors > 0 { - let h = iter_to_heap_list( + let functor_list_cell = step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( &mut self.machine_st.heap, - (0..num_functors).map(|i| str_loc_as_cell!(h + 3 * i)), - ); + num_functors, + (0..num_functors).map(|i| str_loc_as_cell!(h + 3 * i)) + ) + ); - unify!( - self.machine_st, - heap_loc_as_cell!(h), - self.machine_st.registers[4] - ); - } else { - unify!( - self.machine_st, - empty_list_as_cell!(), - self.machine_st.registers[4] - ); - } + unify!( + self.machine_st, + functor_list_cell, + self.machine_st.registers[4] + ); } #[inline(always)] pub(crate) fn get_next_op_db_ref(&mut self) { let prec = self.deref_register(1); - - let h = self.machine_st.heap.len(); + let h = self.machine_st.heap.cell_len(); fn write_op_functors_to_heap( heap: &mut Heap, op_descs: impl Iterator, - ) -> usize { + ) -> Result { let mut num_functors = 0; for (name, op_desc) in op_descs { @@ -4031,19 +4143,19 @@ impl Machine { let spec_atom = op_desc.get_spec().get_spec(); - heap.extend(functor!( + let functor = functor!( atom!("op"), - [ - fixnum(prec), - cell(atom_as_cell!(spec_atom)), - cell(atom_as_cell!(name)) - ] - )); + [fixnum(prec), atom_as_cell(spec_atom), atom_as_cell(name)] + ); + + let mut functor_writer = Heap::functor_writer(functor); + + functor_writer(heap)?; num_functors += 1; } - num_functors + Ok(num_functors) } if prec.is_var() { @@ -4064,9 +4176,11 @@ impl Machine { (HeapCellValueTag::Str, s) => { cell_as_atom!(self.machine_st.heap[s]) } + /* (HeapCellValueTag::Char, c) => { AtomTable::build_with(&self.machine_st.atom_tbl, &c.to_string()) } + */ _ => { unreachable!() } @@ -4089,17 +4203,23 @@ impl Machine { if number_of_keys == 0 { self.machine_st.fail = true; } else { - let num_functors = - write_op_functors_to_heap(&mut self.machine_st.heap, op_descs); + let num_functors = step_or_resource_error!( + self.machine_st, + write_op_functors_to_heap(&mut self.machine_st.heap, op_descs,) + ); - let h = iter_to_heap_list( - &mut self.machine_st.heap, - (0..num_functors).map(|i| str_loc_as_cell!(h + 4 * i)), + let functor_list_cell = step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + num_functors, + (0..num_functors).map(|i| str_loc_as_cell!(h + 4 * i)) + ) ); unify!( self.machine_st, - heap_loc_as_cell!(h), + functor_list_cell, self.machine_st.registers[4] ); } @@ -4124,23 +4244,26 @@ impl Machine { Some((key.0, *op_desc)) }); - write_op_functors_to_heap(&mut self.machine_st.heap, op_descs) + step_or_resource_error!( + self.machine_st, + write_op_functors_to_heap(&mut self.machine_st.heap, op_descs,) + ) }; - if num_functors > 0 { - let h = iter_to_heap_list( + let functor_list_cell = step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( &mut self.machine_st.heap, + num_functors, (0..num_functors).map(|i| str_loc_as_cell!(h + 4 * i)), - ); + ) + ); - unify!( - self.machine_st, - heap_loc_as_cell!(h), - self.machine_st.registers[4] - ); - } else { - self.machine_st.fail = true; - } + unify!( + self.machine_st, + functor_list_cell, + self.machine_st.registers[4] + ); } else { let spec = cell_as_atom!(self.deref_register(2)); let op_atom = cell_as_atom!(self.deref_register(3)); @@ -4157,21 +4280,24 @@ impl Machine { match self.indices.op_dir.get(&(op_atom, fixity)).cloned() { Some(op_desc) => { - let num_functors = write_op_functors_to_heap( - &mut self.machine_st.heap, - std::iter::once((op_atom, op_desc)), - ); - - let h = iter_to_heap_list( - &mut self.machine_st.heap, - (0..num_functors).map(|i| str_loc_as_cell!(h + 4 * i)), - ); - - unify!( + let num_functors = step_or_resource_error!( self.machine_st, - heap_loc_as_cell!(h), - self.machine_st.registers[4] + write_op_functors_to_heap( + &mut self.machine_st.heap, + std::iter::once((op_atom, op_desc)) + ) ); + + let functor_list = step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + num_functors, + (0..num_functors).map(|i| str_loc_as_cell!(h + 4 * i)), + ) + ); + + unify!(self.machine_st, functor_list, self.machine_st.registers[4]); } _ => { self.machine_st.fail = true; @@ -4184,7 +4310,10 @@ impl Machine { pub(crate) fn random_integer(&mut self) { let a1 = self.deref_register(1); let a2 = self.deref_register(2); - let value = match (Number::try_from(a1), Number::try_from(a2)) { + let value = match ( + Number::try_from((a1, &self.machine_st.arena.f64_tbl)), + Number::try_from((a2, &self.machine_st.arena.f64_tbl)), + ) { (Ok(Number::Fixnum(lower)), Ok(Number::Fixnum(upper))) => { let (lower, upper) = (lower.get_num(), upper.get_num()); if lower >= upper { @@ -4192,7 +4321,12 @@ impl Machine { return; } let value = self.rng.gen_range(lower..upper); - Number::Fixnum(Fixnum::build_with(value)) + // Safety: + // - lower and uper bounds are Fixnum values + // - value is inbetween lower and upper + // - fixnums value range has no gaps + // so value is also a valid Fixnum value + Number::Fixnum(unsafe { Fixnum::build_with_unchecked(value) }) } (Ok(Number::Fixnum(lower)), Ok(Number::Integer(upper))) => { let lower = Integer::from(lower); @@ -4270,14 +4404,12 @@ impl Machine { let stub_gen = || functor_stub(atom!("length"), 2); let len = self.deref_register(2); - let n = match Number::try_from(len) { + let n = match Number::try_from((len, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => n.get_num() as usize, Ok(Number::Integer(n)) => match (&*n).try_into() as Result { Ok(n) => n, Err(_) => { - let err = self - .machine_st - .resource_error(ResourceError::FiniteMemory(len)); + let err = MachineState::resource_error(ResourceError::FiniteMemory(len)); return Err(self.machine_st.error_form(err, stub_gen())); } }, @@ -4286,17 +4418,18 @@ impl Machine { } }; - let h = self.machine_st.heap.len(); + let h = self.machine_st.heap.cell_len(); - iter_to_heap_list( - &mut self.machine_st.heap, - (0..n).map(|i| heap_loc_as_cell!(h + 2 * i + 1)), + let list_cell = resource_error_call_result!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + n, + (0..n).map(|i| heap_loc_as_cell!(h + 2 * i + 1)), + ) ); - let tail = self.deref_register(1); - self.machine_st - .bind(tail.as_var().unwrap(), heap_loc_as_cell!(h)); - + unify!(self.machine_st, self.deref_register(1), list_cell); Ok(()) } @@ -4371,36 +4504,48 @@ impl Machine { // status code let status = resp.status().as_u16(); self.machine_st - .unify_fixnum(Fixnum::build_with(status as i64), address_status); + .unify_fixnum(Fixnum::build_with(status), address_status); // headers - let headers: Vec = resp - .headers() - .iter() - .map(|(header_name, header_value)| { - let h = self.machine_st.heap.len(); + let mut headers: Vec = vec![]; - let header_term = functor!( - AtomTable::build_with( - &self.machine_st.atom_tbl, - header_name.as_str() - ), - [cell(string_as_cstr_cell!(AtomTable::build_with( - &self.machine_st.atom_tbl, - header_value.to_str().unwrap() - )))] - ); + for (header_name, header_value) in resp.headers().iter() { + let string_cell = resource_error_call_result!( + self.machine_st, + self.machine_st + .heap + .allocate_cstr(header_value.to_str().unwrap()) + ); - self.machine_st.heap.extend(header_term); - str_loc_as_cell!(h) - }) - .collect(); + let header_term = functor!( + AtomTable::build_with( + &self.machine_st.atom_tbl, + header_name.as_str() + ), + [cell(string_cell)] + ); - let headers_list = - iter_to_heap_list(&mut self.machine_st.heap, headers.into_iter()); + let mut functor_writer = Heap::functor_writer(header_term); + + let functor_cell = resource_error_call_result!( + self.machine_st, + functor_writer(&mut self.machine_st.heap) + ); + + headers.push(functor_cell); + } + + let headers_list_cell = resource_error_call_result!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + headers.len(), + headers.into_iter(), + ) + ); unify!( self.machine_st, - heap_loc_as_cell!(headers_list), + headers_list_cell, self.machine_st.registers[6] ); @@ -4427,7 +4572,9 @@ impl Machine { self.machine_st.fail = true; } } - }); + + Ok::<(), _>(()) + })?; } else { let err = self .machine_st @@ -4448,23 +4595,24 @@ impl Machine { let tls_cert = self.deref_register(4); let content_length_limit = self.deref_register(5); const CONTENT_LENGTH_LIMIT_DEFAULT: u64 = 32768; - let content_length_limit = match Number::try_from(content_length_limit) { - Ok(Number::Fixnum(n)) => { - if n.get_num() >= 0 { - n.get_num() as u64 - } else { - CONTENT_LENGTH_LIMIT_DEFAULT + let content_length_limit = + match Number::try_from((content_length_limit, &self.machine_st.arena.f64_tbl)) { + Ok(Number::Fixnum(n)) => { + if n.get_num() >= 0 { + n.get_num() as u64 + } else { + CONTENT_LENGTH_LIMIT_DEFAULT + } } - } - Ok(Number::Integer(n)) => { - let n: Result = (&*n).try_into(); - match n { - Ok(u) => u, - Err(_) => CONTENT_LENGTH_LIMIT_DEFAULT, + Ok(Number::Integer(n)) => { + let n: Result = (&*n).try_into(); + match n { + Ok(u) => u, + Err(_) => CONTENT_LENGTH_LIMIT_DEFAULT, + } } - } - _ => CONTENT_LENGTH_LIMIT_DEFAULT, - }; + _ => CONTENT_LENGTH_LIMIT_DEFAULT, + }; let ssl_server: Option<(String, String)> = { match self.machine_st.value_to_str_like(tls_key) { @@ -4599,92 +4747,122 @@ impl Machine { (ArenaHeaderTag::HttpListener, http_listener) => { loop { match http_listener.incoming.recv_timeout(std::time::Duration::from_millis(200)) { - Ok(request) => { - let method_atom = match request.request_data.method { - Method::GET => atom!("get"), - Method::POST => atom!("post"), - Method::PUT => atom!("put"), - Method::DELETE => atom!("delete"), - Method::PATCH => atom!("patch"), - Method::HEAD => atom!("head"), - Method::OPTIONS => atom!("options"), - Method::TRACE => atom!("trace"), - Method::CONNECT => atom!("connect"), - _ => atom!("unsupported_extension"), - }; - let path_atom = AtomTable::build_with(&self.machine_st.atom_tbl, &request.request_data.path); - let path_cell = atom_as_cstr_cell!(path_atom); - let headers: Vec = request.request_data.headers.iter().map(|(header_name, header_value)| { - let h = self.machine_st.heap.len(); - let header_term = functor!(AtomTable::build_with(&self.machine_st.atom_tbl, header_name.as_str()), [cell(string_as_cstr_cell!(AtomTable::build_with(&self.machine_st.atom_tbl, header_value.to_str().unwrap())))]); + Ok(request) => { + let method_atom = match request.request_data.method { + Method::GET => atom!("get"), + Method::POST => atom!("post"), + Method::PUT => atom!("put"), + Method::DELETE => atom!("delete"), + Method::PATCH => atom!("patch"), + Method::HEAD => atom!("head"), + Method::OPTIONS => atom!("options"), + Method::TRACE => atom!("trace"), + Method::CONNECT => atom!("connect"), + _ => atom!("unsupported_extension"), + }; - self.machine_st.heap.extend(header_term.into_iter()); - str_loc_as_cell!(h) - }).collect(); + let path_atom = AtomTable::build_with(&self.machine_st.atom_tbl, &request.request_data.path); + let path_cell = resource_error_call_result!( + self.machine_st, + self.machine_st.heap.allocate_cstr(&request.request_data.path) + ); - let headers_list = iter_to_heap_list(&mut self.machine_st.heap, headers.into_iter()); + let mut headers = vec![]; - let query_str = request.request_data.query; - let query_atom = AtomTable::build_with(&self.machine_st.atom_tbl, &query_str); - let query_cell = string_as_cstr_cell!(query_atom); + for (header_name, header_value) in request.request_data.headers { + let header_value = resource_error_call_result!( + self.machine_st, + self.machine_st.heap.allocate_cstr(header_value.to_str().unwrap()) + ); - let mut stream = Stream::from_http_stream( - path_atom, - request.request_data.body, - &mut self.machine_st.arena - ); - *stream.options_mut() = StreamOptions::default(); - stream.options_mut().set_stream_type(StreamType::Binary); + let header_term = functor!( + AtomTable::build_with(&self.machine_st.atom_tbl, header_name.unwrap().as_str()), + [cell(header_value)] + ); - self.indices.add_stream(stream, atom!("http_accept"), 7) - .map_err(|stub_gen| stub_gen(&mut self.machine_st))?; + let mut functor_writer = Heap::functor_writer(header_term); - let stream: HeapCellValue = stream.into(); + let functor_cell = resource_error_call_result!( + self.machine_st, + functor_writer(&mut self.machine_st.heap) + ); - let handle: TypedArenaPtr = arena_alloc!(request.response, &mut self.machine_st.arena); + headers.push(functor_cell); + } - self.machine_st.bind(method.as_var().unwrap(), atom_as_cell!(method_atom)); - self.machine_st.bind(path.as_var().unwrap(), path_cell); - unify!(self.machine_st, heap_loc_as_cell!(headers_list), self.machine_st.registers[4]); - self.machine_st.bind(query.as_var().unwrap(), query_cell); - self.machine_st.bind(stream_addr.as_var().unwrap(), stream); - self.machine_st.bind(handle_addr.as_var().unwrap(), typed_arena_ptr_as_cell!(handle)); - break - } - Err(std::sync::mpsc::RecvTimeoutError::Timeout) => { - let interrupted = machine::INTERRUPT.load(std::sync::atomic::Ordering::Relaxed); + let headers_list_cell = resource_error_call_result!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + headers.len(), + headers.into_iter(), + ) + ); + + let query_str = request.request_data.query; + let query_cell = resource_error_call_result!( + self.machine_st, + self.machine_st.heap.allocate_cstr(&query_str) + ); + + let mut stream = Stream::from_http_stream( + path_atom, + request.request_data.body, + &mut self.machine_st.arena + ); + *stream.options_mut() = StreamOptions::default(); + stream.options_mut().set_stream_type(StreamType::Binary); + + self.indices.add_stream(stream, atom!("http_accept"), 7) + .map_err(|stub_gen| stub_gen(&mut self.machine_st))?; + + let stream = stream_as_cell!(stream); + + let handle = arena_alloc!(request.response, &mut self.machine_st.arena) + as TypedArenaPtr; + + self.machine_st.bind(method.as_var().unwrap(), atom_as_cell!(method_atom)); + self.machine_st.bind(path.as_var().unwrap(), path_cell); + unify!(self.machine_st, headers_list_cell, self.machine_st.registers[4]); + self.machine_st.bind(query.as_var().unwrap(), query_cell); + self.machine_st.bind(stream_addr.as_var().unwrap(), stream); + self.machine_st.bind(handle_addr.as_var().unwrap(), typed_arena_ptr_as_cell!(handle)); - match machine::INTERRUPT.compare_exchange( - interrupted, - false, - std::sync::atomic::Ordering::Relaxed, - std::sync::atomic::Ordering::Relaxed, - ) { - Ok(interruption) => { - if interruption { - self.machine_st.throw_interrupt_exception(); - self.machine_st.backtrack(); - // We have extracted controll over the Tokio runtime to the calling context for enabling library use case - // (see https://github.com/mthom/scryer-prolog/pull/1880) - // So we only have access to a runtime handle in here and can't shut it down. - // Since I'm not aware of the consequences of deactivating this new code which came in while PR 1880 - // was not merged, I'm only deactivating it for now. - //let old_runtime = std::mem::replace(&mut self.runtime, tokio::runtime::Runtime::new().unwrap()); - //old_runtime.shutdown_background(); break } - } - Err(_) => unreachable!(), - } + Err(std::sync::mpsc::RecvTimeoutError::Timeout) => { + let interrupted = machine::INTERRUPT.load(std::sync::atomic::Ordering::Relaxed); - } - Err(_) => { - self.machine_st.fail = true; - } - } + match machine::INTERRUPT.compare_exchange( + interrupted, + false, + std::sync::atomic::Ordering::Relaxed, + std::sync::atomic::Ordering::Relaxed, + ) { + Ok(interruption) => { + if interruption { + self.machine_st.throw_interrupt_exception(); + self.machine_st.backtrack(); + // We have extracted controll over the Tokio runtime to the calling context for enabling library use case + // (see https://github.com/mthom/scryer-prolog/pull/1880) + // So we only have access to a runtime handle in here and can't shut it down. + // Since I'm not aware of the consequences of deactivating this new code which came in while PR 1880 + // was not merged, I'm only deactivating it for now. + //let old_runtime = std::mem::replace(&mut self.runtime, tokio::runtime::Runtime::new().unwrap()); + //old_runtime.shutdown_background(); + break + } + } + Err(_) => unreachable!(), + } } + Err(_) => { + self.machine_st.fail = true; + } } - _ => { + } + } + _ => { unreachable!(); } ); @@ -4701,7 +4879,8 @@ impl Machine { pub(crate) fn http_answer(&mut self) -> CallResult { let culprit = self.deref_register(1); let status_code = self.deref_register(2); - let status_code: u16 = match Number::try_from(status_code) { + let status_code: u16 = match Number::try_from((status_code, &self.machine_st.arena.f64_tbl)) + { Ok(Number::Fixnum(n)) => n.get_num() as u16, Ok(Number::Integer(n)) => { let n: Result = (&*n).try_into(); @@ -4831,7 +5010,7 @@ impl Machine { if let Some(function_name) = self.machine_st.value_to_str_like(function_name) { let stub_gen = || functor_stub(atom!("foreign_call"), 3); fn map_arg(machine_st: &mut MachineState, source: HeapCellValue) -> crate::ffi::Value { - match Number::try_from(source) { + match Number::try_from((source, &machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => Value::Int(n.get_num()), Ok(Number::Float(n)) => Value::Float(n.into_inner()), _ => { @@ -4874,23 +5053,31 @@ impl Machine { { Ok(result) => { match result { - Value::Int(n) => self - .machine_st - .unify_fixnum(Fixnum::build_with(n), return_value), + Value::Int(n) => self.machine_st.unify_fixnum( + Fixnum::build_with_checked(n).unwrap_or_else(|_| { + todo!("handle integer values that don't fit in fixnum") + }), + return_value, + ), Value::Float(n) => { let n = float_alloc!(n, self.machine_st.arena); self.machine_st.unify_f64(n, return_value) } Value::Struct(name, args) => { - let struct_value = self.build_struct(&name, args); + let struct_value = resource_error_call_result!( + self.machine_st, + self.build_struct(&name, args) + ); + unify!(self.machine_st, return_value, struct_value); } Value::CString(cstr) => { - let cstr = AtomTable::build_with( - &self.machine_st.atom_tbl, - cstr.to_str().unwrap(), + let str_cell = resource_error_call_result!( + self.machine_st, + self.machine_st.heap.allocate_cstr(cstr.to_str().unwrap()) ); - self.machine_st.unify_complete_string(cstr, return_value); + + unify!(self.machine_st, str_cell, return_value); } } return Ok(()); @@ -4906,30 +5093,43 @@ impl Machine { Err(e) => return Err(e), } } + self.machine_st.fail = true; Ok(()) } #[cfg(feature = "ffi")] - fn build_struct(&mut self, name: &str, mut args: Vec) -> HeapCellValue { + fn build_struct(&mut self, name: &str, mut args: Vec) -> Result { args.insert(0, Value::CString(CString::new(name).unwrap())); - let cells: Vec<_> = args - .into_iter() - .map(|val| match val { - Value::Int(n) => fixnum_as_cell!(Fixnum::build_with(n)), + + let mut expanded_args = Vec::with_capacity(args.len()); + + for val in args { + expanded_args.push(match val { + Value::Int(n) => { + if let Ok(fixnum) = Fixnum::build_with_checked(n) { + fixnum_as_cell!(fixnum) + } else { + integer_as_cell!(Number::Integer(arena_alloc!( + Integer::from(n), + &mut self.machine_st.arena + ))) + } + } Value::Float(n) => HeapCellValue::from(float_alloc!(n, self.machine_st.arena)), Value::CString(cstr) => atom_as_cell!(AtomTable::build_with( &self.machine_st.atom_tbl, &cstr.into_string().unwrap() )), - Value::Struct(name, struct_args) => self.build_struct(&name, struct_args), - }) - .collect(); + Value::Struct(name, struct_args) => self.build_struct(&name, struct_args)?, + }); + } - heap_loc_as_cell!(iter_to_heap_list( + sized_iter_to_heap_list( &mut self.machine_st.heap, - cells.into_iter() - )) + expanded_args.len(), + expanded_args.into_iter(), + ) } #[cfg(feature = "ffi")] @@ -4970,9 +5170,10 @@ impl Machine { let result_reg = self.deref_register(2); if let Some(code) = self.machine_st.value_to_str_like(code) { match js_sys::eval(&code.as_str()) { - Ok(result) => self.unify_js_value(result, result_reg), - Err(result) => self.unify_js_value(result, result_reg), + Ok(result) => self.unify_js_value(result, result_reg)?, + Err(result) => self.unify_js_value(result, result_reg)?, }; + return Ok(()); } self.machine_st.fail = true; @@ -4980,7 +5181,11 @@ impl Machine { } #[cfg(target_arch = "wasm32")] - fn unify_js_value(&mut self, result: wasm_bindgen::JsValue, result_reg: HeapCellValue) { + fn unify_js_value( + &mut self, + result: wasm_bindgen::JsValue, + result_reg: HeapCellValue, + ) -> CallResult { match result.as_bool() { Some(result) => match result { true => self.machine_st.unify_atom(atom!("true"), result_reg), @@ -4993,8 +5198,10 @@ impl Machine { } None => match result.as_string() { Some(result) => { - let result = AtomTable::build_with(&self.machine_st.atom_tbl, &result); - self.machine_st.unify_complete_string(result, result_reg); + resource_error_call_result!( + self.machine_st, + self.machine_st.heap.allocate_cstr(result.as_str()) + ); } None => { if result.is_null() { @@ -5019,34 +5226,46 @@ impl Machine { }, }, } + + Ok(()) } #[inline(always)] pub(crate) fn argv(&mut self) -> CallResult { let args = self.deref_register(1); - let mut args_pstrs = vec![]; - for arg in env::args() { - args_pstrs.push(put_complete_string( - &mut self.machine_st.heap, - &arg, - &self.machine_st.atom_tbl, - )); - } - let cell = heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - args_pstrs.into_iter() - )); - unify!(self.machine_st, args, cell); + for arg in env::args() { + let pstr_cell = resource_error_call_result!( + self.machine_st, + self.machine_st.heap.allocate_cstr(&arg) + ); + + args_pstrs.push(pstr_cell); + } + + let list_cell = resource_error_call_result!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + args_pstrs.len(), + args_pstrs.into_iter(), + ) + ); + + unify!(self.machine_st, args, list_cell); Ok(()) } #[inline(always)] pub(crate) fn current_time(&mut self) { let timestamp = self.systemtime_to_timestamp(SystemTime::now()); - self.machine_st - .unify_complete_string(timestamp, self.machine_st.registers[1]); + let cstr_cell = step_or_resource_error!( + self.machine_st, + self.machine_st.heap.allocate_cstr(×tamp) + ); + + unify!(self.machine_st, cstr_cell, self.machine_st.registers[1]); } #[inline(always)] @@ -5094,7 +5313,7 @@ impl Machine { let priority = self.deref_register(1); let specifier = cell_as_atom_cell!(self.deref_register(2)).get_name(); - let priority = match Number::try_from(priority) { + let priority = match Number::try_from((priority, &self.machine_st.arena.f64_tbl)) { Ok(Number::Integer(n)) => { let n: u16 = (&*n).try_into().unwrap(); n @@ -5106,9 +5325,11 @@ impl Machine { }; let op = read_heap_cell!(self.deref_register(3), + /* (HeapCellValueTag::Char, c) => { AtomTable::build_with(&self.machine_st.atom_tbl, &c.to_string()) } + */ (HeapCellValueTag::Atom, (name, _arity)) => { name } @@ -5247,7 +5468,11 @@ impl Machine { #[inline(always)] pub(crate) fn get_attr_var_queue_delimiter(&mut self) { let addr = self.deref_register(1); - let value = Fixnum::build_with(self.machine_st.attr_var_init.attr_var_queue.len() as i64); + + /* FIXME this is not safe */ + let value = unsafe { + Fixnum::build_with_unchecked(self.machine_st.attr_var_init.attr_var_queue.len() as i64) + }; self.machine_st.unify_fixnum(value, addr); } @@ -5256,7 +5481,7 @@ impl Machine { pub(crate) fn get_attr_var_queue_beyond(&mut self) { let addr = self.deref_register(1); - let b = match Number::try_from(addr) { + let b = match Number::try_from((addr, &self.machine_st.arena.f64_tbl)) { Ok(Number::Integer(n)) => { let value: usize = (&*n).try_into().unwrap(); Some(value) @@ -5269,10 +5494,16 @@ impl Machine { }; if let Some(b) = b { - let iter = self.machine_st.gather_attr_vars_created_since(b); + let attr_vars = self.machine_st.gather_attr_vars_created_since(b); - let var_list_addr = - heap_loc_as_cell!(iter_to_heap_list(&mut self.machine_st.heap, iter)); + let var_list_addr = step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + attr_vars.len(), + attr_vars.into_iter(), + ) + ); let list_addr = self.machine_st.registers[2]; unify!(self.machine_st, var_list_addr, list_addr); @@ -5330,7 +5561,10 @@ impl Machine { pub(crate) fn put_to_attributed_variable_list(&mut self) { let attr_var = self.deref_register(1); let attr = self.deref_register(3); - let attr_var_list = match self.machine_st.get_attr_var_list(attr_var) { + let attr_var_list_result = + step_or_resource_error!(self.machine_st, self.machine_st.get_attr_var_list(attr_var)); + + let attr_var_list = match attr_var_list_result { Some(h) => h, None => { self.machine_st.fail = true; @@ -5357,12 +5591,16 @@ impl Machine { * or str cells (> 0-arity). */ - let h = self.machine_st.heap.len(); + let module_functor = functor!(atom!(":"), [cell(module), cell(attr)]); + let h = self.machine_st.heap.cell_len(); - self.machine_st.heap.push(str_loc_as_cell!(h + 1)); - self.machine_st - .heap - .extend(functor!(atom!(":"), [cell(module), cell(attr)])); + step_or_resource_error!( + self.machine_st, + self.machine_st.heap.push_cell(str_loc_as_cell!(h + 1)) + ); + + let mut functor_writer = Heap::functor_writer(module_functor); + step_or_resource_error!(self.machine_st, functor_writer(&mut self.machine_st.heap)); match self.match_attribute(self.machine_st.heap[attr_var_list], module, attr) { Some(AttrListMatch { match_site, .. }) => { @@ -5372,8 +5610,16 @@ impl Machine { // at the end of the (non-empty) list here. self.machine_st.heap[match_site] = list_loc_as_cell!(h + 4); - self.machine_st.heap.push(heap_loc_as_cell!(h)); - self.machine_st.heap.push(heap_loc_as_cell!(h + 5)); + + let mut writer = step_or_resource_error!( + self.machine_st, + self.machine_st.heap.reserve(2) + ); + + writer.write_with(|section| { + section.push_cell(heap_loc_as_cell!(h)); + section.push_cell(heap_loc_as_cell!(h + 5)); + }); (match_site, l) } @@ -5391,8 +5637,14 @@ impl Machine { None => { // the list is empty. self.machine_st.heap[attr_var_list] = list_loc_as_cell!(h + 4); - self.machine_st.heap.push(heap_loc_as_cell!(h)); - self.machine_st.heap.push(heap_loc_as_cell!(h + 5)); + + let mut writer = + step_or_resource_error!(self.machine_st, self.machine_st.heap.reserve(2)); + + writer.write_with(|section| { + section.push_cell(heap_loc_as_cell!(h)); + section.push_cell(heap_loc_as_cell!(h + 5)); + }); self.machine_st .attr_var_init @@ -5489,66 +5741,82 @@ impl Machine { #[inline(always)] pub(crate) fn get_continuation_chunk(&mut self) { let e = self.deref_register(1); - let e = cell_as_fixnum!(e).get_num() as usize; - - let p_functor = self.deref_register(2); + let e = unsafe { e.to_fixnum_or_cut_point_unchecked() }.get_num() as usize; + let h = self.machine_st.heap.cell_len(); + let p_functor_cell = self.deref_register(2); let num_cells = self.machine_st.stack.index_and_frame(e).prelude.num_cells; - let mut addrs = vec![]; - for idx in 1..num_cells + 1 { - let addr = self.machine_st.stack[stack_loc!(AndFrame, e, idx)]; - let addr = self.machine_st.store(self.machine_st.deref(addr)); + let mut writer = + step_or_resource_error!(self.machine_st, self.machine_st.heap.reserve(2 + num_cells)); - // avoid pushing stack variables to the heap where they - // must not go. - if addr.is_stack_var() { - let h = self.machine_st.heap.len(); + writer.write_with(|section| { + section.push_cell(atom_as_cell!(atom!("cont_chunk"), 1 + num_cells)); + section.push_cell(p_functor_cell); - self.machine_st.heap.push(heap_loc_as_cell!(h)); - self.machine_st.bind(Ref::heap_cell(h), addr); + for idx in 1..=num_cells { + let mut stack_offset = stack_loc!(AndFrame, e, idx); + let mut addr = self.machine_st.stack[stack_offset]; - addrs.push(heap_loc_as_cell!(h)); - } else { - addrs.push(addr); + while addr.get_tag() == HeapCellValueTag::StackVar { + stack_offset = addr.get_value() as usize; + + if self.machine_st.stack[stack_offset] == addr { + break; + } + + addr = self.machine_st.stack[stack_offset]; + } + + if addr.get_tag() == HeapCellValueTag::StackVar { + section.push_cell(heap_loc_as_cell!(h + 1 + idx)); + self.machine_st.stack[stack_offset] = heap_loc_as_cell!(h + 1 + idx); + + // have to inline the TrailRef::Ref(RefTag::StackCell) case of MachineState::trail + // here to get around the borrow checker. + if stack_offset < self.machine_st.b { + self.machine_st.trail.push(TrailEntry::build_with( + TrailEntryTag::TrailedStackVar, + stack_offset as u64, + )); + + self.machine_st.tr += 1; + } + } else { + section.push_cell(addr); + } } - } - - let chunk = str_loc_as_cell!(self.machine_st.heap.len()); - - self.machine_st - .heap - .push(atom_as_cell!(atom!("cont_chunk"), 1 + num_cells)); - self.machine_st.heap.push(p_functor); - self.machine_st.heap.extend(addrs); + }); + let chunk = str_loc_as_cell!(h); unify!(self.machine_st, self.machine_st.registers[3], chunk); } #[inline(always)] pub(crate) fn get_lifted_heap_from_offset_diff(&mut self) { let lh_offset = self.machine_st.registers[1]; - let lh_offset = cell_as_fixnum!(self.machine_st.store(self.machine_st.deref(lh_offset))) - .get_num() as usize; + let lh_offset = unsafe { + self.machine_st + .store(self.machine_st.deref(lh_offset)) + .to_fixnum_or_cut_point_unchecked() + } + .get_num() as usize; - if lh_offset >= self.machine_st.lifted_heap.len() { + if lh_offset >= self.machine_st.lifted_heap.cell_len() { let solutions = self.machine_st.registers[2]; let diff = self.machine_st.registers[3]; unify_fn!(self.machine_st, solutions, diff); } else { - let h = self.machine_st.heap.len(); - let mut last_index = h; + let h = self.machine_st.heap.cell_len(); + self.machine_st.copy_lifted_heap_from_offset(h, lh_offset); - for value in self.machine_st.lifted_heap[lh_offset..].iter().cloned() { - last_index = self.machine_st.heap.len(); - self.machine_st.heap.push(value + h); - } - - if last_index < self.machine_st.heap.len() { - let diff = self.machine_st.registers[3]; - unify_fn!(self.machine_st, diff, self.machine_st.heap[last_index]); - } + let diff = self.machine_st.registers[3]; + unify_fn!( + self.machine_st, + diff, + self.machine_st.heap.last_cell().unwrap() + ); self.machine_st.lifted_heap.truncate(lh_offset); @@ -5560,19 +5828,20 @@ impl Machine { #[inline(always)] pub(crate) fn get_lifted_heap_from_offset(&mut self) { let lh_offset = self.machine_st.registers[1]; - let lh_offset = cell_as_fixnum!(self.machine_st.store(self.machine_st.deref(lh_offset))) - .get_num() as usize; + let lh_offset = unsafe { + self.machine_st + .store(self.machine_st.deref(lh_offset)) + .to_fixnum_or_cut_point_unchecked() + } + .get_num() as usize; - if lh_offset >= self.machine_st.lifted_heap.len() { + if lh_offset >= self.machine_st.lifted_heap.cell_len() { let solutions = self.machine_st.registers[2]; unify_fn!(self.machine_st, solutions, empty_list_as_cell!()); } else { - let h = self.machine_st.heap.len(); - - for addr in self.machine_st.lifted_heap[lh_offset..].iter().cloned() { - self.machine_st.heap.push(addr + h); - } + let h = self.machine_st.heap.cell_len(); + self.machine_st.copy_lifted_heap_from_offset(h, lh_offset); self.machine_st.lifted_heap.truncate(lh_offset); let solutions = self.machine_st.registers[2]; @@ -5639,7 +5908,7 @@ impl Machine { pub(crate) fn halt(&mut self) -> std::process::ExitCode { let code = self.deref_register(1); - let code = match Number::try_from(code) { + let code = match Number::try_from((code, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => u8::try_from(n.get_num()).unwrap(), Ok(Number::Integer(n)) => { let n: u8 = (&*n).try_into().unwrap(); @@ -5649,8 +5918,7 @@ impl Machine { // n has already been confirmed as an integer, and // internally, Rational is assumed reduced, so its // denominator must be 1. - let r = r.numerator().try_into().unwrap(); - r + r.numerator().try_into().unwrap() } _ => { unreachable!() @@ -5667,7 +5935,6 @@ impl Machine { let prev_block = self.machine_st.scc_block; self.machine_st.run_cleaners_fn = Machine::run_cleaners; - self.machine_st.scc_block = b; self.machine_st.cont_pts.push((addr, b, prev_block)); } @@ -5678,18 +5945,17 @@ impl Machine { let a1 = self.deref_register(1); let a2 = self.deref_register(2); - let n = match Number::try_from(a2) { + let n = match Number::try_from((a2, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(bp)) => Integer::from(bp.get_num() as usize), Ok(Number::Integer(n)) => (*n).clone(), _ => { let stub = functor_stub(atom!("call_with_inference_limit"), 3); - let err = self.machine_st.type_error(ValidType::Integer, a2); return Err(self.machine_st.error_form(err, stub)); } }; - let bp = cell_as_fixnum!(a1).get_num() as usize; + let bp = unsafe { a1.to_fixnum_or_cut_point_unchecked() }.get_num() as usize; let a3 = self.deref_register(3); let count = self.machine_st.cwil.add_limit(n, bp).clone(); @@ -5700,9 +5966,11 @@ impl Machine { #[inline(always)] pub(crate) fn inference_count(&mut self, count_var: HeapCellValue, count: Integer) { - if let Ok(value) = <&Integer as TryInto>::try_into(&count) { - self.machine_st - .unify_fixnum(Fixnum::build_with(value), count_var); + if let Some(value) = <&Integer as TryInto>::try_into(&count) + .ok() + .and_then(|i| Fixnum::build_with_checked(i).ok()) + { + self.machine_st.unify_fixnum(value, count_var); } else { let count = arena_alloc!(count, &mut self.machine_st.arena); self.machine_st.unify_big_int(count, count_var); @@ -5722,7 +5990,7 @@ impl Machine { let name = cell_as_atom!(self.deref_register(2)); let a3 = self.deref_register(3); - let arity = match Number::try_from(a3) { + let arity = match Number::try_from((a3, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => n.get_num() as usize, Ok(Number::Integer(n)) => { let result = (&*n).try_into(); @@ -5740,14 +6008,20 @@ impl Machine { self.indices .get_predicate_code_index(name, arity, module_name) - .map(|index| index.local().is_some()) + .map(|idx| { + self.machine_st + .arena + .code_index_tbl + .get_entry(idx.into()) + .local() + .is_some() + }) .unwrap_or(false) } #[inline(always)] pub(crate) fn no_such_predicate(&mut self) -> CallResult { let module_name = cell_as_atom!(self.deref_register(1)); - let head = self.deref_register(2); self.machine_st.fail = read_heap_cell!(head, @@ -5763,7 +6037,10 @@ impl Machine { arity, module_name, ) - .map(|index| index.get()) + .map(|idx| self.machine_st + .arena + .code_index_tbl + .get_entry(idx.into())) .unwrap_or(IndexPtr::dynamic_undefined()); !matches!(index.tag(), IndexPtrTag::DynamicUndefined | IndexPtrTag::Undefined) @@ -5780,7 +6057,10 @@ impl Machine { 0, module_name, ) - .map(|index| index.get()) + .map(|idx| self.machine_st + .arena + .code_index_tbl + .get_entry(idx.into())) .unwrap_or(IndexPtr::dynamic_undefined()); !matches!(index.tag(), IndexPtrTag::DynamicUndefined) @@ -5830,7 +6110,8 @@ impl Machine { #[inline(always)] pub(crate) fn remove_call_policy_check(&mut self) { - let bp = cell_as_fixnum!(self.deref_register(1)).get_num() as usize; + let bp = + unsafe { self.deref_register(1).to_fixnum_or_cut_point_unchecked() }.get_num() as usize; if bp == self.machine_st.b && self.machine_st.cwil.is_empty() { self.machine_st.cwil.reset(); @@ -5842,12 +6123,10 @@ impl Machine { let a1 = self.deref_register(1); let a2 = self.deref_register(2); - let block = cell_as_fixnum!(a1).get_num() as usize; + let block = unsafe { a1.to_fixnum_or_cut_point_unchecked() }.get_num() as usize; let count = self.machine_st.cwil.remove_limit(block).clone(); - let result = count.clone().try_into(); - - if let Ok(value) = result { - self.machine_st.unify_fixnum(Fixnum::build_with(value), a2); + if let Ok(value) = Fixnum::build_with_checked(&count) { + self.machine_st.unify_fixnum(value, a2); } else { let count = arena_alloc!(count.clone(), &mut self.machine_st.arena); self.machine_st.unify_big_int(count, a2); @@ -5865,18 +6144,23 @@ impl Machine { self.machine_st.registers[i] = self.machine_st.stack[stack_loc!(AndFrame, e, i)]; } - self.machine_st.b0 = cell_as_fixnum!( + self.machine_st.b0 = unsafe { self.machine_st.stack[stack_loc!(AndFrame, e, frame_len - 2)] - ) + .to_fixnum_or_cut_point_unchecked() + } .get_num() as usize; - self.machine_st.num_of_args = cell_as_fixnum!( + self.machine_st.num_of_args = unsafe { self.machine_st.stack[stack_loc!(AndFrame, e, frame_len - 1)] - ) + .to_fixnum_or_cut_point_unchecked() + } .get_num() as usize; - let p = cell_as_fixnum!(self.machine_st.stack[stack_loc!(AndFrame, e, frame_len)]).get_num() - as usize; + let p = unsafe { + self.machine_st.stack[stack_loc!(AndFrame, e, frame_len)] + .to_fixnum_or_cut_point_unchecked() + } + .get_num() as usize; self.machine_st.deallocate(); self.machine_st.p = p; @@ -5993,7 +6277,7 @@ impl Machine { let a1 = self.deref_register(1); let a2 = self.deref_register(2); - let bp = cell_as_fixnum!(a2).get_num() as usize; + let bp = unsafe { a2.to_fixnum_or_cut_point_unchecked() }.get_num() as usize; let prev_b = self .machine_st .stack @@ -6016,7 +6300,7 @@ impl Machine { #[inline(always)] pub(crate) fn clean_up_block(&mut self) { let nb = self.deref_register(1); - let nb = cell_as_fixnum!(nb).get_num() as usize; + let nb = unsafe { nb.to_fixnum_or_cut_point_unchecked() }.get_num() as usize; let b = self.machine_st.b; @@ -6028,15 +6312,17 @@ impl Machine { #[inline(always)] pub(crate) fn get_ball(&mut self) { let addr = self.deref_register(1); - let h = self.machine_st.heap.len(); - - if !self.machine_st.ball.stub.is_empty() { - let stub = self.machine_st.ball.copy_and_align(h); - self.machine_st.heap.extend(stub); + let h = if !self.machine_st.ball.stub.is_empty() { + step_or_resource_error!( + self.machine_st, + self.machine_st + .ball + .copy_and_align_to(&mut self.machine_st.heap) + ) } else { self.machine_st.fail = true; return; - } + }; match addr.as_var() { Some(r) => self.machine_st.bind(r, self.machine_st.heap[h]), @@ -6070,7 +6356,9 @@ impl Machine { #[inline(always)] pub(crate) fn get_current_block(&mut self) { let addr = self.machine_st.registers[1]; - let block = Fixnum::build_with(self.machine_st.block as i64); + + /* FIXME this is not safe */ + let block = unsafe { Fixnum::build_with_unchecked(self.machine_st.block as i64) }; self.machine_st.unify_fixnum(block, addr); } @@ -6078,21 +6366,27 @@ impl Machine { #[inline(always)] pub(crate) fn get_current_scc_block(&mut self) { let addr = self.machine_st.registers[1]; - let block = Fixnum::build_with(self.machine_st.scc_block as i64); + + /* FIXME this is not safe */ + let block = unsafe { Fixnum::build_with_unchecked(self.machine_st.scc_block as i64) }; self.machine_st.unify_fixnum(block, addr); } #[inline(always)] pub(crate) fn get_b_value(&mut self) { - let n = Fixnum::as_cutpoint(i64::try_from(self.machine_st.b).unwrap()); + /* FIXME this is not safe */ + let n = unsafe { Fixnum::build_with_unchecked(i64::try_from(self.machine_st.b).unwrap()) } + .as_cutpoint(); self.machine_st .unify_fixnum(n, self.machine_st.registers[1]); } #[inline(always)] pub(crate) fn get_cut_point(&mut self) { - let n = Fixnum::as_cutpoint(i64::try_from(self.machine_st.b0).unwrap()); + /* FIXME this is not safe */ + let n = unsafe { Fixnum::build_with_unchecked(i64::try_from(self.machine_st.b0).unwrap()) } + .as_cutpoint(); self.machine_st .unify_fixnum(n, self.machine_st.registers[1]); } @@ -6114,17 +6408,16 @@ impl Machine { let cp = and_frame.prelude.cp - 1; let e = and_frame.prelude.e; - let e = Fixnum::build_with(i64::try_from(e).unwrap()); + let e = Fixnum::build_with_checked(e).unwrap(); - let p = str_loc_as_cell!(machine_st.heap.len()); - - machine_st - .heap - .extend(functor!(atom!("dir_entry"), [fixnum(cp)])); machine_st.unify_fixnum(e, machine_st.registers[2]); if !machine_st.fail { - unify!(machine_st, p, machine_st.registers[3]); + let mut writer = Heap::functor_writer(functor!(atom!("dir_entry"), [fixnum(cp)])); + let p_functor_cell = + step_or_resource_error!(machine_st, writer(&mut machine_st.heap)); + + unify!(machine_st, p_functor_cell, machine_st.registers[3]); } }; @@ -6150,16 +6443,25 @@ impl Machine { // active permanent variables can be read from // it later. let and_frame = self.machine_st.stack.index_and_frame(e); + + if and_frame.prelude.cp == 0 { + self.machine_st.fail = true; + return; + } + let cp = and_frame.prelude.cp - 1; + let mut writer = Heap::functor_writer(functor!(atom!("dir_entry"), [fixnum(cp)])); - let p = str_loc_as_cell!(self.machine_st.heap.len()); - self.machine_st.heap.extend(functor!(atom!("dir_entry"), [fixnum(cp)])); + let p_functor_cell = step_or_resource_error!( + self.machine_st, + writer(&mut self.machine_st.heap) + ); - let e = Fixnum::build_with(i64::try_from(and_frame.prelude.e).unwrap()); + let e = Fixnum::build_with_checked(and_frame.prelude.e).unwrap(); self.machine_st.unify_fixnum(e, self.machine_st.registers[2]); if !self.machine_st.fail { - unify!(self.machine_st, p, self.machine_st.registers[3]); + unify!(self.machine_st, p_functor_cell, self.machine_st.registers[3]); } } _ => { @@ -6199,9 +6501,11 @@ impl Machine { None => true, }; } + /* (HeapCellValueTag::Char, c) => { self.machine_st.fail = non_quoted_token(once(c)); } + */ (HeapCellValueTag::Atom, (name, arity)) => { debug_assert_eq!(arity, 0); self.machine_st.fail = non_quoted_token(name.as_str().chars()); @@ -6282,9 +6586,7 @@ impl Machine { let term_write_result = parser .read_term(&op_dir, Tokens::Default) .map_err(|err| error_after_read_term(err, 0, &parser)) - .and_then(|term| { - write_term_to_heap(&term, &mut self.machine_st.heap, &self.machine_st.atom_tbl) - }); + .and_then(|term| write_term_to_heap(&term, &mut self.machine_st.heap)); match term_write_result { Ok(term_write_result) => Ok(Some(term_write_result)), @@ -6333,7 +6635,8 @@ impl Machine { } else { if !self.machine_st.fail { // wrote end_of_file term in this case. - self.machine_st.write_read_term_options(vec![], vec![])?; + self.machine_st + .write_read_term_options(vec![], empty_list_as_cell!())?; } Ok(()) @@ -6373,12 +6676,15 @@ impl Machine { #[inline(always)] pub(crate) fn reset_continuation_marker(&mut self) { - let h = self.machine_st.heap.len(); + let h = self.machine_st.heap.cell_len(); self.machine_st.registers[3] = atom_as_cell!(atom!("none")); self.machine_st.registers[4] = heap_loc_as_cell!(h); - self.machine_st.heap.push(heap_loc_as_cell!(h)); + step_or_resource_error!( + self.machine_st, + self.machine_st.heap.push_cell(heap_loc_as_cell!(h)) + ); } #[inline(always)] @@ -6390,7 +6696,7 @@ impl Machine { pub(crate) fn set_seed(&mut self) { let seed = self.deref_register(1); - match Number::try_from(seed) { + match Number::try_from((seed, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => { let n: u64 = Integer::from(n).try_into().unwrap(); let rng: StdRng = SeedableRng::seed_from_u64(n); @@ -6418,7 +6724,7 @@ impl Machine { pub(crate) fn sleep(&mut self) { let time = self.deref_register(1); - let time = match Number::try_from(time) { + let time = match Number::try_from((time, &self.machine_st.arena.f64_tbl)) { Ok(Number::Float(n)) => n.into_inner(), Ok(Number::Fixnum(n)) => n.get_num() as f64, Ok(Number::Integer(n)) => n.to_f64().value(), @@ -6453,7 +6759,7 @@ impl Machine { name } _ => { - AtomTable::build_with(&self.machine_st.atom_tbl, &match Number::try_from(port) { + AtomTable::build_with(&self.machine_st.atom_tbl, &match Number::try_from((port, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => n.get_num().to_string(), Ok(Number::Integer(n)) => n.to_string(), _ => { @@ -6552,7 +6858,7 @@ impl Machine { let port = if port.is_var() { String::from("0") } else { - match Number::try_from(port) { + match Number::try_from((port, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => n.get_num().to_string(), Ok(Number::Integer(n)) => n.to_string(), _ => { @@ -6575,10 +6881,7 @@ impl Machine { let port = tcp_listener.local_addr().map(|addr| addr.port()).ok(); if let Some(port) = port { - ( - arena_alloc!(tcp_listener, &mut self.machine_st.arena), - port as usize, - ) + (arena_alloc!(tcp_listener, &mut self.machine_st.arena), port) } else { self.machine_st.fail = true; return Ok(()); @@ -6605,7 +6908,7 @@ impl Machine { if had_zero_port { self.machine_st - .unify_fixnum(Fixnum::build_with(port as i64), self.deref_register(2)); + .unify_fixnum(Fixnum::build_with(port), self.deref_register(2)); } Ok(()) @@ -6718,8 +7021,6 @@ impl Machine { .add_stream(stream, atom!("tls_client_negotiate"), 3) .map_err(|stub_gen| stub_gen(&mut self.machine_st))?; - // FIXME: why are we pushing a random, unreferenced cell on the heap? - self.machine_st.heap.push(stream.into()); let stream_addr = self.deref_register(3); self.machine_st .bind(stream_addr.as_var().unwrap(), stream.into()); @@ -6835,7 +7136,7 @@ impl Machine { let position = self.deref_register(2); - let position = match Number::try_from(position) { + let position = match Number::try_from((position, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => n.get_num() as u64, Ok(Number::Integer(n)) => { let n: Result = (&*n).try_into(); @@ -6895,18 +7196,20 @@ impl Machine { } atom!("position") => { if let Some((position, lines_read)) = stream.position() { - let h = self.machine_st.heap.len(); - let position_term = functor!( atom!("position_and_lines_read"), [ - integer(position, &mut self.machine_st.arena), - integer(lines_read, &mut self.machine_st.arena) + number(position, (&mut self.machine_st.arena)), + number(lines_read, (&mut self.machine_st.arena)) ] ); - self.machine_st.heap.extend(position_term); - str_loc_as_cell!(h) + let mut functor_writer = Heap::functor_writer(position_term); + + resource_error_call_result!( + self.machine_st, + functor_writer(&mut self.machine_st.heap) + ) } else { self.machine_st.fail = true; return Ok(()); @@ -6943,17 +7246,19 @@ impl Machine { let value = self.machine_st.registers[2]; let mut ball = Ball::new(); - ball.boundary = self.machine_st.heap.len(); - - copy_term( - CopyBallTerm::new( - &mut self.machine_st.attr_var_init.attr_var_queue, - &mut self.machine_st.stack, - &mut self.machine_st.heap, - &mut ball.stub, - ), - value, - AttrVarPolicy::DeepCopy, + ball.boundary = self.machine_st.heap.cell_len(); + ball.pstr_boundary = step_or_resource_error!( + self.machine_st, + copy_term( + CopyBallTerm::new( + &mut self.machine_st.attr_var_init.attr_var_queue, + &mut self.machine_st.stack, + &mut self.machine_st.heap, + &mut ball.stub, + ), + value, + AttrVarPolicy::DeepCopy, + ) ); self.indices.global_variables.insert(key, (ball, None)); @@ -6997,10 +7302,15 @@ impl Machine { let seen_vars = self .machine_st .attr_vars_of_term(self.machine_st.registers[1]); - let outcome = heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - seen_vars.into_iter() - )); + + let outcome = step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + seen_vars.len(), + seen_vars.into_iter(), + ) + ); unify_fn!(self.machine_st, self.machine_st.registers[2], outcome); } @@ -7016,24 +7326,30 @@ impl Machine { } let stored_v = if stored_v.is_stack_var() { - let h = self.machine_st.heap.len(); + let h = self.machine_st.heap.cell_len(); + + step_or_resource_error!( + self.machine_st, + self.machine_st.heap.push_cell(heap_loc_as_cell!(h)) + ); - self.machine_st.heap.push(heap_loc_as_cell!(h)); self.machine_st.bind(Ref::heap_cell(h), stored_v); - heap_loc_as_cell!(h) } else { stored_v }; let mut seen_set = IndexSet::with_hasher(FxBuildHasher::default()); - self.machine_st.variable_set(&mut seen_set, stored_v); - let outcome = heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - seen_set.into_iter() - )); + let outcome = step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + seen_set.len(), + seen_set.into_iter(), + ) + ); unify_fn!(self.machine_st, a2, outcome); } @@ -7041,7 +7357,8 @@ impl Machine { #[inline(always)] pub(crate) fn term_variables_under_max_depth(&mut self) { // Term, MaxDepth, VarList - let max_depth = cell_as_fixnum!(self.deref_register(2)).get_num() as usize; + let max_depth = + unsafe { self.deref_register(2).to_fixnum_or_cut_point_unchecked() }.get_num() as usize; self.machine_st.term_variables_under_max_depth( self.machine_st.registers[1], @@ -7053,7 +7370,7 @@ impl Machine { #[inline(always)] pub(crate) fn truncate_lifted_heap_to(&mut self) { let a1 = self.deref_register(1); - let lh_offset = cell_as_fixnum!(a1).get_num() as usize; + let lh_offset = unsafe { a1.to_fixnum_or_cut_point_unchecked() }.get_num() as usize; self.machine_st.lifted_heap.truncate(lh_offset); } @@ -7088,43 +7405,48 @@ impl Machine { false } - fn walk_code_at_ptr(&mut self, index_ptr: usize) -> HeapCellValue { - let mut h = self.machine_st.heap.len(); + fn walk_code_at_ptr(&mut self, index_ptr: usize) -> Result { + let orig_h = self.machine_st.heap.cell_len(); + let mut h = orig_h; let mut functors = vec![]; let mut functor_list = vec![]; walk_code(&self.code, index_ptr, |instr| { let old_len = functors.len(); - instr.enqueue_functors(h, &mut self.machine_st.arena, &mut functors); - let new_len = functors.len(); + instr.enqueue_functors(&mut self.machine_st.arena, &mut functors); - #[allow(clippy::needless_range_loop)] - for index in old_len..new_len { - let functor_len = functors[index].len(); + for functor in &functors[old_len..] { + let functor_len = functor.len(); match functor_len { 0 => {} 1 => { functor_list.push(heap_loc_as_cell!(h)); - h += functor_len; + h += cell_index!(Heap::compute_functor_byte_size(functor)); } _ => { functor_list.push(str_loc_as_cell!(h)); - h += functor_len; + h += cell_index!(Heap::compute_functor_byte_size(functor)); } - } + }; } }); - for functor in functors { - self.machine_st.heap.extend(functor.into_iter()); - } + let mut writer = self.machine_st.heap.reserve(h - orig_h)?; - heap_loc_as_cell!(iter_to_heap_list( + writer.write_with(|section| { + for functor in functors { + let mut functor_writer = ReservedHeapSection::functor_writer(functor); + functor_writer(section); + } + }); + + sized_iter_to_heap_list( &mut self.machine_st.heap, - functor_list.into_iter() - )) + functor_list.len(), + functor_list.into_iter(), + ) } #[inline(always)] @@ -7133,7 +7455,7 @@ impl Machine { let name = cell_as_atom!(self.deref_register(2)); let arity = self.deref_register(3); - let arity = match Number::try_from(arity) { + let arity = match Number::try_from((arity, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => n.get_num() as usize, Ok(Number::Integer(n)) => { let value: usize = (&*n).try_into().unwrap(); @@ -7161,25 +7483,28 @@ impl Machine { }, }; - let first_idx = match first_idx { - Some(idx) if idx.local().is_some() => { - if let Some(idx) = idx.local() { - idx - } else { - unreachable!() - } - } - _ => { - let stub = functor_stub(name, arity); - let err = self - .machine_st - .existence_error(ExistenceError::Procedure(name, arity)); + let first_idx = first_idx.and_then(|first_idx| { + self.machine_st + .arena + .code_index_tbl + .get_entry(first_idx.into()) + .local() + }); - return Err(self.machine_st.error_form(err, stub)); - } + let first_idx = if let Some(idx) = first_idx { + idx + } else { + let stub = functor_stub(name, arity); + let err = self + .machine_st + .existence_error(ExistenceError::Procedure(name, arity)); + + return Err(self.machine_st.error_form(err, stub)); }; - let listing = self.walk_code_at_ptr(first_idx); + let listing = + resource_error_call_result!(self.machine_st, self.walk_code_at_ptr(first_idx)); + let listing_var = self.machine_st.registers[4]; unify!(self.machine_st, listing, listing_var); @@ -7189,7 +7514,7 @@ impl Machine { #[inline(always)] pub(crate) fn inlined_instructions(&mut self) { let index_ptr = self.deref_register(1); - let index_ptr = match Number::try_from(index_ptr) { + let index_ptr = match Number::try_from((index_ptr, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => n.get_num() as usize, Ok(Number::Integer(n)) => { let value: usize = (&*n).try_into().unwrap(); @@ -7200,7 +7525,8 @@ impl Machine { } }; - let listing = self.walk_code_at_ptr(index_ptr); + let listing = step_or_resource_error!(self.machine_st, self.walk_code_at_ptr(index_ptr)); + let listing_var = self.machine_st.registers[2]; unify!(self.machine_st, listing, listing_var); @@ -7288,19 +7614,15 @@ impl Machine { }; let result = printer.print().result(); - let chars = put_complete_string( - &mut self.machine_st.heap, - &result, - &self.machine_st.atom_tbl, + let chars = resource_error_call_result!( + self.machine_st, + self.machine_st.heap.allocate_cstr(&result) ); let result_addr = self.deref_register(1); + let var = result_addr.as_var().unwrap(); - if let Some(var) = result_addr.as_var() { - self.machine_st.bind(var, chars); - } else { - unreachable!() - } + self.machine_st.bind(var, chars); Ok(()) } @@ -7310,10 +7632,10 @@ impl Machine { use git_version::git_version; let buffer = git_version!(cargo_prefix = "cargo:", fallback = "unknown"); - let buffer_atom = AtomTable::build_with(&self.machine_st.atom_tbl, buffer); + let cstr_cell = + step_or_resource_error!(self.machine_st, self.machine_st.heap.allocate_cstr(buffer)); - let a1 = self.deref_register(1); - self.machine_st.unify_complete_string(buffer_atom, a1); + unify!(self.machine_st, cstr_cell, self.machine_st.registers[1]); } #[inline(always)] @@ -7332,7 +7654,7 @@ impl Machine { } } - let byte = Fixnum::build_with(bytes[0] as i64); + let byte = Fixnum::build_with(bytes[0]); self.machine_st.unify_fixnum(byte, arg); } @@ -7348,84 +7670,121 @@ impl Machine { let mut context = Sha3_224::new(); context.update(&bytes); - heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - context - .finalize() - .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), - )) + let finalized_context = context.finalize(); + let context_len = finalized_context.len(); + + step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + context_len, + finalized_context + .iter() + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) + ) + ) } atom!("sha3_256") => { let mut context = Sha3_256::new(); context.update(&bytes); - heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - context - .finalize() - .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), - )) + let finalized_context = context.finalize(); + let context_len = finalized_context.len(); + + step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + context_len, + finalized_context + .iter() + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) + ) + ) } atom!("sha3_384") => { let mut context = Sha3_384::new(); context.update(&bytes); + let finalized_context = context.finalize(); + let context_len = finalized_context.len(); - heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - context - .finalize() - .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), - )) + step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + context_len, + finalized_context + .iter() + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) + ) + ) } atom!("sha3_512") => { let mut context = Sha3_512::new(); context.update(&bytes); + let finalized_context = context.finalize(); + let context_len = finalized_context.len(); - heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - context - .finalize() - .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), - )) + step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + context_len, + finalized_context + .iter() + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))), + ) + ) } atom!("blake2s256") => { let mut context = Blake2s256::new(); context.update(&bytes); + let finalized_context = context.finalize(); + let context_len = finalized_context.len(); - heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - context - .finalize() - .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), - )) + step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + context_len, + finalized_context + .iter() + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))), + ) + ) } atom!("blake2b512") => { let mut context = Blake2b512::new(); context.update(&bytes); + let finalized_context = context.finalize(); + let context_len = finalized_context.len(); - heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - context - .finalize() - .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), - )) + step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + context_len, + finalized_context + .iter() + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))), + ) + ) } atom!("ripemd160") => { let mut context = Ripemd160::new(); context.update(&bytes); + let finalized_context = context.finalize(); + let context_len = finalized_context.len(); - heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - context - .finalize() - .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), - )) + step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + context_len, + finalized_context + .iter() + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) + ) + ) } _ => { let ints = digest::digest( @@ -7441,12 +7800,16 @@ impl Machine { &bytes, ); - heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - ints.as_ref() - .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), - )) + step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + ints.as_ref().len(), + ints.as_ref() + .iter() + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) + ) + ) } }; @@ -7477,12 +7840,16 @@ impl Machine { let rkey = hmac::Key::new(ralg, key.as_ref()); let tag = hmac::sign(&rkey, &data); - let ints_list = heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - tag.as_ref() - .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), - )); + let ints_list = step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + tag.as_ref().len(), + tag.as_ref() + .iter() + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) + ) + ); unify!(self.machine_st, self.machine_st.registers[4], ints_list); } @@ -7506,7 +7873,7 @@ impl Machine { let length = self.deref_register(6); - let length = match Number::try_from(length) { + let length = match Number::try_from((length, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).unwrap(), Ok(Number::Integer(n)) => match (&*n).try_into() as Result { Ok(u) => u, @@ -7544,12 +7911,16 @@ impl Machine { } } - heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - bytes - .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), - )) + step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + bytes.len(), + bytes + .iter() + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) + ) + ) }; unify!(self.machine_st, self.machine_st.registers[7], ints_list); @@ -7568,7 +7939,7 @@ impl Machine { let iterations = self.deref_register(3); - let iterations = match Number::try_from(iterations) { + let iterations = match Number::try_from((iterations, &self.machine_st.arena.f64_tbl)) { Ok(Number::Fixnum(n)) => u64::try_from(n.get_num()).unwrap(), Ok(Number::Integer(n)) => { let n: Result = (&*n).try_into(); @@ -7596,12 +7967,16 @@ impl Machine { &mut bytes, ); - heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - bytes - .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), - )) + step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + bytes.len(), + bytes + .iter() + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) + ) + ) }; unify!(self.machine_st, self.machine_st.registers[4], ints_list); @@ -7639,14 +8014,18 @@ impl Machine { } }; - let tag_list = heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - tag.as_ref() - .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), - )); + let tag_list = step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + tag.as_ref().len(), + tag.as_ref() + .iter() + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) + ) + ); - let complete_string = self.u8s_to_string(&in_out); + let complete_string = step_or_resource_error!(self.machine_st, self.u8s_to_string(&in_out)); unify!(self.machine_st, self.machine_st.registers[6], tag_list); unify!( @@ -7705,7 +8084,10 @@ impl Machine { if buffer.is_empty() { empty_list_as_cell!() } else { - atom_as_cstr_cell!(AtomTable::build_with(&self.machine_st.atom_tbl, &buffer)) + step_or_resource_error!( + self.machine_st, + self.machine_st.heap.allocate_cstr(&buffer) + ) } }; @@ -7730,7 +8112,10 @@ impl Machine { let scalar = secp256k1::Scalar::decode_reduce(&scalar_bytes); point *= scalar; - let uncompressed = self.u8s_to_string(&point.encode_uncompressed()); + let uncompressed = step_or_resource_error!( + self.machine_st, + self.u8s_to_string(&point.encode_uncompressed()) + ); unify!(self.machine_st, self.machine_st.registers[4], uncompressed); } @@ -7744,7 +8129,10 @@ impl Machine { let skey = ed25519::PrivateKey::from_seed(&seed_bytes); - let complete_string = self.u8s_to_string(skey.public_key.encoded.as_ref()); + let complete_string = step_or_resource_error!( + self.machine_st, + self.u8s_to_string(skey.public_key.encoded.as_ref()) + ); unify!( self.machine_st, @@ -7766,13 +8154,16 @@ impl Machine { let data = self.string_encoding_bytes(self.machine_st.registers[2], encoding); let sig = skey.sign_raw(&data); - - let sig_list = heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - sig.as_ref() - .iter() - .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))), - )); + let sig_list = step_or_resource_error!( + self.machine_st, + sized_iter_to_heap_list( + &mut self.machine_st.heap, + sig.as_ref().len(), + sig.as_ref() + .iter() + .map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) + ) + ); unify!(self.machine_st, self.machine_st.registers[4], sig_list); } @@ -7810,7 +8201,7 @@ impl Machine { &<[u8; 32]>::try_from(&scalar_bytes[..]).unwrap(), ); - let string = self.u8s_to_string(&result[..]); + let string = step_or_resource_error!(self.machine_st, self.u8s_to_string(&result[..])); unify!(self.machine_st, self.machine_st.registers[3], string); } @@ -7835,29 +8226,43 @@ impl Machine { } #[inline(always)] - pub(crate) fn load_html(&mut self) { + pub(crate) fn load_html(&mut self) -> Result<(), usize> { if let Some(string) = self .machine_st .value_to_str_like(self.machine_st.registers[1]) { let document = scraper::Html::parse_document(&string.as_str()); - let result = self.html_node_to_term(document.tree.root().first_child().unwrap()); - unify!(self.machine_st, self.machine_st.registers[2], result); + let root_nodes = document + .tree + .root() + .children() + .map(|child| self.html_node_to_term(child)) + .collect::, _>>()?; + + let nodes = sized_iter_to_heap_list( + &mut self.machine_st.heap, + root_nodes.len(), + root_nodes.into_iter(), + )?; + + unify!(self.machine_st, self.machine_st.registers[2], nodes); } else { self.machine_st.fail = true; } + + Ok(()) } #[inline(always)] - pub(crate) fn load_xml(&mut self) { + pub(crate) fn load_xml(&mut self) -> Result<(), usize> { if let Some(string) = self .machine_st .value_to_str_like(self.machine_st.registers[1]) { match roxmltree::Document::parse(&string.as_str()) { Ok(doc) => { - let result = self.xml_node_to_term(doc.root_element()); + let result = self.xml_node_to_term(doc.root_element())?; unify!(self.machine_st, self.machine_st.registers[2], result); } _ => { @@ -7867,6 +8272,8 @@ impl Machine { } else { self.machine_st.fail = true; } + + Ok(()) } #[inline(always)] @@ -7877,10 +8284,9 @@ impl Machine { { match env::var(&*key.as_str()) { Ok(value) => { - let cstr = put_complete_string( - &mut self.machine_st.heap, - &value, - &self.machine_st.atom_tbl, + let cstr = step_or_resource_error!( + self.machine_st, + self.machine_st.heap.allocate_cstr(&value) ); unify!(self.machine_st, self.machine_st.registers[2], cstr); @@ -7989,6 +8395,431 @@ impl Machine { }; } + pub(crate) fn process_create(&mut self) -> CallResult { + fn stub_gen() -> Vec { + functor_stub(atom!("process_create"), 3) + } + + // String + let exe_r = self.deref_register(1); + // [String,...] + let args_r = self.deref_register(2); + // [std] | [null] | [pipe, Var] | [file, String] + let stdin_r = self.deref_register(3); + let stdout_r = self.deref_register(4); + let stderr_r = self.deref_register(5); + // [env | environment, [[String, String],...]] + let env_r = self.deref_register(6); + // String ("." for keep current cwd) + let cwd_r = self.deref_register(7); + // Var + let pid_r = self.deref_register(8); + + let exe = self + .machine_st + .value_to_str_like(exe_r) + .expect("invalid values should have been rejected on the prolog side"); + + let args = self + .machine_st + .try_from_list(args_r, stub_gen) + .expect("invalid values should have been rejected on the prolog side") + .into_iter() + .map(|arg| { + self.machine_st + .value_to_str_like(arg) + .expect("invalid values should have been rejected on the prolog side") + .as_str() + .to_string() + }) + .collect::>(); + + let stdin_args = self.machine_st.try_from_list(stdin_r, stub_gen)?; + let stdin = self.handle_input_stream(stdin_args)?; + + let stdout_args = self.machine_st.try_from_list(stdout_r, stub_gen)?; + let stdout = self.handle_output_stream(stdout_args)?; + + let stderr_args = self.machine_st.try_from_list(stderr_r, stub_gen)?; + let stderr = self.handle_output_stream(stderr_args)?; + + let env_args = self.machine_st.try_from_list(env_r, stub_gen)?; + + let clear_env = match env_args[0].to_atom() { + Some(atom!("env")) => true, + Some(atom!("environment")) => false, + _ => panic!("Invalid value for clear_env"), + }; + + let envs = self + .machine_st + .try_from_list(env_args[1], stub_gen)? + .into_iter() + .map(|entry| { + let entry = self.machine_st.try_from_list(entry, stub_gen)?; + let name = self + .machine_st + .value_to_str_like(entry[0]) + .expect("invalid values should have been rejected on the prolog side") + .as_str() + .to_string(); + let value = self + .machine_st + .value_to_str_like(entry[1]) + .expect("invalid values should have been rejected on the prolog side") + .as_str() + .to_string(); + Ok((name, value)) + }) + .collect::, MachineStub>>()?; + + let cwd = self + .machine_st + .value_to_str_like(cwd_r) + .expect("invalid values should have been rejected on the prolog side"); + + let mut command = std::process::Command::new(&*exe.as_str()); + command.args(args); + + if &*cwd.as_str() != "." { + command.current_dir(&*cwd.as_str()); + } + + if clear_env { + command.env_clear(); + } + + command + .envs(envs) + .stdin(stdin) + .stdout(stdout) + .stderr(stderr); + + match command.spawn() { + Ok(child) => { + let child_process_alloc: TypedArenaPtr = + arena_alloc!(child, &mut self.machine_st.arena); + + unify!( + self.machine_st, + pid_r, + typed_arena_ptr_as_cell!(child_process_alloc) + ); + + Ok(()) + } + Err(_) => { + let perm_error = self.machine_st.permission_error( + Permission::Create, + atom!("process"), + stub_gen(), + ); + Err(self.machine_st.error_form(perm_error, stub_gen())) + } + } + } + + fn handle_output_stream(&mut self, args: Vec) -> Result { + Ok(match args[0].to_atom() { + Some(atom!("std")) => Stdio::inherit(), + Some(atom!("null")) => Stdio::null(), + Some(atom!("pipe")) => { + let (reader, writer) = match std::io::pipe() { + Ok(pipe_pair) => pipe_pair, + Err(_) => { + return Err(self.machine_st.open_permission_error( + atom!("anonymous_pipe"), + atom!("process_create"), + 3, + )); + } + }; + + let stream = Stream::from_pipe_reader(reader, &mut self.machine_st.arena); + + self.indices + .add_stream(stream, atom!("process_create"), 3) + .map_err(|stub_gen| stub_gen(&mut self.machine_st))?; + + self.machine_st + .bind(args[1].as_var().unwrap(), stream.into()); + + Stdio::from(writer) + } + Some(atom!("file")) => { + let path = self.machine_st.value_to_str_like(args[1]).unwrap(); + + let file = match std::fs::File::open(&*path.as_str()) { + Ok(file) => file, + Err(_) => { + return Err(self.machine_st.open_permission_error( + args[1], + atom!("process_create"), + 3, + )); + } + }; + Stdio::from(file) + } + _ => { + panic!("Invalid stdout tag") + } + }) + } + + fn handle_input_stream(&mut self, args: Vec) -> Result { + Ok(match args[0].to_atom() { + Some(atom!("std")) => Stdio::inherit(), + Some(atom!("null")) => Stdio::null(), + Some(atom!("pipe")) => { + let (reader, writer) = match std::io::pipe() { + Ok(pipe_pair) => pipe_pair, + Err(_) => { + return Err(self.machine_st.open_permission_error( + atom!("anonymous_pipe"), + atom!("process_create"), + 3, + )); + } + }; + + let stream = Stream::from_pipe_writer(writer, &mut self.machine_st.arena); + + self.indices + .add_stream(stream, atom!("process_create"), 3) + .map_err(|stub_gen| stub_gen(&mut self.machine_st))?; + + self.machine_st + .bind(args[1].as_var().unwrap(), stream.into()); + + Stdio::from(reader) + } + Some(atom!("file")) => { + let path = self.machine_st.value_to_str_like(args[1]).unwrap(); + + let file = match std::fs::File::open(&*path.as_str()) { + Ok(file) => file, + Err(_) => { + return Err(self.machine_st.open_permission_error( + args[1], + atom!("process_create"), + 3, + )); + } + }; + Stdio::from(file) + } + _ => { + panic!("Invalid stdin tag") + } + }) + } + + pub(crate) fn process_id(&mut self) -> CallResult { + fn stub_gen() -> Vec { + functor_stub(atom!("process_id"), 2) + } + + // Process + let process_r = self.deref_register(1); + // Pid + let pid_r = self.deref_register(2); + + let Some(ptr) = process_r.to_untyped_arena_ptr() else { + let err = self.machine_st.type_error(ValidType::Process, process_r); + return Err(self.machine_st.error_form(err, stub_gen())); + }; + + let process = match_untyped_arena_ptr!(ptr, + (ArenaHeaderTag::ChildProcess, child_process) => { + child_process + } + (ArenaHeaderTag::Dropped, _dropped) => { + let err = self.machine_st.existence_error(ExistenceError::Process(process_r)); + return Err(self.machine_st.error_form(err, stub_gen())); + } + _ => { + let err = self.machine_st.type_error(ValidType::Process, process_r); + return Err(self.machine_st.error_form(err, stub_gen())); + } + ); + + self.machine_st.bind( + pid_r.as_var().unwrap(), + fixnum_as_cell!(Fixnum::build_with(process.id())), + ); + + Ok(()) + } + + pub(crate) fn process_wait(&mut self) -> CallResult { + fn stub_gen() -> Vec { + functor_stub(atom!("process_wait"), 3) + } + + // Process + let process_r = self.deref_register(1); + // Var | Status + let status_r = self.deref_register(2); + // timeout | 0 + let timeout_r = self.deref_register(3); + + let Some(ptr) = process_r.to_untyped_arena_ptr() else { + let err = self.machine_st.type_error(ValidType::Process, process_r); + return Err(self.machine_st.error_form(err, stub_gen())); + }; + + let mut process = match_untyped_arena_ptr!(ptr, + (ArenaHeaderTag::ChildProcess, child_process) => { + child_process + } + (ArenaHeaderTag::Dropped, _dropped) => { + let err = self.machine_st.existence_error(ExistenceError::Process(process_r)); + return Err(self.machine_st.error_form(err, stub_gen())); + } + _ => { + let err = self.machine_st.type_error(ValidType::Process, process_r); + return Err(self.machine_st.error_form(err, stub_gen())); + } + ); + + let status = if let Some(atom) = timeout_r.to_atom() { + match atom { + atom!("infinite") => process.wait().map(Some), + _ => { + panic!("Invalid Timeout value") + } + } + } else if let Some(timeout) = timeout_r.to_fixnum() { + if timeout.get_num() == 0 { + process.try_wait() + } else { + panic!("Invalid Timeout value") + } + } else { + panic!("Invalid Timeout value") + }; + + match status { + Ok(None) => { + unify!(self.machine_st, status_r, atom_as_cell!(atom!("timeout"))); + Ok(()) + } + Ok(Some(exit_status)) => { + if let Some(exit_code) = exit_status.code() { + let mut writer = + Heap::functor_writer(functor!(atom!("exit"), [fixnum(exit_code)])); + + match writer(&mut self.machine_st.heap) { + Ok(loc) => { + unify!(self.machine_st, status_r, loc); + } + Err(resource_err_loc) => { + self.machine_st.throw_resource_error(resource_err_loc); + } + } + Ok(()) + } else { + #[cfg(unix)] + { + use std::os::unix::process::ExitStatusExt; + + if let Some(signal) = ExitStatusExt::signal(&exit_status) { + let mut writer = + Heap::functor_writer(functor!(atom!("killed"), [fixnum(signal)])); + + match writer(&mut self.machine_st.heap) { + Ok(loc) => { + unify!(self.machine_st, status_r, loc); + } + Err(resource_err_loc) => { + self.machine_st.throw_resource_error(resource_err_loc); + } + } + Ok(()) + } else { + let err = self.machine_st.unreachable_error(); + Err(self.machine_st.error_form(err, stub_gen())) + } + } + #[cfg(not(unix))] + { + let err = self.machine_st.unreachable_error(); + Err(self.machine_st.error_form(err, stub_gen())) + } + } + } + Err(_) => { + let perm_error = self.machine_st.permission_error( + Permission::Modify, + atom!("process"), + stub_gen(), + ); + Err(self.machine_st.error_form(perm_error, stub_gen())) + } + } + } + + pub(crate) fn process_kill(&mut self) -> CallResult { + fn stub_gen() -> Vec { + functor_stub(atom!("process_kill"), 1) + } + + // Pid + let process_r = self.deref_register(1); + + let Some(ptr) = process_r.to_untyped_arena_ptr() else { + let err = self.machine_st.type_error(ValidType::Process, process_r); + return Err(self.machine_st.error_form(err, stub_gen())); + }; + + let mut process = match_untyped_arena_ptr!(ptr, + (ArenaHeaderTag::ChildProcess, child_process) => { + child_process + } + (ArenaHeaderTag::Dropped, _dropped) => { + let err = self.machine_st.existence_error(ExistenceError::Process(process_r)); + return Err(self.machine_st.error_form(err, stub_gen())); + } + _ => { + let err = self.machine_st.type_error(ValidType::Process, process_r); + return Err(self.machine_st.error_form(err, stub_gen())); + } + ); + + if process.kill().is_err() { + let perm_error = + self.machine_st + .permission_error(Permission::Modify, atom!("process"), stub_gen()); + return Err(self.machine_st.error_form(perm_error, stub_gen())); + } + Ok(()) + } + + pub(crate) fn process_release(&mut self) -> CallResult { + fn stub_gen() -> Vec { + functor_stub(atom!("process_release"), 1) + } + + let process = self.deref_register(1); + + if let Some(ptr) = process.to_untyped_arena_ptr() { + match_untyped_arena_ptr!(ptr, + (ArenaHeaderTag::ChildProcess, child_process) => { + child_process.drop_payload(); + + return Ok(()); + } + _ => { + } + ); + } + + let err = self.machine_st.type_error(ValidType::Process, process); + + Err(self.machine_st.error_form(err, stub_gen())) + } + #[inline(always)] pub(crate) fn chars_base64(&mut self) -> CallResult { let padding = cell_as_atom!(self.deref_register(3)); @@ -8011,7 +8842,8 @@ impl Machine { match bytes { Ok(bs) => { - let string = self.u8s_to_string(&bs); + let string = + resource_error_call_result!(self.machine_st, self.u8s_to_string(&bs)); unify!(self.machine_st, self.machine_st.registers[1], string); } @@ -8033,7 +8865,8 @@ impl Machine { } let b64 = b64_engine.encode(bytes); - let string = self.u8s_to_string(b64.as_bytes()); + let string = + resource_error_call_result!(self.machine_st, self.u8s_to_string(b64.as_bytes())); unify!(self.machine_st, self.machine_st.registers[2], string); } @@ -8093,13 +8926,13 @@ impl Machine { let mut parser = Parser::new(stream, &mut self.machine_st); - match devour_whitespace(&mut parser) { + match devour_whitespace(&mut parser.lexer) { Ok(false) => { // not at EOF. stream.add_lines_read(parser.lines_read()); } Ok(true) => { - stream.add_lines_read(parser.lines_read()); + stream.add_lines_read(parser.lexer.line_num); self.machine_st.fail = true; } Err(err) => { @@ -8159,8 +8992,10 @@ impl Machine { if path.is_dir() { if let Some(path) = path.to_str() { - let path_string = - put_complete_string(&mut self.machine_st.heap, path, &self.machine_st.atom_tbl); + let path_string = step_or_resource_error!( + self.machine_st, + self.machine_st.heap.allocate_cstr(path) + ); unify!(self.machine_st, self.machine_st.registers[1], path_string); return; @@ -8175,9 +9010,12 @@ impl Machine { #[inline(always)] pub(crate) fn pop_count(&mut self) { let number = self.deref_register(1); - let pop_count = integer_as_cell!(match Number::try_from(number) { + let pop_count = integer_as_cell!(match Number::try_from(( + number, + &self.machine_st.arena.f64_tbl + )) { Ok(Number::Fixnum(n)) => { - Number::Fixnum(Fixnum::build_with(n.get_num().count_ones() as i64)) + Number::Fixnum(Fixnum::build_with(n.get_num().count_ones())) } Ok(Number::Integer(n)) => { let value: usize = if n.sign() == Sign::Positive { @@ -8195,7 +9033,7 @@ impl Machine { unify!(self.machine_st, self.machine_st.registers[2], pop_count); } - pub(super) fn systemtime_to_timestamp(&mut self, system_time: SystemTime) -> Atom { + pub(super) fn systemtime_to_timestamp(&mut self, system_time: SystemTime) -> String { let datetime: DateTime = system_time.into(); let mut fstr = "[".to_string(); @@ -8205,13 +9043,11 @@ impl Machine { ]; for spec in SPECIFIERS { - fstr.push_str(&format!("'{}'=\"%{}\", ", spec, spec).to_string()); + fstr.push_str(&format!("'{spec}'=\"%{spec}\", ")); } fstr.push_str("finis]."); - let s = datetime.format(&fstr).to_string(); - - AtomTable::build_with(&self.machine_st.atom_tbl, &s) + datetime.format(&fstr).to_string() } pub(super) fn string_encoding_bytes( @@ -8230,128 +9066,172 @@ impl Machine { } } - pub(super) fn xml_node_to_term(&mut self, node: roxmltree::Node) -> HeapCellValue { + pub(super) fn xml_node_to_term( + &mut self, + node: roxmltree::Node, + ) -> Result { if node.is_text() { - put_complete_string( - &mut self.machine_st.heap, - node.text().unwrap(), - &self.machine_st.atom_tbl, - ) + self.machine_st.heap.allocate_cstr(node.text().unwrap()) } else { let mut avec = Vec::new(); for attr in node.attributes() { let name = AtomTable::build_with(&self.machine_st.atom_tbl, attr.name()); - let value = put_complete_string( - &mut self.machine_st.heap, - attr.value(), - &self.machine_st.atom_tbl, - ); + let value = self.machine_st.heap.allocate_cstr(attr.value())?; - avec.push(str_loc_as_cell!(self.machine_st.heap.len())); + avec.push(str_loc_as_cell!(self.machine_st.heap.cell_len())); - self.machine_st.heap.push(atom_as_cell!(atom!("="), 2)); - self.machine_st.heap.push(atom_as_cell!(name)); - self.machine_st.heap.push(value); + let mut writer = self.machine_st.heap.reserve(3)?; + + writer.write_with(|section| { + section.push_cell(atom_as_cell!(atom!("="), 2)); + section.push_cell(atom_as_cell!(name)); + section.push_cell(value); + }); } - let attrs = heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - avec.into_iter() - )); + let attrs = + sized_iter_to_heap_list(&mut self.machine_st.heap, avec.len(), avec.into_iter())?; let mut cvec = Vec::new(); for child in node.children() { - cvec.push(self.xml_node_to_term(child)); + cvec.push(self.xml_node_to_term(child)?); } - let children = heap_loc_as_cell!(iter_to_heap_list( - &mut self.machine_st.heap, - cvec.into_iter() - )); + let children = + sized_iter_to_heap_list(&mut self.machine_st.heap, cvec.len(), cvec.into_iter())?; let tag = AtomTable::build_with(&self.machine_st.atom_tbl, node.tag_name().name()); + let result = str_loc_as_cell!(self.machine_st.heap.cell_len()); + let mut writer = self.machine_st.heap.reserve(4)?; - let result = str_loc_as_cell!(self.machine_st.heap.len()); + writer.write_with(|section| { + section.push_cell(atom_as_cell!(atom!("element"), 3)); + section.push_cell(atom_as_cell!(tag)); + section.push_cell(attrs); + section.push_cell(children); + }); - self.machine_st - .heap - .push(atom_as_cell!(atom!("element"), 3)); - self.machine_st.heap.push(atom_as_cell!(tag)); - self.machine_st.heap.push(attrs); - self.machine_st.heap.push(children); - - result + Ok(result) } } pub(super) fn html_node_to_term( &mut self, node: ego_tree::NodeRef<'_, scraper::Node>, - ) -> HeapCellValue { - match node.value().as_element() { - None => put_complete_string( - &mut self.machine_st.heap, - &node.value().as_text().unwrap().text, - &self.machine_st.atom_tbl, - ), - Some(element) => { + ) -> Result { + match node.value() { + scraper::Node::Document | scraper::Node::Fragment => { + unreachable!("we never iterate the root itself only its children") + } + scraper::Node::Doctype(doctype) => { + // what about public and system id? + let name = self.machine_st.heap.allocate_cstr(&doctype.name)?; + + let result = str_loc_as_cell!(self.machine_st.heap.cell_len()); + let mut writer = self.machine_st.heap.reserve(2)?; + + writer.write_with(|section| { + section.push_cell(atom_as_cell!(atom!("doctype"), 1)); + section.push_cell(name); + }); + + Ok(result) + } + scraper::Node::Comment(comment) => { + let comment = self.machine_st.heap.allocate_cstr(comment)?; + + let result = str_loc_as_cell!(self.machine_st.heap.cell_len()); + let mut writer = self.machine_st.heap.reserve(2)?; + + writer.write_with(|section| { + section.push_cell(atom_as_cell!(atom!("comment"), 1)); + section.push_cell(comment); + }); + + Ok(result) + } + scraper::Node::Text(text) => self.machine_st.heap.allocate_cstr(&text.text), + scraper::Node::Element(element) => { let mut avec = Vec::new(); for attr in element.attrs() { let name = AtomTable::build_with(&self.machine_st.atom_tbl, attr.0); - let value = put_complete_string( - &mut self.machine_st.heap, - attr.1, - &self.machine_st.atom_tbl, - ); + let value = self.machine_st.heap.allocate_cstr(attr.1)?; - avec.push(str_loc_as_cell!(self.machine_st.heap.len())); + avec.push(str_loc_as_cell!(self.machine_st.heap.cell_len())); - self.machine_st.heap.push(atom_as_cell!(atom!("="), 2)); - self.machine_st.heap.push(atom_as_cell!(name)); - self.machine_st.heap.push(value); + let mut writer = self.machine_st.heap.reserve(3)?; + + writer.write_with(|section| { + section.push_cell(atom_as_cell!(atom!("="), 2)); + section.push_cell(atom_as_cell!(name)); + section.push_cell(value); + }); } - let attrs = heap_loc_as_cell!(iter_to_heap_list( + let attrs = sized_iter_to_heap_list( &mut self.machine_st.heap, - avec.into_iter() - )); + avec.len(), + avec.into_iter(), + )?; - let mut cvec = Vec::new(); + let cvec = node + .children() + .map(|child| self.html_node_to_term(child)) + .collect::, _>>()?; - for child in node.children() { - cvec.push(self.html_node_to_term(child)); - } - - let children = heap_loc_as_cell!(iter_to_heap_list( + let children = sized_iter_to_heap_list( &mut self.machine_st.heap, - cvec.into_iter() - )); + cvec.len(), + cvec.into_iter(), + )?; let tag = AtomTable::build_with(&self.machine_st.atom_tbl, element.name()); - let result = str_loc_as_cell!(self.machine_st.heap.len()); + let result = str_loc_as_cell!(self.machine_st.heap.cell_len()); + let mut writer = self.machine_st.heap.reserve(4)?; - self.machine_st + writer.write_with(|section| { + section.push_cell(atom_as_cell!(atom!("element"), 3)); + section.push_cell(atom_as_cell!(tag)); + section.push_cell(attrs); + section.push_cell(children); + }); + + Ok(result) + } + scraper::Node::ProcessingInstruction(processing_instruction) => { + let target = self + .machine_st .heap - .push(atom_as_cell!(atom!("element"), 3)); - self.machine_st.heap.push(atom_as_cell!(tag)); - self.machine_st.heap.push(attrs); - self.machine_st.heap.push(children); + .allocate_cstr(&processing_instruction.target)?; + let data = self + .machine_st + .heap + .allocate_cstr(&processing_instruction.data)?; - result + let result = str_loc_as_cell!(self.machine_st.heap.cell_len()); + let mut writer = self.machine_st.heap.reserve(3)?; + + writer.write_with(|section| { + section.push_cell(atom_as_cell!(atom!("processing_instruction"), 2)); + section.push_cell(target); + section.push_cell(data); + }); + + Ok(result) } } } - pub(super) fn u8s_to_string(&mut self, data: &[u8]) -> HeapCellValue { + pub(super) fn u8s_to_string(&mut self, data: &[u8]) -> Result { let buffer = String::from_iter(data.iter().map(|b| *b as char)); if buffer.is_empty() { - empty_list_as_cell!() + Ok(empty_list_as_cell!()) } else { - atom_as_cstr_cell!(AtomTable::build_with(&self.machine_st.atom_tbl, &buffer)) + self.machine_st.heap.allocate_cstr(&buffer) } } } diff --git a/src/machine/term_stream.rs b/src/machine/term_stream.rs index 686a8c19..a0f5ae22 100644 --- a/src/machine/term_stream.rs +++ b/src/machine/term_stream.rs @@ -4,6 +4,7 @@ use crate::machine::loader::*; use crate::machine::machine_errors::*; use crate::machine::*; use crate::parser::ast::*; +use crate::parser::lexer::*; use crate::parser::parser::*; use crate::read::devour_whitespace; @@ -61,7 +62,7 @@ impl<'a> TermStream for BootstrappingTermStream<'a> { #[inline] fn eof(&mut self) -> Result { - devour_whitespace(&mut self.parser) // eliminate dangling comments before checking for EOF. + devour_whitespace(&mut self.parser.lexer) // eliminate dangling comments before checking for EOF. .map_err(CompilationError::from) } diff --git a/src/machine/unify.rs b/src/machine/unify.rs index 2e9dd54b..fdc430ee 100644 --- a/src/machine/unify.rs +++ b/src/machine/unify.rs @@ -2,11 +2,10 @@ use crate::arena::*; use crate::forms::*; use crate::heap_iter::{stackful_preorder_iter, NonListElider}; use crate::machine::machine_state::*; -use crate::machine::partial_string::*; use crate::machine::*; +use crate::offset_table::*; use crate::types::*; -use std::cmp::Ordering; use std::ops::{Deref, DerefMut}; use derive_more::*; @@ -14,6 +13,18 @@ use fxhash::FxBuildHasher; use indexmap::IndexSet; use num_order::NumOrd; +impl MachineState { + pub(crate) fn partial_string_to_pdl(&mut self, pstr_loc: usize, l: usize) { + let (c, succ_cell) = self.heap.last_str_char_and_tail(pstr_loc); + + self.pdl.push(heap_loc_as_cell!(l + 1)); + self.pdl.push(succ_cell); + + self.pdl.push(heap_loc_as_cell!(l)); + self.pdl.push(char_as_cell!(c)); + } +} + pub(crate) trait Unifier: DerefMut { fn unify_structure(&mut self, s1: usize, value: HeapCellValue) { // s1 is the value of a STR cell. @@ -82,8 +93,8 @@ pub(crate) trait Unifier: DerefMut { self.fail = true; } } - (HeapCellValueTag::PStrLoc | HeapCellValueTag::CStr | HeapCellValueTag::PStr) => { - Self::unify_partial_string(self, list_loc_as_cell!(l1), value) + (HeapCellValueTag::PStrLoc, l) => { + Self::unify_partial_string(self, l, list_loc_as_cell!(l1)) } (HeapCellValueTag::AttrVar, h) => { Self::bind(self, Ref::attr_var(h), list_loc_as_cell!(l1)); @@ -100,261 +111,43 @@ pub(crate) trait Unifier: DerefMut { ); } - fn unify_complete_string(&mut self, atom: Atom, value: HeapCellValue) { + fn unify_partial_string(&mut self, pstr_loc: usize, value: HeapCellValue) { if let Some(r) = value.as_var() { - if atom == atom!("") { - Self::bind(self, r, atom_as_cell!(atom!("[]"))); - } else { - Self::bind(self, r, atom_as_cstr_cell!(atom)); - } - - return; - } - - read_heap_cell!(value, - (HeapCellValueTag::Atom, (cstr_atom, arity)) if atom == atom!("") => { - debug_assert_eq!(arity, 0); - self.fail = cstr_atom != atom!("[]"); - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(self.heap[s]) - .get_name_and_arity(); - - if arity == 0 { - self.fail = atom == atom!("") && name != atom!("[]"); - } else { - // this is intentionally the same policy for - // value.tag() == Lis and PStrLoc. they're not - // grouped together to allow for arity == 0. - Self::unify_partial_string(self, atom_as_cstr_cell!(atom), value); - - if !self.pdl.is_empty() { - Self::unify_internal(self); - } - } - } - (HeapCellValueTag::CStr, cstr_atom) => { - self.fail = atom != cstr_atom; - } - (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => { - Self::unify_partial_string(self, atom_as_cstr_cell!(atom), value); - - if !self.pdl.is_empty() { - Self::unify_internal(self); - } - } - _ => { - self.fail = true; - } - ); - } - - // the return value of unify_partial_string is interpreted as - // follows: - // - // Some(None) -- the strings are equal, nothing to unify - // Some(Some(f2,f1)) -- prefixes equal, try to unify focus values f2, f1 - // None -- prefixes not equal, unification fails - // - // d1's tag is assumed to be one of LIS, STR or PSTRLOC. - fn unify_partial_string(&mut self, value_1: HeapCellValue, value_2: HeapCellValue) { - if let Some(r) = value_2.as_var() { - Self::bind(self, r, value_1); + Self::bind(self, r, pstr_loc_as_cell!(pstr_loc)); return; } let machine_st = self.deref_mut(); - let s1 = machine_st.heap.len(); + read_heap_cell!(value, + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(machine_st.heap[s]) + .get_name_and_arity(); - machine_st.heap.push(value_1); - machine_st.heap.push(value_2); - - let mut pstr_iter1 = HeapPStrIter::new(&machine_st.heap, s1); - let mut pstr_iter2 = HeapPStrIter::new(&machine_st.heap, s1 + 1); - - fn unify_sequence( - machine_st: &mut MachineState, - iter: PStrIteratee, - source_cell: HeapCellValue, - ) -> bool { - match iter { - PStrIteratee::Char(focus, _) => { - machine_st.pdl.push(machine_st.heap[focus]); - machine_st.pdl.push(source_cell); - } - PStrIteratee::PStrSegment(focus, _, n) => { - read_heap_cell!(machine_st.heap[focus], - (HeapCellValueTag::CStr | HeapCellValueTag::PStr, pstr_atom) => { - if focus < machine_st.heap.len() - 2 { - machine_st.heap.pop(); - machine_st.heap.pop(); - } - - if n == 0 { - let target_cell = match machine_st.heap[focus].get_tag() { - HeapCellValueTag::CStr => { - atom_as_cstr_cell!(pstr_atom) - } - HeapCellValueTag::PStr => { - pstr_loc_as_cell!(focus) - } - _ => { - unreachable!() - } - }; - - machine_st.pdl.push(target_cell); - machine_st.pdl.push(source_cell); - } else { - let h_len = machine_st.heap.len(); - - machine_st.heap.push(pstr_offset_as_cell!(focus)); - machine_st.heap.push(fixnum_as_cell!( - Fixnum::build_with(n as i64) - )); - - machine_st.pdl.push(pstr_loc_as_cell!(h_len)); - machine_st.pdl.push(source_cell); - } - - return true; - } - (HeapCellValueTag::PStrOffset, pstr_loc) => { - let n0 = cell_as_fixnum!(machine_st.heap[focus+1]) - .get_num() as usize; - - if pstr_loc < machine_st.heap.len() - 2 { - machine_st.heap.pop(); - machine_st.heap.pop(); - } - - if n == n0 { - machine_st.pdl.push(pstr_loc_as_cell!(focus)); - machine_st.pdl.push(source_cell); - } else { - let h_len = machine_st.heap.len(); - - machine_st.heap.push(pstr_offset_as_cell!(pstr_loc)); - machine_st.heap.push(fixnum_as_cell!( - Fixnum::build_with(n as i64) - )); - - machine_st.pdl.push(pstr_loc_as_cell!(h_len)); - machine_st.pdl.push(source_cell); - } - - return true; - } - _ => { - } - ); - - if focus < machine_st.heap.len() - 2 { - machine_st.heap.pop(); - machine_st.heap.pop(); - } - - machine_st.pdl.push(machine_st.heap[focus]); - machine_st.pdl.push(source_cell); - - return true; - } - } - - false - } - - match compare_pstr_prefixes(&mut pstr_iter1, &mut pstr_iter2) { - PStrCmpResult::Ordered(Ordering::Equal) => {} - PStrCmpResult::Ordered(Ordering::Less) => { - if pstr_iter2.focus.as_var().is_none() { - machine_st.fail = true; + if name == atom!(".") && arity == 2 { + machine_st.partial_string_to_pdl(pstr_loc, s+1); } else { - machine_st.pdl.push(empty_list_as_cell!()); - machine_st.pdl.push(pstr_iter2.focus); - } - } - PStrCmpResult::Ordered(Ordering::Greater) => { - if pstr_iter1.focus.as_var().is_none() { machine_st.fail = true; - } else { - machine_st.pdl.push(empty_list_as_cell!()); - machine_st.pdl.push(pstr_iter1.focus); } } - continuable @ PStrCmpResult::FirstIterContinuable(iteratee) - | continuable @ PStrCmpResult::SecondIterContinuable(iteratee) => { - if continuable.is_second_iter() { - std::mem::swap(&mut pstr_iter1, &mut pstr_iter2); - } - - let mut chars_iter = PStrCharsIter { - iter: pstr_iter1, - item: Some(iteratee), - }; - - let mut focus = pstr_iter2.focus; - - 'outer: { - while let Some(c) = chars_iter.peek() { - read_heap_cell!(focus, - (HeapCellValueTag::Lis, l) => { - let val = pstr_iter2.heap[l]; - - machine_st.pdl.push(val); - machine_st.pdl.push(char_as_cell!(c)); - - focus = pstr_iter2.heap[l+1]; - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(pstr_iter2.heap[s]) - .get_name_and_arity(); - - if name == atom!(".") && arity == 2 { - machine_st.pdl.push(pstr_iter2.heap[s+1]); - machine_st.pdl.push(char_as_cell!(c)); - - focus = pstr_iter2.heap[s+2]; - } else { - machine_st.fail = true; - break 'outer; - } - } - (HeapCellValueTag::CStr | HeapCellValueTag::PStrLoc) => { - unify_sequence(machine_st, chars_iter.item.unwrap(), focus); - return; - } - (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { - if unify_sequence(machine_st, chars_iter.item.unwrap(), heap_loc_as_cell!(h)) { - return; - } - - break 'outer; - } - _ => { - machine_st.fail = true; - break 'outer; - } - ); - - chars_iter.next(); + (HeapCellValueTag::Lis, l) => { + machine_st.partial_string_to_pdl(pstr_loc, l); + } + (HeapCellValueTag::PStrLoc, other_pstr_loc) => { + match machine_st.heap.compare_pstr_segments(pstr_loc, other_pstr_loc) { + PStrSegmentCmpResult::Continue(v1, v2) => { + machine_st.pdl.push(v1.offset_by(pstr_loc)); + machine_st.pdl.push(v2.offset_by(other_pstr_loc)); + } + _ => { + machine_st.fail = true; } - - chars_iter.iter.next(); - - machine_st.pdl.push(focus); - machine_st.pdl.push(chars_iter.iter.focus); } } - PStrCmpResult::Unordered => { - machine_st.pdl.push(pstr_iter1.focus); - machine_st.pdl.push(pstr_iter2.focus); + _ => { + machine_st.fail = true; } - } - - machine_st.heap.pop(); - machine_st.heap.pop(); + ); } fn unify_atom(&mut self, atom: Atom, value: HeapCellValue) { @@ -368,16 +161,6 @@ pub(crate) trait Unifier: DerefMut { self.fail = !(arity == 0 && name == atom); } - (HeapCellValueTag::CStr, cstr_atom) if atom == atom!("[]") => { - self.fail = cstr_atom != atom!(""); - } - (HeapCellValueTag::Char, c1) => { - if let Some(c2) = atom.as_char() { - self.fail = c1 != c2; - } else { - self.fail = true; - } - } (HeapCellValueTag::AttrVar, h) => { Self::bind(self, Ref::attr_var(h), atom_as_cell!(atom)); } @@ -412,11 +195,6 @@ pub(crate) trait Unifier: DerefMut { self.fail = true; } } - (HeapCellValueTag::Char, c2) => { - if c != c2 { - self.fail = true; - } - } (HeapCellValueTag::AttrVar, h) => { Self::bind(self, Ref::attr_var(h), char_as_cell!(c)); } @@ -438,7 +216,9 @@ pub(crate) trait Unifier: DerefMut { return; } - match Number::try_from(value) { + let machine_st = self.deref(); + + match Number::try_from((value, &machine_st.arena.f64_tbl)) { Ok(n2) => match n2 { Number::Fixnum(n2) if n1.get_num() == n2.get_num() => {} Number::Integer(n2) if (*n2).num_eq(&n1.get_num()) => {} @@ -459,7 +239,9 @@ pub(crate) trait Unifier: DerefMut { return; } - match Number::try_from(value) { + let machine_st = self.deref(); + + match Number::try_from((value, &machine_st.arena.f64_tbl)) { Ok(n2) => match n2 { Number::Fixnum(n2) if (*n1).num_eq(&n2.get_num()) => {} Number::Integer(n2) if (*n1).num_eq(&*n2) => {} @@ -480,7 +262,9 @@ pub(crate) trait Unifier: DerefMut { return; } - match Number::try_from(value) { + let machine_st = self.deref_mut(); + + match Number::try_from((value, &machine_st.arena.f64_tbl)) { Ok(n2) => match n2 { Number::Fixnum(n2) if (*n1).num_eq(&Integer::from(n2.get_num())) => {} Number::Integer(n2) if (*n1).num_eq(&*n2) => {} @@ -495,15 +279,20 @@ pub(crate) trait Unifier: DerefMut { } } - fn unify_f64(&mut self, f1: F64Ptr, value: HeapCellValue) { + fn unify_f64(&mut self, f1: F64Offset, value: HeapCellValue) { if let Some(r) = value.as_var() { Self::bind(self, r, HeapCellValue::from(f1)); return; } read_heap_cell!(value, - (HeapCellValueTag::F64, f2) => { - self.fail = **f1 != **f2; + (HeapCellValueTag::F64Offset, f2) => { + let machine_st = self.deref_mut(); + + let f1 = machine_st.arena.f64_tbl.get_entry(f1); + let f2 = machine_st.arena.f64_tbl.get_entry(f2); + + self.fail = f1 != f2; } _ => { self.fail = true; @@ -610,7 +399,7 @@ pub(crate) trait Unifier: DerefMut { tabu_list.insert((d1, d2)); } } - (HeapCellValueTag::PStrLoc) => { + (HeapCellValueTag::PStrLoc, l) => { read_heap_cell!(d2, (HeapCellValueTag::PStrLoc | HeapCellValueTag::Lis | @@ -619,8 +408,7 @@ pub(crate) trait Unifier: DerefMut { continue; } } - (HeapCellValueTag::CStr | - HeapCellValueTag::AttrVar | + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { } @@ -630,52 +418,19 @@ pub(crate) trait Unifier: DerefMut { } ); - Self::unify_partial_string(self, d1, d2); + Self::unify_partial_string(self, l, d2); if !self.fail && !d2.is_constant() { let d2 = self.store(d2); tabu_list.insert((d1, d2)); } } - (HeapCellValueTag::CStr) => { - read_heap_cell!(d2, - (HeapCellValueTag::AttrVar, h) => { - Self::bind(self, Ref::attr_var(h), d1); - continue; - } - (HeapCellValueTag::Var, h) => { - Self::bind(self, Ref::heap_cell(h), d1); - continue; - } - (HeapCellValueTag::StackVar, s) => { - Self::bind(self, Ref::stack_cell(s), d1); - continue; - } - (HeapCellValueTag::Str | - HeapCellValueTag::Lis | - HeapCellValueTag::PStrLoc) => { - } - (HeapCellValueTag::CStr) => { - self.fail = d1 != d2; - continue; - } - _ => { - self.fail = true; - return; - } - ); - - Self::unify_partial_string(self, d2, d1); - } - (HeapCellValueTag::F64, f1) => { + (HeapCellValueTag::F64Offset, f1) => { Self::unify_f64(self, f1, d2); } (HeapCellValueTag::Fixnum, n1) => { Self::unify_fixnum(self, n1, d2); } - (HeapCellValueTag::Char, c1) => { - Self::unify_char(self, c1, d2); - } (HeapCellValueTag::Cons, ptr_1) => { Self::unify_constant(self, ptr_1, d2); } @@ -707,12 +462,11 @@ fn bind_with_occurs_check(unifier: &mut U, r: Ref, value: HeapCellVa if !value.is_constant() { let machine_st: &mut MachineState = unifier.deref_mut(); + machine_st.heap[0] = value; - for cell in stackful_preorder_iter::( - &mut machine_st.heap, - &mut machine_st.stack, - value, - ) { + for cell in + stackful_preorder_iter::(&mut machine_st.heap, &mut machine_st.stack, 0) + { let cell = unmark_cell_bits!(cell); if let Some(inner_r) = cell.as_var() { diff --git a/src/macros.rs b/src/macros.rs index 4b4b51f9..79470f18 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,15 +1,10 @@ /* A simple macro to count the arguments in a variadic list * of token trees. */ -macro_rules! count_tt { - () => { 0 }; - ($odd:tt $($a:tt $b:tt)*) => { (count_tt!($($a)*) << 1) | 1 }; - ($($a:tt $even:tt)*) => { count_tt!($($a)*) << 1 }; -} macro_rules! char_as_cell { ($c: expr) => { - HeapCellValue::build_with(HeapCellValueTag::Char, $c as u64) + HeapCellValue::from_bytes(AtomCell::new_char_inlined($c).into_bytes()) }; } @@ -19,12 +14,6 @@ macro_rules! fixnum_as_cell { }; } -macro_rules! cell_as_fixnum { - ($cell:expr) => { - Fixnum::from_bytes($cell.into_bytes()) - }; -} - macro_rules! integer_as_cell { ($n: expr) => {{ match $n { @@ -45,31 +34,17 @@ macro_rules! empty_list_as_cell { macro_rules! atom_as_cell { ($atom:expr) => { - HeapCellValue::from_bytes( - AtomCell::build_with($atom.flat_index(), 0, HeapCellValueTag::Atom).into_bytes(), - ) + HeapCellValue::from_bytes(AtomCell::build_with($atom.index, 0).into_bytes()) }; ($atom:expr, $arity:expr) => { - HeapCellValue::from_bytes( - AtomCell::build_with($atom.flat_index(), $arity as u16, HeapCellValueTag::Atom) - .into_bytes(), - ) - }; -} - -macro_rules! cell_as_string { - ($cell:expr) => { - PartialString::from(cell_as_atom!($cell)) + HeapCellValue::from_bytes(AtomCell::build_with($atom.index, $arity as u8).into_bytes()) }; } macro_rules! cell_as_atom { - ($cell:expr) => {{ - let cell = AtomCell::from_bytes($cell.into_bytes()); - let name = (cell.get_index() as u64) << 3; - - Atom::from(name) - }}; + ($cell:expr) => { + AtomCell::from_bytes($cell.into_bytes()).get_name() + }; } macro_rules! cell_as_atom_cell { @@ -78,10 +53,17 @@ macro_rules! cell_as_atom_cell { }; } -macro_rules! cell_as_f64_ptr { +macro_rules! cell_as_f64_offset { ($cell:expr) => {{ let offset = $cell.get_value() as usize; - F64Ptr::from_offset(F64Offset::new(offset)) + F64Offset::from(offset) + }}; +} + +macro_rules! cell_as_code_index_offset { + ($cell:expr) => {{ + let offset = $cell.get_value() as usize; + CodeIndexOffset::from(offset) }}; } @@ -91,26 +73,12 @@ macro_rules! cell_as_untyped_arena_ptr { }; } -macro_rules! pstr_as_cell { - ($atom:expr) => { - HeapCellValue::from_bytes( - AtomCell::build_with($atom.flat_index(), 0, HeapCellValueTag::PStr).into_bytes(), - ) - }; -} - macro_rules! pstr_loc_as_cell { ($h:expr) => { HeapCellValue::build_with(HeapCellValueTag::PStrLoc, $h as u64) }; } -macro_rules! pstr_offset_as_cell { - ($h:expr) => { - HeapCellValue::build_with(HeapCellValueTag::PStrOffset, $h as u64) - }; -} - macro_rules! list_loc_as_cell { ($h:expr) => { HeapCellValue::build_with(HeapCellValueTag::Lis, $h as u64) @@ -172,11 +140,11 @@ macro_rules! typed_arena_ptr_as_cell { macro_rules! raw_ptr_as_cell { ($ptr:expr) => {{ // Cell is 64-bit, but raw ptr is 32-bit in 32-bit systems - // TODO use <*{const,mut} _>::addr instead of as when the strict_provenance feature is stable rust-lang/rust#95228 - // we might need <*{const,mut} _>::expose_provenance for strict provenance, dependening on how we recreate a pointer later - let ptr : *const _ = $ptr; - debug_assert!(!$ptr.is_null()); - HeapCellValue::from_ptr_addr(ptr as usize) + let ptr: *const _ = $ptr; + // This needs to expose provenance because it needs to be turned back into a pointer + // in contexts where there is no available provenance locally. For example, in + // `ConsPtr::as_ptr`. + HeapCellValue::from_ptr_addr(ptr.expose_provenance()) }}; } @@ -186,36 +154,10 @@ macro_rules! untyped_arena_ptr_as_cell { }; } -macro_rules! atom_as_cstr_cell { - ($atom:expr) => {{ - let offset = $atom.flat_index(); - - HeapCellValue::from_bytes( - AtomCell::build_with(offset as u64, 0, HeapCellValueTag::CStr).into_bytes(), - ) - }}; -} - -macro_rules! string_as_cstr_cell { - ($ptr:expr) => {{ - let atom: Atom = $ptr.into(); - let offset = atom.flat_index(); - - HeapCellValue::from_bytes( - AtomCell::build_with(offset as u64, 0, HeapCellValueTag::CStr).into_bytes(), - ) - }}; -} - -macro_rules! string_as_pstr_cell { - ($ptr:expr) => {{ - let atom: Atom = $ptr.into(); - let offset = atom.flat_index(); - - HeapCellValue::from_bytes( - AtomCell::build_with(offset as u64, 0, HeapCellValueTag::PStr).into_bytes(), - ) - }}; +macro_rules! stream_as_cell { + ($ptr:expr) => { + raw_ptr_as_cell!($ptr.as_ptr()) + }; } macro_rules! cell_as_stream { @@ -276,9 +218,21 @@ macro_rules! match_untyped_arena_ptr_pat_body { #[allow(unused_braces)] $code }}; - ($ptr:ident, IndexPtr, $ip:ident, $code:expr) => {{ + ($ptr:ident, PipeReader, $listener:ident, $code:expr) => {{ #[allow(unused_mut)] - let mut $ip = unsafe { $ptr.as_typed_ptr::() }; + let mut $listener = unsafe { $ptr.as_typed_ptr::() }; + #[allow(unused_braces)] + $code + }}; + ($ptr:ident, PipeWriter, $listener:ident, $code:expr) => {{ + #[allow(unused_mut)] + let mut $listener = unsafe { $ptr.as_typed_ptr::() }; + #[allow(unused_braces)] + $code + }}; + ($ptr:ident, ChildProcess, $listener:ident, $code:expr) => {{ + #[allow(unused_mut)] + let mut $listener = unsafe { $ptr.as_typed_ptr::() }; #[allow(unused_braces)] $code }}; @@ -304,12 +258,8 @@ macro_rules! match_untyped_arena_ptr_pat { | ArenaHeaderTag::InputChannelStream | ArenaHeaderTag::StandardOutputStream | ArenaHeaderTag::StandardErrorStream - }; - (IndexPtr) => { - ArenaHeaderTag::IndexPtrUndefined - | ArenaHeaderTag::IndexPtrDynamicUndefined - | ArenaHeaderTag::IndexPtrDynamicIndex - | ArenaHeaderTag::IndexPtrIndex + | ArenaHeaderTag::PipeReader + | ArenaHeaderTag::PipeWriter }; ($tag:ident) => { ArenaHeaderTag::$tag @@ -336,8 +286,13 @@ macro_rules! read_heap_cell_pat_body { #[allow(unused_braces)] $code }}; - ($cell:ident, F64, $n:ident, $code:expr) => {{ - let $n = cell_as_f64_ptr!($cell); + ($cell:ident, F64Offset, $n:ident, $code:expr) => {{ + let $n = cell_as_f64_offset!($cell); + #[allow(unused_braces)] + $code + }}; + ($cell:ident, CodeIndexOffset, $n:ident, $code:expr) => {{ + let $n = cell_as_code_index_offset!($cell); #[allow(unused_braces)] $code }}; @@ -346,26 +301,6 @@ macro_rules! read_heap_cell_pat_body { #[allow(unused_braces)] $code }}; - ($cell:ident, PStr, $atom:ident, $code:expr) => {{ - let $atom = cell_as_atom!($cell); - #[allow(unused_braces)] - $code - }}; - ($cell:ident, CStr, $atom:ident, $code:expr) => {{ - let $atom = cell_as_atom!($cell); - #[allow(unused_braces)] - $code - }}; - ($cell:ident, CStr | PStr, $atom:ident, $code:expr) => {{ - let $atom = cell_as_atom!($cell); - #[allow(unused_braces)] - $code - }}; - ($cell:ident, PStr | CStr, $atom:ident, $code:expr) => {{ - let $atom = cell_as_atom!($cell); - #[allow(unused_braces)] - $code - }}; ($cell:ident, Fixnum, $value:ident, $code:expr) => {{ let $value = Fixnum::from_bytes($cell.into_bytes()); #[allow(unused_braces)] @@ -386,11 +321,6 @@ macro_rules! read_heap_cell_pat_body { #[allow(unused_braces)] $code }}; - ($cell:ident, Char, $value:ident, $code:expr) => {{ - let $value = unsafe { char::from_u32_unchecked($cell.get_value() as u32) }; - #[allow(unused_braces)] - $code - }}; ($cell:ident, $($tags:tt)|+, $value:ident, $code:expr) => {{ let $value = $cell.get_value() as usize; #[allow(unused_braces)] @@ -432,119 +362,6 @@ macro_rules! read_heap_cell { }); } -macro_rules! functor { - ($name:expr, [$($dt:ident($($value:expr),*)),+], [$($aux:ident),*]) => ({ - { - #[allow(unused_variables, unused_mut)] - let mut addendum = Heap::new(); - let arity: usize = count_tt!($($dt) +); - - #[allow(unused_variables)] - let aux_lens: [usize; count_tt!($($aux) *)] = [$($aux.len()),*]; - - let mut result = - vec![ atom_as_cell!($name, arity as u16), - $(functor_term!( $dt($($value),*), arity, aux_lens, addendum ),)+ ]; - - $( - result.extend($aux.iter()); - )* - - result.extend(addendum.into_iter()); - result - } - }); - ($name:expr, [$($dt:ident($($value:expr),*)),+]) => ({ - { - let arity: usize = count_tt!($($dt) +); - - #[allow(unused_variables, unused_mut)] - let mut addendum = Heap::new(); - - let mut result = - vec![ atom_as_cell!($name, arity as u16), - $(functor_term!( $dt($($value),*), arity, [], addendum ),)+ ]; - - result.extend(addendum.into_iter()); - result - } - }); - ($name:expr) => ({ - vec![ atom_as_cell!($name) ] - }); -} - -macro_rules! functor_term { - (str(0), $arity:expr, $aux_lens:expr, $addendum:ident) => ({ - str_loc_as_cell!($arity + 1) - }); - (str($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ({ - let len: usize = $aux_lens[0 .. $e].iter().sum(); - str_loc_as_cell!($arity + 1 + len) - }); - (str($h:expr, 0), $arity:expr, $aux_lens:expr, $addendum:ident) => ({ - str_loc_as_cell!($arity + $h + 1) - }); - (str($h:expr, $e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ({ - let len: usize = $aux_lens[0 .. $e].iter().sum(); - str_loc_as_cell!($arity + $h + 1 + len) - }); - (literal($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ( - HeapCellValue::from($e) - ); - (integer($e:expr, $arena:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ( - HeapCellValue::arena_from(Number::arena_from($e, $arena), $arena) - ); - (fixnum($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ( - fixnum_as_cell!(Fixnum::build_with($e as i64)) - ); - (indexing_code_ptr($h:expr, $e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ({ - let stub = - match $e { - IndexingCodePtr::DynamicExternal(o) => functor!(atom!("dynamic_external"), [fixnum(o)]), - IndexingCodePtr::External(o) => functor!(atom!("external"), [fixnum(o)]), - IndexingCodePtr::Internal(o) => functor!(atom!("internal"), [fixnum(o)]), - IndexingCodePtr::Fail => { - vec![atom_as_cell!(atom!("fail"))] - }, - }; - - let len: usize = $aux_lens.iter().sum(); - let h = len + $arity + 1 + $addendum.len() + $h; - - $addendum.extend(stub.into_iter()); - - str_loc_as_cell!(h) - }); - (number($arena:expr, $e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ( - HeapCellValue::from(($e, $arena)) - ); - (atom($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ( - atom_as_cell!($e) - ); - (string($h:expr, $e:expr), $arity:expr, $aux_lens:expr, $addendum: ident) => ({ - let len: usize = $aux_lens.iter().sum(); - let h = len + $arity + 1 + $addendum.len() + $h; - - let cell = string_as_pstr_cell!($e); - - $addendum.push(cell); - $addendum.push(empty_list_as_cell!()); - - heap_loc_as_cell!(h) - }); - (boolean($e:expr), $arity:expr, $aux_lens:expr, $addendum: ident) => ({ - if $e { - functor_term!(atom(atom!("true")), $arity, $aux_lens, $addendum) - } else { - functor_term!(atom(atom!("false")), $arity, $aux_lens, $addendum) - } - }); - (cell($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ( - $e - ); -} - macro_rules! compare_number_instr { ($cmp: expr, $at_1: expr, $at_2: expr) => {{ $cmp.set_terms($at_1, $at_2); @@ -629,3 +446,44 @@ macro_rules! compare_term_test { $machine_st.compare_term_test($var_comparison) }}; } + +macro_rules! step_or_resource_error { + ($machine_st:expr, $val:expr) => {{ + match $val { + Ok(r) => r, + Err(err_loc) => { + $machine_st.throw_resource_error(err_loc); + return; + } + } + }}; + ($machine_st:expr, $val:expr, $fail:block) => {{ + match $val { + Ok(r) => r, + Err(err_loc) => { + $machine_st.throw_resource_error(err_loc); + $fail + } + } + }}; +} + +macro_rules! resource_error_call_result { + ($machine_st:expr, $val:expr) => { + step_or_resource_error!($machine_st, $val, { + return Err(vec![]); + }) + }; +} + +macro_rules! heap_index { + ($idx:expr) => { + ($idx) * std::mem::size_of::() + }; +} + +macro_rules! cell_index { + ($idx:expr) => { + (($idx) / std::mem::size_of::()) + }; +} diff --git a/src/offset_table.rs b/src/offset_table.rs new file mode 100644 index 00000000..c2b2ea8b --- /dev/null +++ b/src/offset_table.rs @@ -0,0 +1,422 @@ +use std::cell::UnsafeCell; +use std::sync::Arc; +use std::{fmt, mem, ptr}; + +use arcu::atomic::Arcu; +use arcu::epoch_counters::GlobalEpochCounterPool; +use arcu::rcu_ref::RcuRef; +use arcu::Rcu; +use parking_lot::RwLock; + +use crate::machine::machine_indices::IndexPtr; +use crate::raw_block::RawBlock; +use crate::raw_block::RawBlockTraits; + +use ordered_float::OrderedFloat; + +const F64_TABLE_INIT_SIZE: usize = 1 << 16; +const F64_TABLE_ALIGN: usize = 8; + +const CODE_INDEX_TABLE_INIT_SIZE: usize = 1 << 16; +const CODE_INDEX_TABLE_ALIGN: usize = 8; + +impl RawBlockTraits for OrderedFloat { + #[inline] + fn init_size() -> usize { + F64_TABLE_INIT_SIZE + } + + #[inline] + fn align() -> usize { + F64_TABLE_ALIGN + } +} + +impl RawBlockTraits for IndexPtr { + #[inline] + fn init_size() -> usize { + CODE_INDEX_TABLE_INIT_SIZE + } + + #[inline] + fn align() -> usize { + CODE_INDEX_TABLE_ALIGN + } +} + +#[derive(Debug)] +pub struct OffsetTableImpl(InnerOffsetTableImpl); + +impl From>> for OffsetTableImpl { + #[inline] + fn from(value: Arc>) -> Self { + OffsetTableImpl(InnerOffsetTableImpl::Concurrent(value)) + } +} + +impl OffsetTableImpl { + #[inline(always)] + pub fn new() -> Self { + Self(InnerOffsetTableImpl::Serial(SerialOffsetTable::new())) + } + + #[must_use = "the returned concurrent table must be absorbed into the owned OffsetTable"] + pub fn single_to_concurrent(&mut self) -> Arc> { + match &mut self.0 { + InnerOffsetTableImpl::Serial(serial_tbl) => { + let empty_serial_tbl = SerialOffsetTable { + block: RawBlock::empty_block(), + }; + + let serial_tbl = mem::replace(serial_tbl, empty_serial_tbl); + let num_tbl_entries = serial_tbl.block.size() / size_of::(); + let block = Arcu::new(serial_tbl.block, GlobalEpochCounterPool); + + let offset_locks: Vec> = + (0..num_tbl_entries).map(|_| RwLock::new(())).collect(); + + let concurrent_tbl = Arc::new(ConcurrentOffsetTable { + block, + growth_lock: RwLock::new(()), + offset_locks: RwLock::new(offset_locks), + }); + + self.0 = InnerOffsetTableImpl::Concurrent(concurrent_tbl.clone()); + + concurrent_tbl + } + InnerOffsetTableImpl::Concurrent(concurrent_tbl) => concurrent_tbl.clone(), + } + } + + #[must_use = "the transition to a single-threaded offset table may fail if the concurrent table is held from multiple places"] + pub fn concurrent_to_single(&mut self) -> Result<(), ()> { + match &mut self.0 { + InnerOffsetTableImpl::Serial(_serial_tbl) => Ok(()), + InnerOffsetTableImpl::Concurrent(concurrent_tbl) => { + let table_arc = std::mem::replace( + concurrent_tbl, + Arc::new(ConcurrentOffsetTable { + block: Arcu::new(RawBlock::empty_block(), GlobalEpochCounterPool), + growth_lock: RwLock::new(()), + offset_locks: RwLock::new(vec![]), + }), + ); + + match Arc::try_unwrap(table_arc) { + Ok(table) => { + // this was the only instance of the concurrent table, as such + // at this point no build_with/with_entry{_mut} call can be in-progress/made + + // this shouldn't be able to fail + let raw_block = + Arc::try_unwrap(table.block.replace(RawBlock::empty_block())).unwrap(); + self.0 = + InnerOffsetTableImpl::Serial(SerialOffsetTable { block: raw_block }); + Ok(()) + } + Err(table_arc) => { + // restore the concurrent_tbl + *concurrent_tbl = table_arc; + Err(()) + } + } + } + } + } + + #[inline] + pub fn get_entry(&self, offset: >::Offset) -> T + where + Self: OffsetTable, + T: Copy, + { + self.with_entry(offset, |value| *value) + } +} + +impl Default for OffsetTableImpl { + fn default() -> Self { + Self::new() + } +} + +#[derive(Debug)] +struct SerialOffsetTable { + block: RawBlock, +} + +#[derive(Debug)] +pub struct ConcurrentOffsetTable { + block: Arcu, GlobalEpochCounterPool>, + growth_lock: RwLock<()>, + offset_locks: RwLock>>, +} + +#[derive(Debug)] +enum InnerOffsetTableImpl { + Serial(SerialOffsetTable), + #[allow(dead_code)] + Concurrent(Arc>), +} + +impl InnerOffsetTableImpl { + #[inline(always)] + fn build_with(&mut self, value: T) -> usize { + match self { + Self::Concurrent(concurrent_tbl) => concurrent_tbl.build_with(value), + Self::Serial(serial_tbl) => unsafe { serial_tbl.build_with(value) }, + } + } + + #[inline(always)] + fn with_entry R>(&self, offset: usize, f: F) -> R { + match self { + Self::Concurrent(concurrent_tbl) => concurrent_tbl.with_entry(offset, f), + Self::Serial(serial_tbl) => f(unsafe { serial_tbl.lookup(offset) }), + } + } + + #[inline(always)] + fn with_entry_mut R>(&mut self, offset: usize, f: F) -> R { + match self { + Self::Concurrent(concurrent_tbl) => concurrent_tbl.with_entry_mut(offset, f), + Self::Serial(serial_tbl) => f(unsafe { serial_tbl.lookup_mut(offset) }), + } + } +} + +pub trait OffsetTable { + type Offset: Copy + Into; + + fn build_with(&mut self, value: T) -> Self::Offset; + + fn with_entry R>(&self, offset: Self::Offset, f: F) -> R; + fn with_entry_mut R>(&mut self, offset: Self::Offset, f: F) -> R; +} + +impl OffsetTable> for OffsetTableImpl> { + type Offset = F64Offset; + + fn build_with(&mut self, value: OrderedFloat) -> F64Offset { + F64Offset(self.0.build_with(value)) + } + + #[inline] + fn with_entry) -> R>(&self, offset: F64Offset, f: F) -> R { + self.0.with_entry(offset.into(), f) + } + + #[inline] + fn with_entry_mut) -> R>( + &mut self, + offset: F64Offset, + f: F, + ) -> R { + self.0.with_entry_mut(offset.into(), f) + } +} + +impl OffsetTable for OffsetTableImpl { + type Offset = CodeIndexOffset; + + fn build_with(&mut self, value: IndexPtr) -> CodeIndexOffset { + CodeIndexOffset(self.0.build_with(value)) + } + + #[inline] + fn with_entry R>(&self, offset: CodeIndexOffset, f: F) -> R { + self.0.with_entry(offset.into(), f) + } + + #[inline] + fn with_entry_mut R>( + &mut self, + offset: CodeIndexOffset, + f: F, + ) -> R { + self.0.with_entry_mut(offset.into(), f) + } +} + +impl SerialOffsetTable { + #[inline] + fn new() -> Self { + Self { + block: RawBlock::new(), + } + } + + unsafe fn build_with(&mut self, value: T) -> usize { + let mut ptr; + + loop { + ptr = self.block.alloc(size_of::()); + + if ptr.is_null() { + let new_block = self.block.grow_new().unwrap(); + self.block = new_block; + } else { + break; + } + } + + ptr::write(ptr as *mut T, value); + ptr.addr() - self.block.base.addr() + } + + #[inline] + unsafe fn lookup(&self, offset: usize) -> &T { + &*self.block.base.add(offset).cast::() + } + + #[inline] + unsafe fn lookup_mut(&mut self, offset: usize) -> &mut T { + &mut *self.block.base.add(offset).cast::().cast_mut() + } +} + +impl ConcurrentOffsetTable { + #[allow(clippy::missing_safety_doc)] + fn build_with(&self, value: T) -> usize { + let growth_lock = self.growth_lock.write(); + + // we don't have an index table for lookups as AtomTable does so + // just get the epoch after we take the upgrade lock + let mut block_epoch = self.block.read(); + let mut ptr; + + loop { + ptr = unsafe { block_epoch.alloc(mem::size_of::()) }; + + if ptr.is_null() { + let new_block = unsafe { block_epoch.grow_new().unwrap() }; + self.block.replace(new_block); + block_epoch = self.block.read(); + } else { + break; + } + } + + let new_tbl_sz = block_epoch.size() / size_of::(); + let mut offset_locks = self.offset_locks.write(); + + offset_locks.resize_with(new_tbl_sz, || RwLock::new(())); + + unsafe { + ptr::write(ptr as *mut T, value); + } + + let value = ptr.addr() - block_epoch.base.addr(); + + // AtomTable would have to update the index table at this point + // explicit drop to ensure we don't accidentally drop it early + drop(offset_locks); + drop(growth_lock); + + value + } + + fn with_entry R>(&self, offset: usize, f: F) -> R { + let outer_offset_lock = self.offset_locks.read(); + let inner_offset_lock = outer_offset_lock[offset / size_of::()].read(); + + let rcu_ref = RcuRef::try_map(self.block.read(), |raw_block| unsafe { + raw_block.base.add(offset).cast::().as_ref() + }) + .expect("offset valid"); + + let result = f(&*rcu_ref); + + drop(inner_offset_lock); + drop(outer_offset_lock); + + result + } + + fn with_entry_mut R>(&self, offset: usize, f: F) -> R { + let growth_lock = self.growth_lock.read(); + let outer_offset_lock = self.offset_locks.read(); + let inner_offset_lock = outer_offset_lock[offset / size_of::()].write(); + + let rcu_ref = RcuRef::try_map(self.block.read(), |raw_block| unsafe { + raw_block + .base + .add(offset) + .cast_mut() + .cast::>() + .as_ref() + }) + .expect("offset valid"); + + let result = f(unsafe { &mut *rcu_ref.get().as_mut().unwrap() }); + + drop(inner_offset_lock); + drop(outer_offset_lock); + drop(growth_lock); + + result + } +} + +pub type F64Table = OffsetTableImpl>; +pub type CodeIndexTable = OffsetTableImpl; + +#[derive(Clone, Copy, Debug)] +pub struct F64Offset(usize); + +impl From for F64Offset { + #[inline(always)] + fn from(offset: usize) -> Self { + Self(offset) + } +} + +impl From for usize { + fn from(val: F64Offset) -> Self { + val.0 + } +} + +#[derive(Debug, Clone, Copy)] +pub struct CodeIndexOffset(usize); + +impl From for CodeIndexOffset { + #[inline(always)] + fn from(offset: usize) -> Self { + Self(offset) + } +} + +impl From for usize { + #[inline(always)] + fn from(val: CodeIndexOffset) -> Self { + val.0 + } +} + +impl CodeIndexOffset { + #[inline(always)] + pub fn to_u64(self) -> u64 { + self.0 as u64 + } +} + +impl fmt::Display for CodeIndexOffset { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "CodeIndexOffset({})", self.0) + } +} + +impl F64Offset { + #[inline(always)] + pub fn to_u64(self) -> u64 { + self.0 as u64 + } +} + +impl fmt::Display for F64Offset { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "F64Offset({})", self.0) + } +} diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 9a400c36..18389225 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -2,21 +2,24 @@ use crate::arena::*; use crate::atom_table::*; -use crate::machine::machine_indices::*; +use crate::offset_table::*; use crate::parser::char_reader::*; use crate::types::HeapCellValueTag; use std::cell::{Cell, Ref, RefCell, RefMut}; use std::fmt; -use std::hash::{Hash, Hasher}; +use std::hash::Hash; +use std::hash::Hasher; use std::io::{Error as IOError, ErrorKind}; +use std::ops::Not; +use std::ops::RangeInclusive; use std::ops::{Deref, Neg}; use std::rc::Rc; use std::sync::Arc; use std::vec::Vec; -use crate::parser::dashu::{Integer, Rational}; - +use dashu::Integer; +use dashu::Rational; use fxhash::FxBuildHasher; use indexmap::IndexMap; use scryer_modular_bitfield::error::OutOfBounds; @@ -24,7 +27,7 @@ use scryer_modular_bitfield::prelude::*; pub type Specifier = u32; -pub const MAX_ARITY: usize = 1023; +pub const MAX_ARITY: usize = 255; #[allow(clippy::upper_case_acronyms)] #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -264,8 +267,8 @@ impl RegType { impl fmt::Display for RegType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - RegType::Perm(val) => write!(f, "Y{}", val), - RegType::Temp(val) => write!(f, "X{}", val), + RegType::Perm(val) => write!(f, "Y{val}"), + RegType::Temp(val) => write!(f, "X{val}"), } } } @@ -287,10 +290,10 @@ impl VarReg { impl fmt::Display for VarReg { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - VarReg::Norm(RegType::Perm(reg)) => write!(f, "Y{}", reg), - VarReg::Norm(RegType::Temp(reg)) => write!(f, "X{}", reg), - VarReg::ArgAndNorm(RegType::Perm(reg), arg) => write!(f, "Y{} A{}", reg, arg), - VarReg::ArgAndNorm(RegType::Temp(reg), arg) => write!(f, "X{} A{}", reg, arg), + VarReg::Norm(RegType::Perm(reg)) => write!(f, "Y{reg}"), + VarReg::Norm(RegType::Temp(reg)) => write!(f, "X{reg}"), + VarReg::ArgAndNorm(RegType::Perm(reg), arg) => write!(f, "Y{reg} A{arg}"), + VarReg::ArgAndNorm(RegType::Temp(reg), arg) => write!(f, "X{reg} A{arg}"), } } } @@ -307,28 +310,6 @@ macro_rules! temp_v { }; } -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum GenContext { - Head, - Mid(usize), - Last(usize), // Mid & Last: chunk_num -} - -impl GenContext { - #[inline] - pub fn chunk_num(self) -> usize { - match self { - GenContext::Head => 0, - GenContext::Mid(cn) | GenContext::Last(cn) => cn, - } - } - - #[inline] - pub fn is_last(self) -> bool { - matches!(self, GenContext::Last(_)) - } -} - #[bitfield] #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct OpDesc { @@ -420,7 +401,7 @@ pub fn default_op_dir() -> OpDir { op_dir } -#[derive(Debug, Clone)] +#[derive(Debug, Copy, Clone)] pub enum ArithmeticError { NonEvaluableFunctor(Literal, usize), UninstantiatedVar, @@ -465,9 +446,9 @@ impl ParserError { ParserError::InvalidSingleQuotedCharacter(..) => { atom!("invalid_single_quoted_character") } - ParserError::InfiniteFloat(..) => { - atom!("infinite_float") - } + ParserError::InfiniteFloat(..) => { + atom!("infinite_float") + } ParserError::IO(e) if e.kind() == ErrorKind::UnexpectedEof => { atom!("unexpected_end_of_file") } @@ -570,9 +551,90 @@ pub struct Fixnum { tag: B6, } +mod private { + use dashu::Integer; + + pub(crate) trait FitsInFixnumSeal {} + pub(crate) trait MightNotFitInFixnumSeal {} + + macro_rules! impl_fits_in_fixnum { + ($t:ty) => { + impl $crate::parser::ast::private::FitsInFixnumSeal for $t {} + + impl $crate::parser::ast::FitsInFixnum for $t { + fn into_i56(self) -> i64 { + self.into() + } + } + }; + } + + impl_fits_in_fixnum!(u8); + impl_fits_in_fixnum!(i8); + impl_fits_in_fixnum!(u16); + impl_fits_in_fixnum!(i16); + impl_fits_in_fixnum!(u32); + impl_fits_in_fixnum!(i32); + + impl FitsInFixnumSeal for char {} + impl super::FitsInFixnum for char { + fn into_i56(self) -> i64 { + u32::from(self) as i64 + } + } + + impl MightNotFitInFixnumSeal for i64 {} + impl MightNotFitInFixnumSeal for &Integer {} + impl MightNotFitInFixnumSeal for Integer {} + impl MightNotFitInFixnumSeal for usize {} +} + +#[allow(private_bounds)] +pub trait FitsInFixnum: private::FitsInFixnumSeal { + fn into_i56(self) -> i64; +} + +#[allow(private_bounds)] +pub trait MightNotFitInFixnum: private::MightNotFitInFixnumSeal { + fn try_into_i56(self) -> Option; +} + +impl MightNotFitInFixnum for T +where + T: private::MightNotFitInFixnumSeal + TryInto, +{ + fn try_into_i56(self) -> Option { + let val = self.try_into().ok()?; + if Fixnum::RANGE.contains(&val) { + Some(val) + } else { + None + } + } +} + impl Fixnum { + pub(crate) const MIN: i64 = -(1 << 55); + pub(crate) const MAX: i64 = (1 << 55) - 1; + const RANGE: RangeInclusive = Self::MIN..=Self::MAX; + + // if you have a type that is not guaranteed to fit use `Fixnum::build_with_checked` or `Fixnum::build_with_unchecked` instead #[inline] - pub fn build_with(num: i64) -> Self { + pub fn build_with(num: impl FitsInFixnum) -> Self { + // Safety: FitsInFixnum is only implemented by types that only have valid values + // and FitsInFixnumSeal ensures no one outside this crate can violate that + unsafe { Self::build_with_unchecked(num.into_i56()) } + } + + #[inline] + pub unsafe fn build_with_unchecked(num: i64) -> Self { + debug_assert!( + Self::RANGE.contains(&num), + "{num} should be in the range {}..={}", + Self::MIN, + Self::MAX + ); + Fixnum::new() .with_num(u64::from_ne_bytes(num.to_ne_bytes()) & ((1 << 56) - 1)) .with_tag(HeapCellValueTag::Fixnum as u8) @@ -581,12 +643,8 @@ impl Fixnum { } #[inline] - pub fn as_cutpoint(num: i64) -> Self { - Fixnum::new() - .with_num(u64::from_ne_bytes(num.to_ne_bytes()) & ((1 << 56) - 1)) - .with_tag(HeapCellValueTag::CutPoint as u8) - .with_m(false) - .with_f(false) + pub fn as_cutpoint(self) -> Self { + self.with_tag(HeapCellValueTag::CutPoint as u8) } #[inline] @@ -595,20 +653,14 @@ impl Fixnum { HeapCellValueTag::from_bytes(self.tag()).unwrap() } + // if you have a type that is guaranteed to fit use `Fixnum::build_with` instead #[inline] - pub fn build_with_checked(num: i64) -> Result { - const UPPER_BOUND: i64 = (1 << 55) - 1; - const LOWER_BOUND: i64 = -(1 << 55); - - if (LOWER_BOUND..=UPPER_BOUND).contains(&num) { - Ok(Fixnum::new() - .with_m(false) - .with_f(false) - .with_tag(HeapCellValueTag::Fixnum as u8) - .with_num(u64::from_ne_bytes(num.to_ne_bytes()) & ((1 << 56) - 1))) - } else { - Err(OutOfBounds {}) - } + pub fn build_with_checked(num: impl MightNotFitInFixnum) -> Result { + Ok(unsafe { + // Safety: all MightNotFitInFixnum impls return None when the value is out-of-bounds + // and MightNotFitInFixnumSeal ensures no one outside this crate can violate that + Self::build_with_unchecked(num.try_into_i56().ok_or(OutOfBounds {})?) + }) } #[inline] @@ -618,6 +670,10 @@ impl Fixnum { debug_assert!(!overflowed); n } + + pub fn checked_abs(self) -> Option { + Self::build_with_checked(self.get_num().abs()).ok() + } } impl Neg for Fixnum { @@ -625,23 +681,33 @@ impl Neg for Fixnum { #[inline] fn neg(self) -> Self::Output { - Fixnum::build_with(-self.get_num()) + // Safety: the truncating behaviour is correct + unsafe { Self::build_with_unchecked(-self.get_num()) } } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +impl Not for Fixnum { + type Output = Self; + + #[inline] + fn not(self) -> Self::Output { + // Safety: the truncating behaviour is correct + unsafe { Self::build_with_unchecked(!self.get_num()) } + } +} + +#[derive(Debug, Copy, Clone)] pub enum Literal { Atom(Atom), - Char(char), - CodeIndex(CodeIndex), + CodeIndexOffset(CodeIndexOffset), Fixnum(Fixnum), Integer(TypedArenaPtr), Rational(TypedArenaPtr), - Float(F64Offset), - String(Atom), + F64Offset(F64Offset), } -impl From for Literal { +/* +impl From> for Literal { #[inline(always)] fn from(ptr: F64Ptr) -> Literal { Literal::Float(ptr.as_offset()) @@ -654,16 +720,15 @@ impl fmt::Display for Literal { Literal::Atom(ref atom) => { write!(f, "{}", atom.flat_index()) } - Literal::Char(c) => write!(f, "'{}'", *c as u32), - Literal::CodeIndex(i) => write!(f, "{:x}", i.as_ptr() as u64), + Literal::CodeIndexOffset(i) => write!(f, "{}", *i), Literal::Fixnum(n) => write!(f, "{}", n.get_num()), Literal::Integer(ref n) => write!(f, "{}", n), Literal::Rational(ref n) => write!(f, "{}", n), - Literal::Float(ref n) => write!(f, "{}", *n), - Literal::String(ref s) => write!(f, "\"{}\"", s.as_str()), + Literal::FloatOffset(ref n) => write!(f, "{}", *n), } } } +*/ impl Literal { pub fn as_atom(&self, atom_tbl: &Arc) -> Option { @@ -742,20 +807,20 @@ impl From<&str> for VarPtr { pub enum Var { Generated(usize), InSitu(usize), - Named(String), + Named(Rc), } impl From for Var { #[inline(always)] fn from(value: String) -> Var { - Var::Named(value) + Var::Named(Rc::new(value)) } } impl From<&str> for Var { #[inline(always)] fn from(value: &str) -> Var { - Var::Named(value.to_owned()) + Var::Named(Rc::new(value.to_owned())) } } @@ -764,8 +829,8 @@ impl Var { #[inline(always)] pub fn to_string(&self) -> String { match self { - Var::InSitu(n) | Var::Generated(n) => format!("_{}", n), - Var::Named(value) => value.to_owned(), + Var::InSitu(n) | Var::Generated(n) => format!("_{n}"), + Var::Named(value) => value.as_ref().clone(), } } } @@ -778,8 +843,8 @@ pub enum Term { Literal(Cell, Literal), // PartialString wraps a String in anticipation of it absorbing // other PartialString variants in as_partial_string. - PartialString(Cell, String, Box), - CompleteString(Cell, Atom), + PartialString(Cell, Rc, Box), + CompleteString(Cell, Rc), Var(Cell, VarPtr), } @@ -792,10 +857,9 @@ impl Term { } pub fn name(&self) -> Option { - match self { - &Term::Literal(_, Literal::Atom(ref atom)) | &Term::Clause(_, ref atom, ..) => { - Some(*atom) - } + match *self { + Term::Literal(_, Literal::Atom(atom)) => Some(atom), + Term::Clause(_, atom, ..) => Some(atom), _ => None, } } @@ -810,7 +874,7 @@ impl Term { pub(crate) fn unfold_by_str_once(term: &mut Term, s: Atom) -> Option<(Term, Term)> { if let Term::Clause(_, ref name, ref mut subterms) = term { - if let Some(Term::Literal(_, Literal::CodeIndex(_))) = subterms.last() { + if let Some(Term::Literal(_, Literal::CodeIndexOffset(_))) = subterms.last() { subterms.pop(); } diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index 75700f38..0ca5ef69 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -1,11 +1,13 @@ -use crate::arena::F64Ptr; -use crate::arena::TypedArenaPtr; +use crate::arena::*; use crate::atom_table::*; pub use crate::machine::machine_state::*; +use crate::offset_table::*; use crate::parser::ast::*; use crate::parser::char_reader::*; use crate::parser::dashu::Integer; +use ordered_float::OrderedFloat; + use std::convert::TryFrom; use std::fmt; @@ -28,10 +30,11 @@ struct LayoutInfo { more: bool, } -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub enum Token { Literal(Literal), Var(String), + String(String), Open, // '(' OpenCT, // '(' Close, // ')' @@ -55,16 +58,17 @@ impl Token { enum Number { BigInt(TypedArenaPtr), Fixnum(Fixnum), - Float(F64Ptr), + Float(F64Offset), } impl Number { #[inline] + #[allow(clippy::wrong_self_convention)] fn to_literal(self) -> Literal { match self { Number::BigInt(ibig) => Literal::Integer(ibig), Number::Fixnum(fixnum) => Literal::Fixnum(fixnum), - Number::Float(f) => Literal::Float(f.as_offset()), + Number::Float(f) => Literal::F64Offset(f), } } } @@ -77,6 +81,7 @@ enum NumberToken { impl NumberToken { #[inline] + #[allow(clippy::wrong_self_convention)] fn to_token(self) -> Option { match self { NumberToken::Number(number) => Some(Token::Literal(number.to_literal())), @@ -100,7 +105,7 @@ macro_rules! try_nt { }}; } -pub struct Lexer<'a, R> { +pub(crate) struct Lexer<'a, R> { pub(crate) reader: R, pub(crate) machine_st: &'a mut MachineState, pub(crate) line_num: usize, @@ -109,7 +114,7 @@ pub struct Lexer<'a, R> { impl<'a, R: fmt::Debug> fmt::Debug for Lexer<'a, R> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Lexer") + f.debug_struct("LexerParser") .field("reader", &"&'a mut R") // Hacky solution. .field("line_num", &self.line_num) .field("col_num", &self.col_num) @@ -119,7 +124,7 @@ impl<'a, R: fmt::Debug> fmt::Debug for Lexer<'a, R> { impl<'a, R: CharRead> Lexer<'a, R> { pub fn new(src: R, machine_st: &'a mut MachineState) -> Self { - Lexer { + Self { reader: src, machine_st, line_num: 0, @@ -505,7 +510,13 @@ impl<'a, R: CharRead> Lexer<'a, R> { if hexadecimal_digit_char!(c) { self.skip_char(c); token.push(c); - c = try_nt!(token, self.lookahead_char()); + c = match self.lookahead_char() { + Ok(c) => c, + Err(e) if e.is_unexpected_eof() => { + break; + } + Err(e) => return Err(e), + }; } else { break; } @@ -530,7 +541,13 @@ impl<'a, R: CharRead> Lexer<'a, R> { if octal_digit_char!(c) { self.skip_char(c); token.push(c); - c = try_nt!(token, self.lookahead_char()); + c = match self.lookahead_char() { + Ok(c) => c, + Err(e) if e.is_unexpected_eof() => { + break; + } + Err(e) => return Err(e), + }; } else { break; } @@ -555,7 +572,13 @@ impl<'a, R: CharRead> Lexer<'a, R> { if binary_digit_char!(c) { self.skip_char(c); token.push(c); - c = try_nt!(token, self.lookahead_char()); + c = match self.lookahead_char() { + Ok(c) => c, + Err(e) if e.is_unexpected_eof() => { + break; + } + Err(e) => return Err(e), + }; } else { break; } @@ -632,7 +655,9 @@ impl<'a, R: CharRead> Lexer<'a, R> { if !token.is_empty() && token.chars().nth(1).is_none() { if let Some(c) = token.chars().next() { - return Ok(Token::Literal(Literal::Char(c))); + return Ok(Token::Literal(Literal::Atom( + AtomCell::new_char_inlined(c).get_name(), + ))); } } } else { @@ -679,12 +704,8 @@ impl<'a, R: CharRead> Lexer<'a, R> { } } - fn parse_integer_by_radix( - &mut self, - token: &String, - radix: u32, - ) -> Result { - i64::from_str_radix(&token, radix) + fn parse_integer_by_radix(&mut self, token: &str, radix: u32) -> Result { + i64::from_str_radix(token, radix) .map(|n| { Fixnum::build_with_checked(n) .map(Number::Fixnum) @@ -701,7 +722,7 @@ impl<'a, R: CharRead> Lexer<'a, R> { } #[inline] - fn parse_integer(&mut self, token: &String) -> Result { + fn parse_integer(&mut self, token: &str) -> Result { self.parse_integer_by_radix(token, 10) } @@ -785,6 +806,7 @@ impl<'a, R: CharRead> Lexer<'a, R> { } let n = parse_float_lossy(&token)?; + Ok(NumberToken::Number(Number::Float(float_alloc!( n, self.machine_st.arena @@ -847,7 +869,7 @@ impl<'a, R: CharRead> Lexer<'a, R> { } self.get_single_quoted_char() - .map(|c| NumberToken::Number(Number::Fixnum(Fixnum::build_with(c as i64)))) + .map(|c| NumberToken::Number(Number::Fixnum(Fixnum::build_with(c)))) .or_else(|err| { match err { ParserError::UnexpectedChar('\'', ..) => {} @@ -933,13 +955,14 @@ impl<'a, R: CharRead> Lexer<'a, R> { Ok(n) => Ok(Token::Literal(n.to_literal())), Err(_) => { let n = parse_float_lossy(&token_string)?; - Ok(Token::Literal(Literal::Float( - float_alloc!(n, self.machine_st.arena).as_offset(), - ))) + Ok(Token::Literal(Literal::F64Offset(float_alloc!( + n, + self.machine_st.arena + )))) } }, - Ok(NumberToken::Number(n)) => return Ok(Token::Literal(n.to_literal())), - Err(e) => return Err(e), + Ok(NumberToken::Number(n)) => Ok(Token::Literal(n.to_literal())), + Err(e) => Err(e), } } @@ -1028,12 +1051,12 @@ impl<'a, R: CharRead> Lexer<'a, R> { if c == '"' { let s = self.char_code_list_token(c)?; - let atom = AtomTable::build_with(&self.machine_st.atom_tbl, &s); return if let DoubleQuotes::Atom = self.machine_st.flags.double_quotes { + let atom = AtomTable::build_with(&self.machine_st.atom_tbl, &s); Ok(Token::Literal(Literal::Atom(atom))) } else { - Ok(Token::Literal(Literal::String(atom))) + Ok(Token::String(s)) }; } diff --git a/src/parser/parser.rs b/src/parser/parser.rs index dd7b007b..a874face 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -3,6 +3,7 @@ use dashu::Rational; use crate::arena::*; use crate::atom_table::*; +use crate::offset_table::OffsetTable; use crate::parser::ast::*; use crate::parser::char_reader::*; use crate::parser::lexer::*; @@ -10,6 +11,7 @@ use crate::parser::lexer::*; use std::cell::Cell; use std::mem; use std::ops::Neg; +use std::rc::Rc; #[derive(Debug, Clone, Copy, PartialEq)] enum TokenType { @@ -73,7 +75,6 @@ pub(crate) fn as_partial_string( return Err(Term::Cons(Cell::default(), Box::new(head), Box::new(tail))); } } - Term::Literal(_, Literal::Char(c)) => c.to_string(), _ => { return Err(Term::Cons(Cell::default(), Box::new(head), Box::new(tail))); } @@ -93,9 +94,6 @@ pub(crate) fn as_partial_string( return Err(Term::Cons(Cell::default(), Box::new(head), orig_tail)); } } - Term::Literal(_, Literal::Char(c)) => { - string.push(*c); - } _ => { tail = Term::Cons( Cell::default(), @@ -113,7 +111,7 @@ pub(crate) fn as_partial_string( tail_ref = tail; } Term::CompleteString(_, cstr) => { - string += &*cstr.as_str(); + string += cstr.as_str(); tail = Term::Literal(Cell::default(), Literal::Atom(atom!("[]"))); break; } @@ -124,13 +122,17 @@ pub(crate) fn as_partial_string( } } - match &tail { + match tail { Term::AnonVar | Term::Var(..) => Ok((string, Some(Box::new(tail)))), Term::Literal(_, Literal::Atom(atom!("[]"))) => Ok((string, None)), - Term::Literal(_, Literal::String(tail)) => { - string += &*tail.as_str(); + Term::CompleteString(_, tail) => { + string += &tail; Ok((string, None)) } + Term::PartialString(_, tail_string, tail) => { + string += &tail_string; + Ok((string, Some(tail))) + } _ => Ok((string, Some(Box::new(tail)))), } } @@ -237,7 +239,7 @@ pub struct Parser<'a, R> { terms: Vec, } -fn read_tokens(lexer: &mut Lexer) -> Result, ParserError> { +pub fn read_tokens(lexer: &mut Lexer<'_, R>) -> Result, ParserError> { let mut tokens = vec![]; loop { @@ -263,22 +265,30 @@ fn read_tokens(lexer: &mut Lexer) -> Result, ParserEr } tokens.reverse(); - Ok(tokens) } -fn atomize_term(atom_tbl: &AtomTable, term: &Term) -> Option { +fn atomize_term(term: &Term) -> Option { match term { - Term::Literal(_, ref c) => atomize_constant(atom_tbl, *c), + &Term::Literal(_, Literal::Atom(c)) => Some(c), _ => None, } } -fn atomize_constant(atom_tbl: &AtomTable, c: Literal) -> Option { - match c { - Literal::Atom(ref name) => Some(*name), - Literal::Char(c) => Some(AtomTable::build_with(atom_tbl, &c.to_string())), - _ => None, +impl TokenType { + fn sep_to_atom(&mut self) -> Option { + match self { + TokenType::Open | TokenType::OpenCT => Some(atom!("(")), + TokenType::Close => Some(atom!(")")), + TokenType::OpenList => Some(atom!("[")), + TokenType::CloseList => Some(atom!("]")), + TokenType::OpenCurly => Some(atom!("{")), + TokenType::CloseCurly => Some(atom!("}")), + TokenType::HeadTailSeparator => Some(atom!("|")), + TokenType::Comma => Some(atom!(",")), + TokenType::End => Some(atom!(".")), + _ => None, + } } } @@ -301,21 +311,6 @@ impl<'a, R: CharRead> Parser<'a, R> { } } - fn sep_to_atom(&mut self, tt: TokenType) -> Option { - match tt { - TokenType::Open | TokenType::OpenCT => Some(atom!("(")), - TokenType::Close => Some(atom!(")")), - TokenType::OpenList => Some(atom!("[")), - TokenType::CloseList => Some(atom!("]")), - TokenType::OpenCurly => Some(atom!("{")), - TokenType::CloseCurly => Some(atom!("}")), - TokenType::HeadTailSeparator => Some(atom!("|")), - TokenType::Comma => Some(atom!(",")), - TokenType::End => Some(atom!(".")), - _ => None, - } - } - fn get_term_name(&mut self, td: TokenDesc) -> Option { match td.tt { TokenType::HeadTailSeparator => Some(atom!("|")), @@ -385,9 +380,7 @@ impl<'a, R: CharRead> Parser<'a, R> { fn shift(&mut self, token: Token, priority: usize, spec: Specifier) { let tt = match token { - Token::Literal(Literal::String(s)) - if self.lexer.machine_st.flags.double_quotes.is_codes() => - { + Token::String(s) if self.lexer.machine_st.flags.double_quotes.is_codes() => { let mut list = Term::Literal(Cell::default(), Literal::Atom(atom!("[]"))); for c in s.as_str().chars().rev() { @@ -395,7 +388,7 @@ impl<'a, R: CharRead> Parser<'a, R> { Cell::default(), Box::new(Term::Literal( Cell::default(), - Literal::Fixnum(Fixnum::build_with(c as i64)), + Literal::Fixnum(Fixnum::build_with(c)), )), Box::new(list), ); @@ -404,10 +397,10 @@ impl<'a, R: CharRead> Parser<'a, R> { self.terms.push(list); TokenType::Term } - Token::Literal(Literal::String(s)) - if self.lexer.machine_st.flags.double_quotes.is_chars() => - { - self.terms.push(Term::CompleteString(Cell::default(), s)); + Token::String(s) => { + debug_assert!(self.lexer.machine_st.flags.double_quotes.is_chars()); + self.terms + .push(Term::CompleteString(Cell::default(), Rc::new(s))); TokenType::Term } Token::Literal(c) => { @@ -549,17 +542,13 @@ impl<'a, R: CharRead> Parser<'a, R> { let idx = self.terms.len() - arity; if TokenType::Term == self.stack[stack_len].tt - && atomize_term(&self.lexer.machine_st.atom_tbl, &self.terms[idx - 1]).is_some() + && atomize_term(&self.terms[idx - 1]).is_some() { self.stack.truncate(stack_len + 1); let mut subterms: Vec<_> = self.terms.drain(idx..).collect(); - if let Some(name) = self - .terms - .pop() - .and_then(|t| atomize_term(&self.lexer.machine_st.atom_tbl, &t)) - { + if let Some(name) = self.terms.pop().and_then(|t| atomize_term(&t)) { // reduce the '.' functor to a cons cell if it applies. if name == atom!(".") && subterms.len() == 2 { let tail = subterms.pop().unwrap(); @@ -567,12 +556,10 @@ impl<'a, R: CharRead> Parser<'a, R> { self.terms.push(match as_partial_string(head, tail) { Ok((string_buf, Some(tail))) => { - Term::PartialString(Cell::default(), string_buf, tail) + Term::PartialString(Cell::default(), Rc::new(string_buf), tail) } Ok((string_buf, None)) => { - let atom = - AtomTable::build_with(&self.lexer.machine_st.atom_tbl, &string_buf); - Term::CompleteString(Cell::default(), atom) + Term::CompleteString(Cell::default(), Rc::new(string_buf)) } Err(term) => term, }); @@ -754,11 +741,10 @@ impl<'a, R: CharRead> Parser<'a, R> { self.terms.push(match list { Term::Cons(_, head, tail) => match as_partial_string(*head, *tail) { Ok((string_buf, Some(tail))) => { - Term::PartialString(Cell::default(), string_buf, tail) + Term::PartialString(Cell::default(), Rc::new(string_buf), tail) } Ok((string_buf, None)) => { - let atom = AtomTable::build_with(&self.lexer.machine_st.atom_tbl, &string_buf); - Term::CompleteString(Cell::default(), atom) + Term::CompleteString(Cell::default(), Rc::new(string_buf)) } Err(term) => term, }, @@ -846,7 +832,7 @@ impl<'a, R: CharRead> Parser<'a, R> { return false; } - if let Some(atom) = self.sep_to_atom(self.stack[idx].tt) { + if let Some(atom) = self.stack[idx].tt.sep_to_atom() { self.terms .push(Term::Literal(Cell::default(), Literal::Atom(atom))); } @@ -967,8 +953,8 @@ impl<'a, R: CharRead> Parser<'a, R> { } match token { - Token::Literal(Literal::Fixnum(n)) => { - self.negate_number(n, |n, _| -n, |n, _| Literal::Fixnum(n)) + Token::String(string) => { + self.shift(Token::String(string), 0, TERM); } Token::Literal(Literal::Integer(n)) => { self.negate_number(n, negate_int_rc, |n, _| Literal::Integer(n)) @@ -976,21 +962,34 @@ impl<'a, R: CharRead> Parser<'a, R> { Token::Literal(Literal::Rational(n)) => { self.negate_number(n, negate_rat_rc, |r, _| Literal::Rational(r)) } - Token::Literal(Literal::Float(n)) if F64Ptr::from_offset(n).is_infinite() => { - return Err(ParserError::InfiniteFloat( - self.lexer.line_num, - self.lexer.col_num, - )); - } - Token::Literal(Literal::Float(n)) => self.negate_number( - **n.as_ptr(), - |n, _| -n, - |n, arena| Literal::from(float_alloc!(n, arena)), - ), - Token::Literal(c) => { - let atomized = atomize_constant(&self.lexer.machine_st.atom_tbl, c); + Token::Literal(Literal::F64Offset(n)) + if self + .lexer + .machine_st + .arena + .f64_tbl + .get_entry(n) + .is_infinite() => + { + return Err(ParserError::InfiniteFloat( + self.lexer.line_num, + self.lexer.col_num, + )); + } + Token::Literal(Literal::F64Offset(n)) => { + let n = self.lexer.machine_st.arena.f64_tbl.get_entry(n); - if let Some(name) = atomized { + self.negate_number( + n, + |n, _| -n, + |n, arena| Literal::F64Offset(arena.f64_tbl.build_with(n)), + ) + } + Token::Literal(Literal::Fixnum(n)) => { + self.negate_number(n, |n, _| -n, |n, _| Literal::Fixnum(n)) + } + Token::Literal(c) => { + if let Literal::Atom(name) = c { if !self.shift_op(name, op_dir)? { self.shift(Token::Literal(c), 0, TERM); } diff --git a/src/raw_block.rs b/src/raw_block.rs index da757415..02bea9f0 100644 --- a/src/raw_block.rs +++ b/src/raw_block.rs @@ -19,7 +19,7 @@ pub struct RawBlock { impl RawBlock { #[inline] - fn empty_block() -> Self { + pub fn empty_block() -> Self { RawBlock { base: ptr::null(), top: ptr::null(), @@ -66,8 +66,8 @@ impl RawBlock { false } else { self.base = new_base; - self.top = (self.base as usize + size * 2) as *const _; - *self.ptr.get_mut() = (self.base as usize + size) as *mut _; + self.top = self.base.add(size * 2); + *self.ptr.get_mut() = self.base.add(size).cast_mut(); true } } @@ -83,7 +83,7 @@ impl RawBlock { // allocation failed None } else { - let allocated = (*self.ptr.get()) as usize - self.base as usize; + let allocated = (*self.ptr.get()).addr() - self.base.addr(); self.base.copy_to(new_block.base.cast_mut(), allocated); *new_block.ptr.get_mut() = new_block.base.add(allocated).cast_mut(); Some(new_block) @@ -93,7 +93,7 @@ impl RawBlock { #[inline] pub fn size(&self) -> usize { - self.top as usize - self.base as usize + self.top.addr() - self.base.addr() } #[inline(always)] @@ -105,7 +105,7 @@ impl RawBlock { self.base ); - self.top as usize - (*self.ptr.get()) as usize + self.top.addr() - (*self.ptr.get()).addr() } pub unsafe fn alloc(&self, size: usize) -> *mut u8 { diff --git a/src/read.rs b/src/read.rs index c13c0250..4c6b033c 100644 --- a/src/read.rs +++ b/src/read.rs @@ -1,4 +1,5 @@ use crate::parser::ast::*; +use crate::parser::lexer::Lexer; use crate::parser::parser::*; use crate::atom_table::*; @@ -32,9 +33,9 @@ use std::sync::Arc; type SubtermDeque = VecDeque<(usize, usize)>; pub(crate) fn devour_whitespace( - parser: &mut Parser<'_, R>, + lexer: &mut Lexer<'_, R>, ) -> Result { - match parser.lexer.scan_for_layout() { + match lexer.scan_for_layout() { Err(e) if e.is_unexpected_eof() => Ok(true), Err(e) => Err(e), Ok(_) => Ok(false), @@ -80,7 +81,7 @@ impl MachineState { }; inner.add_lines_read(num_lines_read); - write_term_to_heap(&term, &mut self.heap, &self.atom_tbl) + write_term_to_heap(&term, &mut self.heap) } } @@ -295,16 +296,14 @@ impl CharRead for ReadlineStream { pub(crate) fn write_term_to_heap( term: &Term, heap: &mut Heap, - atom_tbl: &AtomTable, ) -> Result { - let term_writer = TermWriter::new(heap, atom_tbl); + let term_writer = TermWriter::new(heap); term_writer.write_term_to_heap(term) } #[derive(Debug)] -struct TermWriter<'a, 'b> { +struct TermWriter<'a> { heap: &'a mut Heap, - atom_tbl: &'b AtomTable, queue: SubtermDeque, var_dict: HeapVarDict, } @@ -315,12 +314,11 @@ pub struct TermWriteResult { pub var_dict: HeapVarDict, } -impl<'a, 'b> TermWriter<'a, 'b> { +impl<'a> TermWriter<'a> { #[inline] - fn new(heap: &'a mut Heap, atom_tbl: &'b AtomTable) -> Self { + fn new(heap: &'a mut Heap) -> Self { TermWriter { heap, - atom_tbl, queue: SubtermDeque::new(), var_dict: HeapVarDict::with_hasher(FxBuildHasher::default()), } @@ -338,25 +336,23 @@ impl<'a, 'b> TermWriter<'a, 'b> { } #[inline] - fn push_stub_addr(&mut self) { - let h = self.heap.len(); - self.heap.push(heap_loc_as_cell!(h)); + fn push_stub_addr(&mut self) -> Result<(), CompilationError> { + let h = self.heap.cell_len(); + self.push_cell(heap_loc_as_cell!(h)) + } + + #[inline] + fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), CompilationError> { + self.heap + .push_cell(cell) + .map_err(CompilationError::FiniteMemoryInHeap) } fn term_as_addr(&mut self, term: &TermRef, h: usize) -> HeapCellValue { match term { &TermRef::Cons(..) => list_loc_as_cell!(h), &TermRef::AnonVar(_) | &TermRef::Var(..) => heap_loc_as_cell!(h), - TermRef::CompleteString(_, _, src) => { - if src.as_str().is_empty() { - empty_list_as_cell!() - } else if self.heap[h].get_tag() == HeapCellValueTag::CStr { - heap_loc_as_cell!(h) - } else { - pstr_loc_as_cell!(h) - } - } - &TermRef::PartialString(..) => pstr_loc_as_cell!(h), + TermRef::PartialString(..) | TermRef::CompleteString(..) => heap_loc_as_cell!(h), &TermRef::Literal(_, _, literal) => HeapCellValue::from(*literal), &TermRef::Clause(_, _, _, subterms) if subterms.is_empty() => heap_loc_as_cell!(h), &TermRef::Clause(..) => str_loc_as_cell!(h), @@ -364,45 +360,45 @@ impl<'a, 'b> TermWriter<'a, 'b> { } fn write_term_to_heap(mut self, term: &Term) -> Result { - let heap_loc = self.heap.len(); + let heap_loc = self.heap.cell_len(); for term in breadth_first_iter(term, RootIterationPolicy::Iterated) { - let h = self.heap.len(); + let h = self.heap.cell_len(); match &term { &TermRef::Cons(Level::Root, ..) => { self.queue.push_back((2, h + 1)); - self.heap.push(list_loc_as_cell!(h + 1)); + self.push_cell(list_loc_as_cell!(h + 1))?; - self.push_stub_addr(); - self.push_stub_addr(); + self.push_stub_addr()?; + self.push_stub_addr()?; continue; } &TermRef::Cons(..) => { self.queue.push_back((2, h)); - self.push_stub_addr(); - self.push_stub_addr(); + self.push_stub_addr()?; + self.push_stub_addr()?; } &TermRef::Clause(Level::Root, _, name, subterms) => { if subterms.len() > MAX_ARITY { return Err(CompilationError::ExceededMaxArity); } - self.heap.push(if subterms.is_empty() { + self.push_cell(if subterms.is_empty() { heap_loc_as_cell!(heap_loc + 1) } else { str_loc_as_cell!(heap_loc + 1) - }); + })?; self.queue.push_back((subterms.len(), h + 2)); let named = atom_as_cell!(name, subterms.len()); - self.heap.push(named); + self.push_cell(named)?; for _ in 0..subterms.len() { - self.push_stub_addr(); + self.push_stub_addr()?; } continue; @@ -411,20 +407,20 @@ impl<'a, 'b> TermWriter<'a, 'b> { self.queue.push_back((subterms.len(), h + 1)); let named = atom_as_cell!(name, subterms.len()); - self.heap.push(named); + self.push_cell(named)?; for _ in 0..subterms.len() { - self.push_stub_addr(); + self.push_stub_addr()?; } } &TermRef::AnonVar(Level::Root) | TermRef::Literal(Level::Root, ..) => { let addr = self.term_as_addr(&term, h); - self.heap.push(addr); + self.push_cell(addr)?; } &TermRef::Var(Level::Root, _, ref var_ptr) => { let addr = self.term_as_addr(&term, h); self.var_dict.insert(VarKey::VarPtr(var_ptr.clone()), addr); - self.heap.push(addr); + self.push_cell(addr)?; } &TermRef::AnonVar(_) => { if let Some((arity, site_h)) = self.queue.pop_front() { @@ -438,25 +434,53 @@ impl<'a, 'b> TermWriter<'a, 'b> { continue; } - TermRef::CompleteString(_, _, src) => { - let src = src.as_str().to_owned(); - put_complete_string(self.heap, &src, self.atom_tbl); + TermRef::CompleteString(lvl, _, src) => { + if let Level::Root = lvl { + self.push_stub_addr()?; + } + + let cell = self + .heap + .allocate_cstr(src) + .map_err(CompilationError::FiniteMemoryInHeap)?; + + let new_h = self.heap.cell_len(); + self.push_cell(cell)?; + + if !matches!(lvl, Level::Root) { + self.modify_head_of_queue(&term, new_h); + } else { + self.heap[h] = cell; + } + + continue; } - &TermRef::PartialString(lvl, _, src, _) => { + TermRef::PartialString(lvl, _, src, _) => { if let Level::Root = lvl { - // Var tags can't refer directly to partial strings, - // so a PStrLoc cell must be pushed. - self.heap.push(pstr_loc_as_cell!(heap_loc + 1)); + self.push_stub_addr()?; } - allocate_pstr(self.heap, src.as_str(), self.atom_tbl); + let cell = self + .heap + .allocate_pstr(src) + .map_err(CompilationError::FiniteMemoryInHeap)?; - let h = self.heap.len(); - self.queue.push_back((1, h - 1)); + let tail_h = self.heap.cell_len(); + self.push_stub_addr()?; - if let Level::Root = lvl { - continue; + if matches!(lvl, Level::Root) { + self.heap[h] = cell; + } else { + self.push_cell(cell)?; + }; + + self.queue.push_back((1, tail_h)); + + if !matches!(lvl, Level::Root) { + self.modify_head_of_queue(&term, tail_h + 1); } + + continue; } TermRef::Var(.., var) => { if let Some((arity, site_h)) = self.queue.pop_front() { diff --git a/src/targets.rs b/src/targets.rs index 4a1ce362..dbb036c2 100644 --- a/src/targets.rs +++ b/src/targets.rs @@ -6,6 +6,8 @@ use crate::instructions::*; use crate::iterators::*; use crate::types::*; +use std::rc::Rc; + pub(crate) struct FactInstruction; pub(crate) struct QueryInstruction; @@ -14,14 +16,14 @@ pub(crate) trait CompilationTarget<'a> { fn iter(term: &'a Term) -> Self::Iterator; - fn to_constant(lvl: Level, literal: Literal, r: RegType) -> Instruction; + fn to_constant(lvl: Level, constant: Literal, r: RegType) -> Instruction; fn to_list(lvl: Level, r: RegType) -> Instruction; fn to_structure(lvl: Level, name: Atom, arity: usize, r: RegType) -> Instruction; fn to_void(num_subterms: usize) -> Instruction; fn is_void_instr(instr: &Instruction) -> bool; - fn to_pstr(lvl: Level, string: Atom, r: RegType, has_tail: bool) -> Instruction; + fn to_pstr(lvl: Level, string: Rc, r: RegType) -> Instruction; fn incr_void_instr(instr: &mut Instruction); @@ -67,8 +69,8 @@ impl<'a> CompilationTarget<'a> for FactInstruction { matches!(instr, &Instruction::UnifyVoid(_)) } - fn to_pstr(lvl: Level, string: Atom, r: RegType, has_tail: bool) -> Instruction { - Instruction::GetPartialString(lvl, string, r, has_tail) + fn to_pstr(lvl: Level, string: Rc, r: RegType) -> Instruction { + Instruction::GetPartialString(lvl, string, r) } fn incr_void_instr(instr: &mut Instruction) { @@ -125,16 +127,12 @@ impl<'a> CompilationTarget<'a> for QueryInstruction { Instruction::PutStructure(name, arity, r) } - fn to_constant(lvl: Level, constant: Literal, reg: RegType) -> Instruction { - Instruction::PutConstant(lvl, HeapCellValue::from(constant), reg) - } - fn to_list(lvl: Level, reg: RegType) -> Instruction { Instruction::PutList(lvl, reg) } - fn to_pstr(lvl: Level, string: Atom, r: RegType, has_tail: bool) -> Instruction { - Instruction::PutPartialString(lvl, string, r, has_tail) + fn to_pstr(lvl: Level, string: Rc, r: RegType) -> Instruction { + Instruction::PutPartialString(lvl, string, r) } fn to_void(subterms: usize) -> Instruction { @@ -155,6 +153,10 @@ impl<'a> CompilationTarget<'a> for QueryInstruction { Instruction::SetConstant(HeapCellValue::from(constant)) } + fn to_constant(lvl: Level, constant: Literal, reg: RegType) -> Instruction { + Instruction::PutConstant(lvl, HeapCellValue::from(constant), reg) + } + fn argument_to_variable(arg: RegType, val: usize) -> Instruction { Instruction::PutVariable(arg, val) } diff --git a/src/types.rs b/src/types.rs index d68daac7..60b3b132 100644 --- a/src/types.rs +++ b/src/types.rs @@ -3,10 +3,12 @@ use crate::arena::*; use crate::atom_table::*; use crate::forms::*; +use crate::machine::heap::*; use crate::machine::machine_indices::*; -use crate::machine::partial_string::PartialString; use crate::machine::streams::*; +use crate::offset_table::*; use crate::parser::ast::Fixnum; +use crate::parser::ast::Literal; use std::cmp::Ordering; use std::convert::TryFrom; @@ -14,60 +16,67 @@ use std::fmt; use std::mem; use std::ops::{Add, Sub, SubAssign}; +use dashu::{Integer, Rational}; + #[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[repr(u8)] #[bits = 6] pub enum HeapCellValueTag { - Str = 0b000011, + Str = 0b000001, Lis = 0b000101, - Var = 0b000111, - StackVar = 0b001001, - AttrVar = 0b001011, - PStrLoc = 0b001101, - PStrOffset = 0b001111, + Var = 0b001011, + StackVar = 0b001101, + AttrVar = 0b010001, + PStrLoc = 0b010011, // constants. Cons = 0b0, - F64 = 0b010001, - Fixnum = 0b010011, - Char = 0b010101, - Atom = 0b010111, - PStr = 0b011001, - CStr = 0b011011, - CutPoint = 0b011111, + F64Offset = 0b010101, + Fixnum = 0b011001, + CodeIndexOffset = 0b011011, + Atom = 0b011111, + CutPoint = 0b011101, + // trail elements. + TrailedHeapVar = 0b100001, + TrailedStackVar = 0b100011, + TrailedAttrVar = 0b100101, + TrailedAttrVarListLink = 0b101001, + TrailedAttachedValue = 0b101011, + TrailedBlackboardEntry = 0b101101, + TrailedBlackboardOffset = 0b110001, } #[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[repr(u8)] #[bits = 6] pub enum HeapCellValueView { - Str = 0b000011, + Str = 0b000001, Lis = 0b000101, - Var = 0b000111, - StackVar = 0b001001, - AttrVar = 0b001011, - PStrLoc = 0b001101, - PStrOffset = 0b001111, + Var = 0b001011, + StackVar = 0b001101, + AttrVar = 0b010001, + PStrLoc = 0b010011, // constants. Cons = 0b0, - F64 = 0b010001, - Fixnum = 0b010011, - Char = 0b010101, - Atom = 0b010111, - PStr = 0b011001, - CStr = 0b011011, - CutPoint = 0b011111, + F64Offset = 0b010101, + Fixnum = 0b011001, + CodeIndexOffset = 0b011011, + Atom = 0b011111, + CutPoint = 0b011101, // trail elements. - TrailedHeapVar = 0b101111, - TrailedStackVar = 0b101011, - TrailedAttrVar = 0b100001, - TrailedAttrVarListLink = 0b100011, - TrailedAttachedValue = 0b100101, - TrailedBlackboardEntry = 0b100111, - TrailedBlackboardOffset = 0b110011, + TrailedHeapVar = 0b100001, + TrailedStackVar = 0b100011, + TrailedAttrVar = 0b100101, + TrailedAttrVarListLink = 0b101001, + TrailedAttachedValue = 0b101011, + TrailedBlackboardEntry = 0b101101, + TrailedBlackboardOffset = 0b110001, } #[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[bits = 1] pub enum ConsPtrMaskTag { Cons = 0b0, + Atom = 0b1, } #[bitfield] @@ -84,7 +93,7 @@ impl ConsPtr { #[inline(always)] pub fn build_with(ptr: *const ArenaHeader, tag: ConsPtrMaskTag) -> Self { ConsPtr::new() - .with_ptr(ptr as *const u8 as u64) + .with_ptr(ptr.expose_provenance() as u64) .with_f(false) .with_m(false) .with_tag(tag) @@ -93,7 +102,7 @@ impl ConsPtr { #[inline(always)] pub fn as_ptr(self) -> *mut u8 { let addr: u64 = self.ptr(); - addr as usize as *mut _ + std::ptr::with_exposed_provenance_mut(addr as usize) } #[inline(always)] @@ -105,9 +114,9 @@ impl ConsPtr { #[derive(BitfieldSpecifier, Copy, Clone, Debug)] #[bits = 6] pub(crate) enum RefTag { - HeapCell = 0b000111, - StackCell = 0b001001, - AttrVar = 0b001011, + HeapCell = 0b001011, + StackCell = 0b001101, + AttrVar = 0b010001, } #[bitfield] @@ -245,15 +254,16 @@ pub struct HeapCellValue { val: B56, f: bool, m: bool, + #[allow(dead_code)] tag: HeapCellValueTag, } impl fmt::Debug for HeapCellValue { fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result { match self.get_tag() { - HeapCellValueTag::F64 => f + HeapCellValueTag::F64Offset => f .debug_struct("HeapCellValue") - .field("tag", &HeapCellValueTag::F64) + .field("tag", &HeapCellValueTag::F64Offset) .field("offset", &self.get_value()) .field("m", &self.m()) .field("f", &self.f()) @@ -279,16 +289,6 @@ impl fmt::Debug for HeapCellValue { .field("f", &self.f()) .finish() } - HeapCellValueTag::PStr => { - let (name, _) = cell_as_atom_cell!(self).get_name_and_arity(); - - f.debug_struct("HeapCellValue") - .field("tag", &HeapCellValueTag::PStr) - .field("contents", &name.as_str()) - .field("m", &self.m()) - .field("f", &self.f()) - .finish() - } tag => f .debug_struct("HeapCellValue") .field("tag", &tag) @@ -300,20 +300,89 @@ impl fmt::Debug for HeapCellValue { } } +impl From for HeapCellValue { + #[inline] + fn from(literal: Literal) -> Self { + match literal { + Literal::Atom(name) => atom_as_cell!(name), + Literal::CodeIndexOffset(idx) => HeapCellValue::from(idx), + Literal::Fixnum(n) => fixnum_as_cell!(n), + Literal::Integer(bigint_ptr) => { + typed_arena_ptr_as_cell!(bigint_ptr) + } + Literal::Rational(bigint_ptr) => { + typed_arena_ptr_as_cell!(bigint_ptr) + } + Literal::F64Offset(f) => HeapCellValue::from(f), + } + } +} + +impl TryFrom for Literal { + type Error = (); + + fn try_from(value: HeapCellValue) -> Result { + read_heap_cell!(value, + (HeapCellValueTag::Atom, (name, arity)) => { + if arity == 0 { + Ok(Literal::Atom(name)) + } else { + Err(()) + } + } + (HeapCellValueTag::Fixnum, n) => { + Ok(Literal::Fixnum(n)) + } + (HeapCellValueTag::F64Offset, f) => { + Ok(Literal::F64Offset(f)) + } + (HeapCellValueTag::CodeIndexOffset, idx) => { + Ok(Literal::CodeIndexOffset(idx)) + } + (HeapCellValueTag::Cons, cons_ptr) => { + match_untyped_arena_ptr!(cons_ptr, + (ArenaHeaderTag::Integer, n) => { + Ok(Literal::Integer(n)) + } + (ArenaHeaderTag::Rational, n) => { + Ok(Literal::Rational(n)) + } + _ => { + Err(()) + } + ) + } + _ => { + Err(()) + } + ) + } +} + impl From> for HeapCellValue where T::Payload: Sized, { #[inline] fn from(arena_ptr: TypedArenaPtr) -> HeapCellValue { - HeapCellValue::from(arena_ptr.header_ptr() as u64) + HeapCellValue::from(arena_ptr.header_ptr().expose_provenance() as u64) } } -impl From for HeapCellValue { +impl From for HeapCellValue { #[inline] - fn from(f64_ptr: F64Ptr) -> HeapCellValue { - HeapCellValue::build_with(HeapCellValueTag::F64, f64_ptr.as_offset().to_u64()) + fn from(f64_offset: F64Offset) -> HeapCellValue { + HeapCellValue::build_with(HeapCellValueTag::F64Offset, f64_offset.to_u64()) + } +} + +impl From for HeapCellValue { + #[inline] + fn from(code_index_offset: CodeIndexOffset) -> HeapCellValue { + HeapCellValue::build_with( + HeapCellValueTag::CodeIndexOffset, + code_index_offset.to_u64(), + ) } } @@ -321,7 +390,7 @@ impl From for HeapCellValue { #[inline(always)] fn from(cons_ptr: ConsPtr) -> HeapCellValue { HeapCellValue::from_bytes( - ConsPtr::from(cons_ptr.as_ptr() as u64) + ConsPtr::from(cons_ptr.as_ptr().expose_provenance() as u64) .with_tag(ConsPtrMaskTag::Cons) .with_m(false) .into_bytes(), @@ -354,19 +423,15 @@ impl HeapCellValue { } #[inline] - pub fn is_string_terminator(mut self, heap: &[HeapCellValue]) -> bool { - use crate::machine::heap::*; - + pub fn is_string_terminator(mut self, heap: &impl SizedHeap) -> bool { loop { return read_heap_cell!(self, (HeapCellValueTag::Atom, (name, arity)) => { name == atom!("[]") && arity == 0 } - (HeapCellValueTag::CStr) => { - true - } (HeapCellValueTag::PStrLoc, h) => { - self = heap[h]; + let HeapStringScan { tail_idx, .. } = heap.scan_slice_to_str(h); + self = heap[tail_idx]; continue; } (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { @@ -379,9 +444,6 @@ impl HeapCellValue { self = cell; continue; } - (HeapCellValueTag::PStrOffset, pstr_offset) => { - heap[pstr_offset].get_tag() == HeapCellValueTag::CStr - } _ => { false } @@ -399,16 +461,12 @@ impl HeapCellValue { | HeapCellValueTag::StackVar | HeapCellValueTag::AttrVar | HeapCellValueTag::PStrLoc - | HeapCellValueTag::PStrOffset ) } #[inline] pub fn as_char(self) -> Option { read_heap_cell!(self, - (HeapCellValueTag::Char, c) => { - Some(c) - } (HeapCellValueTag::Atom, (name, arity)) => { if arity > 0 { return None; @@ -426,11 +484,9 @@ impl HeapCellValue { pub fn is_constant(self) -> bool { match self.get_tag() { HeapCellValueTag::Cons - | HeapCellValueTag::F64 + | HeapCellValueTag::F64Offset | HeapCellValueTag::Fixnum - | HeapCellValueTag::CutPoint - | HeapCellValueTag::Char - | HeapCellValueTag::CStr => true, + | HeapCellValueTag::CutPoint => true, HeapCellValueTag::Atom => cell_as_atom_cell!(self).get_arity() == 0, _ => false, } @@ -442,16 +498,12 @@ impl HeapCellValue { } #[inline] - pub fn is_compound(self, heap: &[HeapCellValue]) -> bool { + pub fn is_compound(self, heap: &Heap) -> bool { match self.get_tag() { HeapCellValueTag::Str => { cell_as_atom_cell!(heap[self.get_value() as usize]).get_arity() > 0 } - HeapCellValueTag::Lis - | HeapCellValueTag::CStr - | HeapCellValueTag::PStr - | HeapCellValueTag::PStrLoc - | HeapCellValueTag::PStrOffset => true, + HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc => true, HeapCellValueTag::Atom => cell_as_atom_cell!(self).get_arity() > 0, _ => false, } @@ -502,6 +554,7 @@ impl HeapCellValue { match self.tag_or_err() { Ok(tag) => tag, Err(_) => match ConsPtr::from_bytes(self.into_bytes()).tag() { + ConsPtrMaskTag::Atom => HeapCellValueTag::Atom, ConsPtrMaskTag::Cons => HeapCellValueTag::Cons, }, } @@ -509,16 +562,8 @@ impl HeapCellValue { #[inline] pub fn to_atom(self) -> Option { - match self.tag() { - HeapCellValueTag::Atom => Some(Atom::from(self.val() << 3)), - _ => None, - } - } - - #[inline] - pub fn to_pstr(self) -> Option { - match self.tag() { - HeapCellValueTag::PStr => Some(PartialString::from(Atom::from(self.val() << 3))), + match self.get_tag() { + HeapCellValueTag::Atom => Some(AtomCell::from_bytes(self.into_bytes()).get_name()), _ => None, } } @@ -531,6 +576,16 @@ impl HeapCellValue { } } + // FIXME: someone that knows this better should check if this can be split into `to_fixnum_unchecked` and `to_cut_point_unchecked` assuming thats always unambigusly knowable + #[inline] + pub unsafe fn to_fixnum_or_cut_point_unchecked(self) -> Fixnum { + debug_assert!(matches!( + self.get_tag(), + HeapCellValueTag::Fixnum | HeapCellValueTag::CutPoint + )); + Fixnum::from_bytes(self.into_bytes()) + } + #[inline] pub fn from_ptr_addr(ptr_bytes: usize) -> Self { HeapCellValue::from_bytes((ptr_bytes as u64).to_ne_bytes()) @@ -593,38 +648,53 @@ impl HeapCellValue { } } - pub fn order_category(self, heap: &[HeapCellValue]) -> Option { - match Number::try_from(self).ok() { - Some(Number::Integer(_)) | Some(Number::Fixnum(_)) | Some(Number::Rational(_)) => { + pub fn order_category(self, heap: &Heap) -> Option { + read_heap_cell!(self, + (HeapCellValueTag::Cons, c) => { + match_untyped_arena_ptr!(c, + (ArenaHeaderTag::Integer, _n) => { + Some(TermOrderCategory::Integer) + } + (ArenaHeaderTag::Rational, _n) => { + Some(TermOrderCategory::Integer) + } + _ => { + None + } + ) + } + (HeapCellValueTag::F64Offset) => { + Some(TermOrderCategory::FloatingPoint) + } + (HeapCellValueTag::Fixnum | HeapCellValueTag::CutPoint) => { Some(TermOrderCategory::Integer) } - Some(Number::Float(_)) => Some(TermOrderCategory::FloatingPoint), - None => match self.get_tag() { - HeapCellValueTag::Var | HeapCellValueTag::StackVar | HeapCellValueTag::AttrVar => { - Some(TermOrderCategory::Variable) - } - HeapCellValueTag::Char => Some(TermOrderCategory::Atom), - HeapCellValueTag::Atom => Some(if cell_as_atom_cell!(self).get_arity() > 0 { + (HeapCellValueTag::Var | HeapCellValueTag::StackVar | HeapCellValueTag::AttrVar) => { + Some(TermOrderCategory::Variable) + } + (HeapCellValueTag::Atom, (_name, arity)) => { + Some(if arity > 0 { TermOrderCategory::Compound } else { TermOrderCategory::Atom - }), - HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc | HeapCellValueTag::CStr => { + }) + } + (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => { + Some(TermOrderCategory::Compound) + } + (HeapCellValueTag::Str, s) => { + let arity = cell_as_atom_cell!(heap[s]).get_arity(); + + if arity == 0 { + Some(TermOrderCategory::Atom) + } else { Some(TermOrderCategory::Compound) } - HeapCellValueTag::Str => { - let value = heap[self.get_value() as usize]; - let arity = cell_as_atom_cell!(value).get_arity(); - - if arity == 0 { - Some(TermOrderCategory::Atom) - } else { - Some(TermOrderCategory::Compound) - } - } - _ => None, - }, - } + } + _ => { + None + } + ) } #[inline(always)] @@ -640,7 +710,7 @@ impl HeapCellValue { } } -const_assert!(mem::size_of::() == 8); +const_assert!(size_of::() == 8); #[bitfield] #[repr(u64)] @@ -666,21 +736,21 @@ const_assert!(mem::size_of::() == 8); impl From<*const ArenaHeader> for UntypedArenaPtr { #[inline] fn from(ptr: *const ArenaHeader) -> UntypedArenaPtr { - UntypedArenaPtr::build_with(ptr as usize) + UntypedArenaPtr::build_with(ptr.expose_provenance()) } } impl From<*const IndexPtr> for UntypedArenaPtr { #[inline] fn from(ptr: *const IndexPtr) -> UntypedArenaPtr { - UntypedArenaPtr::build_with(ptr as usize) + UntypedArenaPtr::build_with(ptr.expose_provenance()) } } impl From for *const ArenaHeader { #[inline] fn from(ptr: UntypedArenaPtr) -> *const ArenaHeader { - ptr.get_ptr() as *const ArenaHeader + ptr.get_ptr().cast::() } } @@ -693,21 +763,21 @@ impl UntypedArenaPtr { #[inline] pub fn get_ptr(self) -> *const u8 { let addr: u64 = self.ptr(); - addr as usize as *const u8 + std::ptr::with_exposed_provenance(addr as usize) } #[inline] pub fn get_tag(self) -> ArenaHeaderTag { unsafe { debug_assert!(!self.get_ptr().is_null()); - let header = *(self.get_ptr() as *const ArenaHeader); + let header = *self.get_ptr().cast::(); header.get_tag() } } #[inline] pub fn payload_offset(self) -> *const u8 { - unsafe { self.get_ptr().add(mem::size_of::()) } + unsafe { self.get_ptr().add(size_of::()) } } /// # Safety @@ -734,12 +804,14 @@ impl Add for HeapCellValue { match self.get_tag() { tag @ HeapCellValueTag::Str | tag @ HeapCellValueTag::Lis - | tag @ HeapCellValueTag::PStrOffset - | tag @ HeapCellValueTag::PStrLoc | tag @ HeapCellValueTag::Var | tag @ HeapCellValueTag::AttrVar => { HeapCellValue::build_with(tag, (self.get_value() as usize + rhs) as u64) } + tag @ HeapCellValueTag::PStrLoc => { + let value = (self.get_value() as usize + heap_index!(rhs)) as u64; + HeapCellValue::build_with(tag, value) + } _ => self, } } @@ -752,12 +824,14 @@ impl Sub for HeapCellValue { match self.get_tag() { tag @ HeapCellValueTag::Str | tag @ HeapCellValueTag::Lis - | tag @ HeapCellValueTag::PStrOffset - | tag @ HeapCellValueTag::PStrLoc | tag @ HeapCellValueTag::Var | tag @ HeapCellValueTag::AttrVar => { HeapCellValue::build_with(tag, (self.get_value() as usize - rhs) as u64) } + tag @ HeapCellValueTag::PStrLoc => { + let value = self.get_value() as usize - heap_index!(rhs); + HeapCellValue::build_with(tag, value as u64) + } _ => self, } } @@ -778,12 +852,15 @@ impl Sub for HeapCellValue { match self.get_tag() { tag @ HeapCellValueTag::Str | tag @ HeapCellValueTag::Lis - | tag @ HeapCellValueTag::PStrOffset - | tag @ HeapCellValueTag::PStrLoc | tag @ HeapCellValueTag::Var | tag @ HeapCellValueTag::AttrVar => { HeapCellValue::build_with(tag, self.get_value() + rhs.unsigned_abs()) } + tag @ HeapCellValueTag::PStrLoc => { + let value = + self.get_value() as usize + heap_index!(rhs.unsigned_abs() as usize); + HeapCellValue::build_with(tag, value as u64) + } _ => self, } } else { diff --git a/src/variable_records.rs b/src/variable_records.rs index b3546f4c..c918067e 100644 --- a/src/variable_records.rs +++ b/src/variable_records.rs @@ -1,3 +1,4 @@ +use crate::forms::GenContext; use crate::parser::ast::*; use bit_set::*; diff --git a/tests-pl/invalid_decl11.pl b/tests-pl/invalid_decl11.pl index 9d4c6193..d099faff 100644 --- a/tests-pl/invalid_decl11.pl +++ b/tests-pl/invalid_decl11.pl @@ -1 +1 @@ -:- op(10, xf, [example, Var]). \ No newline at end of file +:- op(10, xf, [example, Var]). diff --git a/tests-pl/issue2588.pl b/tests-pl/issue2588.pl index 4e87a39a..dbea342e 100644 --- a/tests-pl/issue2588.pl +++ b/tests-pl/issue2588.pl @@ -2,4 +2,4 @@ test :- load_html("Hello!", Es, []), write(Es). -:- initialization(test). \ No newline at end of file +:- initialization(test). diff --git a/tests-pl/issue2949.pl b/tests-pl/issue2949.pl new file mode 100644 index 00000000..901bf031 --- /dev/null +++ b/tests-pl/issue2949.pl @@ -0,0 +1,11 @@ +:- use_module(library(sgml)). + +test :- + load_html("Hello!", Es, []), + write(Es), + load_html("Hello!", Es2, []), + write(Es2), + load_html("