1use general_parser::{
2 Adjacency, BinaryOperator, Configuration, Expression, ExpressionRepresentation,
3 TernaryOperator, UnaryOperator,
4};
5
6fn main() {
7 let arg = std::env::args().nth(1);
8
9 if let Some("--interactive") = arg.as_deref() {
10 run_interactive();
11 return;
12 }
13
14 if let Some(path) = std::env::args().nth(1) {
15 let source = std::fs::read_to_string(&path).unwrap();
16 let (configuration, source) = extract_configuration_and_source(&source);
17
18 eprintln!("{configuration:?}");
19
20 let allocator = bumpalo::Bump::new();
21 let expression: Expression<&str> =
22 Expression::from_string(&source, &configuration, &allocator);
23
24 let expression = ExpressionRepresentation(&expression);
25 println!("{source}\n -> {expression}");
26 } else {
27 let configuration = Configuration {
28 binary_operators: vec![
29 BinaryOperator { representation: "*", precedence: 4 },
30 BinaryOperator { representation: "+", precedence: 3 },
31 ],
32 ..Default::default()
33 };
34
35 let sources: &[&str] = &["(x (a * b) (d * 2 + e))", "(x (a b) (c d e))"];
36
37 for source in sources {
38 let allocator = bumpalo::Bump::new();
39 let expression: Expression<&str> =
40 Expression::from_string(source, &configuration, &allocator);
41 let expression = ExpressionRepresentation(&expression);
42 eprintln!("{source}\n -> {expression}");
43 }
44 }
45}
46
47fn run_interactive() {
48 use std::io::{BufRead, stdin};
49 let stdin = stdin();
50 let mut buf = Vec::new();
51
52 println!("start");
53
54 for line in stdin.lock().lines().map_while(Result::ok) {
55 if line == "close" {
56 if !buf.is_empty() {
57 eprintln!("no end to message {buf:?}");
58 }
59 break;
60 }
61
62 if line == "end" {
63 let output = String::from_utf8_lossy(&buf);
64
65 let (configuration, source) = extract_configuration_and_source(&output);
66
67 {
70 let allocator = bumpalo::Bump::new();
71 let expression: Expression<&str> =
72 Expression::from_string(&source, &configuration, &allocator);
73 let expression = ExpressionRepresentation(&expression);
74 println!("{expression}");
75 }
76
77 println!("end");
82 buf.clear();
83 continue;
84 }
85
86 buf.extend_from_slice(line.as_bytes());
87 buf.push(b'\n');
88 }
89}
90
91fn extract_configuration_and_source(input: &str) -> (Configuration<'_>, &str) {
92 if let Some((config, source)) = input.split_once("\n---") {
93 let mut configuration = Configuration::default();
94 for line in config.lines().map(str::trim_end) {
95 let (adjacent, rest) = if let Some(rest) = line.strip_suffix(" (adjacent)") {
96 (true, rest)
97 } else {
98 (false, line)
99 };
100
101 let (name, rest) = if let Some((name, rest)) = rest.split_once(':')
102 && name.trim_end().chars().all(is_identifier)
103 {
104 (name.trim_end(), rest)
105 } else {
106 ("", rest)
107 };
108
109 let (syntax, precedence) = rest.rsplit_once('#').unwrap_or((rest, "1"));
110 let precedence: u8 = precedence.parse().expect("invalid precedence");
111
112 let parts: Vec<_> = syntax.trim().split('_').map(str::trim).collect();
113 match parts.as_slice() {
114 ["", first, part1, part2] => {
115 configuration.postfix_ternary_operators.push(TernaryOperator {
116 name,
117 parts: (first, part1, part2),
118 precedence,
119 });
120 }
121 [first, part1, part2, ""] => {
122 configuration.prefix_ternary_operators.push(TernaryOperator {
123 name,
124 parts: (first, part1, part2),
125 precedence,
126 });
127 }
128 ["", binary_operator, ""] => {
129 let operator = BinaryOperator { representation: binary_operator, precedence };
130 if adjacent {
131 configuration.adjacency =
132 Some(Adjacency { operator, functions: Vec::default() });
133 } else {
134 configuration.binary_operators.push(operator);
135 }
136 }
137 ["", after] => {
138 configuration
139 .postfix_unary_operators
140 .push(UnaryOperator { representation: after, precedence });
141 }
142 [before, ""] => {
143 configuration
144 .prefix_unary_operators
145 .push(UnaryOperator { representation: before, precedence });
146 }
147 sequence => panic!("unknown sequence {sequence:?}"),
148 }
149 }
150 (configuration, source.trim())
151 } else {
152 (Configuration::default(), input.trim())
153 }
154}
155
156fn is_identifier(chr: char) -> bool {
157 chr.is_alphanumeric() || matches!(chr, '_' | '$')
158}