19 Commits

Author SHA1 Message Date
Mark Thom
17b2510480 update version number 2019-11-16 19:18:41 -07:00
Mark Thom
bcfd7cbf8d begin migrating tests to pure prolog, correct bug in toplevel 2019-11-16 19:15:07 -07:00
Mark Thom
cee3dbc453 quote values and goals in equations, eliminate dead code, correct retract/1 2019-11-16 17:15:25 -07:00
Mark Thom
ed985c3cfe move more of the toplevel from rust into prolog 2019-11-16 00:26:15 -07:00
Mark Thom
4ad62e4606 add warnings when initialization goals fail (#168) 2019-10-30 00:25:17 -06:00
Mark Thom
d34811aa36 handle asserts in modules a little better (#223, #224) 2019-10-30 00:14:36 -06:00
Mark Thom
b90d69a45b install asserted predicates into modules from initialization directives (#222) 2019-10-29 00:06:51 -06:00
Mark Thom
81f220a4d2 replace \n\r and \r\n by \n (#221) 2019-10-27 20:50:21 -06:00
Mark Thom
e594ade84d finish #214, add needed ambiguity checks if 0 is the final character 2019-10-27 19:23:35 -06:00
Mark Thom
0dcd9e5805 correct sign/1 (#216) 2019-10-27 14:18:32 -06:00
Mark Thom
67a00e4deb update to prolog_parser v0.8.34 with increased MAX_ARITY (#218) 2019-10-27 13:10:32 -06:00
Mark Thom
e656e7fbae add sign/1 (#216) and gcd/2 (#217) as evaluable functors, update the README 2019-10-27 13:05:05 -06:00
Mark Thom
0fdf0d8d06 correct #215 by fixing bug in ambiguity_check 2019-10-27 12:32:18 -06:00
Mark Thom
7cdbd2f16c add consult/1 and shorthand for consult/1 (#214) 2019-10-27 11:33:49 -06:00
Mark Thom
ccfcc654a5 remove module from toplevel in unqualified use_module 2019-10-26 12:39:42 -06:00
Mark Thom
2eea0528b1 remove all mention of [clear] from the README 2019-10-26 01:35:26 -06:00
Mark Thom
a358431d30 reload files properly (re: #213) 2019-10-26 01:34:41 -06:00
Mark Thom
e9c1556c32 remove scratch comments from clpb.pl 2019-10-22 21:19:01 -06:00
Mark Thom
1b1879a6fa fix attributed variables bug causing weighted_maximum/3 example to omit a variable binding 2019-10-20 14:50:46 -06:00
33 changed files with 702 additions and 4382 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "scryer-prolog"
version = "0.8.114"
version = "0.8.116"
authors = ["Mark Thom <markjordanthom@gmail.com>"]
build = "build.rs"
repository = "https://github.com/mthom/scryer-prolog"
@@ -19,7 +19,7 @@ lazy_static = "1.4.0"
libc = "0.2.62"
nix = "0.15.0"
ordered-float = "0.5.0"
prolog_parser = "0.8.33"
prolog_parser = "0.8.34"
ref_thread_local = "0.0.0"
rug = "1.4.0"
rustyline = "5.0.3"

View File

@@ -50,11 +50,11 @@ Extend Scryer Prolog to include the following, among other features:
- [x] Backtrackable and non-backtrackable global variables via `bb_get/2`
`bb_put/2` (non-backtrackable) and `bb_b_put/2`
(backtrackable).
- [ ] clp(B) and clp($\mathbb{Z}$) as builtin libraries (_in progress_).
- [ ] Streams and predicates for stream control (_in progress_).
- [ ] An incremental compacting garbage collector satisfying the five
properties of "Precise Garbage Collection in Prolog."
- [ ] Mode declarations.
- [ ] Extensions for clp(FD).
## Phase 3
@@ -93,7 +93,7 @@ strings.
First, install the latest stable version of
[Rust](https://www.rust-lang.org/en-US/install.html) using your
preferred method. Then install the latest Scryer Prolog with cargo,
preferred method. Then install Scryer Prolog with cargo,
like so:
```
@@ -101,13 +101,27 @@ $> cargo install scryer-prolog
```
cargo will download and install the libraries Scryer Prolog uses
automatically. You can find the `scryer-prolog` executable in
`~/.cargo/bin`.
automatically from crates.io. You can find the `scryer-prolog`
executable in `~/.cargo/bin`.
Publishing Rust crates to crates.io and pushing to git are entirely
distinct, independent processes, so to be sure you have the latest
commit, it is recommended to clone directly from this git repository,
which can be done as follows:
```
$> git clone https://github.com/mthom/scryer-prolog
$> cd scryer-prolog
$> cargo run [--release]
```
The optional `--release` flag will perform various optimizations,
producing a faster executable.
Note on compatibility: Scryer Prolog should work on Linux, Mac OS X,
and BSD variants on which Rust runs. Windows support hinges on
rustyline and Termion being functional in that environment, which to
my knowledge is not currently the case.
my knowledge is not presently the case.
## Built-in predicates
@@ -120,7 +134,7 @@ The following predicates are built-in to Scryer.
`abs/1`, `sin/1`, `cos/1`, `tan/1`, `asin/1`, `acos/1`,
`atan/1`, `atan2/2`, `log/1`, `exp/1`, `sqrt/1`, `float/1`,
`truncate/1`, `round/1`, `floor/1`, `ceiling/1`, `pi/0`,
`min/1`, `max/1`
`min/1`, `max/1`, `gcd/2`, `sign/1`
* Comparison operators: `>`, `<`, `=<`, `>=`, `=:=`, `=\=`.
* `(:)/2`
* `(@>)/2`
@@ -285,11 +299,6 @@ Note that the values of variables belonging to successful queries are
printed out, on one line each. Uninstantiated variables are denoted by
a number preceded by an underscore (`X = _0` in an example above).
To clear the database, type
```
?- [clear].
```
To quit scryer-prolog, type
```
?- halt.

View File

@@ -159,6 +159,7 @@ impl<'a> ArithmeticEvaluator<'a> {
"round" => Ok(ArithmeticInstruction::Round(a1, t)),
"ceiling" => Ok(ArithmeticInstruction::Ceiling(a1, t)),
"floor" => Ok(ArithmeticInstruction::Floor(a1, t)),
"sign" => Ok(ArithmeticInstruction::Sign(a1, t)),
"\\" => Ok(ArithmeticInstruction::BitwiseComplement(a1, t)),
_ => Err(ArithmeticError::NonEvaluableFunctor(
Constant::Atom(name, None),
@@ -192,6 +193,7 @@ impl<'a> ArithmeticEvaluator<'a> {
"xor" => Ok(ArithmeticInstruction::Xor(a1, a2, t)),
"mod" => Ok(ArithmeticInstruction::Mod(a1, a2, t)),
"rem" => Ok(ArithmeticInstruction::Rem(a1, a2, t)),
"gcd" => Ok(ArithmeticInstruction::Gcd(a1, a2, t)),
"atan2" => Ok(ArithmeticInstruction::ATan2(a1, a2, t)),
_ => Err(ArithmeticError::NonEvaluableFunctor(
Constant::Atom(name, None),

View File

@@ -162,8 +162,7 @@ pub enum SystemClauseType {
AtomChars,
AtomCodes,
AtomLength,
ModuleAssertDynamicPredicateToFront,
ModuleAssertDynamicPredicateToBack,
CallAttributeGoals,
CharCode,
CharsToNumber,
CodesToNumber,
@@ -176,6 +175,7 @@ pub enum SystemClauseType {
EnqueueAttributedVar,
ExpandGoal,
ExpandTerm,
FetchAttributeGoals,
FetchGlobalVar,
FetchGlobalVarWithOffset,
GetChar,
@@ -200,6 +200,8 @@ pub enum SystemClauseType {
InstallSCCCleaner,
InstallInferenceCounter,
LiftedHeapLength,
ModuleAssertDynamicPredicateToFront,
ModuleAssertDynamicPredicateToBack,
ModuleOf,
ModuleRetractClause,
NoSuchPredicate,
@@ -229,8 +231,8 @@ pub enum SystemClauseType {
GetDoubleQuotes,
InstallNewBlock,
Maybe,
RawInputReadChar,
ResetBlock,
ReturnFromAttributeGoals,
ReturnFromVerifyAttr,
SetBall,
SetCutPointByDefault(RegType),
@@ -257,20 +259,12 @@ impl SystemClauseType {
&SystemClauseType::AtomChars => clause_name!("$atom_chars"),
&SystemClauseType::AtomCodes => clause_name!("$atom_codes"),
&SystemClauseType::AtomLength => clause_name!("$atom_length"),
&SystemClauseType::ModuleAssertDynamicPredicateToFront => {
clause_name!("$module_asserta")
}
&SystemClauseType::ModuleAssertDynamicPredicateToBack => {
clause_name!("$module_assertz")
}
&SystemClauseType::CallAttributeGoals => clause_name!("$call_attribute_goals"),
&SystemClauseType::CharCode => clause_name!("$char_code"),
&SystemClauseType::CharsToNumber => clause_name!("$chars_to_number"),
&SystemClauseType::CodesToNumber => clause_name!("$codes_to_number"),
&SystemClauseType::CheckCutPoint => clause_name!("$check_cp"),
&SystemClauseType::REPL(REPLCodePtr::CompileBatch) => clause_name!("$compile_batch"),
&SystemClauseType::REPL(REPLCodePtr::SubmitQueryAndPrintResults) => {
clause_name!("$submit_query_and_print_results")
}
&SystemClauseType::REPL(REPLCodePtr::UseModule) => clause_name!("$use_module"),
&SystemClauseType::REPL(REPLCodePtr::UseQualifiedModule) => {
clause_name!("$use_qualified_module")
@@ -289,6 +283,7 @@ impl SystemClauseType {
&SystemClauseType::EnqueueAttributedVar => clause_name!("$enqueue_attr_var"),
&SystemClauseType::ExpandTerm => clause_name!("$expand_term"),
&SystemClauseType::ExpandGoal => clause_name!("$expand_goal"),
&SystemClauseType::FetchAttributeGoals => clause_name!("$fetch_attribute_goals"),
&SystemClauseType::FetchGlobalVar => clause_name!("$fetch_global_var"),
&SystemClauseType::FetchGlobalVarWithOffset => {
clause_name!("$fetch_global_var_with_offset")
@@ -327,11 +322,18 @@ impl SystemClauseType {
}
&SystemClauseType::LiftedHeapLength => clause_name!("$lh_length"),
&SystemClauseType::Maybe => clause_name!("maybe"),
&SystemClauseType::ModuleAssertDynamicPredicateToFront => {
clause_name!("$module_asserta")
}
&SystemClauseType::ModuleAssertDynamicPredicateToBack => {
clause_name!("$module_assertz")
}
&SystemClauseType::ModuleHeadIsDynamic => clause_name!("$module_head_is_dynamic"),
&SystemClauseType::ModuleOf => clause_name!("$module_of"),
&SystemClauseType::NoSuchPredicate => clause_name!("$no_such_predicate"),
&SystemClauseType::NumberToChars => clause_name!("$number_to_chars"),
&SystemClauseType::NumberToCodes => clause_name!("$number_to_codes"),
&SystemClauseType::RawInputReadChar => clause_name!("$raw_input_read_char"),
&SystemClauseType::RedoAttrVarBindings => clause_name!("$redo_attr_var_bindings"),
&SystemClauseType::RemoveCallPolicyCheck => clause_name!("$remove_call_policy_check"),
&SystemClauseType::RemoveInferenceCounter => clause_name!("$remove_inference_counter"),
@@ -357,9 +359,6 @@ impl SystemClauseType {
&SystemClauseType::ResetGlobalVarAtOffset => clause_name!("$reset_global_var_at_offset"),
&SystemClauseType::RetractClause => clause_name!("$retract_clause"),
&SystemClauseType::ResetBlock => clause_name!("$reset_block"),
&SystemClauseType::ReturnFromAttributeGoals => {
clause_name!("$return_from_attribute_goals")
}
&SystemClauseType::ReturnFromVerifyAttr => clause_name!("$return_from_verify_attr"),
&SystemClauseType::SetBall => clause_name!("$set_ball"),
&SystemClauseType::SetCutPointByDefault(_) => clause_name!("$set_cp_by_default"),
@@ -387,6 +386,7 @@ impl SystemClauseType {
("$module_assertz", 5) => Some(SystemClauseType::ModuleAssertDynamicPredicateToBack),
("$asserta", 4) => Some(SystemClauseType::AssertDynamicPredicateToFront),
("$assertz", 4) => Some(SystemClauseType::AssertDynamicPredicateToBack),
("$call_attribute_goals", 2) => Some(SystemClauseType::CallAttributeGoals),
("$char_code", 2) => Some(SystemClauseType::CharCode),
("$chars_to_number", 2) => Some(SystemClauseType::CharsToNumber),
("$codes_to_number", 2) => Some(SystemClauseType::CodesToNumber),
@@ -404,6 +404,7 @@ impl SystemClauseType {
("$enqueue_attr_var", 1) => Some(SystemClauseType::EnqueueAttributedVar),
("$expand_term", 2) => Some(SystemClauseType::ExpandTerm),
("$expand_goal", 2) => Some(SystemClauseType::ExpandGoal),
("$fetch_attribute_goals", 1) => Some(SystemClauseType::FetchAttributeGoals),
("$fetch_global_var", 2) => Some(SystemClauseType::FetchGlobalVar),
("$fetch_global_var_with_offset", 3) => Some(SystemClauseType::FetchGlobalVarWithOffset),
("$get_char", 1) => Some(SystemClauseType::GetChar),
@@ -449,13 +450,13 @@ impl SystemClauseType {
("$get_current_block", 1) => Some(SystemClauseType::GetCurrentBlock),
("$get_cp", 1) => Some(SystemClauseType::GetCutPoint),
("$install_new_block", 1) => Some(SystemClauseType::InstallNewBlock),
("$raw_input_read_char", 1) => Some(SystemClauseType::RawInputReadChar),
("$read_query_term", 2) => Some(SystemClauseType::ReadQueryTerm),
("$read_term", 2) => Some(SystemClauseType::ReadTerm),
("$reset_block", 1) => Some(SystemClauseType::ResetBlock),
("$reset_global_var_at_key", 1) => Some(SystemClauseType::ResetGlobalVarAtKey),
("$reset_global_var_at_offset", 3) => Some(SystemClauseType::ResetGlobalVarAtOffset),
("$retract_clause", 4) => Some(SystemClauseType::RetractClause),
("$return_from_attribute_goals", 0) => Some(SystemClauseType::ReturnFromAttributeGoals),
("$return_from_verify_attr", 0) => Some(SystemClauseType::ReturnFromVerifyAttr),
("$set_ball", 1) => Some(SystemClauseType::SetBall),
("$set_cp_by_default", 1) => Some(SystemClauseType::SetCutPointByDefault(temp_v!(1))),
@@ -464,9 +465,6 @@ impl SystemClauseType {
("$skip_max_list", 4) => Some(SystemClauseType::SkipMaxList),
("$store_global_var", 2) => Some(SystemClauseType::StoreGlobalVar),
("$store_global_var_with_offset", 2) => Some(SystemClauseType::StoreGlobalVarWithOffset),
("$submit_query_and_print_results", 2) => Some(SystemClauseType::REPL(
REPLCodePtr::SubmitQueryAndPrintResults,
)),
("$term_variables", 2) => Some(SystemClauseType::TermVariables),
("$truncate_lh_to", 1) => Some(SystemClauseType::TruncateLiftedHeapTo),
("$unwind_stack", 0) => Some(SystemClauseType::UnwindStack),

View File

@@ -338,6 +338,7 @@ pub struct Module {
pub user_term_expansions: (Predicate, VecDeque<TopLevel>), // term expansions inherited from the user scope.
pub user_goal_expansions: (Predicate, VecDeque<TopLevel>), // same for goal_expansions.
pub inserted_expansions: bool, // has the module been successfully inserted into toplevel??
pub is_impromptu_module: bool,
}
#[derive(Clone, PartialEq, Eq)]

View File

@@ -329,7 +329,7 @@ pub fn requires_space(atom: &str, op: &str) -> bool {
.next()
.map(|oc| {
if ac == '0' {
oc == 'b' || oc == 'x' || oc == 'o' || oc == '\''
oc == '\'' || oc == '(' || alpha_numeric_char!(oc)
} else if alpha_numeric_char!(ac) {
oc == '(' || alpha_numeric_char!(oc)
} else if graphic_token_char!(ac) {
@@ -625,6 +625,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
iter.stack().last().cloned().and_then(|addr| {
let addr = self.machine_st.store(self.machine_st.deref(addr));
if let Some(var) = self.var_names.get(&addr) {
self.heap_locs.insert(addr.clone(), Rc::new(var.clone()));
}
match self.heap_locs.get(&addr).cloned() {
Some(var) => {
if !self.printed_vars.contains(&addr) {

View File

@@ -189,6 +189,8 @@ pub enum ArithmeticInstruction {
Or(ArithmeticTerm, ArithmeticTerm, usize),
Mod(ArithmeticTerm, ArithmeticTerm, usize),
Rem(ArithmeticTerm, ArithmeticTerm, usize),
Gcd(ArithmeticTerm, ArithmeticTerm, usize),
Sign(ArithmeticTerm, usize),
Cos(ArithmeticTerm, usize),
Sin(ArithmeticTerm, usize),
Tan(ArithmeticTerm, usize),
@@ -314,6 +316,10 @@ impl ArithmeticInstruction {
&ArithmeticInstruction::ATan2(ref at_1, ref at_2, t) => {
arith_instr_bin_functor(h, "rem", at_1, at_2, t)
}
&ArithmeticInstruction::Gcd(ref at_1, ref at_2, t) => {
arith_instr_bin_functor(h, "gcd", at_1, at_2, t)
}
&ArithmeticInstruction::Sign(ref at, t) => arith_instr_unary_functor(h, "sign", at, t),
&ArithmeticInstruction::Cos(ref at, t) => arith_instr_unary_functor(h, "cos", at, t),
&ArithmeticInstruction::Sin(ref at, t) => arith_instr_unary_functor(h, "sin", at, t),
&ArithmeticInstruction::Tan(ref at, t) => arith_instr_unary_functor(h, "tan", at, t),
@@ -358,14 +364,6 @@ pub enum ControlInstruction {
}
impl ControlInstruction {
pub fn is_jump_instr(&self) -> bool {
match self {
&ControlInstruction::CallClause(..) => true,
&ControlInstruction::JmpBy(..) => true,
_ => false,
}
}
pub fn to_functor(&self) -> MachineStub {
match self {
&ControlInstruction::Allocate(num_frames) => {

View File

@@ -150,7 +150,8 @@ comma_errors(G1, G2, B) :- '$call_with_default_policy'(','(G1, G2, B)).
','(!, Atom, B) :- Atom == !, '$set_cp'(B).
','(!, G, B) :- '$set_cp'(B), G.
','(G, CF, B) :- compound(CF),
'$call_with_default_policy'(CF = ','(G1, G2)), !, G,
'$call_with_default_policy'(CF = ','(G1, G2)),
!, G,
'$call_with_default_policy'(comma_errors(G1, G2, B)).
','(G, Atom, B) :- Atom == !, !, G, '$set_cp'(B).
','(G1, G2, _) :- G1, G2.
@@ -561,12 +562,13 @@ assertz(Clause) :-
).
first_match_index([Clause0 | Clauses], Clause1, N0, N) :-
( Clause0 \= Clause1 -> N1 is N0 + 1,
first_match_index(Clauses, Clause1, N1, N)
( Clause0 \= Clause1 ->
N1 is N0 + 1,
first_match_index(Clauses, Clause1, N1, N)
; N0 = N, Clause0 = Clause1
).
retract_clauses([Clause|Clauses0], Head, Body, Name, Arity) :-
retract_clauses([Clause | Clauses0], Head, Body, Name, Arity) :-
functor(VarHead, Name, Arity),
findall((VarHead :- VarBody), clause(VarHead, VarBody), Clauses1),
first_match_index(Clauses1, (Head :- Body), 0, N),
@@ -574,7 +576,7 @@ retract_clauses([Clause|Clauses0], Head, Body, Name, Arity) :-
; true
),
'$retract_clause'(Name, Arity, N, Clauses1).
retract_clauses([_|Clauses0], Head, Body, Name, Arity) :-
retract_clauses([_ | Clauses0], Head, Body, Name, Arity) :-
retract_clauses(Clauses0, Head, Body, Name, Arity).
call_retract(Head, Body, Name, Arity) :-

View File

@@ -1159,7 +1159,7 @@ labeling_var(V) :- V == 1, !.
labeling_var(V) :- domain_error(clpb_variable, V).
variables_in_index_order(Vs0, Vs) :-
maplist(var_with_index, Vs0, IVs0),
maplist(var_with_index, Vs0, IVs0),
keysort(IVs0, IVs),
pairs_values(IVs, Vs).
@@ -1244,8 +1244,7 @@ bdd_count(Node, VNum, Count) :-
bdd_count(High, VNum, HCount),
bdd_pow(Low, V, VNum, LPow),
bdd_pow(High, V, VNum, HPow),
Count0 is LPow*LCount + HPow*HCount,
Count = Count0
Count is LPow*LCount + HPow*HCount
)
).
@@ -1509,7 +1508,7 @@ max_variable_node(Node, V0-N0, V-N) :-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
ands_fusion(Ands0, Ands) :-
maplist(with_variables, Ands0, Pairs0),
maplist(with_variables, Ands0, Pairs0),
keysort(Pairs0, Pairs),
group_pairs_by_key(Pairs, Groups),
pairs_values(Groups, Andss),

View File

@@ -51,4 +51,5 @@ gather_dif_goals([(X \== Y) | Goals]) -->
attribute_goals(X) -->
{ get_atts(X, +dif(Goals)) },
gather_dif_goals(Goals).
gather_dif_goals(Goals),
{ put_atts(X, -dif(_)) }.

View File

@@ -26,6 +26,7 @@ gather_freeze_goals(Attrs, _) -->
!.
gather_freeze_goals([frozen(X) | _], Var) -->
[freeze(Var, X)],
{ put_atts(Var, -frozen(_)) },
!.
gather_freeze_goals([_ | Attrs], Var) -->
gather_freeze_goals(Attrs, Var).

View File

@@ -3,6 +3,7 @@
maplist/4, maplist/5, maplist/6, maplist/7,
maplist/8, maplist/9]).
length(Xs, N) :-
var(N), !,
'$skip_max_list'(M, -1, Xs, Xs0),
@@ -30,17 +31,22 @@ length_rundown([_|Xs], N) :-
N1 is N-1,
length_rundown(Xs, N1).
member(X, [X|_]).
member(X, [_|Xs]) :- member(X, Xs).
select(X, [X|Xs], Xs).
select(X, [Y|Xs], [Y|Ys]) :- select(X, Xs, Ys).
append([], R, R).
append([X|L], R, [X|S]) :- append(L, R, S).
memberchk(X, Xs) :- member(X, Xs), !.
reverse(Xs, Ys) :-
( nonvar(Xs) -> reverse(Xs, Ys, [], Xs)
; reverse(Ys, Xs, [], Ys)
@@ -50,6 +56,7 @@ reverse([], [], YsRev, YsRev).
reverse([X1|Xs], [Y1|Ys], YsPreludeRev, Xss) :-
reverse(Xs, Ys, [Y1|YsPreludeRev], Xss).
maplist(_, []).
maplist(Cont1, [E1|E1s]) :-
call(Cont1, E1),

View File

@@ -1,7 +1,5 @@
use crate::prolog::machine::*;
use indexmap::IndexSet;
use std::vec::IntoIter;
pub static VERIFY_ATTRS: &str = include_str!("attributed_variables.pl");
@@ -101,54 +99,18 @@ impl MachineState {
attr_vars.into_iter()
}
fn populate_project_attr_lists(&mut self) -> (Addr, Addr) {
let mut query_vars = IndexSet::new();
let attr_vars = self.gather_attr_vars_created_since(0);
for (_, addr) in self.heap_locs.iter() {
let iter = self.acyclic_pre_order_iter(addr.clone());
for value in iter {
match value {
HeapCellValue::Addr(Addr::HeapCell(h)) => {
query_vars.insert(Addr::HeapCell(h));
}
HeapCellValue::Addr(Addr::StackCell(fr, sc)) => {
query_vars.insert(Addr::StackCell(fr, sc));
}
HeapCellValue::Addr(Addr::AttrVar(h)) => {
query_vars.insert(Addr::AttrVar(h));
}
_ => {}
};
}
}
let query_var_list = Addr::HeapCell(self.heap.to_list(query_vars.into_iter()));
let attr_var_list = Addr::HeapCell(self.heap.to_list(attr_vars));
(query_var_list, attr_var_list)
}
pub(super) fn verify_attr_interrupt(&mut self, p: usize) {
let rs = MAX_ARITY;
// store temp vars in perm vars slots along with self.b0 and
// self.num_of_args. why self.b0? if we return to a NeckCut
// after finishing the interrupt, it won't work correctly if
// self.b == self.b0. we must change it back when we return,
// as if nothing happened.
self.allocate(rs + 2);
self.allocate(self.num_of_args + 2);
let e = self.e;
self.and_stack[e].interrupt_cp = self.attr_var_init.cp;
for i in 1..rs + 1 {
for i in 1 .. self.num_of_args + 1 {
self.and_stack[e][i] = self[RegType::Temp(i)].clone();
}
self.and_stack[e][rs + 1] = Addr::Con(Constant::Usize(self.b0));
self.and_stack[e][rs + 2] = Addr::Con(Constant::Usize(self.num_of_args));
self.and_stack[e][self.num_of_args + 1] = Addr::Con(Constant::Usize(self.b0));
self.and_stack[e][self.num_of_args + 2] = Addr::Con(Constant::Usize(self.num_of_args));
self.verify_attributes();
@@ -156,57 +118,4 @@ impl MachineState {
self.b0 = self.b;
self.p = CodePtr::Local(LocalCodePtr::DirEntry(p));
}
fn print_attribute_goals_string(&mut self, op_dir: &OpDir) -> String {
let mut attr_goals = mem::replace(&mut self.attr_var_init.attribute_goals, vec![]);
if attr_goals.is_empty() {
return String::from("");
}
attr_goals.sort_unstable_by(|a1, a2| self.compare_term_test(a1, a2));
self.term_dedup(&mut attr_goals);
let mut output = PrinterOutputter::new();
for goal_addr in attr_goals {
let mut printer = HCPrinter::from_heap_locs(&self, op_dir, output);
printer.see_all_locs();
printer.numbervars = false;
printer.quoted = true;
output = printer.print(goal_addr);
output.append(", ");
}
// cut trailing ", "
let output_len = output.len();
output.truncate(output_len - 2);
output.result()
}
}
impl Machine {
pub fn attribute_goals(&mut self) -> String {
let p = self.machine_st.attr_var_init.project_attrs_loc;
let (query_vars, attr_vars) = self.machine_st.populate_project_attr_lists();
self.machine_st.allocate(0);
self.machine_st[temp_v!(1)] = query_vars;
self.machine_st[temp_v!(2)] = attr_vars;
self.machine_st.p = CodePtr::Local(LocalCodePtr::DirEntry(p));
self.machine_st.query_stepper(
&mut self.indices,
&mut self.policies,
&mut self.code_repo,
&mut readline::input_stream(),
);
self.machine_st
.print_attribute_goals_string(&self.indices.op_dir)
}
}

View File

@@ -62,25 +62,34 @@ fn load_module<R: Read>(
stream: ParsingStream<R>,
suppress_warnings: bool,
listing_src: ClauseName,
) -> Result<Option<ClauseName>, SessionError> {
) -> Result<ClauseName, SessionError> {
// follow the operation of compile_user_module, but before
// compiling, check that a module is declared in the file. if not,
// throw an exception.
let mut indices = default_index_store!(wam.indices.atom_tbl.clone());
setup_indices(wam, clause_name!("builtins"), &mut indices)?;
let mut compiler = ListingCompiler::new(
&wam.code_repo,
suppress_warnings,
listing_src,
listing_src.clone(),
);
let results = compiler.gather_items(wam, stream, &mut indices);
let module_name = if let Some(ref module) = &compiler.module {
Some(module.module_decl.name.clone())
module.module_decl.name.clone()
} else {
None
// this impromptu definition (namely, its exports) will be filled out later.
let module_decl = ModuleDecl { name: listing_src, exports: vec![] };
let mut module = Module::new(module_decl, wam.indices.atom_tbl.clone());
let module_name = module.module_decl.name.clone();
module.is_impromptu_module = true;
compiler.module = Some(module);
module_name
};
results.and_then(|results| compile_work_impl(&mut compiler, wam, indices, results))
@@ -97,7 +106,7 @@ fn load_module_from_file(
wam: &mut Machine,
filename: &str,
suppress_warnings: bool,
) -> Result<Option<ClauseName>, SessionError> {
) -> Result<ClauseName, SessionError> {
let path = fix_filename(wam.indices.atom_tbl.clone(), filename)?;
let filename = clause_name!(path.to_string_lossy().to_string(), wam.indices.atom_tbl);
@@ -234,68 +243,6 @@ fn compile_query(
Ok((code, cg.take_vars()))
}
fn compile_decl(
wam: &mut Machine,
compiler: &mut ListingCompiler,
decl: Declaration,
) -> Result<IndexStore, SessionError> {
let flags = wam.machine_flags();
let mut indices = default_index_store!(wam.indices.atom_tbl.clone());
compiler.process_decl(decl, wam, &mut indices, flags)?;
Ok(indices)
}
pub fn compile_term(wam: &mut Machine, packet: TopLevelPacket) -> EvalSession {
match packet {
TopLevelPacket::Query(terms, queue) => {
match compile_query(terms, queue, wam.machine_flags()) {
Ok((code, vars)) => wam.submit_query(code, vars),
Err(e) => EvalSession::from(e),
}
}
TopLevelPacket::Decl(TopLevel::Declaration(decl), _) => {
let mut compiler = ListingCompiler::new(&wam.code_repo, false, clause_name!("user"));
let indices = try_eval_session!(compile_decl(wam, &mut compiler, decl));
try_eval_session!(wam.check_toplevel_code(&indices));
add_toplevel_code(wam, vec![], indices);
EvalSession::EntrySuccess
}
_ => EvalSession::from(SessionError::UserPrompt),
}
}
fn update_module_indices(wam: &Machine, module_name: ClauseName, mut indices: IndexStore) {
match wam.indices.modules.get(&module_name) {
Some(module) => {
let code_dir = mem::replace(&mut indices.code_dir, CodeDir::new());
// replace the "user" module src's in the indices with module_name.
for (key, idx) in code_dir.iter() {
let p = idx.0.borrow().0;
match module.code_dir.get(&key) {
Some(idx) => set_code_index!(idx, p, module_name.clone()),
_ => {}
}
match wam.indices.code_dir.get(&key) {
Some(idx) => {
if idx.0.borrow().1 == module_name {
set_code_index!(idx, p, module_name.clone());
}
}
_ => {}
}
}
}
_ => unreachable!(),
};
}
fn add_hooks_to_mockup(
code_repo: &mut CodeRepo,
hook: CompileTimeHook,
@@ -311,25 +258,21 @@ fn add_hooks_to_mockup(
preds.1.extend(expansions.1.into_iter());
}
fn setup_module_expansions(wam: &mut Machine, module_name: ClauseName) {
match wam.indices.modules.get(&module_name) {
Some(module) => {
let term_expansions = module.term_expansions.clone();
let goal_expansions = module.goal_expansions.clone();
fn setup_module_expansions(wam: &mut Machine, module: &Module) {
let term_expansions = module.term_expansions.clone();
let goal_expansions = module.goal_expansions.clone();
add_hooks_to_mockup(
&mut wam.code_repo,
CompileTimeHook::TermExpansion,
term_expansions,
);
add_hooks_to_mockup(
&mut wam.code_repo,
CompileTimeHook::GoalExpansion,
goal_expansions,
);
}
_ => unreachable!(),
}
add_hooks_to_mockup(
&mut wam.code_repo,
CompileTimeHook::TermExpansion,
term_expansions,
);
add_hooks_to_mockup(
&mut wam.code_repo,
CompileTimeHook::GoalExpansion,
goal_expansions,
);
}
pub(super) fn compile_into_module<R: Read>(
@@ -339,13 +282,21 @@ pub(super) fn compile_into_module<R: Read>(
name: ClauseName,
) -> EvalSession {
let mut indices = default_index_store!(wam.atom_tbl_of(&name));
try_eval_session!(setup_indices(wam, module_name.clone(), &mut indices));
let module = wam.indices.take_module(module_name.clone()).unwrap();
indices.code_dir = module.code_dir.clone();
indices.op_dir = module.op_dir.clone();
indices.atom_tbl = module.atom_tbl.clone();
let mut compiler = ListingCompiler::new(&wam.code_repo, true, module_name.clone());
match compile_into_module_impl(wam, &mut compiler, module_name, src, indices) {
match compile_into_module_impl(wam, &mut compiler, module, src, indices) {
Ok(()) => EvalSession::EntrySuccess,
Err(e) => {
if let Some(module) = compiler.module.take() {
wam.indices.insert_module(module);
}
compiler.drop_expansions(wam.machine_flags(), &mut wam.code_repo);
EvalSession::from(e)
}
@@ -355,29 +306,28 @@ pub(super) fn compile_into_module<R: Read>(
fn compile_into_module_impl<R: Read>(
wam: &mut Machine,
compiler: &mut ListingCompiler,
module_name: ClauseName,
module: Module,
src: ParsingStream<R>,
mut indices: IndexStore,
) -> Result<(), SessionError> {
setup_module_expansions(wam, module_name.clone());
setup_module_expansions(wam, &module);
let module_name = module.module_decl.name.clone();
compiler.module = Some(module);
let flags = wam.machine_flags();
wam.code_repo
.compile_hook(CompileTimeHook::TermExpansion, flags)?;
wam.code_repo
.compile_hook(CompileTimeHook::GoalExpansion, flags)?;
wam.code_repo.compile_hook(CompileTimeHook::TermExpansion, flags)?;
wam.code_repo.compile_hook(CompileTimeHook::GoalExpansion, flags)?;
let results = compiler.gather_items(wam, src, &mut indices)?;
let module_code =
compiler.generate_code(results.worker_results, wam, &mut indices.code_dir, 0)?;
let mut clause_code_generator = ClauseCodeGenerator::new(module_code.len(), module_name.clone());
clause_code_generator.generate_clause_code(&results.dynamic_clause_map, wam)?;
update_module_indices(wam, module_name, indices);
wam.code_repo.code.extend(module_code.into_iter());
add_module_code(wam, compiler.module.take().unwrap(), module_code, indices);
clause_code_generator.add_clause_code(wam, results.dynamic_clause_map);
Ok(compiler.drop_expansions(wam.machine_flags(), &mut wam.code_repo))
@@ -410,6 +360,7 @@ impl ClauseCodeGenerator {
}
}
// compiles the latest version of clause/2.
fn generate_clause_code(
&mut self,
dynamic_clause_map: &DynamicClauseMap,
@@ -491,29 +442,15 @@ pub struct ListingCompiler {
listing_src: ClauseName // a file? a module?
}
fn add_toplevel_code(wam: &mut Machine, code: Code, mut indices: IndexStore) {
let code_dir = mem::replace(&mut indices.code_dir, CodeDir::new());
let op_dir = mem::replace(&mut indices.op_dir, OpDir::new());
wam.add_batched_code(code, code_dir);
wam.add_batched_ops(op_dir);
fn add_toplevel_code(wam: &mut Machine, code: Code, indices: IndexStore) {
wam.add_batched_code(code, indices.code_dir);
wam.add_batched_ops(indices.op_dir);
}
#[inline]
fn add_module_code(wam: &mut Machine, mut module: Module, code: Code, mut indices: IndexStore) {
let code_dir = mem::replace(&mut indices.code_dir, CodeDir::new());
let op_dir = mem::replace(&mut indices.op_dir, OpDir::new());
module.code_dir.extend(code_dir);
module.op_dir.extend(op_dir.into_iter());
for (name, arity) in indices.code_dir.keys().cloned() {
if name.owning_module() == module.module_decl.name {
wam.indices
.dynamic_code_dir
.swap_remove(&(name.owning_module(), name, arity));
}
}
fn add_module_code(wam: &mut Machine, mut module: Module, code: Code, indices: IndexStore) {
module.code_dir.extend(indices.code_dir);
module.op_dir.extend(indices.op_dir.into_iter());
wam.add_module(module, code);
}
@@ -543,9 +480,8 @@ fn load_library(
) -> Result<ClauseName, SessionError> {
match LIBRARIES.borrow().get(name.as_str()) {
Some(code) => {
let module_name = load_module(wam, parsing_stream(code.as_bytes()),
suppress_warnings, name.clone())?;
module_name.ok_or(SessionError::NoModuleDeclaration(name))
load_module(wam, parsing_stream(code.as_bytes()),
suppress_warnings, name.clone())
}
None => Err(SessionError::ModuleNotFound)
}
@@ -848,28 +784,19 @@ impl ListingCompiler {
}
Declaration::UseModule(ModuleSource::File(filename)) => {
let name = load_module_from_file(wam, filename.as_str(), true)?;
if let Some(name) = name {
self.use_module(name, &mut wam.code_repo, flags, &mut wam.indices, indices)
} else {
Ok(())
}
self.use_module(name, &mut wam.code_repo, flags, &mut wam.indices, indices)
}
Declaration::UseQualifiedModule(ModuleSource::File(filename), exports) => {
let name = load_module_from_file(wam, filename.as_str(), true)?;
if let Some(name) = name {
self.use_qualified_module(
name,
&mut wam.code_repo,
flags,
&exports,
&mut wam.indices,
indices,
)
} else {
Ok(())
}
self.use_qualified_module(
name,
&mut wam.code_repo,
flags,
&exports,
&mut wam.indices,
indices,
)
}
Declaration::ModuleInitialization(query_terms, queue) => {
self.initialization_goals.0.extend(query_terms.into_iter());
@@ -901,7 +828,7 @@ impl ListingCompiler {
CodeIndex::dynamic_undefined(self.get_module_name()));
}
&Declaration::Hook(hook, _, ref queue) if self.module.is_none() => worker
.term_stream
.term_stream
.incr_expansion_lens(hook.user_scope(), 1, queue.len()),
&Declaration::Hook(hook, _, ref queue) if !hook.has_module_scope() => {
worker.term_stream.incr_expansion_lens(hook, 1, queue.len())
@@ -996,6 +923,7 @@ fn compile_work_impl(
&mut indices.code_dir,
0
)?;
let toplvl_code = compiler.generate_code(
results.toplevel_results,
wam,
@@ -1004,8 +932,10 @@ fn compile_work_impl(
)?;
if let Some(ref mut module) = &mut compiler.module {
module.user_term_expansions = results.addition_results.take_term_expansions();
module.user_goal_expansions = results.addition_results.take_goal_expansions();
if !module.is_impromptu_module {
module.user_term_expansions = results.addition_results.take_term_expansions();
module.user_goal_expansions = results.addition_results.take_goal_expansions();
}
}
let flags = wam.machine_flags();
@@ -1013,7 +943,13 @@ fn compile_work_impl(
wam.code_repo.compile_hook(CompileTimeHook::UserTermExpansion, flags)?;
wam.code_repo.compile_hook(CompileTimeHook::UserGoalExpansion, flags)?;
if let Some(module) = compiler.module.take() {
if let Some(mut module) = compiler.module.take() {
if module.is_impromptu_module {
module.module_decl.exports = indices.code_dir.keys().cloned()
.filter(|(name, _)| name.owning_module().as_str() != "builtins")
.collect();
}
let mut clause_code_generator =
ClauseCodeGenerator::new(module_code.len() + toplvl_code.len(),
module.module_decl.name.clone());
@@ -1021,9 +957,22 @@ fn compile_work_impl(
wam.check_toplevel_code(&results.toplevel_indices)?;
clause_code_generator.generate_clause_code(&results.dynamic_clause_map, wam)?;
add_module_code(wam, module, module_code, indices);
add_toplevel_code(wam, toplvl_code, results.toplevel_indices);
if let Some(ref module) = wam.indices.modules.swap_remove(&module.module_decl.name) {
wam.indices.remove_module(clause_name!("user"), module);
}
if module.is_impromptu_module {
add_module_code(wam, module, module_code, indices);
let module = wam.indices.take_module(compiler.listing_src.clone()).unwrap();
wam.indices.use_module(&mut wam.code_repo, wam.machine_st.flags, &module)?;
wam.indices.insert_module(module);
} else {
add_module_code(wam, module, module_code, indices);
}
add_toplevel_code(wam, toplvl_code, results.toplevel_indices);
clause_code_generator.add_clause_code(wam, results.dynamic_clause_map);
} else {
add_non_module_code(
@@ -1039,7 +988,10 @@ fn compile_work_impl(
)?;
if init_goal_code.len() > 0 {
wam.run_init_code(init_goal_code);
if !wam.run_init_code(init_goal_code) {
println!("Warning: initialization goal for {} failed",
compiler.listing_src);
}
}
if !compiler.suppress_warnings {

View File

@@ -30,7 +30,7 @@ impl Machine {
match module.as_str() {
"user" => compile_user_module(self, src, true, user_src),
_ => compile_into_module(self, module, src, name)
_ => compile_into_module(self, module, src, name)
}
}
None => compile_user_module(self, src, true, user_src),
@@ -76,16 +76,27 @@ impl Machine {
output.result()
}
fn make_undefined(&mut self, name: ClauseName, arity: usize) {
if let Some(idx) = self.indices.code_dir.get(&(name, arity)) {
set_code_index!(idx, IndexPtr::DynamicUndefined, clause_name!("user"));
}
}
fn make_undefined_in_module(&mut self, module_name: ClauseName, name: ClauseName, arity: usize) {
if let Some(idx) = self.indices.code_dir.get(&(name, arity)) {
if idx.module_name() == module_name {
set_code_index!(idx, IndexPtr::DynamicUndefined, clause_name!("user"));
}
}
}
fn abolish_dynamic_clause(&mut self, name: RegType, arity: RegType) {
let (name, arity) = self.get_predicate_key(name, arity);
if let Some(idx) = self.indices.code_dir.get(&(name.clone(), arity)) {
set_code_index!(idx, IndexPtr::DynamicUndefined, clause_name!("user"));
}
self.make_undefined(name.clone(), arity);
self.indices.remove_code_index((name.clone(), arity));
self.indices
.remove_clause_subsection(name.owning_module(), name, arity);
self.indices.remove_clause_subsection(name.owning_module(), name, arity);
}
fn abolish_dynamic_clause_in_module(&mut self, name: RegType, arity: RegType, module: RegType) {
@@ -106,15 +117,10 @@ impl Machine {
_ => unreachable!(),
};
if let Some(idx) = self.indices.code_dir.get(&(name.clone(), arity)) {
if idx.module_name() == module_name {
set_code_index!(idx, IndexPtr::DynamicUndefined, clause_name!("user"));
}
}
self.make_undefined_in_module(module_name.clone(), name.clone(), arity);
self.indices.remove_code_index((name.clone(), arity));
self.indices
.remove_clause_subsection(module_name, name, arity);
self.indices.remove_clause_subsection(module_name, name, arity);
}
fn handle_eval_result_from_dynamic_compile(
@@ -210,6 +216,10 @@ impl Machine {
let mut addrs = VecDeque::from(addrs);
addrs.remove(index);
if addrs.is_empty() {
self.make_undefined(name.clone(), arity);
}
self.print_new_dynamic_clause(addrs, name.clone(), arity)
}
Err(err) => return self.machine_st.throw_exception(err),
@@ -239,6 +249,10 @@ impl Machine {
let mut addrs = VecDeque::from(addrs);
addrs.remove(index);
if addrs.is_empty() {
self.make_undefined(name.clone(), arity);
}
self.print_new_dynamic_clause(addrs, name.clone(), arity)
}
Err(err) => return self.machine_st.throw_exception(err),

View File

@@ -143,9 +143,6 @@ impl MachineError {
"private_procedure",
clause_name!("module_does_not_exist"),
),
SessionError::NoModuleDeclaration(name) => {
Self::existence_error(h, ExistenceError::Module(name))
}
SessionError::OpIsInfixAndPostFix(op) => {
Self::permission_error(PermissionError::Create, "operator", op)
}
@@ -538,18 +535,13 @@ pub enum SessionError {
ModuleDoesNotContainExport,
ModuleNotFound,
NamelessEntry,
NoModuleDeclaration(ClauseName),
OpIsInfixAndPostFix(ClauseName),
ParserError(ParserError),
UserPrompt,
}
pub enum EvalSession {
EntrySuccess,
Error(SessionError),
InitialQuerySuccess(AllocVarDict),
QueryFailure,
SubsequentQuerySuccess,
}
impl From<SessionError> for EvalSession {

View File

@@ -300,7 +300,6 @@ pub enum DynamicTransactionType {
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
pub enum REPLCodePtr {
CompileBatch,
SubmitQueryAndPrintResults,
UseModule,
UseQualifiedModule,
UseModuleFromFile,
@@ -468,13 +467,7 @@ pub struct IndexStore {
pub(super) op_dir: OpDir,
}
impl IndexStore {
pub fn reset_global_variable_offsets(&mut self) {
for (_, ref mut offset) in self.global_variables.values_mut() {
*offset = None;
}
}
impl IndexStore {
pub fn predicate_exists(
&self,
name: ClauseName,

View File

@@ -318,7 +318,7 @@ impl MachineState {
Ok(codes)
}
fn call_at_index(&mut self, arity: usize, p: usize) {
pub(super) fn call_at_index(&mut self, arity: usize, p: usize) {
self.cp.assign_if_local(self.p.clone() + 1);
self.num_of_args = arity;
self.b0 = self.b;
@@ -706,7 +706,7 @@ pub(crate) trait CallPolicy: Any {
&BuiltInClauseType::Nl => {
let mut stdout = stdout();
write!(stdout, "\n\r").unwrap();
write!(stdout, "\n").unwrap();
stdout.flush().unwrap();
return_from_clause!(machine_st.last_call, machine_st)
}

View File

@@ -6,7 +6,6 @@ use crate::prolog::arithmetic::*;
use crate::prolog::clause_types::*;
use crate::prolog::forms::*;
use crate::prolog::heap_iter::*;
use crate::prolog::heap_print::*;
use crate::prolog::instructions::*;
use crate::prolog::machine::INTERRUPT;
use crate::prolog::machine::and_stack::*;
@@ -27,7 +26,6 @@ use indexmap::{IndexMap, IndexSet};
use std::cmp::{max, min, Ordering};
use std::f64;
use std::mem;
use std::rc::Rc;
macro_rules! try_numeric_result {
($s: ident, $e: expr, $caller: expr) => {{
@@ -208,39 +206,6 @@ impl MachineState {
}
}
pub(super) fn print_var_eq<Outputter>(
&self,
var: Rc<Var>,
addr: Addr,
op_dir: &OpDir,
mut output: Outputter,
) -> Outputter
where
Outputter: HCValueOutputter,
{
let orig_len = output.len();
output.begin_new_var();
output.append(var.as_str());
output.append(" = ");
let mut printer = HCPrinter::from_heap_locs(&self, op_dir, output);
printer.numbervars = false;
printer.quoted = true;
let mut output = printer.print(addr);
let bad_ending = format!("= {}", &var);
if output.ends_with(&bad_ending) {
output.truncate(orig_len);
}
output
}
pub(super) fn unify_strings(
&mut self,
pdl: &mut Vec<Addr>,
@@ -754,7 +719,7 @@ impl MachineState {
offset += 1;
} else {
self.trail[i - offset] = self.trail[i];
},
},
TrailRef::Ref(Ref::StackCell(fr, _)) => {
let fr_gi = self.and_stack[fr].global_index;
let b_gi = if !self.or_stack.is_empty() {
@@ -775,7 +740,7 @@ impl MachineState {
self.tr -= offset;
self.trail.truncate(self.tr);
}
#[inline]
fn write_char_to_string(&mut self, s: &mut StringList, c: char) -> bool {
self.pstr_trail(s.clone());
@@ -940,6 +905,7 @@ impl MachineState {
"mod" => interms.push(Number::Integer(self.modulus(a1, a2)?)),
"rem" => interms.push(Number::Integer(self.remainder(a1, a2)?)),
"atan2" => interms.push(Number::Float(OrderedFloat(self.atan2(a1, a2)?))),
"gcd" => interms.push(Number::Integer(self.gcd(a1, a2)?)),
_ => {
return Err(self.error_form(MachineError::instantiation_error(), caller))
}
@@ -967,6 +933,7 @@ impl MachineState {
"ceiling" => interms.push(Number::Integer(self.ceiling(a1))),
"floor" => interms.push(Number::Integer(self.floor(a1))),
"\\" => interms.push(Number::Integer(self.bitwise_complement(a1)?)),
"sign" => interms.push(Number::Integer(self.sign(a1))),
_ => {
return Err(self.error_form(MachineError::instantiation_error(), caller))
}
@@ -1099,6 +1066,26 @@ impl MachineState {
}
}
fn gcd(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
match (n1, n2) {
(Number::Integer(n1), Number::Integer(n2)) => {
Ok(n1.gcd(&n2))
}
(Number::Float(f), _) | (_, Number::Float(f)) => {
let n = Addr::Con(Constant::Float(f));
let stub = MachineError::functor_stub(clause_name!("gcd"), 2);
Err(self.error_form(MachineError::type_error(ValidType::Integer, n), stub))
}
(Number::Rational(r), _) | (_, Number::Rational(r)) => {
let n = Addr::Con(Constant::Rational(r));
let stub = MachineError::functor_stub(clause_name!("gcd"), 2);
Err(self.error_form(MachineError::type_error(ValidType::Integer, n), stub))
}
}
}
fn float_pow(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
let f1 = result_f(&n1, rnd_f);
let f2 = result_f(&n2, rnd_f);
@@ -1355,6 +1342,16 @@ impl MachineState {
}
}
fn sign(&self, n: Number) -> Integer {
if n.is_positive() {
Integer::from(1)
} else if n.is_negative() {
Integer::from(-1)
} else {
Integer::from(0)
}
}
fn remainder(&self, n1: Number, n2: Number) -> Result<Integer, MachineStub> {
let stub = MachineError::functor_stub(clause_name!("(rem)"), 2);
@@ -1440,6 +1437,13 @@ impl MachineState {
self.interms[t - 1] = try_or_fail!(self, self.int_pow(n1, n2));
self.p += 1;
}
&ArithmeticInstruction::Gcd(ref a1, ref a2, t) => {
let n1 = try_or_fail!(self, self.get_number(a1));
let n2 = try_or_fail!(self, self.get_number(a2));
self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.gcd(n1, n2)));
self.p += 1;
}
&ArithmeticInstruction::Pow(ref a1, ref a2, t) => {
let n1 = try_or_fail!(self, self.get_number(a1));
let n2 = try_or_fail!(self, self.get_number(a2));
@@ -1477,6 +1481,12 @@ impl MachineState {
self.interms[t - 1] = n1.abs();
self.p += 1;
}
&ArithmeticInstruction::Sign(ref a1, t) => {
let n = try_or_fail!(self, self.get_number(a1));
self.interms[t - 1] = Number::Integer(self.sign(n));
self.p += 1;
}
&ArithmeticInstruction::Neg(ref a1, t) => {
let n1 = try_or_fail!(self, self.get_number(a1));
@@ -3231,6 +3241,8 @@ impl MachineState {
)
),
};
self.last_call = false;
}
pub(super) fn execute_ctrl_instr(
@@ -3266,7 +3278,7 @@ impl MachineState {
self.b0 = self.b;
self.p += offset;
}
&ControlInstruction::Proceed => self.p = CodePtr::Local(self.cp.clone()),
&ControlInstruction::Proceed => self.p = CodePtr::Local(self.cp.clone())
};
}

View File

@@ -2,13 +2,11 @@ use prolog_parser::ast::*;
use prolog_parser::tabled_rc::*;
use crate::prolog::clause_types::*;
use crate::prolog::fixtures::*;
use crate::prolog::forms::*;
use crate::prolog::heap_print::*;
use crate::prolog::instructions::*;
use crate::prolog::machine::heap::Heap;
use crate::prolog::read::*;
use crate::prolog::write::{next_keypress, ContinueResult};
mod and_stack;
mod attributed_variables;
@@ -36,21 +34,18 @@ use crate::prolog::machine::machine_errors::*;
use crate::prolog::machine::machine_indices::*;
use crate::prolog::machine::machine_state::*;
use crate::prolog::machine::modules::*;
use crate::prolog::machine::toplevel::stream_to_toplevel;
use crate::prolog::read::PrologStream;
use indexmap::IndexMap;
use std::collections::VecDeque;
use std::fs::File;
use std::io::{stdout, Read, Write};
use std::io::Read;
use std::mem;
use std::ops::Index;
use std::rc::Rc;
use std::sync::atomic::AtomicBool;
use termion::raw::IntoRawMode;
pub struct MachinePolicies {
call_policy: Box<dyn CallPolicy>,
cut_policy: Box<dyn CutPolicy>,
@@ -128,7 +123,7 @@ impl SubModuleUser for IndexStore {
fn insert_dir_entry(&mut self, name: ClauseName, arity: usize, idx: CodeIndex) {
if let Some(ref code_idx) = self.code_dir.get(&(name.clone(), arity)) {
if !code_idx.is_undefined() {
println!("warning: overwriting {}/{}", &name, arity);
println!("Warning: overwriting {}/{}", &name, arity);
}
let (p, module_name) = idx.0.borrow().clone();
@@ -184,7 +179,8 @@ impl Machine {
Ok(p) => {
self.machine_st.attr_var_init.verify_attrs_loc = p;
}
Err(_) => panic!("Machine::compile_special_forms() failed at VERIFY_ATTRS"),
Err(_) =>
panic!("Machine::compile_special_forms() failed at VERIFY_ATTRS"),
}
match compile_special_form(self, parsing_stream(PROJECT_ATTRS.as_bytes()), project_attrs_src)
@@ -192,14 +188,28 @@ impl Machine {
Ok(p) => {
self.machine_st.attr_var_init.project_attrs_loc = p;
}
Err(e) => panic!("Machine::compile_special_forms() failed at PROJECT_ATTRS: {}", e),
Err(e) =>
panic!("Machine::compile_special_forms() failed at PROJECT_ATTRS: {}", e),
}
}
fn compile_top_level(&mut self) {
fn compile_top_level(&mut self) -> Result<(), SessionError>
{
self.toplevel_idx = self.code_repo.code.len();
compile_user_module(self, parsing_stream(TOPLEVEL.as_bytes()),
true, clause_name!("toplevel.pl"));
if let Some(module) = self.indices.take_module(clause_name!("$toplevel")) {
self.indices.use_module(
&mut self.code_repo,
self.machine_st.flags,
&module,
)?;
Ok(self.indices.insert_module(module))
} else {
Err(SessionError::ModuleNotFound)
}
}
fn compile_scryerrc(&mut self) {
@@ -228,14 +238,17 @@ impl Machine {
self.machine_st.reset();
}
pub fn run_init_code(&mut self, code: Code) {
pub fn run_init_code(&mut self, code: Code) -> bool {
let old_machine_st = self.sink_to_snapshot();
self.machine_st.reset();
self.machine_st.reset();
self.code_repo.cached_query = code;
self.run_query(&AllocVarDict::new());
self.run_query();
let result = self.machine_st.fail;
self.absorb_snapshot(old_machine_st);
!result
}
pub fn run_top_level(&mut self) {
@@ -256,7 +269,7 @@ impl Machine {
self.machine_st[temp_v!(1)] = list_addr;
self.machine_st.p = CodePtr::Local(LocalCodePtr::DirEntry(self.toplevel_idx));
self.run_query(&AllocVarDict::new());
self.run_query();
}
pub fn new(prolog_stream: PrologStream) -> Self {
@@ -291,7 +304,10 @@ impl Machine {
compile_user_module(&mut wam, parsing_stream(SI.as_bytes()), true,
clause_name!("si"));
wam.compile_top_level();
if wam.compile_top_level().is_err() {
panic!("Loading '$toplevel' module failed");
}
wam.compile_scryerrc();
wam
@@ -373,17 +389,6 @@ impl Machine {
self.code_repo.code.extend(code.into_iter());
}
pub fn submit_query(&mut self, code: Code, alloc_locs: AllocVarDict) -> EvalSession {
self.code_repo.cached_query = code;
self.run_query(&alloc_locs);
if self.machine_st.fail {
EvalSession::QueryFailure
} else {
EvalSession::InitialQuerySuccess(alloc_locs)
}
}
fn throw_session_error(&mut self, err: SessionError, key: PredicateKey) {
let h = self.machine_st.heap.h;
@@ -443,29 +448,27 @@ impl Machine {
let load_result = match to_src(name) {
ModuleSource::Library(name) =>
if !self.indices.modules.contains_key(&name) {
load_library(self, name, false).map(Some)
if let Some(module) = self.indices.take_module(name.clone()) {
self.indices.remove_module(clause_name!("user"), &module);
self.indices.modules.insert(name.clone(), module);
Ok(name)
} else {
Ok(Some(name))
load_library(self, name, false)
},
ModuleSource::File(name) =>
load_module_from_file(self, name.as_str(), false)
};
let result = load_result.and_then(|name|
if let Some(name) = name {
let module = self.indices.take_module(name).unwrap();
let result = load_result.and_then(|name| {
let module = self.indices.take_module(name.clone()).unwrap();
// remove previous exports.
self.indices.remove_module(clause_name!("user"), &module);
self.indices.use_module(&mut self.code_repo, self.machine_st.flags,
&module)?;
Ok(self.indices.insert_module(module))
} else {
Ok(())
if !module.is_impromptu_module {
self.indices.use_module(&mut self.code_repo, self.machine_st.flags, &module)?;
}
);
Ok(self.indices.insert_module(module))
});
self.code_repo.cached_query = cached_query;
@@ -490,31 +493,30 @@ impl Machine {
let load_result = match to_src(name) {
ModuleSource::Library(name) =>
if !self.indices.modules.contains_key(&name) {
load_library(self, name, false).map(Some)
if let Some(module) = self.indices.take_module(name.clone()) {
self.indices.remove_module(clause_name!("user"), &module);
self.indices.modules.insert(name.clone(), module);
Ok(name)
} else {
Ok(Some(name))
load_library(self, name, false)
},
ModuleSource::File(name) =>
load_module_from_file(self, name.as_str(), false)
};
let result = load_result.and_then(|name|
if let Some(name) = name {
let module = self.indices.take_module(name).unwrap();
let result = load_result.and_then(|name| {
let module = self.indices.take_module(name.clone()).unwrap();
// remove previous exports.
self.indices.remove_module(clause_name!("user"), &module);
if !module.is_impromptu_module {
self.indices.use_qualified_module(&mut self.code_repo,
self.machine_st.flags,
&module,
&exports)?;
Ok(self.indices.insert_module(module))
} else {
Ok(())
}
);
Ok(self.indices.insert_module(module))
});
self.code_repo.cached_query = cached_query;
@@ -535,62 +537,6 @@ impl Machine {
self.throw_session_error(e, (clause_name!("repl"), 0));
}
}
REPLCodePtr::SubmitQueryAndPrintResults => {
let term = self.machine_st[temp_v!(1)].clone();
let stub = MachineError::functor_stub(clause_name!("repl"), 0);
let s = match self.machine_st.try_from_list(temp_v!(2), stub) {
Ok(addrs) => {
let mut var_dict = HeapVarDict::new();
for addr in addrs {
match addr {
Addr::Str(s) => {
let var_atom = match self.machine_st.heap[s + 1].as_addr(s + 1)
{
Addr::Con(Constant::Atom(var_atom, _)) => {
Rc::new(var_atom.to_string())
}
_ => unreachable!(),
};
let var_addr = self.machine_st.heap[s + 2].as_addr(s + 2);
var_dict.insert(var_atom, var_addr);
}
_ => unreachable!(),
};
}
self.machine_st.heap_locs = var_dict;
let term_output = self.machine_st.print_query(term, &self.indices.op_dir);
term_output.result()
}
Err(err_stub) => {
self.machine_st.throw_exception(err_stub);
return;
}
};
let stream = parsing_stream(s.as_bytes());
let snapshot = self.sink_to_snapshot();
let policies = mem::replace(&mut self.policies, MachinePolicies::new());
self.machine_st.reset();
self.machine_st.heap = mem::replace(
&mut self.inner_heap,
Heap::with_capacity(0),
);
let result = match stream_to_toplevel(stream, self) {
Ok(packet) => compile_term(self, packet),
Err(e) => EvalSession::from(e),
};
self.handle_eval_session(result, snapshot);
self.indices.reset_global_variable_offsets();
self.policies = policies;
}
REPLCodePtr::UseModule =>
self.use_module(ModuleSource::Library),
REPLCodePtr::UseModuleFromFile =>
@@ -606,7 +552,7 @@ impl Machine {
fn sink_to_snapshot(&mut self) -> MachineState {
let mut snapshot = MachineState::with_capacity(0);
snapshot.hb = self.machine_st.hb;
snapshot.e = self.machine_st.e;
snapshot.b = self.machine_st.b;
@@ -614,6 +560,12 @@ impl Machine {
snapshot.s = self.machine_st.s;
snapshot.tr = self.machine_st.tr;
snapshot.pstr_tr = self.machine_st.pstr_tr;
snapshot.p = self.machine_st.p.clone();
snapshot.cp = self.machine_st.cp;
snapshot.attr_var_init = mem::replace(
&mut self.machine_st.attr_var_init,
AttrVarInitializer::new(0, 0)
);
snapshot.num_of_args = self.machine_st.num_of_args;
snapshot.fail = self.machine_st.fail;
@@ -627,12 +579,13 @@ impl Machine {
snapshot.block = self.machine_st.block;
snapshot.ball = self.machine_st.ball.take();
snapshot.heap_locs = mem::replace(&mut self.machine_st.heap_locs, IndexMap::new());
snapshot.lifted_heap = mem::replace(&mut self.machine_st.lifted_heap, vec![]);
snapshot
}
fn absorb_snapshot(&mut self, mut snapshot: MachineState) {
fn absorb_snapshot(&mut self, mut snapshot: MachineState) {
self.machine_st.hb = snapshot.hb;
self.machine_st.e = snapshot.e;
self.machine_st.b = snapshot.b;
@@ -640,6 +593,9 @@ impl Machine {
self.machine_st.s = snapshot.s;
self.machine_st.tr = snapshot.tr;
self.machine_st.pstr_tr = snapshot.pstr_tr;
self.machine_st.p = snapshot.p;
self.machine_st.cp = snapshot.cp;
self.machine_st.attr_var_init = snapshot.attr_var_init;
self.machine_st.num_of_args = snapshot.num_of_args;
self.machine_st.fail = snapshot.fail;
@@ -648,7 +604,7 @@ impl Machine {
self.inner_heap = self.machine_st.heap.take();
self.inner_heap.truncate(0);
self.machine_st.heap = snapshot.heap.take();
self.machine_st.mode = snapshot.mode;
self.machine_st.and_stack = snapshot.and_stack.take();
@@ -657,160 +613,15 @@ impl Machine {
self.machine_st.block = snapshot.block;
self.machine_st.ball = snapshot.ball.take();
self.machine_st.heap_locs = mem::replace(&mut snapshot.heap_locs, IndexMap::new());
self.machine_st.lifted_heap = mem::replace(&mut snapshot.lifted_heap, vec![]);
}
fn propagate_exception_to_toplevel(&mut self, snapshot: MachineState) {
let ball = self.machine_st.ball.take();
self.absorb_snapshot(snapshot);
self.machine_st.ball = ball;
let h = self.machine_st.heap.h;
let stub = self.machine_st.ball.copy_and_align(h);
self.machine_st.throw_exception(stub);
return;
}
fn handle_eval_session(&mut self, result: EvalSession, snapshot: MachineState) {
match result {
EvalSession::InitialQuerySuccess(alloc_locs) => loop {
let bindings = {
let output = PrinterOutputter::new();
self.toplevel_heap_view(output).result()
};
let attr_goals = self.attribute_goals();
if !(self.machine_st.b > 0) {
if bindings.is_empty() {
let space = if requires_space(&attr_goals, ".") {
" "
} else {
""
};
if !attr_goals.is_empty() {
println!("{}{}.", attr_goals, space);
} else {
println!("true.");
}
self.absorb_snapshot(snapshot);
return;
}
} else if bindings.is_empty() && attr_goals.is_empty() {
print!("true");
stdout().flush().unwrap();
}
if !attr_goals.is_empty() {
if bindings.is_empty() {
print!("{}", attr_goals);
} else {
print!("{}, {}", bindings, attr_goals);
}
} else if !bindings.is_empty() {
print!("{}", bindings);
}
if self.machine_st.b > 0 {
let keypress = {
let mut raw_stdout = stdout().into_raw_mode().unwrap();
raw_stdout.flush().unwrap();
next_keypress()
};
let result = match keypress {
ContinueResult::ContinueQuery => {
print!(" ;\r\n");
self.continue_query(&alloc_locs)
}
ContinueResult::Conclude => {
print!(" ...\r\n");
self.absorb_snapshot(snapshot);
return;
}
};
match result {
EvalSession::QueryFailure => {
if self.machine_st.ball.stub.len() > 0 {
self.propagate_exception_to_toplevel(snapshot);
return;
} else {
print!("false.\r\n");
self.absorb_snapshot(snapshot);
return;
}
}
EvalSession::Error(err) => {
self.absorb_snapshot(snapshot);
self.throw_session_error(err, (clause_name!("repl"), 0));
return;
}
_ => {}
}
} else {
if bindings.is_empty() && attr_goals.is_empty() {
print!("true.\r\n");
} else {
let space = if !attr_goals.is_empty() {
if requires_space(&attr_goals, ".") {
" "
} else {
""
}
} else {
if requires_space(&bindings, ".") {
" "
} else {
""
}
};
print!("{}.\r\n", space);
}
break;
}
},
EvalSession::Error(err) => {
self.absorb_snapshot(snapshot);
self.throw_session_error(err, (clause_name!("repl"), 0));
return;
}
EvalSession::QueryFailure =>
if self.machine_st.ball.stub.len() > 0 {
return self.propagate_exception_to_toplevel(snapshot);
} else {
println!("false.");
},
_ => println!("true.")
}
self.absorb_snapshot(snapshot);
}
pub(super) fn run_query(&mut self, alloc_locs: &AllocVarDict) {
pub(super) fn run_query(&mut self) {
self.machine_st.cp = LocalCodePtr::TopLevel(0, self.code_repo.size_of_cached_query());
let end_ptr = CodePtr::Local(self.machine_st.cp);
while self.machine_st.p < end_ptr {
if let CodePtr::Local(LocalCodePtr::TopLevel(mut cn, p)) = self.machine_st.p {
match &self.code_repo[LocalCodePtr::TopLevel(cn, p)] {
&Line::Control(ref ctrl_instr) if ctrl_instr.is_jump_instr() => {
self.machine_st.record_var_places(cn, alloc_locs);
cn += 1;
}
_ => {}
}
self.machine_st.p = top_level_code_ptr!(cn, p);
}
self.machine_st.query_stepper(
&mut self.indices,
&mut self.policies,
@@ -829,63 +640,18 @@ impl Machine {
self.dynamic_transaction(trans_type, p);
if let CodePtr::Local(LocalCodePtr::TopLevel(_, 0)) = self.machine_st.p {
if self.machine_st.heap_locs.is_empty() {
self.machine_st.record_var_places(0, alloc_locs);
}
self.code_repo.cached_query = cached_query;
break;
}
self.code_repo.cached_query = cached_query;
}
_ => {
if self.machine_st.heap_locs.is_empty() {
self.machine_st.record_var_places(0, alloc_locs);
}
break;
}
_ =>
break
};
}
}
pub fn continue_query(&mut self, alloc_locs: &AllocVarDict) -> EvalSession {
if !self.or_stack_is_empty() {
let b = self.machine_st.b - 1;
self.machine_st.p = self.machine_st.or_stack[b].bp.clone();
if let CodePtr::Local(LocalCodePtr::TopLevel(_, 0)) = self.machine_st.p {
self.machine_st.fail = true;
return EvalSession::QueryFailure;
}
self.run_query(alloc_locs);
if self.machine_st.fail {
EvalSession::QueryFailure
} else {
EvalSession::SubsequentQuerySuccess
}
} else {
EvalSession::QueryFailure
}
}
pub fn toplevel_heap_view<Outputter>(&self, mut output: Outputter) -> Outputter
where
Outputter: HCValueOutputter,
{
for (var, addr) in self.machine_st.heap_locs.iter() {
let addr = self.machine_st.store(self.machine_st.deref(addr.clone()));
output = self
.machine_st
.print_var_eq(var.clone(), addr, &self.indices.op_dir, output);
}
output
}
#[cfg(test)]
pub fn test_heap_view<Outputter>(&self, mut output: Outputter) -> Outputter
where
@@ -902,63 +668,9 @@ impl Machine {
output
}
pub fn or_stack_is_empty(&self) -> bool {
self.machine_st.b == 0
}
}
impl MachineState {
fn record_var_places(&mut self, chunk_num: usize, alloc_locs: &AllocVarDict) {
for (var, var_data) in alloc_locs {
match var_data {
&VarData::Perm(p) if p > 0 => {
if !self.heap_locs.contains_key(var) {
let e = self.e;
let r = var_data.as_reg_type().reg_num();
let addr = self.and_stack[e][r].clone();
self.heap_locs.insert(var.clone(), addr);
}
}
&VarData::Temp(cn, _, _) if cn == chunk_num => {
let r = var_data.as_reg_type();
if r.reg_num() != 0 {
let addr = self[r].clone();
self.heap_locs.insert(var.clone(), addr);
}
}
_ => {}
}
}
}
fn print_query(&mut self, addr: Addr, op_dir: &OpDir) -> PrinterOutputter {
let flags = self.flags;
let mut output = {
self.flags = MachineFlags {
double_quotes: DoubleQuotes::Atom,
};
let output = PrinterOutputter::new();
let mut printer = HCPrinter::from_heap_locs(&self, op_dir, output);
printer.quoted = true;
printer.numbervars = false;
printer.drop_toplevel_spec();
printer.see_all_locs();
printer.print(addr)
};
self.flags = flags;
output.append(".");
output
}
fn dispatch_instr(
&mut self,
instr: &Line,
@@ -1117,7 +829,7 @@ impl MachineState {
let instigating_p = CodePtr::Local(self.attr_var_init.instigating_p);
let instigating_instr = code_repo.lookup_instr(false, &instigating_p).unwrap();
if instigating_instr.as_ref().is_head_instr() {
if !instigating_instr.as_ref().is_head_instr() {
let cp = self.p.local();
self.run_verify_attr_interrupt(cp);
} else if !self.verify_attr_stepper(indices, policies, code_repo, prolog_stream) {

View File

@@ -22,6 +22,7 @@ impl Module {
code_dir: CodeDir::new(),
op_dir: default_op_dir(),
inserted_expansions: false,
is_impromptu_module: false,
}
}
@@ -162,7 +163,7 @@ pub trait SubModuleUser {
self.insert_dir_entry(name, arity, code_data.clone());
true
} else {
found_op
found_op || submodule.is_impromptu_module
}
}

View File

@@ -1,10 +1,9 @@
driver(QueryVars, AttrVars) :-
'$attribute_goals_driver'(QueryVars, AttrVars) :-
gather_modules(AttrVars, Modules0, _),
sort(Modules0, Modules),
call_project_attributes(Modules, QueryVars, AttrVars),
call_attribute_goals(Modules, call_query_var_goals, QueryVars),
call_attribute_goals(Modules, call_attr_var_goals, AttrVars),
'$return_from_attribute_goals'.
call_attribute_goals(Modules, call_attr_var_goals, AttrVars).
enqueue_goals(Goals0) :-
nonvar(Goals0),

View File

@@ -22,11 +22,34 @@ use crate::ref_thread_local::RefThreadLocal;
use indexmap::{IndexMap, IndexSet};
use std::collections::VecDeque;
use std::io::{stdout, Write};
use std::io::{stdin, stdout, Write};
use std::iter::once;
use std::mem;
use std::rc::Rc;
use crate::termion::event::Key;
use crate::termion::input::TermRead;
use crate::termion::raw::IntoRawMode;
pub enum ContinueResult {
ContinueQuery,
Conclude,
}
pub fn next_keypress() -> ContinueResult {
let stdin = stdin();
for c in stdin.keys() {
match c.unwrap() {
Key::Char(' ') | Key::Char(';') => return ContinueResult::ContinueQuery,
Key::Char('.') => return ContinueResult::Conclude,
_ => {}
}
}
ContinueResult::Conclude
}
struct BrentAlgState {
hare: usize,
tortoise: usize,
@@ -716,6 +739,17 @@ impl MachineState {
self.unify(a2, Addr::Con(Constant::Integer(len)));
}
&SystemClauseType::CallAttributeGoals => {
let p = self.attr_var_init.project_attrs_loc;
if self.last_call {
self.execute_at_index(2, p);
} else {
self.call_at_index(2, p);
}
return Ok(());
}
&SystemClauseType::CharsToNumber => {
let stub = MachineError::functor_stub(clause_name!("number_chars"), 2);
@@ -1329,6 +1363,17 @@ impl MachineState {
&SystemClauseType::TruncateIfNoLiftedHeapGrowth => {
self.truncate_if_no_lifted_heap_diff(|_| Addr::Con(Constant::EmptyList))
}
&SystemClauseType::FetchAttributeGoals => {
let mut attr_goals = mem::replace(&mut self.attr_var_init.attribute_goals, vec![]);
attr_goals.sort_unstable_by(|a1, a2| self.compare_term_test(a1, a2));
self.term_dedup(&mut attr_goals);
let attr_goals = Addr::HeapCell(self.heap.to_list(attr_goals.into_iter()));
let target = self[temp_v!(1)].clone();
self.unify(attr_goals, target);
}
&SystemClauseType::GetAttributedVariableList => {
let attr_var = self.store(self.deref(self[temp_v!(1)].clone()));
let attr_var_list = match attr_var {
@@ -1370,6 +1415,18 @@ impl MachineState {
self.unify(var_list_addr, list_addr);
}
Addr::Con(Constant::Integer(n)) => {
if let Some(b) = n.to_usize() {
let iter = self.gather_attr_vars_created_since(b);
let var_list_addr = Addr::HeapCell(self.heap.to_list(iter));
let list_addr = self[temp_v!(2)].clone();
self.unify(var_list_addr, list_addr);
} else {
self.fail = true;
}
}
_ => self.fail = true,
}
}
@@ -1685,11 +1742,6 @@ impl MachineState {
self.p = CodePtr::DynamicTransaction(trans_type, p);
return Ok(());
}
&SystemClauseType::ReturnFromAttributeGoals => {
self.deallocate();
self.p = CodePtr::Local(LocalCodePtr::TopLevel(0, 0));
return Ok(());
}
&SystemClauseType::ReturnFromVerifyAttr => {
let e = self.e;
let frame_len = self.and_stack[e].len();
@@ -1839,6 +1891,21 @@ impl MachineState {
&SystemClauseType::InstallNewBlock => {
self.install_new_block(temp_v!(1));
}
&SystemClauseType::RawInputReadChar => {
let keypress = {
let mut raw_stdout = stdout().into_raw_mode().unwrap();
raw_stdout.flush().unwrap();
next_keypress()
};
let c = match keypress {
ContinueResult::ContinueQuery => ';',
ContinueResult::Conclude => '.'
};
let target = self[temp_v!(1)].clone();
self.unify(Addr::Con(Constant::Char(c)), target);
}
&SystemClauseType::ReadQueryTerm => {
readline::set_prompt(true);
let result = self.read_term(current_input_stream, indices);

View File

@@ -136,11 +136,6 @@ impl<'a, R: Read> TermStream<'a, R> {
pub fn top_level_terms(&mut self) -> Vec<(Term, usize, usize)> {
mem::replace(&mut self.top_level_terms, vec![])
}
#[inline]
pub fn add_to_top(&mut self, buf: &str) {
self.parser.add_to_top(buf);
}
#[inline]
pub fn incr_expansion_lens(&mut self, hook: CompileTimeHook, len: usize, queue_len: usize) {

View File

@@ -303,13 +303,6 @@ fn is_consistent(tl: &TopLevel, clauses: &Vec<PredicateClause>) -> bool {
}
}
fn deque_to_packet(head: TopLevel, deque: VecDeque<TopLevel>) -> TopLevelPacket {
match head {
TopLevel::Query(query) => TopLevelPacket::Query(query, deque),
tl => TopLevelPacket::Decl(tl, deque),
}
}
fn merge_clauses(tls: &mut VecDeque<TopLevel>) -> Result<TopLevel, ParserError> {
let mut clauses: Vec<PredicateClause> = vec![];
@@ -496,11 +489,6 @@ fn setup_declaration<'a, 'b, 'c, R: Read>(
}
}
pub enum TopLevelPacket {
Query(Vec<QueryTerm>, VecDeque<TopLevel>),
Decl(TopLevel, VecDeque<TopLevel>),
}
struct RelationWorker {
flags: MachineFlags,
dynamic_clauses: Vec<(Term, Term)>, // Head, Body.
@@ -902,39 +890,6 @@ impl RelationWorker {
}
}
pub fn stream_to_toplevel<R: Read>(
mut buffer: ParsingStream<R>,
wam: &mut Machine,
) -> Result<TopLevelPacket, SessionError> {
let flags = wam.machine_flags();
let mut term_stream = TermStream::new(
&mut buffer,
wam.indices.atom_tbl(),
wam.machine_flags(),
wam,
);
term_stream.add_to_top("?- ");
let term = term_stream.read_term(&OpDir::new())?;
let mut code_dir = CodeDir::new();
let line_num = term_stream.line_num();
let col_num = term_stream.col_num();
let mut rel_worker = RelationWorker::new(flags, line_num, col_num);
let mut indices = CompositeIndices::new(
&mut term_stream,
IndexSource::TermStream,
Some(IndexSource::Local(&mut code_dir))
);
let tl = rel_worker.try_term_to_tl(&mut indices, term, true)?;
let queue = rel_worker.parse_queue(&mut indices)?;
Ok(deque_to_packet(tl, queue))
}
pub type DynamicClauseMap = IndexMap<(ClauseName, usize), Vec<(Term, Term)>>;
pub struct TopLevelBatchWorker<'a, R: Read> {

View File

@@ -262,12 +262,6 @@ macro_rules! put_constant {
};
}
macro_rules! top_level_code_ptr {
($p:expr, $q_sz:expr) => {
CodePtr::Local(LocalCodePtr::TopLevel($p, $q_sz))
};
}
macro_rules! get_level_and_unify {
($r: expr) => {
Line::Cut(CutInstruction::GetLevelAndUnify($r))

View File

@@ -1,8 +1,4 @@
/*
* inserting the modules should not result in the insertion of
* code. this is because they're already loaded by this point -- see
* Machine::new.
*/
:- module('$toplevel', ['$repl'/1, consult/1, use_module/1, use_module/2]).
:- use_module(library(lists)).
:- use_module(library(si)).
@@ -27,12 +23,88 @@
% make '$compile_batch', a system routine, callable.
'$$compile_batch' :- '$compile_batch'.
'$instruction_match'([user], []) :-
!, catch('$$compile_batch', E, '$print_exception_with_check'(E)).
'$instruction_match'([Item], []) :-
( atom(Item) -> !,
( Item == user ->
catch('$$compile_batch', E, '$print_exception_with_check'(E))
; consult(Item)
)
; catch(throw(error(type_error(atom, Item), repl/0)),
'$print_exception_with_check'(E))
).
'$instruction_match'(Term, VarList) :-
'$submit_query_and_print_results'(Term, VarList),
!.
'$submit_query_and_print_results'(Term0, VarList) :-
( expand_goals(Term0, Term) -> true
; Term = Term0
),
( '$get_b_value'(B), call(Term), '$write_eqs_and_read_input'(B, VarList), !
; write('false.'), nl
).
'$write_goal'(G, VarList) :-
( G = (Var = Value) ->
write(Var),
write(' = '),
write_term(Value, [quoted(true), variable_names(VarList)])
; G == [] ->
write('true')
; write_term(G, [quoted(true), variable_names(VarList)])
).
'$write_eq'((G1, G2), VarList) :-
!,
'$write_goal'(G1, VarList),
write(', '),
'$write_eq'(G2, VarList).
'$write_eq'(G, VarList) :-
'$write_goal'(G, VarList).
'$write_eqs_and_read_input'(B, VarList) :-
sort(VarList, SortedVarList),
'$get_b_value'(B0),
'$gather_goals'(SortedVarList, VarList, Goals),
( B0 == B ->
( Goals == [] ->
write('true.'), nl
; thread_goals(Goals, ThreadedGoals, (',')),
'$write_eq'(ThreadedGoals, VarList),
write(' .'),
nl
)
; repeat,
thread_goals(Goals, ThreadedGoals, (',')),
'$write_eq'(ThreadedGoals, VarList),
'$raw_input_read_char'(C),
( C == (';'), !,
write(' ;'), nl, false
; C == ('.'), !,
write(' ...'), nl
)
).
'$gather_query_vars'([_ = Var | Vars], QueryVars) :-
( var(Var) ->
QueryVars = [Var | QueryVars1],
'$gather_query_vars'(Vars, QueryVars1)
; '$gather_query_vars'(Vars, QueryVars)
).
'$gather_query_vars'([], []).
'$gather_goals'([], VarList, Goals) :-
'$get_attr_var_queue_beyond'(0, AttrVars),
'$gather_query_vars'(VarList, QueryVars),
'$call_attribute_goals'(QueryVars, AttrVars),
'$fetch_attribute_goals'(Goals).
'$gather_goals'([Var = Value | Pairs], VarList, Goals) :-
( nonvar(Value) ->
Goals = [Var = Value | Goals0],
'$gather_goals'(Pairs, VarList, Goals0)
; '$gather_goals'(Pairs, VarList, Goals)
).
'$print_exception'(E) :-
write_term('caught: ', [quoted(false)]),
writeq(E),
@@ -61,6 +133,11 @@
; throw(error(instantiation_error, Source))
).
consult(Item) :-
( atom(Item) -> use_module(Item)
; throw(error(type_error(atom, Item), consult/1))
).
use_module(Module) :-
( nonvar(Module) ->
( Module = library(Filename) -> '$use_module'(Filename)
@@ -90,40 +167,50 @@ user:term_expansion(Term0, (:- initialization(ExpandedGoals))) :-
expand_goals(Goals, ExpandedGoals),
Goals \== ExpandedGoals.
expand_goals(Goals, ExpandedGoals) :-
nonvar(Goals),
expand_goals(UnexpandedGoals, ExpandedGoals) :-
nonvar(UnexpandedGoals),
var(ExpandedGoals),
( expand_goal(UnexpandedGoals, Goals) -> true
; Goals = UnexpandedGoals
),
( Goals = (Goal0, Goals0) ->
( expand_goal(Goal0, Goal1) ->
Expanded = true,
expand_goals(Goals0, Goals1),
thread_goals(Goal1, ExpandedGoals, Goals1)
thread_goals(Goal1, ExpandedGoals, Goals1, (','))
; expand_goals(Goals0, Goals1),
ExpandedGoals = (Goal0, Goals1)
)
; expand_goal(Goals, ExpandedGoals0) ->
thread_goals(ExpandedGoals0, ExpandedGoals)
; Goals = (Goals0 -> Goals1) ->
expand_goals(Goals0, ExpandedGoals0),
expand_goals(Goals1, ExpandedGoals1),
ExpandedGoals = (ExpandedGoals0 -> ExpandedGoals1)
; Goals = (Goals0 ; Goals1) ->
expand_goals(Goals0, ExpandedGoals0),
expand_goals(Goals1, ExpandedGoals1),
ExpandedGoals = (ExpandedGoals0 ; ExpandedGoals1)
; thread_goals(Goals, ExpandedGoals, (','))
; Goals = ExpandedGoals
).
thread_goals(Goals0, Goals1, Hole) :-
thread_goals(Goals0, Goals1, Hole, Functor) :-
nonvar(Goals0),
( Goals0 = [G | Gs] ->
( Gs == [] ->
Goals1 = (G, Hole)
; Goals1 = (G, Goals2),
thread_goals(Gs, Goals2, Hole)
Goals1 =.. [Functor, G, Hole]
; Goals1 =.. [Functor, G, Goals2],
thread_goals(Gs, Goals2, Hole, Functor)
)
; Goals1 = (Goals0, Hole)
; Goals1 =.. [Functor, Goals0, Hole]
).
thread_goals(Goals0, Goals1) :-
thread_goals(Goals0, Goals1, Functor) :-
nonvar(Goals0),
( Goals0 = [G | Gs] ->
( Gs = [] ->
Goals1 = G
; Goals1 = (G, Goals2),
thread_goals(Gs, Goals2)
; Goals1 =.. [Functor, G, Goals2],
thread_goals(Gs, Goals2, Functor)
)
; Goals1 = Goals0
).

View File

@@ -4,11 +4,7 @@ use crate::prolog::instructions::*;
use crate::prolog::machine::machine_errors::*;
use crate::prolog::machine::machine_indices::*;
use termion::event::Key;
use termion::input::TermRead;
use std::fmt;
use std::io::stdin;
impl fmt::Display for LocalCodePtr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -29,10 +25,8 @@ impl fmt::Display for LocalCodePtr {
impl fmt::Display for REPLCodePtr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
REPLCodePtr::CompileBatch => write!(f, "REPLCodePtr::CompileBatch"),
REPLCodePtr::SubmitQueryAndPrintResults => {
write!(f, "REPLCodePtr::SubmitQueryAndPrintResults")
}
REPLCodePtr::CompileBatch =>
write!(f, "REPLCodePtr::CompileBatch"),
REPLCodePtr::UseModule =>
write!(f, "REPLCodePtr::UseModule"),
REPLCodePtr::UseQualifiedModule =>
@@ -268,9 +262,6 @@ impl fmt::Display for SessionError {
&SessionError::ModuleDoesNotContainExport => {
write!(f, "module does not contain claimed export.")
}
&SessionError::NoModuleDeclaration(ref name) => {
write!(f, "file {}.pl lacks an expected module declaration.", name)
}
&SessionError::OpIsInfixAndPostFix(_) => {
write!(f, "cannot define an op to be both postfix and infix.")
}
@@ -278,7 +269,6 @@ impl fmt::Display for SessionError {
write!(f, "the predicate head is not an atom or clause.")
}
&SessionError::ParserError(ref e) => write!(f, "syntax_error({})", e.as_str()),
&SessionError::UserPrompt => write!(f, "enter predicate at [user] prompt"),
}
}
}
@@ -355,6 +345,9 @@ impl fmt::Display for ArithmeticInstruction {
&ArithmeticInstruction::RDiv(ref a1, ref a2, ref t) => {
write!(f, "rdiv {}, {}, @{}", a1, a2, t)
}
&ArithmeticInstruction::Gcd(ref a1, ref a2, ref t) => {
write!(f, "gcd {}, {}, @{}", a1, a2, t)
}
&ArithmeticInstruction::Shl(ref a1, ref a2, ref t) => {
write!(f, "shl {}, {}, @{}", a1, a2, t)
}
@@ -380,6 +373,7 @@ impl fmt::Display for ArithmeticInstruction {
write!(f, "atan2 {}, {}, @{}", a1, a2, t)
}
&ArithmeticInstruction::Plus(ref a, ref t) => write!(f, "plus {}, @{}", a, t),
&ArithmeticInstruction::Sign(ref a, ref t) => write!(f, "sign {}, @{}", a, t),
&ArithmeticInstruction::Neg(ref a, ref t) => write!(f, "neg {}, @{}", a, t),
&ArithmeticInstruction::Cos(ref a, ref t) => write!(f, "cos {}, @{}", a, t),
&ArithmeticInstruction::Sin(ref a, ref t) => write!(f, "sin {}, @{}", a, t),
@@ -421,22 +415,3 @@ impl fmt::Display for Level {
}
}
}
pub enum ContinueResult {
ContinueQuery,
Conclude,
}
pub fn next_keypress() -> ContinueResult {
let stdin = stdin();
for c in stdin.keys() {
match c.unwrap() {
Key::Char(' ') | Key::Char(';') => return ContinueResult::ContinueQuery,
Key::Char('.') => return ContinueResult::Conclude,
_ => {}
}
}
ContinueResult::Conclude
}

File diff suppressed because it is too large Load Diff

64
src/tests/builtins.pl Normal file
View File

@@ -0,0 +1,64 @@
:- use_module(library(lists)).
test_queries_on_builtins :-
\+ atom(_),
atom(a),
\+ atom("string"),
atom([]),
\+ atom(1),
\+ atom(0),
\+ atom(0.0),
\+ atom([a,b,c]),
\+ atom(atop(the_trees)),
\+ atomic(_),
atomic(a),
atomic("string"),
atomic([]),
atomic(1),
atomic(0),
atomic(0.0),
\+ atomic([a,b,c]),
\+ atomic(atop(the_trees)),
( var(X), X = 3, atomic(X) ),
\+ ( var(X), X = 3, var(X) ),
arg(1, f(a,b,c,d), a),
arg(2, f(a,b,c,d), b),
arg(3, f(a,b,c,d), c),
arg(4, f(a,b,c,d), d),
catch(arg(_, f, _), error(instantiation_error, _), true),
\+ arg(1, f(arg, not_arg, not_arg), not_arg),
arg(2, f(arg, not_arg, not_arg), not_arg),
arg(3, f(arg, not_arg, not_arg), not_arg),
functor(f(a,b,c), f, 3),
catch(functor(_,"sdf",3),error(type_error(atom,[s,d,f]),_),true),
f(1,2,3) =.. [f,1,2,3],
length([a,b,c], 3),
copy_term([[[[X,Y],Y],X]],[[[[Z,V],V],Z]]),
\+ ( X = g(X,Y), Y = f(X), copy_term(Y,g(Z)) ),
(X = g(X,Y), Y = f(X), copy_term(Y,f(Z))),
float(3.14159269),
\+ float(3),
\+ float("sdfsa"),
\+ float(structure(functor)),
\+ float([1,2,3]),
\+ float([1,2,_]),
\+ (X is 3 rdiv 4, float(X)),
( X is 3 rdiv 4, rational(X) ),
\+ rational(3),
\+ rational(f(_)),
\+ rational("sdfa"),
\+ rational(atom),
\+ rational(structure(f)),
\+ rational([1,2,3]),
\+ rational([1,2,_]),
compound(functor(compound)),
compound(f(_)),
compound([1,2,3]),
\+ compound([]),
\+ compound(3.14159269),
\+ compound(3),
\+ compound("sdfsa"),
\+ compound(atom).
:- initialization(test_queries_on_builtins).

26
src/tests/facts.pl Normal file
View File

@@ -0,0 +1,26 @@
:- dynamic(p/2).
:- dynamic(p/3).
p(Z, Z).
clouds(are, nice).
p(Z, h(Z, W), f(W)).
test_queries_on_facts :-
findall(Z, p(Z, Z), [Z]),
findall(Z, p(Z, z), [z]),
findall(Z, p(Z, w), [w]),
\+ p(z, w),
p(w, w),
\+ clouds(Z, Z),
findall(Z, clouds(are, Z), [nice]),
\+ p(z, h(z, z), f(w)),
p(z, h(z, w), f(w)),
findall(W, p(z, h(z, W), f(w)), [w]),
findall(Z, p(Z, h(Z, w), f(Z)), [w]),
\+ p(z, h(Z, w), f(Z)),
retract(p(_,_,_)),
assertz(p(Z, h(Z, W), f(W))),
p(f(f(a)), h(f(f(a)), f(a)), f(f(a))),
retract(p(Z, h(Z, W), f(W))).
:- initialization(test_queries_on_facts).

37
src/tests/predicates.pl Normal file
View File

@@ -0,0 +1,37 @@
:- dynamic(p/2).
:- dynamic(p/3).
:- dynamic(q/1).
p(_, a).
p(b, _).
test_queries_on_predicates :-
findall(Y, p(x, Y), [a]),
findall(X, p(X, a), [_,b]),
findall(X, p(b, X), [a,_]),
findall(X, p(X, X), [a,b]),
p(b, a),
\+ p(a, b),
retract(p(_,a)),
retract(p(b,_)),
assertz(p(_, _,a)),
assertz(p(_,a,_)),
assertz(p(_,_,a)),
findall(X, p(c,d,X), [a,a]),
findall(X, p(a,a,a), [a,a,a]),
\+ p(b,c,d),
findall(., retract(p(_,_,_)), _),
assertz(p(_, a)),
assertz(q(z)),
findall(Y, p(_,Y), [a]),
p(x,a),
p(_,a),
\+ p(_,b),
assertz((p(X, Y) :- q(Z), p(X, X))),
once(p(X,b)),
retract((p(X, Y) :- q(Z), p(X, X))),
retract(q(z)).
:- initialization(test_queries_on_predicates).

55
src/tests/rules.pl Normal file
View File

@@ -0,0 +1,55 @@
:- dynamic(p/3).
:- dynamic(p/2).
:- dynamic(q/2).
:- dynamic(r/2).
:- dynamic(r/1).
:- dynamic(h/1).
p(X, Y) :- q(X, Z), r(Z, Y).
q(q, s).
r(s, t).
test_queries_on_rules :-
findall([X,Y], p(X, Y), [[q, t]]),
p(q, t),
\+ p(t, q),
findall(T, p(q, T), [t]),
\+ p(t, t),
retract((p(X,Y) :- q(X,Z), r(Z, Y))),
retract(q(_,_)),
assertz((p(X,_) :- q(f(f(X)), _), r(_, _))),
assertz(q(f(f(X)), r)),
p(_,_),
retract(q(_,_)),
assertz(q(f(f(x)), r)),
findall(X, p(X,_), [x]),
retract((p(X,_) :- q(f(f(X)), _), r(_, _))),
retract(q(_,_)),
assertz((p(X, Y) :- q(X, Y), r(X, Y))),
assertz(q(s, t)),
retract(r(_,_)),
assertz((r(X, Y) :- r(a))),
assertz(r(a)),
findall([X,Y], p(X, Y), [[s,t]]),
\+ p(t, _),
findall(T, p(s, T), [t]),
findall(S, p(S, t), [s]),
assertz((p(f(f(a), g(b), X), g(b), h) :- q(X, Y))),
retract(q(_,_)),
assertz(q(_,_)),
findall([X,Y,Z], p(f(X, Y, Z), g(b), h), [[f(a), g(b), _]]),
\+ p(f(X, g(_), Z), g(Z), X),
findall([X,Y,Z], p(f(X, g(Y), Z), g(Z), h), [[f(a), b, b]]),
findall([X,Y,Z], p(Z, Y, X), [[h, g(b), f(f(a),g(b),_)]]),
findall([X,Y,Z], p(f(X, Y, Z), Y, h), [[f(a), g(b), _]]),
retract((p(X, Y) :- q(X, Y), r(X, Y))),
retract((p(f(f(a), g(b), X), g(b), h) :- q(X, _))),
assertz((p(_, f(_, Y, _)) :- h(Y))),
assertz(h(y)),
findall(Y, p(_, f(_, Y, _)), [y]).
p(_, f(_, y, _)),
\+ p(_, f(_, z, _)),
retract((p(_, f(_, Y, _)) :- h(Y))).
:- initialization(test_queries_on_rules).