Merge branch 'master' into http-server
This commit is contained in:
35
Cargo.lock
generated
35
Cargo.lock
generated
@@ -265,16 +265,6 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dirs"
|
|
||||||
version = "2.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if 0.1.10",
|
|
||||||
"dirs-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs"
|
name = "dirs"
|
||||||
version = "3.0.1"
|
version = "3.0.1"
|
||||||
@@ -284,6 +274,16 @@ dependencies = [
|
|||||||
"dirs-sys",
|
"dirs-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-next"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"dirs-sys-next",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs-sys"
|
name = "dirs-sys"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
@@ -295,6 +295,17 @@ dependencies = [
|
|||||||
"winapi 0.3.8",
|
"winapi 0.3.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys-next"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99de365f605554ae33f115102a02057d4fc18b01f3284d6870be0938743cfe7d"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"redox_users",
|
||||||
|
"winapi 0.3.8",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "divrem"
|
name = "divrem"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -1220,7 +1231,7 @@ checksum = "1a5f54deba50e65ee4cf786dbc37e8b3c63bdccccbcf9d3a8a9fd0c1bb7e1984"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"dirs 3.0.1",
|
"dirs",
|
||||||
"fs2",
|
"fs2",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@@ -1264,7 +1275,7 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"cpu-time",
|
"cpu-time",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"dirs 2.0.2",
|
"dirs-next",
|
||||||
"divrem",
|
"divrem",
|
||||||
"downcast",
|
"downcast",
|
||||||
"git-version",
|
"git-version",
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ num = ["num-rug-adapter", "prolog_parser/num"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cpu-time = "1.0.0"
|
cpu-time = "1.0.0"
|
||||||
crossterm = "0.16.0"
|
crossterm = "0.16.0"
|
||||||
dirs = "2.0.2"
|
dirs-next = "2.0.0"
|
||||||
divrem = "0.1.0"
|
divrem = "0.1.0"
|
||||||
downcast = "0.10.0"
|
downcast = "0.10.0"
|
||||||
git-version = "0.3.4"
|
git-version = "0.3.4"
|
||||||
|
|||||||
30
README.md
30
README.md
@@ -226,6 +226,36 @@ arithmetic operators with the usual precedences,
|
|||||||
|
|
||||||
New operators can be defined using the `op` declaration.
|
New operators can be defined using the `op` declaration.
|
||||||
|
|
||||||
|
### First instantiated argument indexing
|
||||||
|
|
||||||
|
Scryer Prolog indexes on the leftmost argument that is not a variable
|
||||||
|
in all clauses of a predicate's definition. We call this strategy
|
||||||
|
first *instantiated* argument indexing.
|
||||||
|
|
||||||
|
A key motivation for first instantiated argument indexing is to enable
|
||||||
|
indexing for meta-predicates such as `maplist/N` and `foldl/N`, whose
|
||||||
|
first argument is a partial goal that is a variable in the definition
|
||||||
|
of these predicates and therefore cannot be used for indexing.
|
||||||
|
|
||||||
|
For example, a natural definiton of `maplist/2` reads:
|
||||||
|
|
||||||
|
```
|
||||||
|
maplist(_, []).
|
||||||
|
maplist(Goal_1, [L|Ls]) :-
|
||||||
|
call(Goal_1, L),
|
||||||
|
maplist(Goal_1, Ls).
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, first instantianted argument indexing automatically uses
|
||||||
|
the *second* argument for indexing, and thus prevents choicepoints for
|
||||||
|
calls with lists of fixed lengths (and deterministic goals).
|
||||||
|
Conveniently, no auxiliary predicates with reordered arguments are
|
||||||
|
needed to benefit from indexing in such cases.
|
||||||
|
|
||||||
|
Conventional first argument indexing naturally arises as a
|
||||||
|
special case of this strategy, if the first argument is instantiated
|
||||||
|
in any clause of a predicate's definition.
|
||||||
|
|
||||||
### Strings and partial strings
|
### Strings and partial strings
|
||||||
|
|
||||||
In Scryer Prolog, the default value of the Prolog flag `double_quotes`
|
In Scryer Prolog, the default value of the Prolog flag `double_quotes`
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/// Code generation to WAM-like instructions.
|
||||||
use crate::prolog_parser::ast::*;
|
use crate::prolog_parser::ast::*;
|
||||||
|
|
||||||
use crate::allocator::*;
|
use crate::allocator::*;
|
||||||
@@ -857,13 +858,53 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
|
|||||||
Ok(code)
|
Ok(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_predicate(clauses: &Vec<PredicateClause>) -> Vec<(usize, usize)> {
|
/// Returns the index of the first instantiated argument.
|
||||||
|
fn first_instantiated_index(clauses: &[PredicateClause]) -> Option<usize> {
|
||||||
|
let mut optimal_index = None;
|
||||||
|
let has_args = match clauses.first() {
|
||||||
|
Some(clause) => match clause.args() {
|
||||||
|
Some(args) => !args.is_empty(),
|
||||||
|
None => false,
|
||||||
|
},
|
||||||
|
None => false,
|
||||||
|
};
|
||||||
|
if !has_args {
|
||||||
|
return optimal_index;
|
||||||
|
}
|
||||||
|
for clause in clauses.iter() {
|
||||||
|
let args = clause.args().unwrap();
|
||||||
|
for (i, arg) in args.iter().enumerate() {
|
||||||
|
if let Some(optimal_index) = optimal_index {
|
||||||
|
if i >= optimal_index {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match **arg {
|
||||||
|
Term::AnonVar | Term::Var(..) => (),
|
||||||
|
_ => {
|
||||||
|
match optimal_index {
|
||||||
|
Some(ref mut optimal_i) => *optimal_i = i,
|
||||||
|
None => optimal_index = Some(i),
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
optimal_index
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_predicate(clauses: &[PredicateClause], optimal_index: usize) -> Vec<(usize, usize)> {
|
||||||
let mut subseqs = Vec::new();
|
let mut subseqs = Vec::new();
|
||||||
let mut left_index = 0;
|
let mut left_index = 0;
|
||||||
|
|
||||||
|
if clauses.first().unwrap().args().is_some() {
|
||||||
for (right_index, clause) in clauses.iter().enumerate() {
|
for (right_index, clause) in clauses.iter().enumerate() {
|
||||||
match clause.first_arg() {
|
// Can unwrap safely.
|
||||||
Some(&Term::Var(_, _)) | Some(&Term::AnonVar) => {
|
if let Some(arg) = clause.args().unwrap().iter().nth(optimal_index) {
|
||||||
|
match **arg {
|
||||||
|
Term::Var(..) | Term::AnonVar => {
|
||||||
if left_index < right_index {
|
if left_index < right_index {
|
||||||
subseqs.push((left_index, right_index));
|
subseqs.push((left_index, right_index));
|
||||||
}
|
}
|
||||||
@@ -871,7 +912,9 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
|
|||||||
subseqs.push((right_index, right_index + 1));
|
subseqs.push((right_index, right_index + 1));
|
||||||
left_index = right_index + 1;
|
left_index = right_index + 1;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -901,6 +944,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
|
|||||||
fn compile_pred_subseq<'b: 'a>(
|
fn compile_pred_subseq<'b: 'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
clauses: &'b [PredicateClause],
|
clauses: &'b [PredicateClause],
|
||||||
|
optimal_index: usize,
|
||||||
) -> Result<Code, ParserError> {
|
) -> Result<Code, ParserError> {
|
||||||
let mut code_body = Vec::new();
|
let mut code_body = Vec::new();
|
||||||
let mut code_offsets = CodeOffsets::new();
|
let mut code_offsets = CodeOffsets::new();
|
||||||
@@ -910,9 +954,9 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
|
|||||||
for (i, clause) in clauses.iter().enumerate() {
|
for (i, clause) in clauses.iter().enumerate() {
|
||||||
self.marker.reset();
|
self.marker.reset();
|
||||||
|
|
||||||
let mut clause_code = match clause {
|
let mut clause_code = match *clause {
|
||||||
&PredicateClause::Fact(ref fact, ..) => self.compile_fact(fact),
|
PredicateClause::Fact(ref fact, ..) => self.compile_fact(fact),
|
||||||
&PredicateClause::Rule(ref rule, ..) => self.compile_rule(rule)?,
|
PredicateClause::Rule(ref rule, ..) => self.compile_rule(rule)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
if num_clauses > 1 {
|
if num_clauses > 1 {
|
||||||
@@ -925,17 +969,24 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
|
|||||||
code_body.push(Line::Choice(choice));
|
code_body.push(Line::Choice(choice));
|
||||||
}
|
}
|
||||||
|
|
||||||
clause.first_arg().map(|arg| {
|
let arg = match clause.args() {
|
||||||
|
Some(args) => match args.iter().nth(optimal_index) {
|
||||||
|
Some(term) => Some(term),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
if let Some(arg) = arg {
|
||||||
let index = code_body.len();
|
let index = code_body.len();
|
||||||
code_offsets.index_term(arg, index);
|
code_offsets.index_term(arg, index);
|
||||||
});
|
}
|
||||||
|
|
||||||
code_body.append(&mut clause_code);
|
code_body.append(&mut clause_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut code = Vec::new();
|
let mut code = Vec::new();
|
||||||
|
code_offsets.add_indices(&mut code, code_body, optimal_index + 1);
|
||||||
|
|
||||||
code_offsets.add_indices(&mut code, code_body);
|
|
||||||
Ok(code)
|
Ok(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -944,11 +995,16 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
|
|||||||
clauses: &'b Vec<PredicateClause>,
|
clauses: &'b Vec<PredicateClause>,
|
||||||
) -> Result<Code, ParserError> {
|
) -> Result<Code, ParserError> {
|
||||||
let mut code = Vec::new();
|
let mut code = Vec::new();
|
||||||
let split_pred = Self::split_predicate(&clauses);
|
let optimal_index = match Self::first_instantiated_index(&clauses) {
|
||||||
|
Some(index) => index,
|
||||||
|
None => 0, // Default to first argument indexing.
|
||||||
|
};
|
||||||
|
let split_pred = Self::split_predicate(&clauses, optimal_index);
|
||||||
let multi_seq = split_pred.len() > 1;
|
let multi_seq = split_pred.len() > 1;
|
||||||
|
|
||||||
for (l, r) in split_pred {
|
for (l, r) in split_pred {
|
||||||
let mut code_segment = self.compile_pred_subseq(&clauses[l..r])?;
|
let mut code_segment =
|
||||||
|
self.compile_pred_subseq(&clauses[l..r], optimal_index)?;
|
||||||
|
|
||||||
if multi_seq {
|
if multi_seq {
|
||||||
let choice = match l {
|
let choice = match l {
|
||||||
|
|||||||
14
src/forms.rs
14
src/forms.rs
@@ -332,10 +332,16 @@ pub enum PredicateClause {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PredicateClause {
|
impl PredicateClause {
|
||||||
pub fn first_arg(&self) -> Option<&Term> {
|
// TODO: add this to `Term` in `prolog_parser` like `first_arg`.
|
||||||
match self {
|
pub fn args(&self) -> Option<&[Box<Term>]> {
|
||||||
&PredicateClause::Fact(ref term, ..) => term.first_arg(),
|
match *self {
|
||||||
&PredicateClause::Rule(ref rule, ..) => rule.head.1.first().map(|bt| bt.as_ref()),
|
PredicateClause::Fact(ref term, ..) => {
|
||||||
|
match term {
|
||||||
|
Term::Clause(_, _, args, _) => Some(&args),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PredicateClause::Rule(ref rule, ..) => Some(&rule.head.1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -143,8 +143,8 @@ impl CodeOffsets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn index_term(&mut self, first_arg: &Term, index: usize) {
|
pub fn index_term(&mut self, optimal_arg: &Term, index: usize) {
|
||||||
match first_arg {
|
match optimal_arg {
|
||||||
&Term::Clause(_, ref name, ref terms, _) => {
|
&Term::Clause(_, ref name, ref terms, _) => {
|
||||||
let code = self
|
let code = self
|
||||||
.structures
|
.structures
|
||||||
@@ -237,12 +237,17 @@ impl CodeOffsets {
|
|||||||
fn switch_on_constant(
|
fn switch_on_constant(
|
||||||
con_ind: IndexMap<Constant, ThirdLevelIndex>,
|
con_ind: IndexMap<Constant, ThirdLevelIndex>,
|
||||||
prelude: &mut CodeDeque,
|
prelude: &mut CodeDeque,
|
||||||
|
optimal_index: usize,
|
||||||
) -> IntIndex {
|
) -> IntIndex {
|
||||||
let con_ind = Self::second_level_index(con_ind, prelude);
|
let con_ind = Self::second_level_index(con_ind, prelude);
|
||||||
|
|
||||||
if con_ind.len() > 1 {
|
if con_ind.len() > 1 {
|
||||||
let index = Self::flatten_index(con_ind, prelude.len());
|
let index = Self::flatten_index(con_ind, prelude.len());
|
||||||
let instr = IndexingInstruction::SwitchOnConstant(index.len(), index);
|
let instr = IndexingInstruction::SwitchOnConstant(
|
||||||
|
optimal_index,
|
||||||
|
index.len(),
|
||||||
|
index
|
||||||
|
);
|
||||||
|
|
||||||
prelude.push_front(Line::from(instr));
|
prelude.push_front(Line::from(instr));
|
||||||
|
|
||||||
@@ -259,12 +264,17 @@ impl CodeOffsets {
|
|||||||
fn switch_on_structure(
|
fn switch_on_structure(
|
||||||
str_ind: IndexMap<(ClauseName, usize), ThirdLevelIndex>,
|
str_ind: IndexMap<(ClauseName, usize), ThirdLevelIndex>,
|
||||||
prelude: &mut CodeDeque,
|
prelude: &mut CodeDeque,
|
||||||
|
optimal_index: usize,
|
||||||
) -> IntIndex {
|
) -> IntIndex {
|
||||||
let str_ind = Self::second_level_index(str_ind, prelude);
|
let str_ind = Self::second_level_index(str_ind, prelude);
|
||||||
|
|
||||||
if str_ind.len() > 1 {
|
if str_ind.len() > 1 {
|
||||||
let index = Self::flatten_index(str_ind, prelude.len());
|
let index = Self::flatten_index(str_ind, prelude.len());
|
||||||
let instr = IndexingInstruction::SwitchOnStructure(index.len(), index);
|
let instr = IndexingInstruction::SwitchOnStructure(
|
||||||
|
optimal_index,
|
||||||
|
index.len(),
|
||||||
|
index
|
||||||
|
);
|
||||||
|
|
||||||
prelude.push_front(Line::from(instr));
|
prelude.push_front(Line::from(instr));
|
||||||
|
|
||||||
@@ -325,7 +335,7 @@ impl CodeOffsets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_indices(self, code: &mut Code, mut code_body: Code) {
|
pub fn add_indices(self, code: &mut Code, mut code_body: Code, optimal_index: usize) {
|
||||||
if self.no_indices() {
|
if self.no_indices() {
|
||||||
*code = code_body;
|
*code = code_body;
|
||||||
return;
|
return;
|
||||||
@@ -334,8 +344,10 @@ impl CodeOffsets {
|
|||||||
let mut prelude = VecDeque::new();
|
let mut prelude = VecDeque::new();
|
||||||
|
|
||||||
let lst_loc = Self::switch_on_list(self.lists, &mut prelude);
|
let lst_loc = Self::switch_on_list(self.lists, &mut prelude);
|
||||||
let str_loc = Self::switch_on_structure(self.structures, &mut prelude);
|
let str_loc =
|
||||||
let con_loc = Self::switch_on_constant(self.constants, &mut prelude);
|
Self::switch_on_structure(self.structures, &mut prelude, optimal_index);
|
||||||
|
let con_loc =
|
||||||
|
Self::switch_on_constant(self.constants, &mut prelude, optimal_index);
|
||||||
|
|
||||||
let prelude_length = prelude.len();
|
let prelude_length = prelude.len();
|
||||||
|
|
||||||
@@ -355,8 +367,13 @@ impl CodeOffsets {
|
|||||||
let con_loc = Self::switch_on_con_offset_from(con_loc, prelude.len());
|
let con_loc = Self::switch_on_con_offset_from(con_loc, prelude.len());
|
||||||
let lst_loc = Self::switch_on_lst_offset_from(lst_loc, prelude.len());
|
let lst_loc = Self::switch_on_lst_offset_from(lst_loc, prelude.len());
|
||||||
|
|
||||||
let switch_instr =
|
let switch_instr = IndexingInstruction::SwitchOnTerm(
|
||||||
IndexingInstruction::SwitchOnTerm(prelude.len() + 1, con_loc, lst_loc, str_loc);
|
optimal_index,
|
||||||
|
prelude.len() + 1,
|
||||||
|
con_loc,
|
||||||
|
lst_loc,
|
||||||
|
str_loc
|
||||||
|
);
|
||||||
|
|
||||||
prelude.push_front(Line::from(switch_instr));
|
prelude.push_front(Line::from(switch_instr));
|
||||||
|
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ impl IndexedChoiceInstruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A `Line` is an instruction (cf. page 98 of wambook).
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Line {
|
pub enum Line {
|
||||||
Arithmetic(ArithmeticInstruction),
|
Arithmetic(ArithmeticInstruction),
|
||||||
@@ -424,11 +425,13 @@ impl ControlInstruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `IndexingInstruction` cf. page 110 of wambook.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum IndexingInstruction {
|
pub enum IndexingInstruction {
|
||||||
SwitchOnTerm(usize, usize, usize, usize),
|
// The first index is the optimal argument being indexed.
|
||||||
SwitchOnConstant(usize, IndexMap<Constant, usize>),
|
SwitchOnTerm(usize, usize, usize, usize, usize),
|
||||||
SwitchOnStructure(usize, IndexMap<(ClauseName, usize), usize>),
|
SwitchOnConstant(usize, usize, IndexMap<Constant, usize>),
|
||||||
|
SwitchOnStructure(usize, usize, IndexMap<(ClauseName, usize), usize>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<IndexingInstruction> for Line {
|
impl From<IndexingInstruction> for Line {
|
||||||
@@ -440,25 +443,26 @@ impl From<IndexingInstruction> for Line {
|
|||||||
impl IndexingInstruction {
|
impl IndexingInstruction {
|
||||||
pub fn to_functor(&self) -> MachineStub {
|
pub fn to_functor(&self) -> MachineStub {
|
||||||
match self {
|
match self {
|
||||||
&IndexingInstruction::SwitchOnTerm(vars, constants, lists, structures) => {
|
&IndexingInstruction::SwitchOnTerm(arg, vars, constants, lists, structures) => {
|
||||||
functor!(
|
functor!(
|
||||||
"switch_on_term",
|
"switch_on_term",
|
||||||
[integer(vars),
|
[integer(arg),
|
||||||
|
integer(vars),
|
||||||
integer(constants),
|
integer(constants),
|
||||||
integer(lists),
|
integer(lists),
|
||||||
integer(structures)]
|
integer(structures)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
&IndexingInstruction::SwitchOnConstant(constants, _) => {
|
&IndexingInstruction::SwitchOnConstant(arg, constants, _) => {
|
||||||
functor!(
|
functor!(
|
||||||
"switch_on_constant",
|
"switch_on_constant",
|
||||||
[integer(constants)]
|
[integer(arg), integer(constants)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
&IndexingInstruction::SwitchOnStructure(structures, _) => {
|
&IndexingInstruction::SwitchOnStructure(arg, structures, _) => {
|
||||||
functor!(
|
functor!(
|
||||||
"switch_on_structure",
|
"switch_on_structure",
|
||||||
[integer(structures)]
|
[integer(arg), integer(structures)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,8 +212,7 @@ read_line_to_chars(Stream, Cs0, Cs) :-
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
?- chars_base64("hello", Bs, []).
|
?- chars_base64("hello", Bs, []).
|
||||||
Bs = "aGVsbG8="
|
Bs = "aGVsbG8=".
|
||||||
; false.
|
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
chars_base64(Cs, Bs, Options) :-
|
chars_base64(Cs, Bs, Options) :-
|
||||||
|
|||||||
@@ -58,8 +58,7 @@
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
?- hex_bytes("501ACE", Bs).
|
?- hex_bytes("501ACE", Bs).
|
||||||
Bs = [80,26,206]
|
Bs = [80,26,206].
|
||||||
; false.
|
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
|
||||||
@@ -146,7 +145,7 @@ must_be_byte_chars(Chars, Context) :-
|
|||||||
?- crypto_n_random_bytes(32, Bs),
|
?- crypto_n_random_bytes(32, Bs),
|
||||||
bytes_integer(Bs, I).
|
bytes_integer(Bs, I).
|
||||||
Bs = [146,166,162,210,242,7,25,132,64,94|...],
|
Bs = [146,166,162,210,242,7,25,132,64,94|...],
|
||||||
I = 337420085690608915485...(56 digits omitted)
|
I = 337420085690608915485...(56 digits omitted).
|
||||||
|
|
||||||
The above relation also works in the other direction, letting you
|
The above relation also works in the other direction, letting you
|
||||||
translate an integer _to_ a list of bytes. In addition, you can
|
translate an integer _to_ a list of bytes. In addition, you can
|
||||||
@@ -155,7 +154,7 @@ must_be_byte_chars(Chars, Context) :-
|
|||||||
|
|
||||||
?- crypto_n_random_bytes(12, Bs),
|
?- crypto_n_random_bytes(12, Bs),
|
||||||
hex_bytes(Hex, Bs).
|
hex_bytes(Hex, Bs).
|
||||||
Bs = [34,25,50,72,58,63,50,172,32,46|...], Hex = "221932483a3f32ac202 ..."
|
Bs = [34,25,50,72,58,63,50,172,32,46|...], Hex = "221932483a3f32ac202 ...".
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
|
||||||
@@ -190,8 +189,7 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B).
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
?- crypto_data_hash("abc", Hs, [algorithm(sha256)]).
|
?- crypto_data_hash("abc", Hs, [algorithm(sha256)]).
|
||||||
Hs = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
Hs = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad".
|
||||||
; false.
|
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|||||||
@@ -166,19 +166,16 @@ file_time_(File, Which, T) :-
|
|||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
?- path_segments("/hello/there", Segments).
|
?- path_segments("/hello/there", Segments).
|
||||||
Segments = [[],"hello","there"]
|
Segments = [[],"hello","there"].
|
||||||
; false.
|
|
||||||
|
|
||||||
?- path_segments(Path, ["hello","there"]).
|
?- path_segments(Path, ["hello","there"]).
|
||||||
Path = "hello/there"
|
Path = "hello/there".
|
||||||
; false.
|
|
||||||
|
|
||||||
|
|
||||||
To obtain the platform-specific directory separator, you can use:
|
To obtain the platform-specific directory separator, you can use:
|
||||||
|
|
||||||
?- path_segments(Separator, ["",""]).
|
?- path_segments(Separator, ["",""]).
|
||||||
Separator = "/"
|
Separator = "/".
|
||||||
; false.
|
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
path_segments(Path, Segments) :-
|
path_segments(Path, Segments) :-
|
||||||
|
|||||||
@@ -64,8 +64,7 @@
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
?- phrase(format_("~s~n~`.t~w!~12|", ["hello",there]), Cs).
|
?- phrase(format_("~s~n~`.t~w!~12|", ["hello",there]), Cs).
|
||||||
%@ Cs = "hello\n......there!"
|
%@ Cs = "hello\n......there!".
|
||||||
%@ ; false.
|
|
||||||
|
|
||||||
I place this code in the public domain. Use it in any way you want.
|
I place this code in the public domain. Use it in any way you want.
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
@@ -158,7 +157,7 @@ element_gluevar(glue(_,V), N, N) --> [V].
|
|||||||
consume whitespace in the sense of format strings.
|
consume whitespace in the sense of format strings.
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
cells([], Args, Tab, Es, _) -->
|
cells([], Args, Tab, Es, _) --> !,
|
||||||
( { Args == [] } -> cell(Tab, Tab, Es)
|
( { Args == [] } -> cell(Tab, Tab, Es)
|
||||||
; { domain_error(empty_list, Args, format_//2) }
|
; { domain_error(empty_list, Args, format_//2) }
|
||||||
).
|
).
|
||||||
@@ -404,32 +403,28 @@ Cs = [cell(0,4,[glue(' ',_A),chars("a"),glue(' ',_B)])]
|
|||||||
?- phrase(format_("hello~n~tthere~6|", []), Ls).
|
?- phrase(format_("hello~n~tthere~6|", []), Ls).
|
||||||
|
|
||||||
?- format("~ta~t~4|", []).
|
?- format("~ta~t~4|", []).
|
||||||
a true
|
a true.
|
||||||
; false.
|
|
||||||
|
|
||||||
?- format("~ta~tb~tc~10|", []).
|
?- format("~ta~tb~tc~10|", []).
|
||||||
a b c true
|
a b c true.
|
||||||
; false.
|
|
||||||
|
|
||||||
?- format("~tabc~3|", []).
|
?- format("~tabc~3|", []).
|
||||||
|
|
||||||
?- format("~ta~t~4|", []).
|
?- format("~ta~t~4|", []).
|
||||||
|
|
||||||
?- format("~ta~t~tb~tc~20|", []).
|
?- format("~ta~t~tb~tc~20|", []).
|
||||||
a b c true
|
a b c true.
|
||||||
; false.
|
|
||||||
|
|
||||||
?- format("~2f~n", [3]).
|
?- format("~2f~n", [3]).
|
||||||
3.00
|
3.00
|
||||||
true
|
true.
|
||||||
|
|
||||||
?- format("~20f", [0.1]).
|
?- format("~20f", [0.1]).
|
||||||
0.10000000000000000000 true % this should use higher accuracy!
|
0.10000000000000000000 true.
|
||||||
; false.
|
|
||||||
|
|
||||||
?- X is atan(2), format("~7f~n", [X]).
|
?- X is atan(2), format("~7f~n", [X]).
|
||||||
1.1071487
|
1.1071487
|
||||||
X = 1.1071487177940906
|
X = 1.1071487177940906.
|
||||||
|
|
||||||
?- format("~`at~50|~n", []).
|
?- format("~`at~50|~n", []).
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
@@ -438,10 +433,10 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|||||||
?- format("~t~N", []).
|
?- format("~t~N", []).
|
||||||
|
|
||||||
?- format("~q", [.]).
|
?- format("~q", [.]).
|
||||||
'.' true
|
'.' true.
|
||||||
|
|
||||||
?- format("~12r", [300]).
|
?- format("~12r", [300]).
|
||||||
210 true
|
210 true.
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
@@ -537,7 +532,7 @@ a :-
|
|||||||
b,
|
b,
|
||||||
c,
|
c,
|
||||||
d.
|
d.
|
||||||
true
|
true.
|
||||||
|
|
||||||
|
|
||||||
?- portray_clause([a,b,c,d]).
|
?- portray_clause([a,b,c,d]).
|
||||||
|
|||||||
@@ -17,8 +17,7 @@
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
?- http_open("https://github.com/mthom/scryer-prolog", S, []).
|
?- http_open("https://github.com/mthom/scryer-prolog", S, []).
|
||||||
%@ S = '$stream'(0x7f86f94a6cd0)
|
%@ S = '$stream'(0x7fcfc9e00f00).
|
||||||
%@ ; false.
|
|
||||||
|
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,7 @@
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
?- getenv("LANG", Ls).
|
?- getenv("LANG", Ls).
|
||||||
Ls = "en_US.UTF-8"
|
Ls = "en_US.UTF-8".
|
||||||
; false.
|
|
||||||
|
|
||||||
Public domain code.
|
Public domain code.
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|||||||
@@ -34,8 +34,7 @@
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
?- current_time(T), phrase(format_time("%d.%m.%Y (%H:%M:%S)", T), Cs).
|
?- current_time(T), phrase(format_time("%d.%m.%Y (%H:%M:%S)", T), Cs).
|
||||||
T = [...], Cs = "11.06.2020 (00:24:32)"
|
T = [...], Cs = "11.06.2020 (00:24:32)".
|
||||||
; false.
|
|
||||||
|
|
||||||
sleep(S) sleeps for S seconds (a floating point number).
|
sleep(S) sleeps for S seconds (a floating point number).
|
||||||
|
|
||||||
@@ -107,15 +106,14 @@ report_time(T0) :-
|
|||||||
false.
|
false.
|
||||||
|
|
||||||
:- time(use_module(library(clpz))).
|
:- time(use_module(library(clpz))).
|
||||||
% CPU time: 2.762 seconds
|
% CPU time: 0.000 seconds
|
||||||
true
|
% CPU time: 0.001 seconds
|
||||||
; false.
|
true.
|
||||||
|
|
||||||
:- time(use_module(library(lists))).
|
:- time(use_module(library(lists))).
|
||||||
% CPU time: 0.000 seconds
|
% CPU time: 0.000 seconds
|
||||||
true
|
% CPU time: 0.001 seconds
|
||||||
; % CPU time: 0.001 seconds
|
true.
|
||||||
false.
|
|
||||||
|
|
||||||
?- time(member(X, [a,b,c])).
|
?- time(member(X, [a,b,c])).
|
||||||
% CPU time: 0.000 seconds
|
% CPU time: 0.000 seconds
|
||||||
|
|||||||
@@ -2,29 +2,56 @@ use crate::instructions::*;
|
|||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
fn scan_for_trust_me(code: &Code, jmp_offsets: &mut VecDeque<usize>, after_idx: &mut usize) {
|
fn scan_for_trust_me(
|
||||||
for (idx, instr) in code[*after_idx..].iter().enumerate() {
|
code: &Code,
|
||||||
|
jmp_offsets: &mut VecDeque<usize>,
|
||||||
|
before_idx: usize,
|
||||||
|
after_idx: &mut usize,
|
||||||
|
) {
|
||||||
|
// record the location of the line after the TrustMe capping the
|
||||||
|
// choice instruction sequence to after_idx.
|
||||||
|
loop {
|
||||||
|
match &code[*after_idx] {
|
||||||
|
&Line::Choice(ChoiceInstruction::DefaultRetryMeElse(offset)) |
|
||||||
|
&Line::Choice(ChoiceInstruction::RetryMeElse(offset)) |
|
||||||
|
&Line::IndexedChoice(IndexedChoiceInstruction::Retry(offset)) => {
|
||||||
|
*after_idx += offset;
|
||||||
|
}
|
||||||
|
&Line::Choice(ChoiceInstruction::DefaultTrustMe) |
|
||||||
|
&Line::Choice(ChoiceInstruction::TrustMe) |
|
||||||
|
&Line::IndexedChoice(IndexedChoiceInstruction::Trust(..)) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
*after_idx += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// search the code in the range for JmpBy instructions and record their
|
||||||
|
// offsets for future scanning.
|
||||||
|
for (idx, instr) in code[before_idx .. *after_idx].iter().enumerate() {
|
||||||
match instr {
|
match instr {
|
||||||
&Line::Choice(ChoiceInstruction::TrustMe)
|
|
||||||
| &Line::IndexedChoice(IndexedChoiceInstruction::Trust(..)) => {
|
|
||||||
*after_idx += idx;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
&Line::Control(ControlInstruction::JmpBy(_, offset, ..)) => {
|
&Line::Control(ControlInstruction::JmpBy(_, offset, ..)) => {
|
||||||
jmp_offsets.push_back(*after_idx + idx + offset)
|
jmp_offsets.push_back(before_idx + idx + offset)
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*after_idx += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn capture_next_range(code: &Code, queue: &mut VecDeque<usize>, last_idx: &mut usize) {
|
fn capture_next_range(code: &Code, queue: &mut VecDeque<usize>, last_idx: &mut usize) {
|
||||||
loop {
|
loop {
|
||||||
match &code[*last_idx] {
|
match &code[*last_idx] {
|
||||||
&Line::Choice(ChoiceInstruction::TryMeElse(..))
|
&Line::Choice(ChoiceInstruction::TryMeElse(offset)) |
|
||||||
| &Line::IndexedChoice(IndexedChoiceInstruction::Try(..)) => {
|
&Line::IndexedChoice(IndexedChoiceInstruction::Try(offset)) => {
|
||||||
*last_idx += 1;
|
let before_idx = *last_idx;
|
||||||
scan_for_trust_me(code, queue, last_idx);
|
*last_idx += offset;
|
||||||
|
|
||||||
|
scan_for_trust_me(code, queue, before_idx, last_idx);
|
||||||
}
|
}
|
||||||
&Line::Control(ControlInstruction::JmpBy(_, offset, _, false)) => {
|
&Line::Control(ControlInstruction::JmpBy(_, offset, _, false)) => {
|
||||||
queue.push_back(*last_idx + offset);
|
queue.push_back(*last_idx + offset);
|
||||||
@@ -34,8 +61,8 @@ fn capture_next_range(code: &Code, queue: &mut VecDeque<usize>, last_idx: &mut u
|
|||||||
queue.push_back(*last_idx + offset);
|
queue.push_back(*last_idx + offset);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
&Line::Control(ControlInstruction::Proceed)
|
&Line::Control(ControlInstruction::Proceed) |
|
||||||
| &Line::Control(ControlInstruction::CallClause(_, _, _, true, _)) =>
|
&Line::Control(ControlInstruction::CallClause(_, _, _, true, _)) =>
|
||||||
break,
|
break,
|
||||||
_ =>
|
_ =>
|
||||||
*last_idx += 1,
|
*last_idx += 1,
|
||||||
|
|||||||
@@ -1392,8 +1392,8 @@ impl MachineState {
|
|||||||
pub(super)
|
pub(super)
|
||||||
fn execute_indexing_instr(&mut self, instr: &IndexingInstruction) {
|
fn execute_indexing_instr(&mut self, instr: &IndexingInstruction) {
|
||||||
match instr {
|
match instr {
|
||||||
&IndexingInstruction::SwitchOnTerm(v, c, l, s) => {
|
&IndexingInstruction::SwitchOnTerm(arg, v, c, l, s) => {
|
||||||
let addr = self[temp_v!(1)];
|
let addr = self[temp_v!(arg)];
|
||||||
let addr = self.store(self.deref(addr));
|
let addr = self.store(self.deref(addr));
|
||||||
|
|
||||||
let offset = match addr {
|
let offset = match addr {
|
||||||
@@ -1423,8 +1423,8 @@ impl MachineState {
|
|||||||
o => self.p += o,
|
o => self.p += o,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
&IndexingInstruction::SwitchOnConstant(_, ref hm) => {
|
&IndexingInstruction::SwitchOnConstant(arg, _, ref hm) => {
|
||||||
let addr = self[temp_v!(1)];
|
let addr = self[temp_v!(arg)];
|
||||||
let addr = self.store(self.deref(addr));
|
let addr = self.store(self.deref(addr));
|
||||||
|
|
||||||
let offset =
|
let offset =
|
||||||
@@ -1445,8 +1445,8 @@ impl MachineState {
|
|||||||
o => self.p += o,
|
o => self.p += o,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
&IndexingInstruction::SwitchOnStructure(_, ref hm) => {
|
&IndexingInstruction::SwitchOnStructure(arg, _, ref hm) => {
|
||||||
let a1 = self.registers[1];
|
let a1 = self.registers[arg];
|
||||||
let addr = self.store(self.deref(a1));
|
let addr = self.store(self.deref(a1));
|
||||||
|
|
||||||
let offset = match addr {
|
let offset = match addr {
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ impl Machine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn compile_scryerrc(&mut self) {
|
fn compile_scryerrc(&mut self) {
|
||||||
let mut path = match dirs::home_dir() {
|
let mut path = match dirs_next::home_dir() {
|
||||||
Some(path) => path,
|
Some(path) => path,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ pub mod readline {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(pending_input: String) -> Self {
|
pub fn new(pending_input: String) -> Self {
|
||||||
let mut rl = Editor::<()>::new();
|
let mut rl = Editor::<()>::new();
|
||||||
if let Some(mut path) = dirs::home_dir() {
|
if let Some(mut path) = dirs_next::home_dir() {
|
||||||
path.push(HISTORY_FILE);
|
path.push(HISTORY_FILE);
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
if rl.load_history(&path).is_err() {
|
if rl.load_history(&path).is_err() {
|
||||||
@@ -95,7 +95,7 @@ pub mod readline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn save_history(&mut self) {
|
fn save_history(&mut self) {
|
||||||
if let Some(mut path) = dirs::home_dir() {
|
if let Some(mut path) = dirs_next::home_dir() {
|
||||||
path.push(HISTORY_FILE);
|
path.push(HISTORY_FILE);
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
if self.rl.append_history(&path).is_err() {
|
if self.rl.append_history(&path).is_err() {
|
||||||
|
|||||||
@@ -276,13 +276,13 @@ impl fmt::Display for ChoiceInstruction {
|
|||||||
impl fmt::Display for IndexingInstruction {
|
impl fmt::Display for IndexingInstruction {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
&IndexingInstruction::SwitchOnTerm(v, c, l, s) => {
|
&IndexingInstruction::SwitchOnTerm(a, v, c, l, s) => {
|
||||||
write!(f, "switch_on_term {}, {}, {}, {}", v, c, l, s)
|
write!(f, "switch_on_term {}, {}, {}, {}, {}", a, v, c, l, s)
|
||||||
}
|
}
|
||||||
&IndexingInstruction::SwitchOnConstant(num_cs, _) => {
|
&IndexingInstruction::SwitchOnConstant(_, num_cs, _) => {
|
||||||
write!(f, "switch_on_constant {}", num_cs)
|
write!(f, "switch_on_constant {}", num_cs)
|
||||||
}
|
}
|
||||||
&IndexingInstruction::SwitchOnStructure(num_ss, _) => {
|
&IndexingInstruction::SwitchOnStructure(_, num_ss, _) => {
|
||||||
write!(f, "switch_on_structure {}", num_ss)
|
write!(f, "switch_on_structure {}", num_ss)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user