pub mod char_tokens {
pub const META: char = '\u{83}';
pub const POUND: char = '\u{84}'; pub const STRING: char = '\u{85}'; pub const HAT: char = '\u{86}'; pub const STAR: char = '\u{87}'; pub const INPAR: char = '\u{88}'; pub const INPARMATH: char = '\u{89}'; pub const OUTPAR: char = '\u{8a}'; pub const OUTPARMATH: char = '\u{8b}'; pub const QSTRING: char = '\u{8c}'; pub const EQUALS: char = '\u{8d}'; pub const BAR: char = '\u{8e}'; pub const INBRACE: char = '\u{8f}'; pub const OUTBRACE: char = '\u{90}'; pub const INBRACK: char = '\u{91}'; pub const OUTBRACK: char = '\u{92}'; pub const TICK: char = '\u{93}'; pub const INANG: char = '\u{94}'; pub const OUTANG: char = '\u{95}'; pub const OUTANGPROC: char = '\u{96}'; pub const QUEST: char = '\u{97}'; pub const TILDE: char = '\u{98}'; pub const QTICK: char = '\u{99}'; pub const COMMA: char = '\u{9a}'; pub const DASH: char = '\u{9b}'; pub const BANG: char = '\u{9c}';
pub const LAST_NORMAL_TOK: char = BANG;
pub const SNULL: char = '\u{9d}'; pub const DNULL: char = '\u{9e}'; pub const BNULL: char = '\u{9f}';
pub const BNULLKEEP: char = '\u{a0}'; pub const NULARG: char = '\u{a1}'; pub const MARKER: char = '\u{a2}';
#[inline]
pub fn is_token(c: char) -> bool {
let b = c as u32;
(0x84..=0xa2).contains(&b)
}
pub fn untokenize(c: char) -> Option<char> {
match c {
POUND => Some('#'),
STRING | QSTRING => Some('$'),
HAT => Some('^'),
STAR => Some('*'),
INPAR | INPARMATH => Some('('),
OUTPAR | OUTPARMATH => Some(')'),
EQUALS => Some('='),
BAR => Some('|'),
INBRACE => Some('{'),
OUTBRACE => Some('}'),
INBRACK => Some('['),
OUTBRACK => Some(']'),
TICK | QTICK => Some('`'),
INANG => Some('<'),
OUTANG | OUTANGPROC => Some('>'),
QUEST => Some('?'),
TILDE => Some('~'),
COMMA => Some(','),
DASH => Some('-'),
BANG => Some('!'),
SNULL | DNULL | BNULL | BNULLKEEP | NULARG | MARKER => None,
_ => None,
}
}
pub const ZTOKENS: &str = "#$^*(())$=|{}[]`<>>?~`,-!'\"\\\\";
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum LexTok {
Nulltok = 0,
Seper, Newlin, Semi, Dsemi, Amper, Inpar, Outpar, Dbar, Damper, Outang, Outangbang, Doutang, Doutangbang, Inang, Inoutang, Dinang, Dinangdash, Inangamp, Outangamp, Ampoutang, Outangampbang, Doutangamp, Doutangampbang, Trinang, Bar, Baramp, Inoutpar, Dinpar, Doutpar, Amperbang, Semiamp, Semibar,
Doutbrack, String, Envstring, Envarray, Endinput, Lexerr,
Bang, Dinbrack, Inbrace, Outbrace, Case, Coproc, Doloop, Done, Elif, Else, Zend, Esac, Fi, For, Foreach, Func, If, Nocorrect, Repeat, Select, Then, Time, Until, While, Typeset, }
impl LexTok {
pub fn is_redirop(self) -> bool {
matches!(
self,
LexTok::Outang
| LexTok::Outangbang
| LexTok::Doutang
| LexTok::Doutangbang
| LexTok::Inang
| LexTok::Inoutang
| LexTok::Dinang
| LexTok::Dinangdash
| LexTok::Inangamp
| LexTok::Outangamp
| LexTok::Ampoutang
| LexTok::Outangampbang
| LexTok::Doutangamp
| LexTok::Doutangampbang
| LexTok::Trinang
)
}
pub fn as_str(self) -> Option<&'static str> {
match self {
LexTok::Nulltok => None,
LexTok::Seper => Some(";"),
LexTok::Newlin => Some("\\n"),
LexTok::Semi => Some(";"),
LexTok::Dsemi => Some(";;"),
LexTok::Amper => Some("&"),
LexTok::Inpar => Some("("),
LexTok::Outpar => Some(")"),
LexTok::Dbar => Some("||"),
LexTok::Damper => Some("&&"),
LexTok::Outang => Some(">"),
LexTok::Outangbang => Some(">|"),
LexTok::Doutang => Some(">>"),
LexTok::Doutangbang => Some(">>|"),
LexTok::Inang => Some("<"),
LexTok::Inoutang => Some("<>"),
LexTok::Dinang => Some("<<"),
LexTok::Dinangdash => Some("<<-"),
LexTok::Inangamp => Some("<&"),
LexTok::Outangamp => Some(">&"),
LexTok::Ampoutang => Some("&>"),
LexTok::Outangampbang => Some("&>|"),
LexTok::Doutangamp => Some(">>&"),
LexTok::Doutangampbang => Some(">>&|"),
LexTok::Trinang => Some("<<<"),
LexTok::Bar => Some("|"),
LexTok::Baramp => Some("|&"),
LexTok::Inoutpar => Some("()"),
LexTok::Dinpar => Some("(("),
LexTok::Doutpar => Some("))"),
LexTok::Amperbang => Some("&|"),
LexTok::Semiamp => Some(";&"),
LexTok::Semibar => Some(";|"),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum RedirType {
Write = 0, Writenow, App, Appnow, Errwrite, Errwritenow, Errapp, Errappnow, Readwrite, Read, Heredoc, Heredocdash, Herestr, Mergein, Mergeout, Close, Inpipe, Outpipe, }
impl RedirType {
pub fn is_read(self) -> bool {
matches!(
self,
RedirType::Read
| RedirType::Readwrite
| RedirType::Heredoc
| RedirType::Heredocdash
| RedirType::Herestr
| RedirType::Mergein
| RedirType::Inpipe
)
}
pub fn is_write_file(self) -> bool {
matches!(
self,
RedirType::Write
| RedirType::Writenow
| RedirType::App
| RedirType::Appnow
| RedirType::Readwrite
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum CondType {
Not = 0,
And,
Or,
Streq, Strdeq, Strneq, Strlt, Strgtr, Nt, Ot, Ef, Eq, Ne, Lt, Gt, Le, Ge, Regex, Mod, Modi, }
pub const SPECCHARS: &str = "#$^*()=|{}[]`<>?~;&\n\t \\'\"";
pub const PATCHARS: &str = "#^*()|[]<>?~\\";
#[inline]
pub fn is_dash(c: char) -> bool {
c == '-' || c == char_tokens::DASH
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum LexAct1 {
Bkslash = 0,
Comment = 1,
Newlin = 2,
Semi = 3,
Amper = 5,
Bar = 6,
Inpar = 7,
Outpar = 8,
Inang = 13,
Outang = 14,
Other = 15,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum LexAct2 {
Break = 0,
Outpar = 1,
Bar = 2,
String = 3,
Inbrack = 4,
Outbrack = 5,
Tilde = 6,
Inpar = 7,
Inbrace = 8,
Outbrace = 9,
Outang = 10,
Inang = 11,
Equals = 12,
Bkslash = 13,
Quote = 14,
Dquote = 15,
Bquote = 16,
Comma = 17,
Dash = 18,
Bang = 19,
Other = 20,
Meta = 21,
}
pub static RESERVED_WORDS: &[(&str, LexTok)] = &[
("!", LexTok::Bang),
("[[", LexTok::Dinbrack),
("{", LexTok::Inbrace),
("}", LexTok::Outbrace),
("case", LexTok::Case),
("coproc", LexTok::Coproc),
("declare", LexTok::Typeset),
("do", LexTok::Doloop),
("done", LexTok::Done),
("elif", LexTok::Elif),
("else", LexTok::Else),
("end", LexTok::Zend),
("esac", LexTok::Esac),
("export", LexTok::Typeset),
("fi", LexTok::Fi),
("float", LexTok::Typeset),
("for", LexTok::For),
("foreach", LexTok::Foreach),
("function", LexTok::Func),
("if", LexTok::If),
("integer", LexTok::Typeset),
("local", LexTok::Typeset),
("nocorrect", LexTok::Nocorrect),
("readonly", LexTok::Typeset),
("repeat", LexTok::Repeat),
("select", LexTok::Select),
("then", LexTok::Then),
("time", LexTok::Time),
("typeset", LexTok::Typeset),
("until", LexTok::Until),
("while", LexTok::While),
];
pub fn lookup_reserved_word(s: &str) -> Option<LexTok> {
RESERVED_WORDS
.iter()
.find(|(word, _)| *word == s)
.map(|(_, tok)| *tok)
}
pub static TYPESET_COMMANDS: &[&str] = &[
"declare", "export", "float", "integer", "local", "readonly", "typeset",
];
pub fn is_typeset_command(s: &str) -> bool {
TYPESET_COMMANDS.contains(&s)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_token_values() {
assert_eq!(char_tokens::SNULL as u32, 0x9d);
assert_eq!(char_tokens::DNULL as u32, 0x9e);
assert_eq!(char_tokens::BNULL as u32, 0x9f);
}
#[test]
fn test_reserved_words() {
assert_eq!(lookup_reserved_word("if"), Some(LexTok::If));
assert_eq!(lookup_reserved_word("then"), Some(LexTok::Then));
assert_eq!(lookup_reserved_word("notakeyword"), None);
}
#[test]
fn test_redirop() {
assert!(LexTok::Outang.is_redirop());
assert!(LexTok::Dinang.is_redirop());
assert!(!LexTok::If.is_redirop());
assert!(!LexTok::String.is_redirop());
}
}