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 if let Some(rest) = line.strip_suffix(" (function)") {
102 configuration.adjacency.as_mut().expect("expected function").functions.push(rest);
103 continue;
104 }
105
106 let (name, rest) = if let Some((name, rest)) = rest.split_once(':')
107 && name.trim_end().chars().all(is_identifier)
108 {
109 (name.trim_end(), rest)
110 } else {
111 ("", rest)
112 };
113
114 let (syntax, precedence) = rest.rsplit_once('#').unwrap_or((rest, "1"));
115 let precedence: u8 = precedence.parse().expect("invalid precedence");
116
117 let parts: Vec<_> = syntax.trim().split('_').map(str::trim).collect();
118 match parts.as_slice() {
119 ["", first, part1, part2] => {
120 configuration.postfix_ternary_operators.push(TernaryOperator {
121 name,
122 parts: (first, part1, part2),
123 precedence,
124 });
125 }
126 [first, part1, part2, ""] => {
127 configuration.prefix_ternary_operators.push(TernaryOperator {
128 name,
129 parts: (first, part1, part2),
130 precedence,
131 });
132 }
133 ["", binary_operator, ""] => {
134 let operator = BinaryOperator { representation: binary_operator, precedence };
135 if adjacent {
136 configuration.adjacency =
137 Some(Adjacency { operator, functions: Vec::default() });
138 } else {
139 configuration.binary_operators.push(operator);
140 }
141 }
142 ["", after] => {
143 configuration
144 .postfix_unary_operators
145 .push(UnaryOperator { representation: after, precedence });
146 }
147 [before, ""] => {
148 configuration
149 .prefix_unary_operators
150 .push(UnaryOperator { representation: before, precedence });
151 }
152 sequence => panic!("unknown sequence {sequence:?}"),
153 }
154 }
155 (configuration, source.trim())
156 } else {
157 (Configuration::default(), input.trim())
158 }
159}
160
161fn is_identifier(chr: char) -> bool {
162 chr.is_alphanumeric() || matches!(chr, '_' | '$')
163}