use new heap term representation
This commit is contained in:
631
src/parser/ast.rs
Normal file
631
src/parser/ast.rs
Normal file
@@ -0,0 +1,631 @@
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::parser::char_reader::*;
|
||||
use crate::types::HeapCellValueTag;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::io::{Error as IOError};
|
||||
use std::ops::Neg;
|
||||
use std::rc::Rc;
|
||||
use std::vec::Vec;
|
||||
|
||||
use rug::{Integer, Rational};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use modular_bitfield::error::OutOfBounds;
|
||||
use modular_bitfield::prelude::*;
|
||||
|
||||
pub type Specifier = u32;
|
||||
|
||||
pub const MAX_ARITY: usize = 1023;
|
||||
|
||||
pub const XFX: u32 = 0x0001;
|
||||
pub const XFY: u32 = 0x0002;
|
||||
pub const YFX: u32 = 0x0004;
|
||||
pub const XF: u32 = 0x0010;
|
||||
pub const YF: u32 = 0x0020;
|
||||
pub const FX: u32 = 0x0040;
|
||||
pub const FY: u32 = 0x0080;
|
||||
pub const DELIMITER: u32 = 0x0100;
|
||||
pub const TERM: u32 = 0x1000;
|
||||
pub const LTERM: u32 = 0x3000;
|
||||
|
||||
pub const NEGATIVE_SIGN: u32 = 0x0200;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! fixnum {
|
||||
($wrapper:tt, $n:expr, $arena:expr) => {
|
||||
Fixnum::build_with_checked($n)
|
||||
.map(<$wrapper>::Fixnum)
|
||||
.unwrap_or_else(|_| <$wrapper>::Integer(arena_alloc!(Integer::from($n), $arena)))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! is_term {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::TERM) != 0
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! is_lterm {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::LTERM) != 0
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! is_op {
|
||||
($x:expr) => {
|
||||
$x as u32
|
||||
& ($crate::parser::ast::XF
|
||||
| $crate::parser::ast::YF
|
||||
| $crate::parser::ast::FX
|
||||
| $crate::parser::ast::FY
|
||||
| $crate::parser::ast::XFX
|
||||
| $crate::parser::ast::XFY
|
||||
| $crate::parser::ast::YFX)
|
||||
!= 0
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! is_negate {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::NEGATIVE_SIGN) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_prefix {
|
||||
($x:expr) => {
|
||||
$x as u32 & ($crate::parser::ast::FX | $crate::parser::ast::FY) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_postfix {
|
||||
($x:expr) => {
|
||||
$x as u32 & ($crate::parser::ast::XF | $crate::parser::ast::YF) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_infix {
|
||||
($x:expr) => {
|
||||
($x as u32
|
||||
& ($crate::parser::ast::XFX | $crate::parser::ast::XFY | $crate::parser::ast::YFX))
|
||||
!= 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_xfx {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::XFX) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_xfy {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::XFY) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_yfx {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::YFX) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_yf {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::YF) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_xf {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::XF) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_fx {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::FX) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_fy {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::FY) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum RegType {
|
||||
Perm(usize),
|
||||
Temp(usize),
|
||||
}
|
||||
|
||||
impl Default for RegType {
|
||||
fn default() -> Self {
|
||||
RegType::Temp(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl RegType {
|
||||
pub fn reg_num(self) -> usize {
|
||||
match self {
|
||||
RegType::Perm(reg_num) | RegType::Temp(reg_num) => reg_num,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_perm(self) -> bool {
|
||||
matches!(self, RegType::Perm(_))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RegType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
RegType::Perm(val) => write!(f, "Y{}", val),
|
||||
RegType::Temp(val) => write!(f, "X{}", val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum VarReg {
|
||||
ArgAndNorm(RegType, usize),
|
||||
Norm(RegType),
|
||||
}
|
||||
|
||||
impl VarReg {
|
||||
pub fn norm(self) -> RegType {
|
||||
match self {
|
||||
VarReg::ArgAndNorm(reg, _) | VarReg::Norm(reg) => reg,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for VarReg {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
VarReg::Norm(RegType::Perm(reg)) => write!(f, "Y{}", reg),
|
||||
VarReg::Norm(RegType::Temp(reg)) => write!(f, "X{}", reg),
|
||||
VarReg::ArgAndNorm(RegType::Perm(reg), arg) => write!(f, "Y{} A{}", reg, arg),
|
||||
VarReg::ArgAndNorm(RegType::Temp(reg), arg) => write!(f, "X{} A{}", reg, arg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VarReg {
|
||||
fn default() -> Self {
|
||||
VarReg::Norm(RegType::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! temp_v {
|
||||
($x:expr) => {
|
||||
$crate::parser::ast::RegType::Temp($x)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! perm_v {
|
||||
($x:expr) => {
|
||||
$crate::parser::ast::RegType::Perm($x)
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum GenContext {
|
||||
Head,
|
||||
Mid(usize),
|
||||
Last(usize), // Mid & Last: chunk_num
|
||||
}
|
||||
|
||||
impl GenContext {
|
||||
pub fn chunk_num(self) -> usize {
|
||||
match self {
|
||||
GenContext::Head => 0,
|
||||
GenContext::Mid(cn) | GenContext::Last(cn) => cn,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[bitfield]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
||||
pub struct OpDesc {
|
||||
prec: B11,
|
||||
spec: B8,
|
||||
#[allow(unused)] padding: B13,
|
||||
}
|
||||
|
||||
impl OpDesc {
|
||||
#[inline]
|
||||
pub fn build_with(prec: u16, spec: u8) -> Self {
|
||||
OpDesc::new().with_spec(spec).with_prec(prec)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(self) -> (u16, u8) {
|
||||
(self.prec(), self.spec())
|
||||
}
|
||||
|
||||
pub fn set(&mut self, prec: u16, spec: u8) {
|
||||
self.set_prec(prec);
|
||||
self.set_spec(spec);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_prec(self) -> u16 {
|
||||
self.prec()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_spec(self) -> u8 {
|
||||
self.spec()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn arity(self) -> usize {
|
||||
if self.spec() as u32 & (XFX | XFY | YFX) == 0 {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// name and fixity -> operator type and precedence.
|
||||
pub type OpDir = IndexMap<(Atom, Fixity), OpDesc>;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct MachineFlags {
|
||||
pub double_quotes: DoubleQuotes,
|
||||
}
|
||||
|
||||
impl Default for MachineFlags {
|
||||
fn default() -> Self {
|
||||
MachineFlags {
|
||||
double_quotes: DoubleQuotes::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum DoubleQuotes {
|
||||
Atom,
|
||||
Chars,
|
||||
Codes,
|
||||
}
|
||||
|
||||
impl DoubleQuotes {
|
||||
pub fn is_chars(self) -> bool {
|
||||
matches!(self, DoubleQuotes::Chars)
|
||||
}
|
||||
|
||||
pub fn is_atom(self) -> bool {
|
||||
matches!(self, DoubleQuotes::Atom)
|
||||
}
|
||||
|
||||
pub fn is_codes(self) -> bool {
|
||||
matches!(self, DoubleQuotes::Codes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DoubleQuotes {
|
||||
fn default() -> Self {
|
||||
DoubleQuotes::Chars
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_op_dir() -> OpDir {
|
||||
let mut op_dir = OpDir::new();
|
||||
|
||||
op_dir.insert(
|
||||
(atom!(":-"), Fixity::In),
|
||||
OpDesc::build_with(1200, XFX as u8),
|
||||
);
|
||||
op_dir.insert(
|
||||
(atom!(":-"), Fixity::Pre),
|
||||
OpDesc::build_with(1200, FX as u8),
|
||||
);
|
||||
op_dir.insert(
|
||||
(atom!("?-"), Fixity::Pre),
|
||||
OpDesc::build_with(1200, FX as u8),
|
||||
);
|
||||
op_dir.insert(
|
||||
(atom!(","), Fixity::In),
|
||||
OpDesc::build_with(1000, XFY as u8),
|
||||
);
|
||||
|
||||
op_dir
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ArithmeticError {
|
||||
NonEvaluableFunctor(Literal, usize),
|
||||
UninstantiatedVar,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ParserError {
|
||||
BackQuotedString(usize, usize),
|
||||
UnexpectedChar(char, usize, usize),
|
||||
UnexpectedEOF,
|
||||
IO(IOError),
|
||||
IncompleteReduction(usize, usize),
|
||||
InvalidSingleQuotedCharacter(char),
|
||||
MissingQuote(usize, usize),
|
||||
NonPrologChar(usize, usize),
|
||||
ParseBigInt(usize, usize),
|
||||
LexicalError(lexical::Error),
|
||||
Utf8Error(usize, usize),
|
||||
}
|
||||
|
||||
impl ParserError {
|
||||
pub fn line_and_col_num(&self) -> Option<(usize, usize)> {
|
||||
match self {
|
||||
&ParserError::BackQuotedString(line_num, col_num)
|
||||
| &ParserError::UnexpectedChar(_, line_num, col_num)
|
||||
| &ParserError::IncompleteReduction(line_num, col_num)
|
||||
| &ParserError::MissingQuote(line_num, col_num)
|
||||
| &ParserError::NonPrologChar(line_num, col_num)
|
||||
| &ParserError::ParseBigInt(line_num, col_num)
|
||||
| &ParserError::Utf8Error(line_num, col_num) => Some((line_num, col_num)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_atom(&self) -> Atom {
|
||||
match self {
|
||||
ParserError::BackQuotedString(..) => atom!("back_quoted_string"),
|
||||
ParserError::UnexpectedChar(..) => atom!("unexpected_char"),
|
||||
ParserError::UnexpectedEOF => atom!("unexpected_end_of_file"),
|
||||
ParserError::IncompleteReduction(..) => atom!("incomplete_reduction"),
|
||||
ParserError::InvalidSingleQuotedCharacter(..) => atom!("invalid_single_quoted_character"),
|
||||
ParserError::IO(_) => atom!("input_output_error"),
|
||||
ParserError::LexicalError(_) => atom!("lexical_error"), // TODO: ?
|
||||
ParserError::MissingQuote(..) => atom!("missing_quote"),
|
||||
ParserError::NonPrologChar(..) => atom!("non_prolog_character"),
|
||||
ParserError::ParseBigInt(..) => atom!("cannot_parse_big_int"),
|
||||
ParserError::Utf8Error(..) => atom!("utf8_conversion_error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<lexical::Error> for ParserError {
|
||||
fn from(e: lexical::Error) -> ParserError {
|
||||
ParserError::LexicalError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IOError> for ParserError {
|
||||
fn from(e: IOError) -> ParserError {
|
||||
ParserError::IO(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&IOError> for ParserError {
|
||||
fn from(error: &IOError) -> ParserError {
|
||||
if error.get_ref().filter(|e| e.is::<BadUtf8Error>()).is_some() {
|
||||
ParserError::Utf8Error(0, 0)
|
||||
} else {
|
||||
ParserError::IO(error.kind().into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct CompositeOpDir<'a, 'b> {
|
||||
pub primary_op_dir: Option<&'b OpDir>,
|
||||
pub secondary_op_dir: &'a OpDir,
|
||||
}
|
||||
|
||||
impl<'a, 'b> CompositeOpDir<'a, 'b> {
|
||||
#[inline]
|
||||
pub fn new(secondary_op_dir: &'a OpDir, primary_op_dir: Option<&'b OpDir>) -> Self {
|
||||
CompositeOpDir {
|
||||
primary_op_dir,
|
||||
secondary_op_dir,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn get(&self, name: Atom, fixity: Fixity) -> Option<OpDesc> {
|
||||
let entry = if let Some(ref primary_op_dir) = &self.primary_op_dir {
|
||||
primary_op_dir.get(&(name, fixity))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
entry
|
||||
.or_else(move || self.secondary_op_dir.get(&(name, fixity)))
|
||||
.cloned()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
pub enum Fixity {
|
||||
In,
|
||||
Post,
|
||||
Pre,
|
||||
}
|
||||
|
||||
#[bitfield]
|
||||
#[repr(u64)]
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct Fixnum {
|
||||
num: B57,
|
||||
#[allow(unused)] m: bool,
|
||||
#[allow(unused)] tag: B6,
|
||||
}
|
||||
|
||||
impl Fixnum {
|
||||
#[inline]
|
||||
pub fn build_with(num: i64) -> Self {
|
||||
Fixnum::new()
|
||||
.with_num(u64::from_ne_bytes(num.to_ne_bytes()) & ((1 << 57) - 1))
|
||||
.with_tag(HeapCellValueTag::Fixnum as u8)
|
||||
.with_m(false)
|
||||
//num as u64).with__m(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn build_with_checked(num: i64) -> Result<Self, OutOfBounds> {
|
||||
const UPPER_BOUND: i64 = (1 << 56) - 1;
|
||||
const LOWER_BOUND: i64 = -(1 << 56);
|
||||
|
||||
if LOWER_BOUND <= num && num <= UPPER_BOUND {
|
||||
Ok(Fixnum::new()
|
||||
.with_m(false)
|
||||
.with_tag(HeapCellValueTag::Fixnum as u8)
|
||||
.with_num(u64::from_ne_bytes(num.to_ne_bytes()) & ((1 << 57) - 1))) //num as u64 & ((1 << 57) - 1)))
|
||||
} else {
|
||||
Err(OutOfBounds {})
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_num(self) -> i64 {
|
||||
let n = self.num() as i64;
|
||||
let (n, overflowed) = (n << 7).overflowing_shr(7); // sign-extend the 57-bit signed fixnum.
|
||||
debug_assert_eq!(overflowed, false);
|
||||
n
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fixnum {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
Fixnum::build_with(-self.get_num())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Literal {
|
||||
Atom(Atom),
|
||||
Char(char),
|
||||
Fixnum(Fixnum),
|
||||
Integer(TypedArenaPtr<Integer>),
|
||||
Rational(TypedArenaPtr<Rational>),
|
||||
Float(F64Ptr),
|
||||
String(Atom),
|
||||
}
|
||||
|
||||
impl fmt::Display for Literal {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Literal::Atom(ref atom) => {
|
||||
// if atom.as_str().chars().any(|c| "`.$'\" ".contains(c)) {
|
||||
// write!(f, "'{}'", atom)
|
||||
// } else {
|
||||
write!(f, "{}", atom.flat_index())
|
||||
// }
|
||||
}
|
||||
Literal::Char(c) => write!(f, "'{}'", *c as u32),
|
||||
Literal::Fixnum(n) => write!(f, "{}", n.get_num()),
|
||||
Literal::Integer(ref n) => write!(f, "{}", n),
|
||||
Literal::Rational(ref n) => write!(f, "{}", n),
|
||||
Literal::Float(ref n) => write!(f, "{}", *n),
|
||||
Literal::String(ref s) => write!(f, "\"{}\"", s.as_str()),
|
||||
// Literal::Usize(integer) => write!(f, "u{}", integer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Literal {
|
||||
pub fn to_atom(&self, atom_tbl: &mut AtomTable) -> Option<Atom> {
|
||||
match self {
|
||||
Literal::Atom(atom) => Some(atom.defrock_brackets(atom_tbl)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Term {
|
||||
AnonVar,
|
||||
Clause(Cell<RegType>, Atom, Vec<Term>),
|
||||
Cons(Cell<RegType>, Box<Term>, Box<Term>),
|
||||
Literal(Cell<RegType>, Literal),
|
||||
PartialString(Cell<RegType>, Atom, Option<Box<Term>>),
|
||||
Var(Cell<VarReg>, Rc<String>),
|
||||
}
|
||||
|
||||
impl Term {
|
||||
pub fn into_literal(self) -> Option<Literal> {
|
||||
match self {
|
||||
Term::Literal(_, c) => Some(c),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn first_arg(&self) -> Option<&Term> {
|
||||
match self {
|
||||
Term::Clause(_, _, ref terms) => terms.first(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_name(&mut self, new_name: Atom) {
|
||||
match self {
|
||||
Term::Literal(_, Literal::Atom(ref mut atom)) | Term::Clause(_, ref mut atom, ..) => {
|
||||
*atom = new_name;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Option<Atom> {
|
||||
match self {
|
||||
&Term::Literal(_, Literal::Atom(ref atom)) | &Term::Clause(_, ref atom, ..) => {
|
||||
Some(*atom)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arity(&self) -> usize {
|
||||
match self {
|
||||
Term::Clause(_, _, ref child_terms, ..) => child_terms.len(),
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unfold_by_str_once(term: &mut Term, s: Atom) -> Option<(Term, Term)> {
|
||||
if let Term::Clause(_, ref name, ref mut subterms) = term {
|
||||
if name == &s && subterms.len() == 2 {
|
||||
let snd = subterms.pop().unwrap();
|
||||
let fst = subterms.pop().unwrap();
|
||||
|
||||
return Some((fst, snd));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn unfold_by_str(mut term: Term, s: Atom) -> Vec<Term> {
|
||||
let mut terms = vec![];
|
||||
|
||||
while let Some((fst, snd)) = unfold_by_str_once(&mut term, s) {
|
||||
terms.push(fst);
|
||||
term = snd;
|
||||
}
|
||||
|
||||
terms.push(term);
|
||||
terms
|
||||
}
|
||||
747
src/parser/char_reader.rs
Normal file
747
src/parser/char_reader.rs
Normal file
@@ -0,0 +1,747 @@
|
||||
/*
|
||||
* CharReader is a not entirely redundant flattening/chimera of std's
|
||||
* BufReader and unicode_reader's CodePoints, introduced to allow
|
||||
* peekable buffered UTF-8 codepoints and access to the underlying
|
||||
* reader.
|
||||
*
|
||||
* Unlike CodePoints, it doesn't make the reader inaccessible by
|
||||
* wrapping it a Bytes struct.
|
||||
*
|
||||
* Unlike BufReader, its buffer is peekable as a char.
|
||||
*/
|
||||
|
||||
use smallvec::*;
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::io::{ErrorKind, IoSliceMut, Read};
|
||||
use std::str;
|
||||
|
||||
pub struct CharReader<R> {
|
||||
inner: R,
|
||||
buf: SmallVec<[u8;4]>,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
/// An error raised when parsing a UTF-8 byte stream fails.
|
||||
#[derive(Debug)]
|
||||
pub struct BadUtf8Error {
|
||||
/// The bytes that could not be parsed as a code point.
|
||||
pub bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Error for BadUtf8Error {
|
||||
fn description(&self) -> &str {
|
||||
"BadUtf8Error"
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BadUtf8Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Bad UTF-8: {:?}", self.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> CharReader<R> {
|
||||
pub fn new(inner: R) -> CharReader<R> {
|
||||
Self {
|
||||
inner,
|
||||
buf: SmallVec::new(),
|
||||
pos: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner(&self) -> &R {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_mut(&mut self) -> &mut R {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CharRead {
|
||||
fn read_char(&mut self) -> Option<io::Result<char>> {
|
||||
match self.peek_char() {
|
||||
Some(Ok(c)) => {
|
||||
self.consume(c.len_utf8());
|
||||
Some(Ok(c))
|
||||
}
|
||||
result => result
|
||||
}
|
||||
}
|
||||
|
||||
fn peek_char(&mut self) -> Option<io::Result<char>>;
|
||||
fn put_back_char(&mut self, c: char);
|
||||
fn consume(&mut self, nread: usize);
|
||||
}
|
||||
|
||||
impl<R> CharReader<R> {
|
||||
pub fn get_ref(&self) -> &R {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut R {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
&self.buf[self.pos..]
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> R {
|
||||
self.inner
|
||||
}
|
||||
|
||||
fn reset_buffer(&mut self) {
|
||||
self.buf.clear();
|
||||
self.pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> CharReader<R> {
|
||||
fn refresh_buffer(&mut self) -> io::Result<&[u8]> {
|
||||
// If we've reached the end of our internal buffer then we need to fetch
|
||||
// some more data from the underlying reader.
|
||||
// Branch using `>=` instead of the more correct `==`
|
||||
// to tell the compiler that the pos..cap slice is always valid.
|
||||
if self.pos >= self.buf.len() {
|
||||
debug_assert!(self.pos == self.buf.len());
|
||||
|
||||
self.buf.clear();
|
||||
|
||||
let mut word = [0u8;4];
|
||||
let nread = self.inner.read(&mut word)?;
|
||||
|
||||
self.buf.extend_from_slice(&word[..nread]);
|
||||
self.pos = 0;
|
||||
}
|
||||
|
||||
Ok(&self.buf[self.pos..])
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> CharRead for CharReader<R> {
|
||||
fn peek_char(&mut self) -> Option<io::Result<char>> {
|
||||
match self.refresh_buffer() {
|
||||
Ok(_buf) => {}
|
||||
Err(e) => return Some(Err(e)),
|
||||
}
|
||||
|
||||
loop {
|
||||
let buf = &self.buf[self.pos..];
|
||||
|
||||
if !buf.is_empty() {
|
||||
let e = match str::from_utf8(buf) {
|
||||
Ok(s) => {
|
||||
let mut chars = s.chars();
|
||||
let c = chars.next().unwrap();
|
||||
|
||||
return Some(Ok(c));
|
||||
}
|
||||
Err(e) => {
|
||||
e
|
||||
}
|
||||
};
|
||||
|
||||
if buf.len() - e.valid_up_to() >= 4 {
|
||||
// If we have 4 bytes that still don't make up
|
||||
// a valid code point, then we have garbage.
|
||||
|
||||
// We have bad data in the buffer. Remove
|
||||
// leading bytes until either the buffer is
|
||||
// empty, or we have a valid code point.
|
||||
|
||||
let mut split_point = 1;
|
||||
let mut badbytes = vec![];
|
||||
|
||||
loop {
|
||||
let (bad, rest) = buf.split_at(split_point);
|
||||
|
||||
if rest.is_empty() || str::from_utf8(rest).is_ok() {
|
||||
badbytes.extend_from_slice(bad);
|
||||
break;
|
||||
}
|
||||
|
||||
split_point += 1;
|
||||
}
|
||||
|
||||
// Raise the error. If we still have data in
|
||||
// the buffer, it will be returned on the next
|
||||
// loop.
|
||||
|
||||
return Some(Err(io::Error::new(io::ErrorKind::InvalidData,
|
||||
BadUtf8Error { bytes: badbytes })));
|
||||
} else {
|
||||
if self.pos >= self.buf.len() {
|
||||
return None;
|
||||
} else if self.buf.len() - self.pos >= 4 {
|
||||
return match str::from_utf8(&self.buf[..e.valid_up_to()]) {
|
||||
Ok(s) => {
|
||||
let mut chars = s.chars();
|
||||
let c = chars.next().unwrap();
|
||||
|
||||
Some(Ok(c))
|
||||
}
|
||||
Err(e) => {
|
||||
let badbytes = self.buf[..e.valid_up_to()].to_vec();
|
||||
|
||||
Some(Err(io::Error::new(io::ErrorKind::InvalidData,
|
||||
BadUtf8Error { bytes: badbytes })))
|
||||
}
|
||||
};
|
||||
} else {
|
||||
let buf_len = self.buf.len();
|
||||
|
||||
for (c, idx) in (self.pos..buf_len).enumerate() {
|
||||
self.buf[c] = self.buf[idx];
|
||||
}
|
||||
|
||||
self.buf.truncate(buf_len - self.pos);
|
||||
|
||||
let buf_len = self.buf.len();
|
||||
|
||||
let mut word = [0u8;4];
|
||||
let word_slice = &mut word[buf_len..4];
|
||||
|
||||
match self.inner.read(word_slice) {
|
||||
Err(e) => return Some(Err(e)),
|
||||
Ok(nread) => {
|
||||
self.buf.extend_from_slice(&word_slice[0..nread]);
|
||||
}
|
||||
}
|
||||
|
||||
self.pos = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn put_back_char(&mut self, c: char) {
|
||||
let src_len = self.buf.len() - self.pos;
|
||||
debug_assert!(src_len <= 4);
|
||||
|
||||
let c_len = c.len_utf8();
|
||||
let mut shifted_slice = [0u8; 4];
|
||||
|
||||
shifted_slice[0..src_len].copy_from_slice(&self.buf[self.pos .. self.buf.len()]);
|
||||
|
||||
self.buf.resize(c_len, 0);
|
||||
self.buf.extend_from_slice(&shifted_slice[0..src_len]);
|
||||
self.pos = 0;
|
||||
|
||||
c.encode_utf8(&mut self.buf[0..c_len]);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn consume(&mut self, nread: usize) {
|
||||
self.pos += nread;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl<R: Seek> BufReader<R> {
|
||||
/// Seeks relative to the current position. If the new position lies within the buffer,
|
||||
/// the buffer will not be flushed, allowing for more efficient seeks.
|
||||
/// This method does not return the location of the underlying reader, so the caller
|
||||
/// must track this information themselves if it is required.
|
||||
#[stable(feature = "bufreader_seek_relative", since = "1.53.0")]
|
||||
pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
|
||||
let pos = self.pos as u64;
|
||||
if offset < 0 {
|
||||
if let Some(new_pos) = pos.checked_sub((-offset) as u64) {
|
||||
self.pos = new_pos as usize;
|
||||
return Ok(());
|
||||
}
|
||||
} else {
|
||||
if let Some(new_pos) = pos.checked_add(offset as u64) {
|
||||
if new_pos <= self.cap as u64 {
|
||||
self.pos = new_pos as usize;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
self.seek(SeekFrom::Current(offset)).map(drop)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl<R: Read> Read for CharReader<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// // If we don't have any buffered data and we're doing a massive read
|
||||
// // (larger than our internal buffer), bypass our internal buffer
|
||||
// // entirely.
|
||||
// if self.pos == self.cap && buf.len() >= self.buf.len() {
|
||||
// self.discard_buffer();
|
||||
// return self.inner.read(buf);
|
||||
// }
|
||||
|
||||
let mut inner_buf = self.refresh_buffer()?;
|
||||
let nread = inner_buf.read(buf)?;
|
||||
|
||||
// let nread = {
|
||||
// let mut rem = self.fill_buf()?;
|
||||
// rem.read(buf)?
|
||||
// };
|
||||
|
||||
self.consume(nread);
|
||||
Ok(nread)
|
||||
}
|
||||
|
||||
// Small read_exacts from a BufReader are extremely common when used with a deserializer.
|
||||
// The default implementation calls read in a loop, which results in surprisingly poor code
|
||||
// generation for the common path where the buffer has enough bytes to fill the passed-in
|
||||
// buffer.
|
||||
fn read_exact(&mut self, mut buf: &mut [u8]) -> io::Result<()> {
|
||||
if self.buffer().len() >= buf.len() {
|
||||
buf.copy_from_slice(&self.buffer()[..buf.len()]);
|
||||
self.consume(buf.len());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
while !buf.is_empty() {
|
||||
match self.read(buf) {
|
||||
Ok(0) => break,
|
||||
Ok(n) => {
|
||||
let tmp = buf;
|
||||
buf = &mut tmp[n..];
|
||||
}
|
||||
Err(e) if e.kind() == ErrorKind::Interrupted => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
if !buf.is_empty() {
|
||||
Err(io::Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
|
||||
|
||||
if self.pos == self.buf.len() && total_len >= self.buf.len() {
|
||||
self.reset_buffer(); // self.discard_buffer();
|
||||
return self.inner.read_vectored(bufs);
|
||||
}
|
||||
|
||||
let nread = {
|
||||
self.refresh_buffer()?;
|
||||
(&self.buf[self.pos..]).read_vectored(bufs)?
|
||||
};
|
||||
|
||||
self.consume(nread);
|
||||
Ok(nread)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R: Read> BufRead for BufReader<R> {
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
// If we've reached the end of our internal buffer then we need to fetch
|
||||
// some more data from the underlying reader.
|
||||
// Branch using `>=` instead of the more correct `==`
|
||||
// to tell the compiler that the pos..cap slice is always valid.
|
||||
if self.pos >= self.cap {
|
||||
debug_assert!(self.pos == self.cap);
|
||||
self.cap = self.inner.read(&mut self.buf)?;
|
||||
self.pos = 0;
|
||||
}
|
||||
Ok(&self.buf[self.pos..self.cap])
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.pos = cmp::min(self.pos + amt, self.cap);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl<R> fmt::Debug for CharReader<R>
|
||||
where
|
||||
R: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_struct("CharReader")
|
||||
.field("reader", &self.inner)
|
||||
.field("buf", &format_args!("{}/{}", self.buf.capacity() - self.pos, self.buf.len()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R: Seek> Seek for BufReader<R> {
|
||||
/// Seek to an offset, in bytes, in the underlying reader.
|
||||
///
|
||||
/// The position used for seeking with [`SeekFrom::Current`]`(_)` is the
|
||||
/// position the underlying reader would be at if the `BufReader<R>` had no
|
||||
/// internal buffer.
|
||||
///
|
||||
/// Seeking always discards the internal buffer, even if the seek position
|
||||
/// would otherwise fall within it. This guarantees that calling
|
||||
/// [`BufReader::into_inner()`] immediately after a seek yields the underlying reader
|
||||
/// at the same position.
|
||||
///
|
||||
/// To seek without discarding the internal buffer, use [`BufReader::seek_relative`].
|
||||
///
|
||||
/// See [`std::io::Seek`] for more details.
|
||||
///
|
||||
/// Note: In the edge case where you're seeking with [`SeekFrom::Current`]`(n)`
|
||||
/// where `n` minus the internal buffer length overflows an `i64`, two
|
||||
/// seeks will be performed instead of one. If the second seek returns
|
||||
/// [`Err`], the underlying reader will be left at the same position it would
|
||||
/// have if you called `seek` with [`SeekFrom::Current`]`(0)`.
|
||||
///
|
||||
/// [`std::io::Seek`]: Seek
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
let result: u64;
|
||||
if let SeekFrom::Current(n) = pos {
|
||||
let remainder = (self.cap - self.pos) as i64;
|
||||
// it should be safe to assume that remainder fits within an i64 as the alternative
|
||||
// means we managed to allocate 8 exbibytes and that's absurd.
|
||||
// But it's not out of the realm of possibility for some weird underlying reader to
|
||||
// support seeking by i64::MIN so we need to handle underflow when subtracting
|
||||
// remainder.
|
||||
if let Some(offset) = n.checked_sub(remainder) {
|
||||
result = self.inner.seek(SeekFrom::Current(offset))?;
|
||||
} else {
|
||||
// seek backwards by our remainder, and then by the offset
|
||||
self.inner.seek(SeekFrom::Current(-remainder))?;
|
||||
self.discard_buffer();
|
||||
result = self.inner.seek(SeekFrom::Current(n))?;
|
||||
}
|
||||
} else {
|
||||
// Seeking with Start/End doesn't care about our buffer length.
|
||||
result = self.inner.seek(pos)?;
|
||||
}
|
||||
self.discard_buffer();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Returns the current seek position from the start of the stream.
|
||||
///
|
||||
/// The value returned is equivalent to `self.seek(SeekFrom::Current(0))`
|
||||
/// but does not flush the internal buffer. Due to this optimization the
|
||||
/// function does not guarantee that calling `.into_inner()` immediately
|
||||
/// afterwards will yield the underlying reader at the same position. Use
|
||||
/// [`BufReader::seek`] instead if you require that guarantee.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if the position of the inner reader is smaller
|
||||
/// than the amount of buffered data. That can happen if the inner reader
|
||||
/// has an incorrect implementation of [`Seek::stream_position`], or if the
|
||||
/// position has gone out of sync due to calling [`Seek::seek`] directly on
|
||||
/// the underlying reader.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::{
|
||||
/// io::{self, BufRead, BufReader, Seek},
|
||||
/// fs::File,
|
||||
/// };
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut f = BufReader::new(File::open("foo.txt")?);
|
||||
///
|
||||
/// let before = f.stream_position()?;
|
||||
/// f.read_line(&mut String::new())?;
|
||||
/// let after = f.stream_position()?;
|
||||
///
|
||||
/// println!("The first line was {} bytes long", after - before);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
let remainder = (self.cap - self.pos) as u64;
|
||||
self.inner.stream_position().map(|pos| {
|
||||
pos.checked_sub(remainder).expect(
|
||||
"overflow when subtracting remaining buffer size from inner stream position",
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*
|
||||
impl<T> SizeHint for CharReader<T> {
|
||||
fn lower_bound(&self) -> usize {
|
||||
self.buffer().len()
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::parser::char_reader::*;
|
||||
use std::io::Cursor;
|
||||
|
||||
#[test]
|
||||
fn plain_string() {
|
||||
let mut read_string = CharReader::new(Cursor::new("a string"));
|
||||
|
||||
for c in "a string".chars() {
|
||||
assert_eq!(read_string.peek_char().unwrap().ok(), Some(c));
|
||||
assert_eq!(read_string.read_char().unwrap().ok(), Some(c));
|
||||
}
|
||||
|
||||
assert!(read_string.read_char().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greek_string() {
|
||||
let mut read_string = CharReader::new(Cursor::new("λέξη"));
|
||||
|
||||
for c in "λέξη".chars() {
|
||||
assert_eq!(read_string.peek_char().unwrap().ok(), Some(c));
|
||||
assert_eq!(read_string.read_char().unwrap().ok(), Some(c));
|
||||
}
|
||||
|
||||
assert!(read_string.read_char().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn russian_string() {
|
||||
let mut read_string = CharReader::new(Cursor::new("слово"));
|
||||
|
||||
for c in "слово".chars() {
|
||||
assert_eq!(read_string.peek_char().unwrap().ok(), Some(c));
|
||||
assert_eq!(read_string.read_char().unwrap().ok(), Some(c));
|
||||
}
|
||||
|
||||
assert!(read_string.read_char().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greek_lorem_ipsum() {
|
||||
let lorem_ipsum = "Λορεμ ιπσθμ δολορ σιτ αμετ, οφφενδιτ
|
||||
εφφιcιενδι σιτ ει, ηαρθμ λεγερε qθαερενδθμ ιθσ νε. Ηασ νο εροσ
|
||||
σιγνιφερθμqθε, σεδ ετ μθτατ jθστο, ει cθμ ελιγενδι σcριπτορεμ
|
||||
ρεπρεηενδθντ. Εοσ ατ αμετ μαλισ ελειφενδ. Ιν cθμ εριπθιτ
|
||||
νομινατι. Θσθ ιν cετεροσ μαιορθμ, μθνερε ατομορθμ ινcιδεριντ θτ
|
||||
ηασ. Αν ηασ λιβρισ πραεσεντ πατριοqθε, ηινc θτιναμ πριμισ νε
|
||||
cθμ. Cθ μοδο ερρεμ σcριβεντθρ cθμ. Ει vισ δεcορε μαλορθμ
|
||||
σεντεντιαε, σεδ νο λιβερ εvερτι μεντιτθμ. Προ φαcερ vολθτπατ
|
||||
σαπιεντεμ ιν. Cθ εροσ περσεqθερισ πρι, εα ποσσιτ cετεροσ δθο. Πρι
|
||||
εα μαλισ μθνερε.
|
||||
|
||||
Qθισ jθστο μαλορθμ cθ qθο. Νεc ατ οδιο σολετ μαιεστατισ, νε
|
||||
φορενσιβθσ σαδιπσcινγ ιθσ, αν qθι ειθσ βρθτε σαπιεντεμ. Cομμθνε
|
||||
περcιπιτθρ ιθσ αδ, μθνερε δολορθμ ιμπεδιτ ηισ νε. Νεc ετιαμ
|
||||
προπριαε vιτθπερατα ιν. Σονετ νεμορε ιθσ cθ, ιν αφφερτ ινερμισ
|
||||
cοτιδιεqθε vισ.
|
||||
|
||||
Ηασ ιδ νονθμυ δοcτθσ cοτιδιεqθε. Σινγθλισ πηιλοσοπηια εξ δθο. Εστ
|
||||
νο ιραcθνδια cονσεqθθντθρ. Τε διcτασ επιcθρει εφφιcιαντθρ δθο, εοσ
|
||||
νε νθλλα νομιναvι. Εθμ cθ ελιτρ λιβεραvισσε, σιτ περσεqθερισ
|
||||
cομπλεcτιτθρ εξ, πονδερθμ σιμιλιqθε ηασ νο.
|
||||
|
||||
Σολθμ ποσσιμ λαβιτθρ εξ ηισ, ει δομινγ εξπετενδισ vελ, διαμ μινιμ
|
||||
σcριπσεριτ ει περ. Αθδιαμ οcθρρερετ προ εξ, δομινγ vολθπταρια ετ
|
||||
qθο. Cονσθλ σανcτθσ αccθμσαν νο ιθσ, αδ εαμ αλβθcιθσ
|
||||
ηονεστατισ. Ετ vιξ φαcιλισ qθαλισqθε ερροριβθσ, ηισ εθ πθρτο
|
||||
ασσεντιορ. Ιθσ βονορθμ ηονεστατισ σcριπσεριτ ατ, ιν ναμ εσσε μοvετ
|
||||
γραεcο. Αθγθε cονσεcτετθερ εστ ατ.
|
||||
|
||||
Αδ ταλε σθασ μθνερε σεδ, vισ φεθγαιτ αντιοπαμ ιδ. Προ εθ ινερμισ
|
||||
σαλθτατθσ, σαεπε qθαεστιο θρβανιτασ cθ περ. Ιν μαλορθμ σαλθτατθσ
|
||||
δετερρθισσετ περ, νε παρτεμ vολθτπατ ινστρθcτιορ vιξ. Νο vισ
|
||||
δεμοcριτθμ εφφιcιαντθρ, επιcθρει αδολεσcενσ εστ cθ, ιδ vιξ
|
||||
λθcιλιθσ αδιπισcινγ. Σεα τε cλιτα ιραcθνδια. Σεα αν σιμθλ
|
||||
εσσεντ. Vοcιβθσ ελειφενδ cονσεqθθντθρ περ αδ, αν ναμ πονδερθμ
|
||||
vολθπταρια.
|
||||
|
||||
Λιβερ ερθδιτι αccθσαμθσ θτ ναμ. Σιτ αντιοπαμ γθβεργρεν νε. Αμετ
|
||||
ανcιλλαε ετ qθι, μεα σολθμ λαθδεμ εα. Εθ μελ παρτεμ οβλιqθε
|
||||
πηαεδρθμ. Εξ μελ jθστο αccομμοδαρε, νε νολθισσε σινγθλισ σενσιβθσ
|
||||
cθμ, vισ εθ τιμεαμ αδιπισcινγ.
|
||||
|
||||
Τε νολθισσε vολθπτατθμ εστ. Ασσθμ νομιναvι πρι νε, ει νοστρθμ
|
||||
επιcθρει μεα. Σεδ cθ ελιτ δεσερθντ, γραεcε ερροριβθσ προ θτ, περ
|
||||
νε εθισμοδ vολθπταρια. Νο εθμ διcατ ποσσιμ, νεc πρινcιπεσ
|
||||
cονcεπταμ νε. Εθ αππαρεατ ιντελλεγατ σεα. Μελ θτ ελιτ λαθδεμ, θσθ
|
||||
δολορεμ cομπλεcτιτθρ ετ, νε μεα δολορεσ μολεστιαε.
|
||||
|
||||
Θσθ λεγενδοσ vολθπτατιβθσ cθ. Qθο νε αδηθc ρεφερρεντθρ, αλια
|
||||
μεδιοcρεμ δθο νε, σεδ ερρεμ δολορθμ αccομμοδαρε νε. Ετιαμ εqθιδεμ
|
||||
δετερρθισσετ cθ μει, ετ εροσ cετεροσ σεα, εξ vιξ ενιμ cασε
|
||||
δετραξιτ. Σεδ σολθτα λιβρισ ειρμοδ τε, νοvθμ ποπθλο νε εθμ. Σθμμο
|
||||
αδμοδθμ δεσερθντ εστ εξ, εστ διcαμ εqθιδεμ cθ.
|
||||
|
||||
Ιλλθμ cορπορα ινvιδθντ εαμ ετ. Σεδ μαλισ ταcιματεσ εvερτιτθρ εα,
|
||||
μαζιμ νθλλαμ vοcιβθσ μεα ει. Μεα ορνατθσ λθπτατθμ αδιπισcινγ
|
||||
αδ. Μεα αφφερτ νοστερ ατ, ναμ αν σολεατ ερροριβθσ. Εξ σεα αεqθε
|
||||
μθνερε cετερο, εοσ ηινc ελειφενδ δεμοcριτθμ.";
|
||||
|
||||
let mut lorem_ipsum_reader = CharReader::new(Cursor::new(lorem_ipsum));
|
||||
|
||||
for c in lorem_ipsum.chars() {
|
||||
assert_eq!(lorem_ipsum_reader.peek_char().unwrap().ok(), Some(c));
|
||||
assert_eq!(lorem_ipsum_reader.read_char().unwrap().ok(), Some(c));
|
||||
}
|
||||
|
||||
assert!(lorem_ipsum_reader.read_char().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn armenian_lorem_ipsum() {
|
||||
let lorem_ipsum = "լոռեմ իպսում դոլոռ սիթ ամեթ, նովում գռաեծո
|
||||
սեա եա, աբհոռռեանթ դիսպութանդո եի քուի. իդ քուոդ ինդոծթում
|
||||
եսթ, մեա թե ծոմմոդո ծոռպոռա. եթ ծոնսուլ ադիպիսծինգ ռեֆոռմիդանս
|
||||
պեռ, ինեռմիս ֆեուգաիթ նո քուո, թալե սալե պռո եա. եթ նիբհ
|
||||
աուգուե վոլումուս դուո, նե ծում եխեռծի սալութաթուս գլոռիաթուռ,
|
||||
ծու թաթիոն պռաեսենթ մեդիոծռեմ վիս.
|
||||
|
||||
վիխ եռոս ռեֆեռռենթուռ եու. պեռսիուս վիթուպեռաթոռիբուս ութ սեա,
|
||||
վիդե ինվիդունթ պռոբաթուս նո քուո. մեի եռոս մելիուս նոմինավի
|
||||
իդ, ութ պռո քուաս քուաեսթիո. եթ նաթում պեթենթիում սուավիթաթե
|
||||
հիս. քուի ծոնսթիթութո մեդիոծռիթաթեմ թե. ծեթեռո դեթռածթո
|
||||
ծոնծեպթամ սեա եթ. դիսսենթիեթ ելոքուենթիամ թհեոպհռասթուս նեծ
|
||||
աթ, աթ ֆածեթե եռիպուիթ վիխ.
|
||||
|
||||
ասսուեվեռիթ սծռիպսեռիթ եսթ եթ, վիդիթ դեբեթ եվեռթի եխ
|
||||
եսթ. աութեմ լաուդեմ պոսիդոնիում մեի եի. ռեբում դիծամ ծեթեռոս
|
||||
եում ծու. նիհիլ եխպեթենդա ասսուեվեռիթ ուսու ան. ւիսի թաթիոն
|
||||
դելենիթ նո իուս, սեդ եխ իդքուե սիգնիֆեռումքուե, բռութե զռիլ
|
||||
ալբուծիուս ան պռի.
|
||||
|
||||
մովեթ իռիուռե սալութանդի պեռ նո, եի ոմնիս աֆֆեռթ պեռսեքուեռիս
|
||||
իուս, եթ պռաեսենթ մալուիսսեթ եսթ. եսթ պռոբո գուբեռգռեն եթ, հաս
|
||||
ին դիամ նումքուամ. ֆեուգաիթ ինվենիռե ռեպուդիանդաե աթ սեդ,
|
||||
իուվառեթ ծոնսուլաթու եֆֆիծիանթուռ ուսու եի. ութ մեա ածծումսան
|
||||
նոմինավի թինծիդունթ, մեի դիծթա ածծումսան ութ. վիմ ոմնիում
|
||||
ելիգենդի սծռիպթոռեմ եու.
|
||||
|
||||
իդ վիս եռռոռ ալիքուիպ ելոքուենթիամ, ադ դելենիթի պեռծիպիթ
|
||||
դեֆինիթիոնես իուս. վիմ իուդիծո դեմոծռիթում ծոմպռեհենսամ թե,
|
||||
ութ նիհիլ լոբոռթիս վոլուպթաթիբուս վել, դիծունթ մենթիթում
|
||||
ֆածիլիսիս եի եում. եսսե սալե մինիմ եոս նե. ագամ ոմնեսքուե ծում
|
||||
ին.
|
||||
|
||||
իուվառեթ իուդիծաբիթ ծում աթ, ուսու նիբհ աթքուի դոմինգ եխ. եի
|
||||
քուի սանծթուս սենսիբուս, նամ ուբիքուե ապպեթեռե պռոդեսսեթ
|
||||
եու. ուսու եթ աուգուե ծոնվենիռե սծռիբենթուռ. ան ոմնիում վեռեառ
|
||||
ութռոքուե դուո, եսթ եի լիբեռ մեդիոծռեմ եխպլիծառի, ոմնիս
|
||||
աուդիռե թե պռի. վիմ մունեռե սոլեաթ ծու, եռոս ինվենիռե
|
||||
դիսպութաթիոնի եի քուո, ան ալթեռա պութենթ լաբոռես պռո. անթիոպամ
|
||||
դեմոծռիթում պեռ ին.
|
||||
|
||||
նե քուի ծիբո ելիթռ. նեծ նե լիբեռ վոլուպթուա. նիսլ ծոմմունե
|
||||
եխպեթենդիս նամ եխ, իուդիծո պլածեռաթ պեռծիպիթուռ մել նո, եթ
|
||||
պառթեմ պութանթ քուի. վիմ թինծիդունթ ածծոմմոդառե աթ, նե նամ
|
||||
վիդիթ իռիուռե, պռո եա ելիգենդի պոսթուլանթ ծոնսթիթութո.
|
||||
|
||||
մել ութ ոդիո նուլլամ եխպլիծառի. պռոպռիաե թինծիդունթ
|
||||
դելիծաթիսսիմի եամ ան, մոդո քուոդսի ապեռիռի եու եսթ, պեռ աթ
|
||||
լաբոռես սենսեռիթ. վիմ ծոնգուե ռեպուդիանդաե եի, նեծ ագամ
|
||||
դիծունթ դելիծաթիսսիմի աթ. պոսսիթ լիբեռավիսսե եոս եու.
|
||||
|
||||
աթ ալիա դեբեթ ելաբոռառեթ քուո, ին ալիի ածծումսան ծոնսթիթուամ
|
||||
հաս, մել թոթա ոմիթթանթուռ ինսթռուծթիոռ նո. պեռ նե ծաուսաե
|
||||
սապիենթեմ, պաուլո ոմնեսքուե եի քուո, եխ ոռաթիո պհիլոսոպհիա
|
||||
սիթ. իգնոթա ծաուսաե աթ ուսու, եխ քուո դիծթաս քուոդսի
|
||||
ռեպուդիառե. ծոռպոռա պռոդեսսեթ ռեֆեռռենթուռ եոս եխ.
|
||||
|
||||
եու եթիամ ելեիֆենդ մել, սալե սծռիպսեռիթ հիս եու. պոռռո
|
||||
ադոլեսծենս մեի եա. ին մեա զռիլ պռոբաթուս սալութաթուս. եոս ադ
|
||||
մինիմ թեմպոռիբուս. սեա նե եթիամ.";
|
||||
|
||||
let mut lorem_ipsum_reader = CharReader::new(Cursor::new(lorem_ipsum));
|
||||
|
||||
for c in lorem_ipsum.chars() {
|
||||
assert_eq!(lorem_ipsum_reader.peek_char().unwrap().ok(), Some(c));
|
||||
assert_eq!(lorem_ipsum_reader.read_char().unwrap().ok(), Some(c));
|
||||
}
|
||||
|
||||
assert!(lorem_ipsum_reader.read_char().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn russian_lorem_ipsum() {
|
||||
let lorem_ipsum = "Лорем ипсум долор сит амет, атяуи дицам еи
|
||||
сит, ид сеа фацилис елаборарет. Меа еу яуас алияуид, те яуи
|
||||
саперет аппеллантур. Ех иус диам дицта волуптариа, еу пер
|
||||
бруте омиттам аццусата. Хис сапиентем губергрен те, яуидам
|
||||
луптатум персеяуерис ад ест.
|
||||
|
||||
Ан алияуип перицулис нам, нец апериам цотидиеяуе волуптатибус
|
||||
но. Солум тритани пер ех, меи не одио тритани рецусабо, цу при
|
||||
веро мелиоре импердиет. Ин граеци индоцтум салутатус нец, диам
|
||||
сцаевола пертинациа про те. Ут сеа дебитис лаборамус
|
||||
диссентиас, еи цум яуот лобортис.
|
||||
|
||||
Децоре сингулис вим не. Еос не риденс оффициис, еу нонумы
|
||||
лабитур еррорибус хас, вел омнис цонституто посидониум но. Вел
|
||||
персиус фастидии репрехендунт ид. Натум иллум ипсум сит ад, еа
|
||||
еам новум латине. Еос нолуиссе патриояуе елояуентиам те.
|
||||
|
||||
Стет малис яуаерендум хас ад, прима цотидиеяуе мел ан,
|
||||
трацтатос десеруиссе нам ех. Ин малорум сусципиантур вим, ех
|
||||
меа граецо тритани адолесценс. Промпта цонцлусионемяуе нам еи,
|
||||
дуо ин лаборе алтерум цотидиеяуе. Но елитр промпта сплендиде
|
||||
еум, аеяуе ассуеверит цонституам яуи ид. Ад тале еррор
|
||||
интеллегебат хас, ерудити граецис хас не, пер ут лабитур
|
||||
еуисмод. Те при суммо путант. Про утинам цоммуне урбанитас еа.
|
||||
|
||||
Идяуе репрехендунт еи нам, алии толлит легере нам не, хис еа
|
||||
виси адверсариум цонцлусионемяуе. Хас ассум омиттам луцилиус
|
||||
ет, вих цонсул малорум фастидии не, сенсибус ассуеверит дуо
|
||||
ут. Дуо алиа видит цетеро ат, еа аппареат пертинах вел. Пер
|
||||
цонституто инцидеринт ин, убияуе риденс сенсерит цум цу. Про
|
||||
ет цетерос темпорибус, те вел пурто суммо, дуо мунере вертерем
|
||||
урбанитас ад. Сит оптион елецтрам форенсибус но. Еи татион
|
||||
сапиентем ест, лаборе сцрипта сингулис но вим, усу еу елигенди
|
||||
персецути.
|
||||
|
||||
Иус ан елецтрам цонтентионес. Меи атяуи нонумес ут, вел амет
|
||||
репрехендунт ан, вис еу яуаестио патриояуе. Про синт легере
|
||||
детрацто ад. Постеа долорем евертитур при ет, вим номинави
|
||||
принципес ирацундиа ех. Доцтус интеллегебат но нам. Фацете
|
||||
оффициис нецесситатибус цу меа.
|
||||
|
||||
Промпта симилияуе вис ин. Пер бонорум перицулис аргументум
|
||||
ад. Еу дицат фацилис губергрен нам, еффициенди цомпрехенсам
|
||||
хас еу. Инани нонумы усу но, ад цонцептам репудиандае
|
||||
про. Тота нуллам делицата еа яуо, усу дуис дебет путент еи.
|
||||
|
||||
Вис апериам доценди елояуентиам еа. Ех яуот детрацто
|
||||
елояуентиам цум, ерос малис дицерет вис ин. Еа цум модус
|
||||
еяуидем, дебет нуллам ан меи. Алтерум омиттам про ет.
|
||||
|
||||
Яуи ех латине алияуам, ан меи одио нуллам. Ид хас омнис ребум
|
||||
либрис. Ет убияуе путант дебитис про, ех хис медиоцрем
|
||||
партиендо, но елит елецтрам дуо. Еу меа сонет номинави
|
||||
цотидиеяуе. Нам фалли новум минимум еу, перфецто ратионибус
|
||||
цонституто ад меа.
|
||||
|
||||
Нобис детрацто еам ид, при еу ассум пертинах, те етиам
|
||||
проприае салутанди яуо. Легимус сусципиантур ет хас, сед
|
||||
поссит дефинитионес еа. Ест не патриояуе омиттантур
|
||||
интеллегебат, еу яуо дебет цонцлудатуряуе. Еум ад мнесарчум
|
||||
дефинитионем, елитр лаборамус перципитур про не, хас феугаит
|
||||
фастидии луцилиус ид. Фастидии интеллегат ех.";
|
||||
|
||||
let mut lorem_ipsum_reader = CharReader::new(Cursor::new(lorem_ipsum));
|
||||
|
||||
for c in lorem_ipsum.chars() {
|
||||
assert_eq!(lorem_ipsum_reader.peek_char().unwrap().ok(), Some(c));
|
||||
assert_eq!(lorem_ipsum_reader.read_char().unwrap().ok(), Some(c));
|
||||
|
||||
lorem_ipsum_reader.put_back_char(c);
|
||||
|
||||
assert_eq!(lorem_ipsum_reader.peek_char().unwrap().ok(), Some(c));
|
||||
assert_eq!(lorem_ipsum_reader.read_char().unwrap().ok(), Some(c));
|
||||
}
|
||||
|
||||
assert!(lorem_ipsum_reader.read_char().is_none());
|
||||
}
|
||||
}
|
||||
1059
src/parser/lexer.rs
Normal file
1059
src/parser/lexer.rs
Normal file
File diff suppressed because it is too large
Load Diff
253
src/parser/macros.rs
Normal file
253
src/parser/macros.rs
Normal file
@@ -0,0 +1,253 @@
|
||||
#[macro_export]
|
||||
macro_rules! char_class {
|
||||
($c: expr, [$head:expr]) => ($c == $head);
|
||||
($c: expr, [$head:expr $(, $cs:expr)+]) => ($c == $head || $crate::char_class!($c, [$($cs),*]));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! alpha_char {
|
||||
($c: expr) => {
|
||||
match $c {
|
||||
'a'..='z' => true,
|
||||
'A'..='Z' => true,
|
||||
'_' => true,
|
||||
'\u{00A0}'..='\u{00BF}' => true,
|
||||
'\u{00C0}'..='\u{00D6}' => true,
|
||||
'\u{00D8}'..='\u{00F6}' => true,
|
||||
'\u{00F8}'..='\u{00FF}' => true,
|
||||
'\u{0100}'..='\u{017F}' => true, // Latin Extended-A
|
||||
'\u{0180}'..='\u{024F}' => true, // Latin Extended-B
|
||||
'\u{0250}'..='\u{02AF}' => true, // IPA Extensions
|
||||
'\u{02B0}'..='\u{02FF}' => true, // Spacing Modifier Letters
|
||||
'\u{0300}'..='\u{036F}' => true, // Combining Diacritical Marks
|
||||
'\u{0370}'..='\u{03FF}' => true, // Greek/Coptic
|
||||
'\u{0400}'..='\u{04FF}' => true, // Cyrillic
|
||||
'\u{0500}'..='\u{052F}' => true, // Cyrillic Supplement
|
||||
'\u{0530}'..='\u{058F}' => true, // Armenian
|
||||
'\u{0590}'..='\u{05FF}' => true, // Hebrew
|
||||
'\u{0600}'..='\u{06FF}' => true, // Arabic
|
||||
'\u{0700}'..='\u{074F}' => true, // Syriac
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! alpha_numeric_char {
|
||||
($c: expr) => {
|
||||
$crate::alpha_char!($c) || $crate::decimal_digit_char!($c)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! backslash_char {
|
||||
($c: expr) => {
|
||||
$c == '\\'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! back_quote_char {
|
||||
($c: expr) => {
|
||||
$c == '`'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! octet_char {
|
||||
($c: expr) => {
|
||||
('\u{0000}'..='\u{00FF}').contains(&$c)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! capital_letter_char {
|
||||
($c: expr) => {
|
||||
('A'..='Z').contains(&$c)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! comment_1_char {
|
||||
($c: expr) => {
|
||||
$c == '/'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! comment_2_char {
|
||||
($c: expr) => {
|
||||
$c == '*'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cut_char {
|
||||
($c: expr) => {
|
||||
$c == '!'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! decimal_digit_char {
|
||||
($c: expr) => {
|
||||
('0'..='9').contains(&$c)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! decimal_point_char {
|
||||
($c: expr) => {
|
||||
$c == '.'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! double_quote_char {
|
||||
($c: expr) => {
|
||||
$c == '"'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! end_line_comment_char {
|
||||
($c: expr) => {
|
||||
$c == '%'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! exponent_char {
|
||||
($c: expr) => {
|
||||
$c == 'e' || $c == 'E'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! graphic_char {
|
||||
($c: expr) => ($crate::char_class!($c, ['#', '$', '&', '*', '+', '-', '.', '/', ':',
|
||||
'<', '=', '>', '?', '@', '^', '~']))
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! graphic_token_char {
|
||||
($c: expr) => {
|
||||
$crate::graphic_char!($c) || $crate::backslash_char!($c)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! hexadecimal_digit_char {
|
||||
($c: expr) => {
|
||||
('0'..='9').contains(&$c) || ('A'..='F').contains(&$c) || ('a'..='f').contains(&$c)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! layout_char {
|
||||
($c: expr) => {
|
||||
$crate::char_class!($c, [' ', '\n', '\t', '\u{0B}', '\u{0C}'])
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! meta_char {
|
||||
($c: expr) => {
|
||||
$crate::char_class!($c, ['\\', '\'', '"', '`'])
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! new_line_char {
|
||||
($c: expr) => {
|
||||
$c == '\n'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! octal_digit_char {
|
||||
($c: expr) => {
|
||||
('0'..='7').contains(&$c)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! binary_digit_char {
|
||||
($c: expr) => {
|
||||
$c >= '0' && $c <= '1'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! prolog_char {
|
||||
($c: expr) => {
|
||||
$crate::graphic_char!($c)
|
||||
|| $crate::alpha_numeric_char!($c)
|
||||
|| $crate::solo_char!($c)
|
||||
|| $crate::layout_char!($c)
|
||||
|| $crate::meta_char!($c)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! semicolon_char {
|
||||
($c: expr) => {
|
||||
$c == ';'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! sign_char {
|
||||
($c: expr) => {
|
||||
$c == '-' || $c == '+'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! single_quote_char {
|
||||
($c: expr) => {
|
||||
$c == '\''
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! small_letter_char {
|
||||
($c: expr) => {
|
||||
('a'..='z').contains(&$c)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! solo_char {
|
||||
($c: expr) => {
|
||||
$crate::char_class!($c, ['!', '(', ')', ',', ';', '[', ']', '{', '}', '|', '%'])
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! space_char {
|
||||
($c: expr) => {
|
||||
$c == ' '
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! symbolic_control_char {
|
||||
($c: expr) => {
|
||||
$crate::char_class!($c, ['a', 'b', 'f', 'n', 'r', 't', 'v', '0'])
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! symbolic_hexadecimal_char {
|
||||
($c: expr) => {
|
||||
$c == 'x'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! variable_indicator_char {
|
||||
($c: expr) => {
|
||||
$c == '_'
|
||||
};
|
||||
}
|
||||
17
src/parser/mod.rs
Normal file
17
src/parser/mod.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
#[cfg(feature = "num-rug-adapter")]
|
||||
use num_rug_adapter as rug;
|
||||
#[cfg(feature = "rug")]
|
||||
pub use rug;
|
||||
|
||||
// #[macro_use]
|
||||
// extern crate lazy_static;
|
||||
// #[macro_use]
|
||||
// extern crate static_assertions;
|
||||
|
||||
pub mod char_reader;
|
||||
#[macro_use]
|
||||
pub mod ast;
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
1080
src/parser/parser.rs
Normal file
1080
src/parser/parser.rs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user