* Built-in predicates for list processing and top-level declarative
control (`setup_call_control/3`, `call_with_inference_limit/3`,
etc.) (_done_)
-* Definite Clause Grammars
+* Default representation of strings as list of chars, using a packed
+ internal representation (_in progress_).
+* `term_expansion/2` and `goal_expansion/2`.
+* Definite Clause Grammars.
* Attributed variables using the SICStus Prolog interface and
semantics. Adding coroutines like `dif/2`, `freeze/2`, etc.
is straightforward with attributed variables.
}
}
-impl ClauseName {
+impl ClauseName {
pub fn as_str(&self) -> &str {
match self {
&ClauseName::BuiltIn(s) => s,
use prolog::fixtures::*;
use prolog::indexing::*;
use prolog::iterators::*;
+use prolog::machine::machine_state::MachineFlags;
use prolog::targets::*;
use std::cell::Cell;
use std::vec::Vec;
pub struct CodeGenerator<TermMarker> {
+ flags: MachineFlags,
marker: TermMarker,
var_count: HashMap<Rc<Var>, usize>,
non_counted_bt: bool
impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker>
{
- pub fn new(non_counted_bt: bool) -> Self {
+ pub fn new(non_counted_bt: bool, flags: MachineFlags) -> Self {
CodeGenerator { marker: Allocator::new(),
var_count: HashMap::new(),
- non_counted_bt }
+ non_counted_bt,
+ flags }
}
pub fn take_vars(self) -> AllocVarDict {
-> Result<Code, ParserError>
{
let mut code_body = Vec::new();
- let mut code_offsets = CodeOffsets::new();
+ let mut code_offsets = CodeOffsets::new(self.flags);
let num_clauses = clauses.len();
use prolog::debray_allocator::*;
use prolog::codegen::*;
use prolog::machine::*;
+use prolog::machine::machine_state::MachineFlags;
use prolog::toplevel::*;
use std::collections::{HashMap, HashSet, VecDeque};
{
let atom_tbl = wam.atom_tbl();
let string_tbl = wam.string_tbl();
+ let flags = wam.machine_flags();
let index = MachineCodeIndices {
code_dir: &mut wam.code_dir,
op_dir: &mut wam.op_dir,
};
-
- let mut worker = TopLevelWorker::new(buffer.as_bytes(), atom_tbl, string_tbl, index);
+
+ let mut worker = TopLevelWorker::new(buffer.as_bytes(), atom_tbl, string_tbl,
+ flags, index);
worker.parse_code()
}
// throw errors if declaration or query found.
-fn compile_relation(tl: &TopLevel, non_counted_bt: bool) -> Result<Code, ParserError>
+fn compile_relation(tl: &TopLevel, non_counted_bt: bool, flags: MachineFlags) -> Result<Code, ParserError>
{
- let mut cg = CodeGenerator::<DebrayAllocator>::new(non_counted_bt);
+ let mut cg = CodeGenerator::<DebrayAllocator>::new(non_counted_bt, flags);
match tl {
&TopLevel::Declaration(_) | &TopLevel::Query(_) =>
}
}
-fn compile_appendix(code: &mut Code, queue: Vec<TopLevel>, non_counted_bt: bool) -> Result<(), ParserError>
+fn compile_appendix(code: &mut Code, queue: Vec<TopLevel>, non_counted_bt: bool, flags: MachineFlags)
+ -> Result<(), ParserError>
{
for tl in queue.iter() {
set_first_index(code);
- code.append(&mut compile_relation(tl, non_counted_bt)?);
+ code.append(&mut compile_relation(tl, non_counted_bt, flags)?);
}
Ok(())
}
-fn compile_query(terms: Vec<QueryTerm>, queue: Vec<TopLevel>) -> Result<(Code, AllocVarDict), ParserError>
+fn compile_query(terms: Vec<QueryTerm>, queue: Vec<TopLevel>, flags: MachineFlags)
+ -> Result<(Code, AllocVarDict), ParserError>
{
- let mut cg = CodeGenerator::<DebrayAllocator>::new(false); // count backtracking inferences.
+ // count backtracking inferences.
+ let mut cg = CodeGenerator::<DebrayAllocator>::new(false, flags);
let mut code = try!(cg.compile_query(&terms));
- compile_appendix(&mut code, queue, false)?;
+ compile_appendix(&mut code, queue, false, flags)?;
Ok((code, cg.take_vars()))
}
Err(SessionError::NamelessEntry)
});
- let mut code = try_eval_session!(compile_relation(&tl, false));
- try_eval_session!(compile_appendix(&mut code, queue, false));
+ let mut code = try_eval_session!(compile_relation(&tl, false, wam.machine_flags()));
+ try_eval_session!(compile_appendix(&mut code, queue, false, wam.machine_flags()));
if !code.is_empty() {
wam.add_user_code(name, tl.arity(), code, tl.as_predicate().ok().unwrap())
{
match tl {
TopLevelPacket::Query(terms, queue) =>
- match compile_query(terms, queue) {
+ match compile_query(terms, queue, wam.machine_flags()) {
Ok((mut code, vars)) => wam.submit_query(code, vars),
Err(e) => EvalSession::from(e)
},
let non_counted_bt = self.non_counted_bt_preds.contains(&(name.clone(), arity));
let p = code.len() + self.wam.code_size();
- let mut decl_code = compile_relation(&TopLevel::Predicate(decl), non_counted_bt)?;
-
- compile_appendix(&mut decl_code, Vec::from(queue), non_counted_bt)?;
+ let mut decl_code = compile_relation(&TopLevel::Predicate(decl), non_counted_bt,
+ self.wam.machine_flags())?;
-// println!("\n{}/{}:\n", name.as_str(), arity);
+ compile_appendix(&mut decl_code, Vec::from(queue), non_counted_bt,
+ self.wam.machine_flags())?;
let idx = code_dir.entry((name, arity)).or_insert(CodeIndex::default());
set_code_index!(idx, IndexPtr::Index(p), self.get_module_name());
-// print_code(&decl_code);
-
code.extend(decl_code.into_iter());
}
pub
fn compile_listing(wam: &mut Machine, src_str: &str, mut indices: MachineCodeIndices) -> EvalSession
{
- let mut worker = TopLevelBatchWorker::new(src_str.as_bytes(), wam.atom_tbl(), wam.string_tbl());
+ let mut worker = TopLevelBatchWorker::new(src_str.as_bytes(), wam.atom_tbl(), wam.string_tbl(),
+ wam.machine_flags());
let mut compiler = ListingCompiler::new(wam);
while let Some(decl) = try_eval_session!(worker.consume(&mut indices)) {
use prolog::ast::*;
use prolog::num::*;
use prolog::heap_iter::*;
-use prolog::machine::machine_state::MachineState;
+use prolog::machine::machine_state::{DoubleQuotes, MachineState};
use prolog::ordered_float::OrderedFloat;
+use prolog::string_list::*;
use std::cell::Cell;
use std::collections::{HashMap, HashSet};
#[derive(Clone)]
pub enum TokenOrRedirect {
Atom(ClauseName),
+ Char(char),
NumberedVar(String),
Redirect,
Open,
OpenList(Rc<Cell<bool>>),
CloseList(Rc<Cell<bool>>),
HeadTailSeparator,
-// Space
}
pub trait HCValueFormatter {
}).collect()
}
+fn non_quoted_token(c: char) -> bool {
+ graphic_token_char!(c) || alpha_numeric_char!(c)
+}
+
impl<'a, Formatter: HCValueFormatter, Outputter: HCValueOutputter>
HCPrinter<'a, Formatter, Outputter>
{
}
fn print_atom(&mut self, atom: &ClauseName) {
- let non_quoted_token = |c| {
- graphic_token_char!(c) || alpha_numeric_char!(c)
- };
-
match atom.as_str() {
";" | "!" => self.outputter.append(atom.as_str()),
s => if s.chars().all(non_quoted_token) {
self.outputter.append(atom.as_str());
} else {
- self.outputter.append(&("'".to_owned() + atom.as_str() + "'"));
+ self.outputter.push_char('\'');
+ self.outputter.append(atom.as_str());
+ self.outputter.push_char('\'');
}
}
}
+
+ fn expand_char_list(&mut self, s: StringList) {
+ let cell = Rc::new(Cell::new(true));
+ let cursor = s.cursor();
+
+ self.state_stack.push(TokenOrRedirect::CloseList(cell.clone()));
+
+ if !s.is_empty() {
+ for c in s.borrow()[cursor ..].chars().rev() {
+ self.state_stack.push(TokenOrRedirect::Char(c));
+ self.state_stack.push(TokenOrRedirect::Comma);
+ }
+
+ self.state_stack.pop();
+ }
+
+ self.state_stack.push(TokenOrRedirect::OpenList(cell));
+ }
+
+ fn print_char(&mut self, c: char) {
+ if non_quoted_token(c) {
+ self.outputter.push_char(c);
+ } else {
+ self.outputter.push_char('\'');
+ self.outputter.push_char(c);
+ self.outputter.push_char('\'');
+ }
+ }
fn print_constant(&mut self, c: Constant) {
match c {
// self.outputter.append("\a"),
// Constant::Char(c) if c == '\\v' =>
// self.outputter.append("\\v"),
- Constant::Char(c) => {
- self.outputter.append("'");
- self.outputter.push_char(c);
- self.outputter.append("'");
- },
+ Constant::Char(c) =>
+ self.print_char(c),
Constant::EmptyList =>
self.outputter.append("[]"),
Constant::Number(Number::Float(fl)) =>
},
Constant::Number(n) =>
self.outputter.append(&format!("{}", n)),
- Constant::String(s) => {
- self.outputter.append("\"");
- self.outputter.append(s.borrow().as_str());
- self.outputter.append("\"");
- },
+ Constant::String(s) =>
+ if let DoubleQuotes::Chars = self.machine_st.machine_flags().double_quotes {
+ self.expand_char_list(s);
+ } else { // for now, == DoubleQuotes::Atom
+ self.outputter.append("\"");
+ self.outputter.append(s.borrow().as_str());
+ self.outputter.append("\"");
+ },
Constant::Usize(i) =>
self.outputter.append(&format!("u{}", i))
}
loop {
if let Some(loc_data) = self.state_stack.pop() {
match loc_data {
-// TokenOrRedirect::Space =>
-// self.outputter.append(" "),
TokenOrRedirect::Atom(atom) =>
self.outputter.append(atom.as_str()),
+ TokenOrRedirect::Char(c) =>
+ self.print_char(c),
TokenOrRedirect::NumberedVar(num_var) =>
self.outputter.append(num_var.as_str()),
TokenOrRedirect::Redirect =>
use prolog::ast::*;
+use prolog::machine::machine_state::MachineFlags;
use std::collections::{HashMap, VecDeque};
use std::hash::Hash;
}
pub struct CodeOffsets {
+ flags: MachineFlags,
pub constants: HashMap<Constant, ThirdLevelIndex>,
pub lists: ThirdLevelIndex,
pub structures: HashMap<(ClauseName, usize), ThirdLevelIndex>
}
impl CodeOffsets {
- pub fn new() -> Self {
+ pub fn new(flags: MachineFlags) -> Self {
CodeOffsets {
+ flags,
constants: HashMap::new(),
lists: Vec::new(),
structures: HashMap::new()
let is_initial_index = self.lists.is_empty();
self.lists.push(Self::add_index(is_initial_index, index));
},
+ &Term::Constant(_, Constant::String(_))
+ if self.flags.double_quotes.is_chars() => { // strings are lists in this case.
+ let is_initial_index = self.lists.is_empty();
+ self.lists.push(Self::add_index(is_initial_index, index));
+ },
&Term::Constant(_, ref constant) => {
let code = self.constants.entry(constant.clone())
.or_insert(Vec::new());
}
#[derive(Clone, Copy)]
-pub(super) enum DoubleQuotes {
+pub enum DoubleQuotes {
Atom, Chars, // Codes
}
+impl DoubleQuotes {
+ pub fn is_chars(self) -> bool {
+ if let DoubleQuotes::Chars = self {
+ true
+ } else {
+ false
+ }
+ }
+}
+
impl Default for DoubleQuotes {
fn default() -> Self {
DoubleQuotes::Chars
}
#[derive(Clone, Copy)]
-pub(super) struct MachineFlags {
- pub(super) double_quotes: DoubleQuotes
+pub struct MachineFlags {
+ pub double_quotes: DoubleQuotes
}
impl Default for MachineFlags {
}
}
+ #[inline]
+ pub fn machine_flags(&self) -> MachineFlags {
+ self.flags
+ }
+
fn next_global_index(&self) -> usize {
max(if self.and_stack.len() > 0 { self.and_stack[self.e].global_index } else { 0 },
if self.b > 0 { self.or_stack[self.b - 1].global_index } else { 0 }) + 1
self.fail = true;
},
+ (Addr::Con(Constant::Char(c)), Addr::Con(Constant::Atom(atom)))
+ | (Addr::Con(Constant::Atom(atom)), Addr::Con(Constant::Char(c))) => {
+ let s = atom.as_str();
+ if c.len_utf8() != s.len() || Some(c) != s.chars().next() {
+ self.fail = true;
+ }
+ },
+ (Addr::Lis(a1), Addr::Con(Constant::String(ref s)))
+ | (Addr::Con(Constant::String(ref s)), Addr::Lis(a1))
+ if self.flags.double_quotes.is_chars() =>
+ if let Some(c) = s.head() {
+ pdl.push(Addr::Con(Constant::String(s.tail())));
+ pdl.push(Addr::HeapCell(a1 + 1));
+
+ pdl.push(Addr::Con(Constant::Char(c)));
+ pdl.push(Addr::HeapCell(a1));
+ } else {
+ self.fail = true;
+ },
+ (Addr::Con(Constant::EmptyList), Addr::Con(Constant::String(ref s)))
+ | (Addr::Con(Constant::String(ref s)), Addr::Con(Constant::EmptyList))
+ if self.flags.double_quotes.is_chars() => {
+ self.fail = !s.is_empty();
+ },
(Addr::Lis(a1), Addr::Lis(a2)) => {
pdl.push(Addr::HeapCell(a1));
pdl.push(Addr::HeapCell(a2));
let addr = self.store(self.deref(self[reg].clone()));
match addr {
+ Addr::Con(Constant::String(ref s))
+ if self.flags.double_quotes.is_chars() => {
+ if let Some(c) = s.head() {
+ let h = self.heap.h;
+
+ self.heap.push(HeapCellValue::Addr(Addr::Con(Constant::Char(c))));
+ self.heap.push(HeapCellValue::Addr(Addr::Con(Constant::String(s.tail()))));
+
+ self.s = h;
+ self.mode = MachineMode::Read;
+ } else {
+ self.fail = true;
+ }
+ },
Addr::HeapCell(hc) => {
let h = self.heap.h;
let offset = match addr {
Addr::HeapCell(_) | Addr::StackCell(_, _) => v,
+ Addr::Con(Constant::String(_)) if self.flags.double_quotes.is_chars() => l,
Addr::Con(_) => c,
Addr::Lis(_) => l,
Addr::Str(_) => s
let d = self.store(self.deref(self[r1].clone()));
match d {
- Addr::Con(Constant::Atom(_)) => self.p += 1,
+ Addr::Con(Constant::Atom(_)) | Addr::Con(Constant::Char(_)) => self.p += 1,
_ => self.fail = true
};
},
wam
}
+ #[inline]
+ pub fn machine_flags(&self) -> MachineFlags {
+ self.ms.flags
+ }
+
fn remove_module(&mut self, module_name: ClauseName) {
let iter = if let Some(submodule) = self.modules.get(&module_name) {
submodule.module_decl.exports.iter().cloned()
-Subproject commit 6d995645d57849f0f16f8ce50c124e8875c9e76b
+Subproject commit b5eafac5128ce9a4831619deb2f2d075f23f8781
let atom_tbl = self.machine_st.atom_tbl.clone();
let string_tbl = self.machine_st.string_tbl.clone();
+ let flags = self.machine_st.machine_flags();
- let mut parser = Parser::new(buffer.as_bytes(), atom_tbl, string_tbl);
+ let mut parser = Parser::new(buffer.as_bytes(), atom_tbl, string_tbl, flags);
Ok(self.write_term_to_heap(parser.read_term(op_dir)?))
}
pub struct StringList {
body: TabledRc<StringListWrapper>,
cursor: usize, // use this to generate a chars() iterator on the fly,
- // and skip over the first cursor chars.
+ // and skip over the first cursor chars.
expandable: bool
}
}
}
+ #[inline]
+ pub fn cursor(&self) -> usize {
+ self.cursor
+ }
+
+ #[inline]
+ pub fn head(&self) -> Option<char> {
+ self.borrow()[self.cursor ..].chars().next()
+ }
+
#[inline]
pub fn tail(&self) -> Self {
let mut new_string_list = self.clone();
-
- if let Some(c) = self.borrow()[self.cursor ..].chars().next() {
+
+ if let Some(c) = self.head() {
new_string_list.cursor += c.len_utf8();
}
-
+
new_string_list
}
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.borrow().len() == self.cursor
+ }
+
#[inline]
pub fn borrow(&self) -> Ref<String> {
self.body.0.borrow()
use prolog::ast::*;
use prolog::machine::*;
+use prolog::machine::machine_state::MachineFlags;
use prolog::num::*;
use prolog::parser::parser::*;
use prolog::string_list::*;
impl<'a, R: Read> TopLevelWorker<'a, R> {
pub fn new(inner: R, atom_tbl: TabledData<Atom>, string_tbl: TabledData<StringListWrapper>,
- indices: MachineCodeIndices<'a>)
+ flags: MachineFlags, indices: MachineCodeIndices<'a>)
-> Self
{
- TopLevelWorker { parser: Parser::new(inner, atom_tbl, string_tbl), indices }
+ TopLevelWorker { parser: Parser::new(inner, atom_tbl, string_tbl, flags), indices }
}
pub fn parse_code(&mut self) -> Result<TopLevelPacket, ParserError>
}
impl<R: Read> TopLevelBatchWorker<R> {
- pub fn new(inner: R, atom_tbl: TabledData<Atom>, string_tbl: TabledData<StringListWrapper>)
+ pub fn new(inner: R, atom_tbl: TabledData<Atom>, string_tbl: TabledData<StringListWrapper>,
+ flags: MachineFlags)
-> Self
{
- TopLevelBatchWorker { parser: Parser::new(inner, atom_tbl, string_tbl),
+ TopLevelBatchWorker { parser: Parser::new(inner, atom_tbl, string_tbl, flags),
rel_worker: RelationWorker::new(),
source_mod: clause_name!("user"),
results: vec![] }
assert_prolog_success!(&mut wam, "?- functor(Func, f, 4).", [["Func = f(_2, _3, _4, _5)"]]);
assert_prolog_success!(&mut wam, "?- catch(functor(F, \"sdf\", 3), error(E, _), true).",
- [["E = type_error(atom, \"sdf\")", "F = _1"]]);
+ [["E = type_error(atom, [s, d, f])", "F = _1"]]);
assert_prolog_success!(&mut wam, "?- catch(functor(Func, F, 3), error(E, _), true).",
[["E = instantiation_error", "Func = _1", "F = _2"]]);
assert_prolog_success!(&mut wam, "?- catch(functor(Func, f, N), error(E, _), true).",
assert_prolog_success!(&mut wam, "?- X is 3 + 3.5, \\+ integer(X).");
assert_prolog_success!(&mut wam, "?- Func =.. [atom].", [["Func = atom"]]);
- assert_prolog_success!(&mut wam, "?- Func =.. [\"sdf\"].", [["Func = \"sdf\""]]);
+ assert_prolog_success!(&mut wam, "?- Func =.. [\"sdf\"].", [["Func = [s, d, f]"]]);
assert_prolog_success!(&mut wam, "?- Func =.. [1].", [["Func = 1"]]);
assert_prolog_success!(&mut wam, "?- catch(Func =.. [1,2], error(type_error(atom, 1), _), true).");
assert_prolog_success!(&mut wam, "?- f(1,2,3) =.. List.", [["List = [f, 1, 2, 3]"]]);