1use cl_ast::{
4 Annotation, At, Bind, DefaultTypes, Expr, Pat, Use,
5 fold::Foldable,
6 macro_matcher::{Match, Subst},
7 visit::Walk,
8};
9use cl_interpret::{convalue::ConValue, env::Environment, interpret::Interpret};
10use cl_lexer::{EOF, LexError, Lexer};
11use cl_parser::{Parse, ParseError, Parser, inliner::ModuleInliner};
12use cl_structures::span::Span;
13use cl_token::{TKind, Token};
14use repline::prebaked::*;
16use std::{
17 error::Error,
18 io::{IsTerminal, stdin},
19 marker::PhantomData,
20};
21
22mod builtin;
23
24fn banner() {
25 println!("--- conlang v{} 💪🦈 ---", env!("CARGO_PKG_VERSION"))
26}
27
28fn clear() {
29 print!("\x1b[H\x1b[2J\x1b[3J");
30}
31
32fn main() -> Result<(), Box<dyn Error>> {
33 let mut verbose = Verbosity::from(std::env::var("DO_VERBOSE").as_deref().unwrap_or_default());
34 let mut parsing = ParseMode::from(std::env::var("DO_PARSING").as_deref().unwrap_or_default());
35 let mut env = builtin::get_env();
36 let color = parsing.color();
37 let begin = verbose.begin();
38 banner();
39
40 if stdin().is_terminal() {
41 read_and_mut(color, begin, " > ", |rl, line| match line.trim_end() {
42 "" => Ok(Response::Continue),
43 "exit" => Ok(Response::Break),
44 "help" => {
45 println!("Parsing: {parsing:?} (expr, pat, bind, use, tokens)");
46 println!("Verbose: {verbose:?} (pretty, debug, quiet)");
47 Ok(Response::Deny)
48 }
49 "clear" => {
50 clear();
51 banner();
52 Ok(Response::Deny)
53 }
54 "macro" => {
55 if let Err(e) = subst() {
56 println!("\x1b[31m{e}\x1b[0m");
57 }
58 Ok(Response::Accept)
59 }
60 line @ ("run" | "tokens" | "expr" | "pat" | "bind" | "use") => {
61 parsing = ParseMode::from(line);
62 println!("Parse mode set to '{parsing:?}'");
63 rl.set_color(parsing.color());
64 Ok(Response::Accept)
65 }
66 line @ ("quiet" | "debug" | "debugpretty" | "dp" | "frob" | "pretty") => {
67 verbose = Verbosity::from(line);
68 println!("Verbosity set to '{verbose:?}'");
69 rl.set_begin(verbose.begin());
70 Ok(Response::Accept)
71 }
72 _ if line.ends_with("\n\n") => {
73 parsing.with()(&mut env, line, verbose);
74 Ok(Response::Accept)
75 }
76 _ => Ok(Response::Continue),
77 })?;
78 } else {
79 let doc = std::io::read_to_string(stdin())?;
80 parsing.with()(&mut env, &doc, verbose);
81 }
82 Ok(())
83}
84
85fn subst() -> Result<(), Box<dyn Error>> {
86 let mut rl = repline::Repline::new("\x1b[35mexp", " >", "?>");
87 let exp = rl.read()?;
88 let exp: At<Expr> = Parser::new(Lexer::new("<interactive>".into(), &exp)).parse(0)?;
89 let mut exp = inline_modules(exp).0;
90 println!("\x1b[G\x1b[J{exp}");
91
92 rl.accept();
93
94 loop {
95 rl.set_color("\x1b[36mpat");
96 let pat = rl.read()?;
97 rl.accept();
98 print!("\x1b[G\x1b[J");
99 let mut p = Parser::new(Lexer::new("<interactive>".into(), &pat));
100
101 let Ok(pat) = p.parse::<Expr>(0) else {
102 println!("{exp}");
103 continue;
104 };
105
106 if p.next_if(TKind::Arrow).is_err() {
107 let Some(Subst { exp, pat }) = exp.match_with(&pat) else {
108 println!("Match failed: {exp} <- {pat}");
109 continue;
110 };
111 let mut pats: Vec<_> = pat.into_iter().collect();
112 pats.sort_by_key(|(a, _)| a.to_ref());
113 for (name, pat) in pats {
114 println!("{name}: {pat}")
115 }
116 let mut exprs: Vec<_> = exp.into_iter().collect();
117 exprs.sort_by_key(|(a, _)| a.to_ref());
118 for (name, expr) in exprs.iter() {
119 println!("{name}: {expr}")
120 }
121 continue;
122 }
123
124 let sub: Expr = p.parse(0)?;
125 if exp.apply_rule(&pat, &sub) {
126 println!("{exp}");
127 } else {
128 println!("No match: {pat} in {exp}\n")
129 }
130 }
131}
132
133fn plural(count: usize) -> &'static str {
134 match count {
135 1 => "",
136 _ => "s",
137 }
138}
139
140fn tokens<'e: 't, 't, T: Parse<'t> + ?Sized>(
141 _: &'e mut Environment,
142 document: &'t str,
143 verbose: Verbosity,
144) {
145 let _: PhantomData<T>; let mut lexer = Lexer::new("<interactive>".into(), document);
147 loop {
148 match (lexer.scan(), verbose) {
149 (Err(LexError { res: EOF, .. }), _) => {
150 break;
151 }
152 (Err(e), _) => {
153 println!("\x1b[31m{e}\x1b[0m");
154 break;
155 }
156 (Ok(Token { lexeme, kind, span: Span { path: _, head, tail } }), Verbosity::Pretty) => {
157 println!("{kind:?}\x1b[11G {head:<4} {tail:<4} {lexeme:?}")
158 }
159 (Ok(token), Verbosity::Debug) => {
160 println!("{token:?}")
161 }
162 _ => {}
163 }
164 }
165}
166
167fn parse<'env: 't, 't, T>(_: &'env mut Environment, document: &'t str, verbose: Verbosity)
168where
169 T: Parse<'t>
170 + Annotation
171 + for<'a> Walk<'a, DefaultTypes>
172 + Foldable<DefaultTypes, DefaultTypes>,
173 <T as Foldable<DefaultTypes, DefaultTypes>>::Out: Annotation,
174{
175 let mut parser = Parser::new(Lexer::new("<interactive>".into(), document));
176 for idx in 0..6 {
177 match (
178 parser
179 .parse::<At<T, _>>(T::Prec::default())
180 .map(inline_modules),
181 verbose,
182 ) {
183 (Err(e @ ParseError::EOF(_)), _) => {
184 println!(
185 "\x1b[92m{e} (total {} byte{}, {idx} expression{})\x1b[0m",
186 document.len(),
187 plural(document.len()),
188 plural(idx),
189 );
190 break;
191 }
192 (Err(e), _) => {
193 println!("\x1b[91m{e}\x1b[0m");
194 break;
195 }
196 (Ok(At(expr, span)), Verbosity::Pretty) => {
197 println!("\x1b[{}m{span:?}:\n{expr}", (idx + 5) % 6 + 31);
198 }
199 (Ok(expr), Verbosity::Debug) => {
204 println!("\x1b[{}m{expr:?}", (idx + 5) % 6 + 31);
205 }
206 (Ok(expr), Verbosity::DebugPretty) => {
207 println!("\x1b[{}m{expr:#?}", (idx + 5) % 6 + 31);
208 }
209 _ => {}
210 }
211 }
212}
213
214fn run<'env: 't, 't>(env: &'env mut Environment, document: &'t str, verbose: Verbosity) {
215 let mut parser = Parser::new(Lexer::new("<interactive>".into(), document));
216 for idx in 0..6 {
217 match (
218 parser
219 .parse::<At<Expr>>(0)
220 .map(inline_modules)
221 .map(|expr| expr.interpret(env)),
222 verbose,
223 ) {
224 (Err(ParseError::EOF(_)), _) => break,
225 (Err(e), _) => {
226 println!("\x1b[91m{e}\x1b[0m");
227 break;
228 }
229 (Ok(Ok(ConValue::Empty)), Verbosity::Pretty) => {}
230 (Ok(Ok(value)), Verbosity::Pretty) => {
231 println!("\x1b[{}m{value}", (idx + 5) % 6 + 31);
232 }
233 (Ok(Err(error)), Verbosity::Pretty) => {
234 println!("\x1b[{}m{error}", (idx + 5) % 6 + 31);
235 }
236 (Ok(value), Verbosity::Debug) => {
237 println!("\x1b[{}m{value:?}", (idx + 5) % 6 + 31);
238 }
239 (Ok(value), Verbosity::DebugPretty) => {
240 println!("\x1b[{}m{value:#?}", (idx + 5) % 6 + 31);
241 }
242 _ => {}
243 }
244 }
245}
246
247fn inline_modules<T>(expr: At<T>) -> At<T::Out>
248where
249 T: Annotation + Foldable<DefaultTypes, DefaultTypes>,
250 T::Out: Annotation,
251{
252 let mut mi = ModuleInliner::new(".");
253 let At(expr, span) = expr;
254 let Ok(expr) = expr.fold_in(&mut mi);
255 if let Some((io_errs, parse_errs)) = mi.into_errs() {
256 for (path, err) in io_errs {
257 println!("{}: {err}", path.display());
258 }
259 for (path, err) in parse_errs {
260 println!("{}: {err}", path.display());
261 }
262 }
263
264 At(expr, span)
265}
266
267#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
268enum Verbosity {
269 #[default]
270 Pretty,
271 Debug,
272 DebugPretty,
273 Frob,
274 Quiet,
275}
276
277impl From<&str> for Verbosity {
278 fn from(value: &str) -> Self {
279 match value {
280 "quiet" | "false" | "0" | "no" => Verbosity::Quiet,
281 "debug" | "d" => Verbosity::Debug,
282 "debugpretty" | "debug_pretty" | "dp" => Verbosity::DebugPretty,
283 "frob" => Verbosity::Frob,
284 "pretty" => Verbosity::Pretty,
285 _ => Default::default(),
286 }
287 }
288}
289
290impl Verbosity {
291 fn begin(self) -> &'static str {
292 match self {
293 Self::Pretty => " .> ",
294 Self::Debug => " ?> ",
295 Self::DebugPretty => " #> ",
296 Self::Frob => "🐸> ",
297 Self::Quiet => " _> ",
298 }
299 }
300}
301
302#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
303enum ParseMode {
304 #[default]
305 Expr,
306 Pat,
307 Bind,
308 Use,
309 Tokens,
310 Run,
311}
312
313impl From<&str> for ParseMode {
314 fn from(value: &str) -> Self {
315 match value {
316 "expr" => Self::Expr,
317 "pat" => Self::Pat,
318 "bind" => Self::Bind,
319 "use" => Self::Use,
320 "tokens" => Self::Tokens,
321 "run" => Self::Run,
322 _ => Default::default(),
323 }
324 }
325}
326impl ParseMode {
327 fn with<'env: 'a, 'a>(&self) -> fn(&'env mut Environment, &'a str, Verbosity) {
328 match self {
329 Self::Expr => parse::<'env, 'a, Expr>,
330 Self::Pat => parse::<'env, 'a, Pat>,
331 Self::Bind => parse::<'env, 'a, Bind>,
332 Self::Use => parse::<'env, 'a, Use>,
333 Self::Tokens => tokens::<'env, 'a, dyn Parse<'a, Prec = ()>>,
334 Self::Run => run::<'env, 'a>,
335 }
336 }
337
338 fn color(&self) -> &'static str {
339 match self {
340 Self::Expr => "\x1b[36m",
341 Self::Pat => "\x1b[35m",
342 Self::Bind => "\x1b[34m",
343 Self::Use => "\x1b[33m",
344 Self::Tokens => "\x1b[32m",
345 Self::Run => "\x1b[31m",
346 }
347 }
348}