1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use crate::generators::NamingHelper as NmHlp;
use crate::parser::parol_grammar::LookaheadExpression;
use crate::{Cfg, Symbol, Terminal};
use parol_runtime::TerminalIndex;
use parol_runtime::lexer::{BLOCK_COMMENT, EOI, LINE_COMMENT, NEW_LINE, WHITESPACE};
/// Generates a terminal name from a terminal definition
/// The parameter of type `Option<TerminalIndex>` is used to handle fixed terminal indices like EOI.
/// When only 'normal' terminal strings are processed and a terminal index is not relevant
/// simply provide None for this value.
pub fn generate_terminal_name(
terminal: &str,
i: Option<TerminalIndex>,
l: Option<&LookaheadExpression>,
cfg: &Cfg,
) -> String {
fn primary_non_terminal(
cfg: &Cfg,
terminal: &str,
l: Option<&LookaheadExpression>,
) -> Option<String> {
cfg.pr
.iter()
.find(|r| {
if r.len() == 1 {
if let Symbol::T(Terminal::Trm(n, k, .., l0)) = &r.1[0] {
k.expand(n) == terminal
&& cfg.matching_productions(&r.get_n()).len() == 1
&& l == l0.as_ref()
} else {
false
}
} else {
false
}
})
.map(|r| r.get_n())
}
fn generate_name(s: &str) -> String {
let (_, name) = s
.chars()
.fold((true, String::new()), |(mut cap, mut acc), c| {
if c.is_alphanumeric() {
if cap {
acc.push(c.to_uppercase().next().unwrap());
} else {
acc.push(c);
}
cap = false;
} else {
acc.push_str(match c {
'\\' => "",
'|' => "Or",
'(' => "LParen",
')' => "RParen",
'[' => "LBracket",
']' => "RBracket",
'{' => "LBrace",
'}' => "RBrace",
'+' => "Plus",
'-' => "Minus",
'*' => "Star",
'/' => "Slash",
'=' => "Equ",
'!' => "Bang",
'.' => "Dot",
'~' => "Tilde",
'$' => "Dollar",
'%' => "Percent",
'<' => "LT",
'>' => "GT",
'?' => "Quest",
'@' => "At",
':' => "Colon",
';' => "Semicolon",
'^' => "Circumflex",
'_' => "Underscore",
'&' => "Amp",
'ยง' => "Para",
'\'' => "Tick",
'"' => "Quote",
'`' => "Backtick",
',' => "Comma",
'#' => "Hash",
_ => "_",
});
cap = true;
}
(cap, acc)
});
if name.is_empty() && !s.is_empty() {
"Esc".to_owned()
} else if name.starts_with(|c: char| c.is_numeric()) {
format!("_{}", name)
} else {
name
}
}
if terminal == "ERROR_TOKEN" {
"Error".to_owned()
} else {
match i {
Some(EOI) => "EndOfInput".to_owned(),
Some(NEW_LINE) => "Newline".to_owned(),
Some(WHITESPACE) => "Whitespace".to_owned(),
Some(LINE_COMMENT) => "LineComment".to_owned(),
Some(BLOCK_COMMENT) => "BlockComment".to_owned(),
_ => {
if let Some(nt) = primary_non_terminal(cfg, terminal, l) {
NmHlp::to_upper_camel_case(&nt)
} else {
generate_name(terminal)
}
}
}
}
}