#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(unused_parens)]
#![allow(unused_mut)]
#![allow(unused_imports)]
#![allow(unused_assignments)]
use std::io::{Read,Error,BufRead,BufReader,Lines,Result};
use std::cell::{RefCell,Ref,RefMut};
use std::rc::Rc;
use std::fs::File;
use std::collections::{HashMap,HashSet};
use crate::Token::*;
mod zero_copy;
pub use zero_copy::*;
#[derive(Clone,PartialEq,Debug)]
pub enum Token
{
Integer(i64), Float(f64),
Symbol(String),
Alphanum(String),
Keyword(String),
Stringlit(String),
Verbatim(String),
Newline,
#[doc(hidden)]
Nothing,
#[doc(hidden)]
Error(&'static str),
}
fn isdelim(c:char) -> bool
{
c=='(' || c=='[' || c=='{' || c==')' || c==']' || c=='}'
}
fn next_token(s:&str) -> (Token, usize)
{
if s.len()<1 {return (Newline,0);}
let first = s.chars().next().unwrap();
let mut index = 0;
if first.is_alphabetic() || first=='_' {
index=match_alphanum(s);
return (Alphanum(String::from(&s[0..index])),index);
} else if first=='.' || first.is_ascii_digit() { index = match_num(s);
if index>0 {
if let Ok(m) = s[0..index].parse::<i64>() {return (Integer(m),index);}
else if let Ok(n) = s[0..index].parse::<f64>()
{return (Float(n),index);}
}
else {return (Symbol(first.to_string()),1);}
}
else if first=='\"' {
index = match_strlit(s);
if index>0
{return (Stringlit(String::from(&s[0..index])),index);} else {return (Verbatim(s.to_string()),s.len());}
}
else {
index = match_symbol(s);
if index>0 {return (Symbol(String::from(&s[0..index])),index);}
}
(Verbatim(s.to_string()),s.len()) }
fn match_alphanum(s:&str) -> usize
{
if s.len()<1 {return 0;}
let mut chars = s.chars();
let mut next = chars.next().unwrap();
if !(next.is_alphabetic() || next=='_') {return 0;}
let mut index = 1;
let mut stop=false;
while !stop && index<s.len()
{
next = chars.next().unwrap();
if next.is_alphanumeric() || next=='_' { index+=1; }
else {stop=true;}
}
return index;
}
fn match_num(s:&str) -> usize {
let mut chars = s.chars();
let mut index = 0;
let mut stop = false;
let mut foundpoint = false;
let mut founddigit = false;
while !stop && index<s.len()
{
let mut next = chars.next().unwrap();
match (next, foundpoint) {
('.', false) => { foundpoint=true; index+=1},
('.', true) => {stop=true; index=0}, (x,_) if x.is_ascii_digit() => { founddigit=true; index+=1},
_ => {stop=true;}
} }
if founddigit {index} else {0}
}
fn match_strlit(s:&str) -> usize
{
if s.len()<1 {return 0;}
let mut chars = s.chars();
let mut index = 0;
let mut stop = false;
let mut next = chars.next().unwrap();
if next !='\"' {return 0;} else { index+= 1; } while !stop && index<s.len()
{
next = chars.next().unwrap();
index += 1;
if next=='\\' && index<s.len() {
next=chars.next().unwrap();
index += 1;
} else if next=='\"' { stop=true; }
} if stop {index} else {0}
}
fn match_symbol(s:&str) -> usize
{
if s.len()<1 {return 0;}
let mut chars = s.chars();
let mut c = chars.next().unwrap();
if isdelim(c) {return 1;}
if c.is_ascii_digit() || c.is_alphanumeric() || c=='_' || c=='\"' || c=='.' {return 0;}
let mut index = 1;
let mut stop = false;
while !stop && index<s.len()
{
c = chars.next().unwrap();
if !c.is_whitespace() && !c.is_ascii_digit() && !c.is_alphanumeric() && c!='_' && c!='\"' && c!='.' && !isdelim(c)
{index+=1;} else {stop=true;}
}
return index;
}
pub struct Str_tokenizer<'t>
{
slice : &'t str,
line_comment : char,
}
impl<'t> Str_tokenizer<'t>
{
pub fn new(s:&'t str) -> Str_tokenizer<'t>
{
Str_tokenizer {
slice : s.trim_start(),
line_comment: '#', }
}
pub fn rest(&self) -> &'t str
{ self.slice }
pub fn set_line_comment(&mut self, c:char) {self.line_comment='c';}
pub fn no_line_comment(&mut self) {self.line_comment=' ';}
pub fn reset<'u:'t>(&mut self, s:&'u str)
{
self.slice = s;
}
}
impl<'t> Iterator for Str_tokenizer<'t>
{
type Item = Token;
fn next(&mut self) -> Option<Token>
{
if self.slice.len()<1 {None}
else {
let (tok,ind) = next_token(self.slice);
let mut return_value = None;
match tok {
Symbol(s) if s.chars().next().unwrap()==self.line_comment => {
return_value = Some(Verbatim(self.slice.to_owned()));
self.slice = "";
},
_ => { return_value = Some(tok); },
};
if self.slice.len()>0 {self.slice = &self.slice[ind..].trim_start();}
return_value
}
}}
#[derive(PartialEq,Eq,Copy,Clone,Debug)]
enum Mode {
normal,
comment(usize), stringlit(usize), }
fn iscomment(m:&Mode) -> bool
{ if let Mode::comment(n) = m {true} else {false} }
fn isstringlit(m:&Mode) -> bool
{ if let Mode::stringlit(n) = m {true} else {false} }
pub struct File_tokenizer
{
linenum: usize,
column: usize,
line : Rc<RefCell<String>>,
reader : BufReader<File>,
mode : Mode, current_string : String,
keywords: HashSet<String>,
singletons: HashSet<char>, line_comment : String,
keep_comments : bool,
begin_comment : String,
end_comment : String,
keep_newline : bool,
}impl File_tokenizer
{
pub fn new(filename:&str) -> File_tokenizer
{
let reader1 = match File::open(filename) {
Ok(fi) => BufReader::new(fi),
_ => {panic!("File {} not found",filename)},
};
let mut singlesyms = HashSet::<char>::new();
let mut kwhash = HashSet::<String>::new();
for c in "[](){}".chars() {singlesyms.insert(c);}
File_tokenizer {
linenum: 0,
column: 0,
line: Rc::new(RefCell::new(String::from(""))),
reader : reader1,
keywords: kwhash,
singletons: singlesyms,
line_comment: String::from("//"),
keep_comments : false,
keep_newline : false,
mode : Mode::normal,
current_string : String::from(""),
begin_comment : String::from("/*"),
end_comment : String::from("*/"),
}
}
pub fn line_number(&self)->usize {self.linenum}
pub fn column_number(&self)->usize {self.column}
pub fn current_line(&self)-> String
{
self.line.borrow().clone()
}
pub fn add_keywords(&mut self, kws:&str)
{
let ki = kws.split_whitespace();
for kw in ki { self.keywords.insert(kw.trim().to_owned());}
}
pub fn add_singletons(&mut self, singles:&str)
{
for c in singles.chars() {self.singletons.insert(c);}
}
pub fn set_line_comment(&mut self, c:&str)
{if c.len()>0 {self.line_comment=String::from(c.trim());} }
pub fn set_comments(&mut self, s:&str)
{
let cs:Vec<_> = s.split_whitespace().collect();
if cs.len()!=2 {return;}
self.begin_comment = cs[0].to_owned();
self.end_comment = cs[1].to_owned();
}
pub fn no_line_comment(&mut self) {self.line_comment=String::from("");}
pub fn set_keep_comments(&mut self, b:bool) {self.keep_comments=b;}
pub fn set_keep_newline(&mut self, b:bool) {self.keep_newline=b;}
fn next_token(&mut self, s:&str) -> (Token, usize)
{
if s.len()<1 {return (Newline,0);}
let first = s.chars().next().unwrap();
let mut index = 0;
if (s.len()>1 && &s[0..2]==&self.begin_comment[..] && self.mode==Mode::normal) || iscomment(&self.mode) {
if !iscomment(&self.mode) {self.mode = Mode::comment(self.linenum);}
match s.find(&self.end_comment) {
Some(index) => {
self.mode=Mode::normal;
let mut ret=std::mem::replace(&mut self.current_string,String::from(""));
if self.keep_comments {
ret.push_str(&s[0..index+self.end_comment.len()]);
return (Verbatim(ret), index+self.end_comment.len());
}
else { return (Nothing,index+self.end_comment.len()); }
},
None => {
self.current_string.push_str(s);
return (Nothing,s.len());
},
} } else
if first=='\"' || isstringlit(&self.mode) {
if !isstringlit(&self.mode) {self.mode = Mode::stringlit(self.linenum);}
index = self.match_strlit(s);
if index>0 { let mut ret=std::mem::replace(&mut self.current_string,String::from(""));
ret.push_str(&s[0..index]);
self.mode = Mode::normal;
return (Stringlit(ret),index);
}
else {
self.current_string.push_str(s);
return (Nothing,s.len());
}
}
else if first.is_alphabetic() || first=='_' {
index=match_alphanum(s);
return (Alphanum(String::from(&s[0..index])),index);
} else if first=='.' || first.is_ascii_digit() { index = match_num(s);
if index>0 {
if let Ok(m) = s[0..index].parse::<i64>() {return (Integer(m),index);}
else if let Ok(n) = s[0..index].parse::<f64>()
{return (Float(n),index);}
}
else {return (Symbol(first.to_string()),1);}
}
else {
index = self.match_symbol(s);
if index>0 {return (Symbol(String::from(&s[0..index])),index);}
}
(Nothing,s.len())
}
fn match_strlit(&mut self, s:&str) -> usize
{
if s.len()<1 {return 0;}
let mut chars = s.chars();
let mut index = 0;
let mut stop = false;
let mut next = chars.next().unwrap();
if next=='\"' && self.current_string.len()>0 { return 1; }
index +=1;
while !stop && index<s.len()
{
next = chars.next().unwrap();
index += 1;
if next=='\\' && index<s.len() {
next=chars.next().unwrap();
index += 1;
} else if next=='\"' { stop=true; }
} if stop {index} else {0}
}
fn match_symbol(&mut self, s:&str) -> usize
{
if s.len()<1 {return 0;}
let mut chars = s.chars();
let mut c = chars.next().unwrap();
if self.singletons.contains(&c) {return 1;} if c.is_ascii_digit() || c.is_alphanumeric() || c=='_' || c=='\"' || c=='.' {return 0;}
let mut index = 1;
let mut stop = false;
while !stop && index<s.len()
{
c = chars.next().unwrap();
if !c.is_whitespace() && !c.is_ascii_digit() && !c.is_alphanumeric() && c!='_' && c!='\"' && c!='.' && !self.singletons.contains(&c)
{index+=1;} else {stop=true;}
}
return index;
}
}
impl Iterator for File_tokenizer
{
type Item = Token;
fn next(&mut self) -> Option<Token>
{
let cmlen = self.line_comment.len();
let mut return_value = None;
loop {
let linerc = Rc::clone(&self.line);
let mut brline = linerc.borrow_mut();
if self.column >= brline.len() {
let mut newline = String::from("");
match self.reader.read_line(&mut newline) {
Ok(n) if n>0 => {
*brline = newline;
self.linenum +=1;
self.column=0;
},
_ => {
match self.mode {
Mode::stringlit(n) => {
panic!("UNCLOSED STRING LITERAL STARTING ON LINE {}",n);
},
Mode::comment(n) => {
panic!("UNCLOSED COMMENT STARTING ON LINE {}",n);
},
_ => {return None;},
} },
} } let slice0 = &brline[self.column..];
let mut slice = slice0.trim_start();
let diff = slice0.len() - slice.len();
self.column += diff;
let (tok,ind) = self.next_token(slice);
let lcm = &self.line_comment[..]; match tok {
Symbol(s) if cmlen>0 && s.len()>=cmlen && &s[0..cmlen]==lcm => {
return_value = if self.keep_comments {Some(Verbatim(String::from(slice)))} else {Some(Nothing)};
self.column = brline.len();
},
Alphanum(a) if self.keywords.contains(&a) => {
return_value = Some(Keyword(a));
},
_ => { return_value = Some(tok); },
}
self.column += ind;
match return_value {
Some(Nothing) => {}, Some(Newline) if !self.keep_newline => {},
_ => {break;}
}
} return_value
}}