prolog_parser/
ast.rs

1use rug::{Integer, Rational};
2use ordered_float::*;
3use tabled_rc::*;
4
5use put_back_n::*;
6
7use std::cell::Cell;
8use std::cmp::Ordering;
9use std::collections::HashMap;
10use std::fmt;
11use std::hash::{Hash, Hasher};
12use std::io::{Bytes, Error as IOError, Read};
13use std::rc::Rc;
14use std::vec::Vec;
15
16use unicode_reader::CodePoints;
17
18pub type Atom = String;
19
20pub type Var = String;
21
22pub type Specifier = u32;
23
24pub const MAX_ARITY: usize = 1023;
25
26pub const XFX: u32 = 0x0001;
27pub const XFY: u32 = 0x0002;
28pub const YFX: u32 = 0x0004;
29pub const XF: u32  = 0x0010;
30pub const YF: u32  = 0x0020;
31pub const FX: u32  = 0x0040;
32pub const FY: u32  = 0x0080;
33pub const DELIMITER: u32 = 0x0100;
34pub const TERM: u32  = 0x1000;
35pub const LTERM: u32 = 0x3000;
36
37pub const NEGATIVE_SIGN: u32 = 0x0200;
38
39#[macro_export]
40macro_rules! clause_name {
41    ($name: expr, $tbl: expr) => (
42        ClauseName::User(TabledRc::new($name, $tbl.clone()))
43    ) ;
44    ($name: expr) => (
45        ClauseName::BuiltIn($name)
46    )
47}
48
49#[macro_export]
50macro_rules! atom {
51    ($e:expr, $tbl:expr) => (
52        Constant::Atom(ClauseName::User(tabled_rc!($e, $tbl)), None)
53    );
54    ($e:expr) => (
55        Constant::Atom(clause_name!($e), None)
56    )
57}
58
59#[macro_export]
60macro_rules! rc_atom {
61    ($e:expr) => (
62        Rc::new(String::from($e))
63    )
64}
65macro_rules! is_term {
66    ($x:expr) => ( ($x & TERM) != 0 )
67}
68
69macro_rules! is_lterm {
70    ($x:expr) => ( ($x & LTERM) != 0 )
71}
72
73macro_rules! is_op {
74    ($x:expr) => ( $x & (XF | YF | FX | FY | XFX | XFY | YFX) != 0 )
75}
76
77macro_rules! is_negate {
78    ($x:expr) => ( ($x & NEGATIVE_SIGN) != 0 )
79}
80
81#[macro_export]
82macro_rules! is_prefix {
83    ($x:expr) => ( $x & (FX | FY) != 0 )
84}
85
86#[macro_export]
87macro_rules! is_postfix {
88    ($x:expr) => ( $x & (XF | YF) != 0 )
89}
90
91#[macro_export]
92macro_rules! is_infix {
93    ($x:expr) => ( ($x & (XFX | XFY | YFX)) != 0 )
94}
95
96#[macro_export]
97macro_rules! is_xfx {
98    ($x:expr) => ( ($x & XFX) != 0 )
99}
100
101#[macro_export]
102macro_rules! is_xfy {
103    ($x:expr) => ( ($x & XFY) != 0 )
104}
105
106#[macro_export]
107macro_rules! is_yfx {
108    ($x:expr) => ( ($x & YFX) != 0 )
109}
110
111#[macro_export]
112macro_rules! is_yf {
113    ($x:expr) => ( ($x & YF) != 0 )
114}
115
116#[macro_export]
117macro_rules! is_xf {
118    ($x:expr) => ( ($x & XF) != 0 )
119}
120
121#[macro_export]
122macro_rules! is_fx {
123    ($x:expr) => ( ($x & FX) != 0 )
124}
125
126#[macro_export]
127macro_rules! is_fy {
128    ($x:expr) => ( ($x & FY) != 0 )
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
132pub enum RegType {
133    Perm(usize),
134    Temp(usize)
135}
136
137impl Default for RegType {
138    fn default() -> Self {
139        RegType::Temp(0)
140    }
141}
142
143impl RegType {
144    pub fn reg_num(self) -> usize {
145        match self {
146            RegType::Perm(reg_num) | RegType::Temp(reg_num) => reg_num
147        }
148    }
149
150    pub fn is_perm(self) -> bool {
151        match self {
152            RegType::Perm(_) => true,
153            _ => false
154        }
155    }
156}
157
158impl fmt::Display for RegType {
159    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
160        match self {
161            &RegType::Perm(val) => write!(f, "Y{}", val),
162            &RegType::Temp(val) => write!(f, "X{}", val)
163        }
164    }
165}
166
167#[derive(Debug, PartialEq, Eq, Clone, Copy)]
168pub enum VarReg {
169    ArgAndNorm(RegType, usize),
170    Norm(RegType)
171}
172
173impl VarReg {
174    pub fn norm(self) -> RegType {
175        match self {
176            VarReg::ArgAndNorm(reg, _) | VarReg::Norm(reg) => reg
177        }
178    }
179}
180
181impl fmt::Display for VarReg {
182    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183        match self {
184            &VarReg::Norm(RegType::Perm(reg)) => write!(f, "Y{}", reg),
185            &VarReg::Norm(RegType::Temp(reg)) => write!(f, "X{}", reg),
186            &VarReg::ArgAndNorm(RegType::Perm(reg), arg) =>
187                write!(f, "Y{} A{}", reg, arg),
188            &VarReg::ArgAndNorm(RegType::Temp(reg), arg) =>
189                write!(f, "X{} A{}", reg, arg)
190        }
191    }
192}
193
194impl Default for VarReg {
195    fn default() -> Self {
196        VarReg::Norm(RegType::default())
197    }
198}
199
200#[macro_export]
201macro_rules! temp_v {
202    ($x:expr) => (
203        RegType::Temp($x)
204    )
205}
206
207#[macro_export]
208macro_rules! perm_v {
209    ($x:expr) => (
210        RegType::Perm($x)
211    )
212}
213
214#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
215pub enum GenContext {
216    Head, Mid(usize), Last(usize) // Mid & Last: chunk_num
217}
218
219impl GenContext {
220    pub fn chunk_num(self) -> usize {
221        match self {
222            GenContext::Head => 0,
223            GenContext::Mid(cn) | GenContext::Last(cn) => cn
224        }
225    }
226}
227
228pub type OpDirKey = (ClauseName, Fixity);
229
230#[derive(Debug, Clone)]
231pub struct OpDirValue(pub SharedOpDesc, pub ClauseName);
232
233impl OpDirValue {
234    pub fn new(spec: Specifier, priority: usize, module_name: ClauseName) -> Self {
235        OpDirValue(SharedOpDesc::new(priority, spec), module_name)
236    }
237
238    #[inline]
239    pub fn shared_op_desc(&self) -> SharedOpDesc {
240        self.0.clone()
241    }
242
243    #[inline]
244    pub fn owning_module(&self) -> ClauseName {
245        self.1.clone()
246    }
247}
248
249// name and fixity -> operator type and precedence.
250pub type OpDir = HashMap<OpDirKey, OpDirValue>;
251
252#[derive(Debug, Clone, Copy)]
253pub struct MachineFlags {
254    pub double_quotes: DoubleQuotes
255}
256
257impl Default for MachineFlags {
258    fn default() -> Self {
259        MachineFlags { double_quotes: DoubleQuotes::default() }
260    }
261}
262
263#[derive(Debug, Clone, Copy)]
264pub enum DoubleQuotes {
265    Atom, Chars, Codes
266}
267
268impl DoubleQuotes {
269    pub fn is_chars(self) -> bool {
270        if let DoubleQuotes::Chars = self {
271            true
272        } else {
273            false
274        }
275    }
276
277    pub fn is_atom(self) -> bool {
278        if let DoubleQuotes::Atom = self {
279            true
280        } else {
281            false
282        }
283    }
284
285    pub fn is_codes(self) -> bool {
286        if let DoubleQuotes::Codes = self {
287            true
288        } else {
289            false
290        }
291    }
292}
293
294impl Default for DoubleQuotes {
295    fn default() -> Self {
296        DoubleQuotes::Chars
297    }
298}
299
300pub fn default_op_dir() -> OpDir {
301    let module_name = clause_name!("builtins");
302    let mut op_dir = OpDir::new();
303
304    op_dir.insert((clause_name!(":-"), Fixity::In),  OpDirValue::new(XFX, 1200, module_name.clone()));
305    op_dir.insert((clause_name!(":-"), Fixity::Pre), OpDirValue::new(FX,  1200, module_name.clone()));
306    op_dir.insert((clause_name!("?-"), Fixity::Pre), OpDirValue::new(FX,  1200, module_name.clone()));
307    op_dir.insert((clause_name!(","), Fixity::In),   OpDirValue::new(XFY, 1000, module_name.clone()));
308
309    op_dir
310}
311
312#[derive(Debug, Clone)]
313pub enum ArithmeticError {
314    NonEvaluableFunctor(Constant, usize),
315    UninstantiatedVar
316}
317
318#[derive(Debug)]
319pub enum ParserError {
320    Arithmetic(ArithmeticError),
321    BackQuotedString(usize, usize),
322    BadPendingByte,
323    CannotParseCyclicTerm,
324    UnexpectedChar(char, usize, usize),
325    UnexpectedEOF,
326    IO(IOError),
327    ExpectedRel,
328    ExpectedTopLevelTerm,
329    InadmissibleFact,
330    InadmissibleQueryTerm,
331    IncompleteReduction(usize, usize),
332    InconsistentEntry,
333    InvalidDoubleQuotesDecl,
334    InvalidHook,
335    InvalidModuleDecl,
336    InvalidModuleExport,
337    InvalidRuleHead,
338    InvalidUseModuleDecl,
339    InvalidModuleResolution,
340    InvalidSingleQuotedCharacter(char),
341    MissingQuote(usize, usize),
342    NonPrologChar(usize, usize),
343    ParseBigInt(usize, usize),
344    ParseFloat(usize, usize),
345    Utf8Error(usize, usize)
346}
347
348impl ParserError {
349    pub fn line_and_col_num(&self) -> Option<(usize, usize)> {
350        match self {
351            &ParserError::BackQuotedString(line_num, col_num)
352          | &ParserError::UnexpectedChar(_, line_num, col_num)
353          | &ParserError::IncompleteReduction(line_num, col_num)
354          | &ParserError::MissingQuote(line_num, col_num)
355          | &ParserError::NonPrologChar(line_num, col_num)
356          | &ParserError::ParseBigInt(line_num, col_num)
357          | &ParserError::ParseFloat(line_num, col_num)
358          | &ParserError::Utf8Error(line_num, col_num) =>
359                Some((line_num, col_num)),
360            _ =>
361                None
362        }
363    }
364
365    pub fn as_str(&self) -> &'static str {
366        match self {
367            &ParserError::Arithmetic(..) =>
368                "arithmetic_error",
369            &ParserError::BackQuotedString(..) =>
370                "back_quoted_string",
371            &ParserError::BadPendingByte =>
372                "bad_pending_byte",
373            &ParserError::UnexpectedChar(..) =>
374                "unexpected_char",
375            &ParserError::UnexpectedEOF =>
376                "unexpected_end_of_file",
377            &ParserError::ExpectedRel =>
378                "expected_relation",
379            &ParserError::ExpectedTopLevelTerm =>
380                "expected_atom_or_cons_or_clause",
381            &ParserError::InadmissibleFact =>
382                "inadmissible_fact",
383            &ParserError::InadmissibleQueryTerm =>
384                "inadmissible_query_term",
385            &ParserError::IncompleteReduction(..) =>
386                "incomplete_reduction",
387            &ParserError::InconsistentEntry =>
388                "inconsistent_entry",
389            &ParserError::InvalidDoubleQuotesDecl =>
390                "invalid_double_quotes_declaration",
391            &ParserError::InvalidHook =>
392                "invalid_hook",
393            &ParserError::InvalidModuleDecl =>
394                "invalid_module_declaration",
395            &ParserError::InvalidModuleExport =>
396                "invalid_module_export",
397            &ParserError::InvalidModuleResolution =>
398                "invalid_module_resolution",
399            &ParserError::InvalidRuleHead =>
400                "invalid_head_of_rule",
401            &ParserError::InvalidUseModuleDecl =>
402                "invalid_use_module_declaration",
403            &ParserError::InvalidSingleQuotedCharacter(..) =>
404                "invalid_single_quoted_character",
405            &ParserError::IO(_) =>
406                "input_output_error",
407            &ParserError::MissingQuote(..) =>
408                "missing_quote",
409            &ParserError::NonPrologChar(..) =>
410                "non_prolog_character",
411            &ParserError::ParseBigInt(..) =>
412                "cannot_parse_big_int",
413            &ParserError::ParseFloat(..) =>
414                "cannot_parse_float",
415            &ParserError::Utf8Error(..) =>
416                "utf8_conversion_error",
417            &ParserError::CannotParseCyclicTerm =>
418                "cannot_parse_cyclic_term"
419        }
420    }
421}
422
423impl From<ArithmeticError> for ParserError {
424    fn from(err: ArithmeticError) -> ParserError {
425        ParserError::Arithmetic(err)
426    }
427}
428
429impl From<IOError> for ParserError {
430    fn from(err: IOError) -> ParserError {
431        ParserError::IO(err)
432    }
433}
434
435impl From<&IOError> for ParserError {
436    fn from(error: &IOError) -> ParserError {
437        if error.get_ref().filter(|e| e.is::<BadUtf8Error>()).is_some() {
438            ParserError::Utf8Error(0, 0)
439        } else {
440            ParserError::IO(error.kind().into())
441        }
442    }
443}
444
445
446#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
447pub enum Fixity {
448    In, Post, Pre
449}
450
451#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
452pub struct SharedOpDesc(Rc<Cell<(usize, Specifier)>>);
453
454impl SharedOpDesc {
455    #[inline]
456    pub fn new(priority: usize, spec: Specifier) -> Self {
457        SharedOpDesc(Rc::new(Cell::new((priority, spec))))
458    }
459
460    #[inline]
461    pub fn ptr_eq(lop_desc: &SharedOpDesc, rop_desc: &SharedOpDesc) -> bool {
462        Rc::ptr_eq(&lop_desc.0, &rop_desc.0)
463    }
464
465    #[inline]
466    pub fn arity(&self) -> usize {
467        if self.get().1 & (XFX | XFY | YFX) == 0 {
468            1
469        } else {
470            2
471        }
472    }
473
474    #[inline]
475    pub fn get(&self) -> (usize, Specifier) {
476        self.0.get()
477    }
478
479    #[inline]
480    pub fn set(&self, prec: usize, spec: Specifier) {
481        self.0.set((prec, spec));
482    }
483
484    #[inline]
485    pub fn prec(&self) -> usize {
486        self.0.get().0
487    }
488
489    #[inline]
490    pub fn assoc(&self) -> Specifier {
491        self.0.get().1
492    }
493}
494
495// this ensures that SharedOpDesc (which is not consistently placed in
496// every atom!) doesn't affect the value of an atom hash. If
497// SharedOpDesc values are to be indexed, a BTreeMap or BTreeSet
498// should be used, obviously.
499impl Hash for SharedOpDesc {
500    fn hash<H: Hasher>(&self, state: &mut H) {
501        0.hash(state)
502    }
503}
504
505#[derive(Debug, Clone, Hash)]
506pub enum Constant {
507    Atom(ClauseName, Option<SharedOpDesc>),
508    Char(char),
509    EmptyList,
510    Fixnum(isize),
511    Integer(Rc<Integer>),
512    Rational(Rc<Rational>),
513    Float(OrderedFloat<f64>),
514    String(Rc<String>),
515    Usize(usize),
516}
517
518impl fmt::Display for Constant {
519    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
520        match self {
521            &Constant::Atom(ref atom, _) =>
522                if atom.as_str().chars().any(|c| "`.$'\" ".contains(c)) {
523                    write!(f, "'{}'", atom.as_str())
524                } else {
525                    write!(f, "{}", atom.as_str())
526                },
527            &Constant::Char(c) =>
528                write!(f, "'{}'", c as u32),
529            &Constant::EmptyList =>
530                write!(f, "[]"),
531            &Constant::Fixnum(n) =>
532                write!(f, "{}", n),
533            &Constant::Integer(ref n) =>
534                write!(f, "{}", n),
535            &Constant::Rational(ref n) =>
536                write!(f, "{}", n),
537            &Constant::Float(ref n) =>
538                write!(f, "{}", n),
539            &Constant::String(ref s) =>
540                write!(f, "\"{}\"", &s),
541            &Constant::Usize(integer) =>
542                write!(f, "u{}", integer),
543        }
544    }
545}
546
547impl PartialEq for Constant {
548    fn eq(&self, other: &Constant) -> bool {
549        match (self, other) {
550            (&Constant::Atom(ref atom, _), &Constant::Char(c))
551          | (&Constant::Char(c), &Constant::Atom(ref atom, _)) => {
552              atom.is_char() && Some(c) == atom.as_str().chars().next()
553            },
554            (&Constant::Atom(ref a1, _), &Constant::Atom(ref a2, _)) =>
555                a1.as_str() == a2.as_str(),
556            (&Constant::Char(c1), &Constant::Char(c2)) =>
557                c1 == c2,
558            (&Constant::Fixnum(n1), &Constant::Fixnum(n2)) =>
559                n1 == n2,
560            (&Constant::Fixnum(n1), &Constant::Integer(ref n2)) |
561            (&Constant::Integer(ref n2), &Constant::Fixnum(n1)) => {
562                if let Some(n2) = n2.to_isize() {
563                    n1 == n2
564                } else {
565                    false
566                }
567            }
568            (&Constant::Integer(ref n1), &Constant::Integer(ref n2)) =>
569                n1 == n2,
570            (&Constant::Rational(ref n1), &Constant::Rational(ref n2)) =>
571                n1 == n2,
572            (&Constant::Float(ref n1), &Constant::Float(ref n2)) =>
573                n1 == n2,
574            (&Constant::String(ref s1), &Constant::String(ref s2)) => {
575                &s1 == &s2
576            }
577            (&Constant::EmptyList, &Constant::EmptyList) =>
578                true,
579            (&Constant::Usize(u1), &Constant::Usize(u2)) =>
580                u1 == u2,
581            _ => false
582        }
583    }
584}
585
586impl Eq for Constant {}
587
588impl Constant {
589    pub fn to_atom(self) -> Option<ClauseName> {
590        match self {
591            Constant::Atom(a, _) => Some(a.defrock_brackets()),
592            _ => None
593        }
594    }
595}
596
597#[derive(Debug, Clone)]
598pub enum ClauseName {
599    BuiltIn(&'static str),
600    User(TabledRc<Atom>)
601}
602
603impl fmt::Display for ClauseName {
604    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
605        write!(f, "{}", self.as_str())
606    }
607}
608
609impl Hash for ClauseName {
610    fn hash<H: Hasher>(&self, state: &mut H) {
611        (*self.as_str()).hash(state)
612    }
613}
614
615impl PartialEq for ClauseName {
616    fn eq(&self, other: &ClauseName) -> bool {
617        *self.as_str() == *other.as_str()
618    }
619}
620
621impl Eq for ClauseName {}
622
623impl Ord for ClauseName {
624    fn cmp(&self, other: &ClauseName) -> Ordering {
625        (*self.as_str()).cmp(other.as_str())
626    }
627}
628
629impl PartialOrd for ClauseName {
630    fn partial_cmp(&self, other: &ClauseName) -> Option<Ordering> {
631        Some(self.cmp(other))
632    }
633}
634
635impl<'a> From<&'a TabledRc<Atom>> for ClauseName {
636    fn from(name: &'a TabledRc<Atom>) -> ClauseName {
637        ClauseName::User(name.clone())
638    }
639}
640
641impl ClauseName {
642    #[inline]
643    pub fn owning_module(&self) -> Self {
644        match self {
645            &ClauseName::User(ref name) => {
646                let module = name.owning_module();
647                ClauseName::User(TabledRc { atom: module.clone(),
648                                            table: TabledData::new(module) })
649            },
650            _ => clause_name!("user")
651        }
652    }
653
654    #[inline]
655    pub fn to_rc(&self) -> Rc<String> {
656        match self {
657            &ClauseName::BuiltIn(s) => Rc::new(s.to_string()),
658            &ClauseName::User(ref rc) => rc.inner()
659        }
660    }
661
662    #[inline]
663    pub fn with_table(self, atom_tbl: TabledData<Atom>) -> Self {
664        match self {
665            ClauseName::BuiltIn(_) => self,
666            ClauseName::User(mut name) => {
667                name.table = atom_tbl;
668                ClauseName::User(name)
669            }
670        }
671    }
672
673    #[inline]
674    pub fn has_table(&self, atom_tbl: &TabledData<Atom>) -> bool {
675        match self {
676            ClauseName::BuiltIn(_) => false,
677            ClauseName::User(ref name) => &name.table == atom_tbl,
678        }
679    }
680
681    #[inline]
682    pub fn has_table_of(&self, other: &ClauseName) -> bool {
683        match self {
684            ClauseName::BuiltIn(_) => {
685                if let ClauseName::BuiltIn(_) = other {
686                    true
687                } else {
688                    false
689                }
690            }
691            ClauseName::User(ref name) => {
692                other.has_table(&name.table)
693            }
694        }
695    }
696
697    #[inline]
698    pub fn as_str(&self) -> &str {
699        match self {
700            &ClauseName::BuiltIn(s) => s,
701            &ClauseName::User(ref name) => name.as_ref()
702        }
703    }
704
705    #[inline]
706    pub fn is_char(&self) -> bool {
707        !self.as_str().is_empty() && self.as_str().chars().skip(1).next().is_none()
708    }
709
710    pub fn defrock_brackets(self) -> Self {
711        fn defrock_brackets(s: &str) -> &str {
712            if s.starts_with('(') && s.ends_with(')') {
713                &s[1 .. s.len() - 1]
714            } else {
715                s
716            }
717        }
718
719        match self {
720            ClauseName::BuiltIn(s) =>
721                ClauseName::BuiltIn(defrock_brackets(s)),
722            ClauseName::User(s) =>
723                ClauseName::User(tabled_rc!(defrock_brackets(s.as_str()).to_owned(), s.table))
724        }
725    }
726}
727
728impl AsRef<str> for ClauseName {
729    #[inline]
730    fn as_ref(self: &Self) -> &str {
731        self.as_str()
732    }
733}
734
735#[derive(Debug, PartialEq, Eq, Clone)]
736pub enum Term {
737    AnonVar,
738    Clause(Cell<RegType>, ClauseName, Vec<Box<Term>>, Option<SharedOpDesc>),
739    Cons(Cell<RegType>, Box<Term>, Box<Term>),
740    Constant(Cell<RegType>, Constant),
741    Var(Cell<VarReg>, Rc<Var>)
742}
743
744impl Term {
745    pub fn shared_op_desc(&self) -> Option<SharedOpDesc> {
746        match self {
747            &Term::Clause(_, _, _, ref spec) => spec.clone(),
748            &Term::Constant(_, Constant::Atom(_, ref spec)) => spec.clone(),
749            _ => None
750        }
751    }
752
753    pub fn to_constant(self) -> Option<Constant> {
754        match self {
755            Term::Constant(_, c) => Some(c),
756            _ => None
757        }
758    }
759
760    pub fn first_arg(&self) -> Option<&Term> {
761        match self {
762            &Term::Clause(_, _, ref terms, _) =>
763                terms.first().map(|bt| bt.as_ref()),
764            _ => None
765        }
766    }
767
768    pub fn set_name(&mut self, new_name: ClauseName) {
769        match self {
770            Term::Constant(_, Constant::Atom(ref mut atom, _))
771          | Term::Clause(_, ref mut atom, ..) => {
772              *atom = new_name;
773            }
774            _ => {}
775        }
776    }
777
778    pub fn name(&self) -> Option<ClauseName> {
779        match self {
780            &Term::Constant(_, Constant::Atom(ref atom, _))
781          | &Term::Clause(_, ref atom, ..) => Some(atom.clone()),
782            _ => None
783        }
784    }
785
786    pub fn arity(&self) -> usize {
787        match self {
788            &Term::Clause(_, _, ref child_terms, ..) => child_terms.len(),
789            _ => 0
790        }
791    }
792}
793
794#[derive(Debug, Clone, Copy)]
795pub struct CompositeOp<'a, 'b> {
796    pub op_dir: &'a OpDir,
797    pub static_op_dir: Option<&'b OpDir>
798}
799
800#[macro_export]
801macro_rules! composite_op {
802    ($include_machine_p:expr, $op_dir:expr, $machine_op_dir:expr) => (
803        CompositeOp { op_dir: $op_dir,
804                      static_op_dir: if !$include_machine_p {
805                          Some($machine_op_dir)
806                      } else {
807                          None
808                      }}
809    );
810    ($op_dir:expr) => (
811        CompositeOp { op_dir: $op_dir, static_op_dir: None }
812    )
813}
814
815impl<'a, 'b> CompositeOp<'a, 'b>
816{
817    #[inline]
818    pub(crate)
819    fn get(&self, name: ClauseName, fixity: Fixity) -> Option<OpDirValue>
820    {
821        let entry =
822            if let Some(ref static_op_dir) = &self.static_op_dir {
823                static_op_dir.get(&(name.clone(), fixity))
824            } else {
825                None
826            };
827
828        entry.or_else(move || self.op_dir.get(&(name, fixity)))
829             .cloned()
830    }
831}
832
833fn unfold_by_str_once(term: &mut Term, s: &str) -> Option<(Term, Term)> {
834    if let &mut Term::Clause(_, ref name, ref mut subterms, _) = term {
835        if name.as_str() == s && subterms.len() == 2 {
836            let snd = *subterms.pop().unwrap();
837            let fst = *subterms.pop().unwrap();
838
839            return Some((fst, snd));
840        }
841    }
842
843    None
844}
845
846pub fn unfold_by_str(mut term: Term, s: &str) -> Vec<Term> {
847    let mut terms = vec![];
848
849    while let Some((fst, snd)) = unfold_by_str_once(&mut term, s) {
850        terms.push(fst);
851        term = snd;
852    }
853
854    terms.push(term);
855    terms
856}
857
858pub type ParsingStream<R> = PutBackN<CodePoints<Bytes<R>>>;
859
860use unicode_reader::BadUtf8Error;
861
862#[inline]
863pub fn parsing_stream<R: Read>(src: R) -> Result<ParsingStream<R>, ParserError> {
864    let mut stream = put_back_n(CodePoints::from(src.bytes()));
865    match stream.peek() {
866        None => Ok(stream), // empty stream is handled gracefully by Lexer::eof
867        Some(Err(error)) => Err(ParserError::from(error)),
868        Some(Ok(c)) => {
869            if *c == '\u{feff}' {
870                // skip UTF-8 BOM
871                stream.next();
872            }
873            Ok(stream)
874        }
875    }
876}