parse/
parse.rs

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			// eprintln!("{configuration:?} {source}");
68
69			{
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			// if let Err(error) = out {
78			//     println!("Error: {error:?}");
79			// }
80
81			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}