1use crate::ast::operators::Op;
2use crate::ast::*;
3use crate::interner::{ExprNodeId, Symbol, ToSymbol, TypeNodeId};
4use crate::pattern::{Pattern, TypedId, TypedPattern};
5use crate::types::{PType, RecordTypeField, Type};
6use crate::utils::error::ReportableError;
7use crate::utils::metadata::*;
8use std::path::PathBuf;
9
10use chumsky::input::{MapExtra, Stream, ValueInput};
11use chumsky::pratt::{Associativity, Operator};
12use chumsky::{Parser, prelude::*};
13mod token;
14pub use token::Token;
15mod error;
16mod lexer;
17use crate::ast::program::{Program, ProgramStatement, expr_from_program};
18use crate::ast::statement;
19use statement::{Statement, into_then_expr};
20
21use super::intrinsics;
22
23#[cfg(test)]
24mod test;
25
26#[derive(Clone)]
27pub(super) struct ParseContext {
28 file_path: PathBuf,
29}
30impl ParseContext {
31 pub fn gen_loc(&self, span: SimpleSpan) -> Location {
32 Location {
33 span: span.start()..span.end(),
34 path: self.file_path.clone(),
35 }
36 }
37}
38pub(crate) type ParseError<'src> = chumsky::extra::Err<Rich<'src, Token, SimpleSpan>>;
39fn merge_span(a: Span, b: Span) -> Span {
40 a.start..b.end
41}
42fn get_span<T: chumsky::span::Span<Offset = usize>>(e: T) -> Span {
43 e.start()..e.end()
44}
45
46fn breakable_comma<'src, I>() -> impl Parser<'src, I, (), ParseError<'src>> + Clone
47where
48 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
49{
50 just(Token::Comma)
51 .ignore_then(just(Token::LineBreak).or_not())
52 .ignored()
53}
54fn breakable_blockbegin<'src, I>() -> impl Parser<'src, I, (), ParseError<'src>> + Clone
55where
56 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
57{
58 just(Token::BlockBegin)
59 .then(just(Token::LineBreak).or(just(Token::SemiColon)).repeated())
60 .ignored()
61}
62fn breakable_blockend<'src, I>() -> impl Parser<'src, I, (), ParseError<'src>> + Clone
63where
64 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
65{
66 just(Token::LineBreak)
67 .or(just(Token::SemiColon))
68 .repeated()
69 .then(just(Token::BlockEnd))
70 .ignored()
71}
72
73fn type_primitive<'src, I>(
74 ctx: ParseContext,
75) -> impl Parser<'src, I, TypeNodeId, ParseError<'src>> + Clone
76where
77 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
78{
79 select! {
80 Token::FloatType => PType::Numeric,
81 Token::StringType => PType::String,
82 Token::IntegerType => PType::Int,
83 }
84 .map_with(move |t, e| {
85 Type::Primitive(t)
86 .into_id_with_location(Location::new(get_span(e.span()), ctx.file_path.clone()))
87 })
88 .labelled("primitive type")
89}
90
91fn type_parser<'src, I>(
92 ctx: ParseContext,
93) -> impl Parser<'src, I, TypeNodeId, ParseError<'src>> + Clone
94where
95 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
96{
97 let path = ctx.file_path.clone();
98 recursive(move |ty| {
99 let path2 = path.clone();
100 let tuple = ty
101 .clone()
102 .separated_by(just(Token::Comma))
103 .at_least(1)
104 .allow_trailing()
105 .collect::<Vec<_>>()
106 .delimited_by(just(Token::ParenBegin), just(Token::ParenEnd))
107 .map_with(move |item: Vec<TypeNodeId>, e| {
108 Type::Tuple(item).into_id_with_location(Location {
109 span: get_span(e.span()),
110 path: path.clone(),
111 })
112 })
113 .recover_with(via_parser(nested_delimiters(
114 Token::ParenBegin,
115 Token::ParenEnd,
116 [],
117 move |_span| Type::Failure.into_id(),
118 )))
119 .labelled("Tuple Type");
120 let path = path2.clone();
121
122 let record = ident_parser()
123 .then_ignore(just(Token::Colon))
124 .then(ty.clone())
125 .map(|(key, ty)| RecordTypeField::new(key, ty, false))
126 .separated_by(breakable_comma())
127 .allow_trailing()
128 .collect::<Vec<_>>()
129 .delimited_by(just(Token::BlockBegin), just(Token::BlockEnd))
130 .map_with(move |fields, e| {
131 Type::Record(fields).into_id_with_location(Location {
132 span: get_span(e.span()),
133 path: path.clone(),
134 })
135 })
136 .recover_with(via_parser(nested_delimiters(
137 Token::BlockBegin,
138 Token::BlockEnd,
139 [],
140 move |_span| Type::Failure.into_id(),
141 )))
142 .labelled("Record Type");
143 let path = path2.clone();
145
146 let array = ty
147 .clone()
148 .delimited_by(just(Token::ArrayBegin), just(Token::ArrayEnd))
149 .map_with(move |element_type, e| {
150 Type::Array(element_type).into_id_with_location(Location {
151 span: get_span(e.span()),
152 path: path.clone(),
153 })
154 })
155 .boxed()
156 .labelled("Array");
157 let path = path2.clone();
158
159 let code = just(Token::BackQuote)
160 .ignore_then(ty.clone())
161 .map_with(move |inner_type, e| {
162 Type::Code(inner_type).into_id_with_location(Location {
163 span: get_span(e.span()),
164 path: path.clone(),
165 })
166 })
167 .labelled("Code");
168 let atom = choice((type_primitive(ctx.clone()), record, tuple, array, code));
169 let func = atom
170 .clone()
171 .separated_by(just(Token::Comma))
172 .collect::<Vec<_>>()
173 .map_with(move |arg, e| (arg, e.span()))
174 .delimited_by(just(Token::ParenBegin), just(Token::ParenEnd))
175 .then(just(Token::Arrow).ignore_then(ty.clone()))
176 .map_with(move |((body, bspan), ret), e| {
177 Type::Function {
178 arg: Type::Tuple(body).into_id_with_location(ctx.gen_loc(bspan)),
179 ret,
180 }
181 .into_id_with_location(ctx.gen_loc(e.span()))
182 })
183 .boxed()
184 .labelled("function");
185
186 func.or(atom).labelled("Type")
187 })
188}
189pub(super) fn ident_parser<'src, I>() -> impl Parser<'src, I, Symbol, ParseError<'src>> + Clone
190where
191 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
192{
193 select! { Token::Ident(s) => s }.labelled("ident")
194}
195fn literals_parser<'src, I>(
196 ctx: ParseContext,
197) -> impl Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone
198where
199 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
200{
201 select! {
202 Token::Int(x)=>Literal::Float(x.to_string().to_symbol()),
205 Token::Float(x) =>Literal::Float(x.to_symbol()),
206 Token::Str(s) => Literal::String(s.to_symbol()),
207 Token::SelfLit => Literal::SelfLit,
208 Token::Now => Literal::Now,
209 Token::SampleRate => Literal::SampleRate,
210 Token::PlaceHolder => Literal::PlaceHolder,
211 }
212 .map_with(move |lit, e| {
213 Expr::Literal(lit).into_id(Location {
214 span: get_span(e.span()),
215 path: ctx.file_path.clone(),
216 })
217 })
218 .labelled("literal")
219}
220fn var_parser<'src, I>(
221 ctx: ParseContext,
222) -> impl Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone
223where
224 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
225{
226 ident_parser().map_with(move |s, e| {
227 Expr::Var(s).into_id(Location {
228 span: get_span(e.span()),
229 path: ctx.file_path.clone(),
230 })
231 })
232}
233fn with_type_annotation<'src, I, T>(
234 parser: impl Parser<'src, I, T, ParseError<'src>> + Clone,
235 ctx: ParseContext,
236) -> impl Parser<'src, I, (T, Option<TypeNodeId>), ParseError<'src>> + Clone
237where
238 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
239{
240 parser.then(just(Token::Colon).ignore_then(type_parser(ctx)).or_not())
241}
242
243fn lvar_parser_typed<'src, I>(
244 ctx: ParseContext,
245) -> impl Parser<'src, I, TypedId, ParseError<'src>> + Clone
246where
247 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
248{
249 with_type_annotation(ident_parser(), ctx.clone())
250 .map_with(move |(sym, t), e| match t {
251 Some(ty) => TypedId {
252 id: sym,
253 ty,
254 default_value: None,
255 },
256 None => TypedId {
257 id: sym,
258 ty: Type::Unknown.into_id_with_location(Location {
259 span: get_span(e.span()),
260 path: ctx.file_path.clone(),
261 }),
262 default_value: None,
263 },
264 })
265 .labelled("lvar_typed")
266}
267
268fn lvar_parser_typed_with_default<'src, I>(
270 ctx: ParseContext,
271 expr: impl Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone,
272) -> impl Parser<'src, I, TypedPattern, ParseError<'src>> + Clone
273where
274 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
275{
276 lvar_parser_typed(ctx.clone())
277 .then(
278 just(Token::Assign).ignore_then(expr).or_not(),
280 )
281 .map(|(param, default_value)| TypedId {
282 id: param.id,
283 ty: param.ty,
284 default_value,
285 })
286 .map(TypedPattern::from)
287 .labelled("lvar_typed_with_default")
288}
289fn pattern_parser<'src, I>(
290 ctx: ParseContext,
291) -> impl Parser<'src, I, TypedPattern, ParseError<'src>> + Clone
292where
293 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
294{
295 let single_pat = select! {
296 Token::Ident(s) => Pattern::Single(s),
297 Token::PlaceHolder => Pattern::Single("_".to_symbol())
300
301 }
302 .labelled("single pattern");
303 let pat = recursive(|pat| {
304 let tup = pat
305 .clone()
306 .separated_by(just(Token::Comma))
307 .at_least(1)
308 .allow_trailing()
309 .collect::<Vec<_>>()
310 .delimited_by(just(Token::ParenBegin), just(Token::ParenEnd))
311 .map(Pattern::Tuple)
312 .labelled("tuple pattern");
313 let record = (ident_parser()
314 .then_ignore(just(Token::Assign))
315 .then(pat.clone()))
316 .separated_by(breakable_comma())
317 .at_least(1)
318 .allow_trailing()
319 .collect::<Vec<_>>()
320 .delimited_by(breakable_blockbegin(), breakable_blockend())
321 .map(Pattern::Record)
322 .recover_with(via_parser(nested_delimiters(
323 Token::BlockBegin,
324 Token::BlockEnd,
325 [],
326 |_| Pattern::Error,
327 )))
328 .labelled("record pattern");
329 choice((single_pat, tup, record)).labelled("pattern")
330 });
331 with_type_annotation(pat, ctx.clone())
332 .map_with(move |(pat, ty), e| match ty {
333 Some(ty) => TypedPattern::new(pat, ty),
334 None => TypedPattern::new(
335 pat,
336 Type::Unknown.into_id_with_location(Location {
337 span: get_span(e.span()),
338 path: ctx.file_path.clone(),
339 }),
340 ),
341 })
342 .boxed()
343}
344
345fn items_parser<'src, I, E>(
346 expr: E,
347 allow_empty: bool,
348) -> impl Parser<'src, I, Vec<ExprNodeId>, ParseError<'src>> + Clone
349where
350 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
351 E: Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone,
352{
353 let least_repeat = if allow_empty { 0 } else { 1 };
354 expr.separated_by(breakable_comma())
355 .allow_trailing()
356 .at_least(least_repeat)
357 .collect::<Vec<_>>()
358}
359enum DotField {
360 Index(i64),
361 Ident(Symbol),
362}
363fn dot_field<'src, I>() -> impl Parser<'src, I, (DotField, Span), ParseError<'src>> + Clone
364where
365 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
366{
367 select! {
368 Token::Int(i) => DotField::Index(i),
369 Token::Ident(s) => DotField::Ident(s),
370 }
371 .map_with(|field, e| (field, get_span(e.span())))
372 .labelled("dot_field")
373}
374fn op_parser<'src, I, P>(
375 apply: P,
376 ctx: ParseContext,
377) -> impl Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone
378where
379 P: Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone + 'src,
380 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
381{
382 let ctx = ctx.clone();
383 let path = ctx.file_path.clone();
384 let dot_prec = 10;
385 let dot_parser = just(Token::Dot).ignore_then(dot_field());
386 let dot_pratt = chumsky::pratt::postfix(dot_prec, dot_parser, {
387 move |lhs: ExprNodeId,
388 (rhs, rspan): (DotField, Span),
389 _extra: &mut MapExtra<'_, '_, I, ParseError<'_>>| {
390 let span = lhs.to_span().start..rspan.end;
391 let loc = Location {
392 span,
393 path: path.clone(),
394 };
395 match rhs {
396 DotField::Ident(name) => Expr::FieldAccess(lhs, name).into_id(loc),
397 DotField::Index(idx) => Expr::Proj(lhs, idx).into_id(loc),
398 }
399 }
400 });
401
402 let path = ctx.file_path.clone();
403 let unary_ops = [Token::Op(Op::Minus), Token::BackQuote, Token::Dollar];
404 let unary_prec = 9;
405 let unary_parser = one_of(unary_ops).map_with(|token, e| (token, get_span(e.span())));
406 let unary_pratt = chumsky::pratt::prefix(unary_prec, unary_parser, {
407 move |(op, op_span): (Token, Span),
408 rhs: ExprNodeId,
409 _extra: &mut MapExtra<'_, '_, I, ParseError<'_>>| {
410 let rhs_span = rhs.to_span();
411 let loc = Location {
412 span: op_span.start..rhs_span.end,
413 path: path.clone(),
414 };
415 match op {
416 Token::BackQuote => Expr::Bracket(rhs).into_id(loc.clone()),
417 Token::Dollar => Expr::Escape(rhs).into_id(loc.clone()),
418 Token::Op(Op::Minus) => Expr::UniOp((Op::Minus, op_span), rhs).into_id(loc),
419 _ => unreachable!("Unexpected unary operator: {:?}", op),
420 }
421 }
422 });
423
424 let optoken = move |target: Op| {
425 select! {
426 Token::Op(o) if o == target => o,
427 }
428 .boxed()
429 };
430 let create_infix = |ops_parser: Boxed<'src, 'src, _, Op, _>, associativity| {
431 let path = ctx.file_path.clone();
432 chumsky::pratt::infix(
433 associativity,
434 just(Token::LineBreak)
435 .repeated()
436 .ignore_then(ops_parser)
437 .then_ignore(just(Token::LineBreak).repeated())
438 .map_with(move |op, e| (op, get_span(e.span()))),
439 move |l: ExprNodeId, (op, opspan), r, _extra| {
440 let span = l.to_span().start..r.to_span().end;
441 let loc = Location {
442 span,
443 path: path.clone(),
444 };
445 Expr::BinOp(l, (op, opspan), r).into_id(loc)
446 },
447 )
448 .boxed()
449 };
450
451 let binary_pratt = (
453 create_infix(optoken(Op::Exponent), Associativity::Right(8)),
454 create_infix(
455 choice((
456 optoken(Op::Product),
457 optoken(Op::Divide),
458 optoken(Op::Modulo),
459 ))
460 .boxed(),
461 Associativity::Left(7),
462 ),
463 create_infix(
464 choice((optoken(Op::Sum), optoken(Op::Minus))).boxed(),
465 Associativity::Left(6),
466 ),
467 create_infix(
468 choice((
469 optoken(Op::LessThan),
470 optoken(Op::LessEqual),
471 optoken(Op::GreaterThan),
472 optoken(Op::GreaterEqual),
473 ))
474 .boxed(),
475 Associativity::Left(5),
476 ),
477 create_infix(
478 choice((optoken(Op::Equal), optoken(Op::NotEqual))).boxed(),
479 Associativity::Left(4),
480 ),
481 create_infix(optoken(Op::And), Associativity::Left(3)),
482 create_infix(optoken(Op::Or), Associativity::Left(2)),
483 create_infix(optoken(Op::At), Associativity::Left(1)),
484 create_infix(optoken(Op::Pipe), Associativity::Left(0)),
485 );
486 apply.pratt((dot_pratt, unary_pratt, binary_pratt))
487}
488fn record_fields<'src, I, P>(expr: P) -> impl Parser<'src, I, RecordField, ParseError<'src>> + Clone
489where
490 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
491 P: Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone,
492{
493 ident_parser()
494 .then_ignore(just(Token::Assign))
495 .then(expr.clone())
496 .map(move |(name, expr)| RecordField { name, expr })
497}
498
499pub(super) fn atom_parser<'src, I>(
500 expr: impl Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone + 'src,
501 expr_group: impl Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone + 'src,
502 ctx: ParseContext,
503) -> impl Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone
504where
505 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
506{
507 let path = ctx.file_path.clone();
508 let lambda = lvar_parser_typed(ctx.clone())
509 .separated_by(just(Token::Comma))
510 .collect::<Vec<_>>()
511 .delimited_by(
512 just(Token::LambdaArgBeginEnd),
513 just(Token::LambdaArgBeginEnd),
514 )
515 .then(
516 just(Token::Arrow)
517 .ignore_then(type_parser(ctx.clone()))
518 .or_not(),
519 )
520 .then(expr_group.clone())
521 .map_with(move |((ids, r_type), body), e| {
522 Expr::Lambda(ids, r_type, body).into_id(Location {
523 span: get_span(e.span()),
524 path: path.clone(),
525 })
526 })
527 .labelled("lambda");
528 let path = ctx.file_path.clone();
529 let path2 = ctx.file_path.clone();
530 let macro_expand = select! { Token::MacroExpand(s) => Expr::Var(s) }
531 .map_with(move |v, e| {
532 v.into_id(Location {
533 span: get_span(e.span()),
534 path: path.clone(),
535 })
536 })
537 .then_ignore(just(Token::ParenBegin))
538 .then(
539 expr_group
540 .clone()
541 .separated_by(breakable_comma())
542 .collect::<Vec<_>>(),
543 )
544 .then_ignore(just(Token::ParenEnd))
545 .map_with(move |(id, then), e| {
546 let loc = Location {
547 span: get_span(e.span()),
548 path: path2.clone(),
549 };
550 Expr::MacroExpand(id, then).into_id(loc.clone())
551 })
552 .labelled("macroexpand");
553 let path = ctx.file_path.clone();
554 let tuple = items_parser(expr.clone(), false)
555 .delimited_by(just(Token::ParenBegin), just(Token::ParenEnd))
556 .map_with(move |items, e| {
557 Expr::Tuple(items).into_id(Location {
558 span: get_span(e.span()),
559 path: path.clone(),
560 })
561 })
562 .labelled("tuple");
563 let path = ctx.file_path.clone();
564 let array_literal = items_parser(expr.clone(), true)
565 .delimited_by(just(Token::ArrayBegin), just(Token::ArrayEnd))
566 .map_with(move |items, e| {
567 let loc = Location {
570 span: get_span(e.span()),
571 path: path.clone(),
572 };
573 Expr::ArrayLiteral(items).into_id(loc)
574 })
575 .labelled("array_literal");
576 let path = ctx.file_path.clone();
577 let path2 = ctx.file_path.clone();
578 let record_parser = choice((
580 expr_group
582 .clone()
583 .then_ignore(just(Token::LeftArrow))
584 .then(
585 record_fields(expr.clone())
586 .separated_by(breakable_comma())
587 .allow_trailing()
588 .collect::<Vec<_>>(),
589 )
590 .delimited_by(breakable_blockbegin(), breakable_blockend())
591 .map_with(move |(record, fields), e| {
592 let mut fields = fields;
593 fields.sort_by(|a, b| a.name.cmp(&b.name));
594 let loc = Location {
595 span: get_span(e.span()),
596 path: path.clone(),
597 };
598 Expr::RecordUpdate(record, fields).into_id(loc)
599 })
600 .labelled("record_update"),
601 record_fields(expr.clone())
603 .separated_by(breakable_comma())
604 .allow_trailing()
605 .collect::<Vec<_>>()
606 .then(just(Token::DoubleDot).or_not())
607 .delimited_by(breakable_blockbegin(), breakable_blockend())
608 .map_with(move |(fields, is_imcomplete), e| {
609 let mut fields = fields;
610 fields.sort_by(|a, b| a.name.cmp(&b.name));
611 let loc = Location {
612 span: get_span(e.span()),
613 path: path2.clone(),
614 };
615 if is_imcomplete.is_some() {
616 log::trace!("is imcomplete record literal");
617 Expr::ImcompleteRecord(fields).into_id(loc)
618 } else {
619 Expr::RecordLiteral(fields).into_id(loc)
620 }
621 })
622 .labelled("record_literal"),
623 ))
624 .labelled("record_parser");
625 let path = ctx.file_path.clone();
626 let parenexpr = expr
627 .clone()
628 .delimited_by(just(Token::ParenBegin), just(Token::ParenEnd))
629 .map_with(move |e, e_s| {
630 Expr::Paren(e).into_id(Location {
631 span: get_span(e_s.span()),
632 path: path.clone(),
633 })
634 })
635 .labelled("paren_expr");
636 choice((
638 literals_parser(ctx.clone()),
639 var_parser(ctx.clone()),
640 lambda,
641 macro_expand,
642 parenexpr,
643 record_parser,
644 array_literal,
645 tuple,
646 ))
647 .boxed()
648 .labelled("atom")
649}
650fn expr_parser<'src, I>(
651 expr_group: impl Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone + 'src,
652 ctx: ParseContext,
653) -> impl Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone
654where
655 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
656{
657 let path = ctx.file_path.clone();
658 recursive(|expr| {
659 enum FoldItem {
660 Args(Vec<ExprNodeId>),
661 ArrayIndex(ExprNodeId),
662 }
663 let parenitems = items_parser(expr.clone(), true)
664 .delimited_by(just(Token::ParenBegin), just(Token::ParenEnd))
665 .map_with(|args, e| (FoldItem::Args(args), get_span(e.span())));
666 let angle_paren_expr = expr
667 .clone()
668 .delimited_by(just(Token::ArrayBegin), just(Token::ArrayEnd))
669 .map_with(|v, e| (FoldItem::ArrayIndex(v), get_span(e.span())));
670
671 let folder = move |f: ExprNodeId, (item, args_span): (FoldItem, Span)| {
672 let f_span = f.to_span();
673 let span = f_span.start..args_span.end;
674 let loc = Location {
675 span,
676 path: path.clone(),
677 };
678 match item {
679 FoldItem::Args(args) => Expr::Apply(f, args).into_id(loc),
680 FoldItem::ArrayIndex(index) => Expr::ArrayAccess(f, index).into_id(loc),
681 }
682 };
683
684 let apply = atom_parser(expr.clone(), expr_group, ctx.clone())
685 .foldl(angle_paren_expr.or(parenitems).repeated(), folder)
686 .labelled("apply");
687
688 op_parser(apply, ctx).boxed()
689 })
690}
691fn validate_reserved_pat<'src>(
692 id: &TypedPattern,
693 span: SimpleSpan,
694) -> Result<(), Rich<'src, Token>> {
695 match &id.pat {
696 Pattern::Single(symbol) => validate_reserved_ident(*symbol, span),
697 _ => Ok(()),
698 }
699}
700
701fn validate_reserved_ident<'src>(id: Symbol, span: SimpleSpan) -> Result<(), Rich<'src, Token>> {
702 if intrinsics::BUILTIN_SYMS.with(|syms| syms.binary_search(&id).is_ok()) {
703 Err(Rich::custom(
704 span,
705 "Builtin functions cannot be re-defined.",
706 ))
707 } else {
708 Ok(())
709 }
710}
711
712fn statement_parser<'src, I>(
713 expr: impl Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone + 'src,
714 ctx: ParseContext,
715) -> impl Parser<'src, I, (Statement, Location), ParseError<'src>> + Clone
716where
717 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
718{
719 let let_ = just(Token::Let)
720 .ignore_then(pattern_parser(ctx.clone()).validate(|pat, e, emitter| {
721 if let Err(e) = validate_reserved_pat(&pat, e.span()) {
722 emitter.emit(e);
723 }
724 pat
725 }))
726 .then_ignore(just(Token::Assign).then(just(Token::LineBreak).repeated()))
727 .then(expr.clone())
728 .map_with(|(ident, body), e| (Statement::Let(ident, body), get_span(e.span())))
729 .labelled("let");
730 let letrec = just(Token::LetRec)
731 .ignore_then(
732 lvar_parser_typed(ctx.clone()).validate(|ident, e, emitter| {
733 if let Err(e) = validate_reserved_ident(ident.id, e.span()) {
734 emitter.emit(e);
735 }
736 ident
737 }),
738 )
739 .then_ignore(just(Token::Assign).then(just(Token::LineBreak).repeated()))
740 .then(expr.clone())
741 .map_with(|(ident, body), e| (Statement::LetRec(ident, body), get_span(e.span())))
742 .labelled("letrec");
743 let assign = expr
744 .clone()
745 .validate(|v, _e, emitter| match v.to_expr() {
746 Expr::Var(_) | Expr::FieldAccess(_, _) | Expr::ArrayAccess(_, _) => v,
747 _ => {
748 emitter.emit(Rich::custom(
749 v.to_span().into(),
750 "Left hand side of assignment must denotes specific memory address.",
751 ));
752 v
753 }
754 })
755 .then_ignore(just(Token::Assign))
756 .then(expr.clone())
757 .map_with(|(lvar, body), e| (Statement::Assign(lvar, body), get_span(e.span())))
758 .labelled("assign");
759 let single = expr
760 .map_with(|s, e| (Statement::Single(s), get_span(e.span())))
761 .labelled("single");
762 choice((let_, letrec, assign, single))
763 .boxed()
764 .map(move |(t, span)| {
765 (
766 t,
767 Location {
768 span: span.start()..span.end(),
769 path: ctx.file_path.clone(),
770 },
771 )
772 })
773}
774fn statements_parser<'src, I>(
775 expr: impl Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone + 'src,
776 ctx: ParseContext,
777) -> impl Parser<'src, I, Option<ExprNodeId>, ParseError<'src>> + Clone
778where
779 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
780{
781 statement_parser(expr, ctx.clone())
782 .separated_by(just(Token::LineBreak).or(just(Token::SemiColon)).repeated())
783 .allow_leading()
784 .allow_trailing()
785 .collect::<Vec<_>>()
786 .recover_with(skip_then_retry_until(
787 any().ignored(),
788 one_of([Token::LineBreak, Token::SemiColon])
789 .ignored()
790 .or(end()),
791 ))
792 .map(|stmts| into_then_expr(&stmts))
793 .boxed()
794}
795
796fn block_parser<'src, I>(
797 expr: impl Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone + 'src,
798 ctx: ParseContext,
799) -> impl Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone
800where
801 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
802{
803 let stmts = statements_parser(expr, ctx.clone());
804 let ctx3 = ctx.clone();
805 let block = stmts
806 .delimited_by(breakable_blockbegin(), breakable_blockend())
807 .map_with(move |stmts, e| Expr::Block(stmts).into_id(ctx.clone().gen_loc(e.span())));
808 one_of([Token::BackQuote, Token::Dollar])
809 .map_with(move |op, e| (op, get_span(e.span())))
810 .repeated()
811 .foldr(block, move |(op, op_span), rhs| {
812 let rhs_span = rhs.to_span();
813 let loc = Location {
814 span: merge_span(op_span, rhs_span),
815 path: ctx3.file_path.clone(),
816 };
817 match op {
818 Token::BackQuote => Expr::Bracket(rhs).into_id(loc.clone()),
819 Token::Dollar => Expr::Escape(rhs).into_id(loc.clone()),
820 _ => unreachable!("Unexpected block operator: {:?}", op),
821 }
822 })
823}
824
825fn exprgroup_parser<'src, I>(
826 ctx: ParseContext,
827) -> impl Parser<'src, I, ExprNodeId, ParseError<'src>> + Clone + 'src
828where
829 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
830{
831 recursive(move |expr_group: Recursive<_>| {
832 let expr = expr_parser(expr_group.clone(), ctx.clone());
833
834 let block = block_parser(expr_group.clone(), ctx.clone());
835 let path = ctx.file_path.clone();
836 let if_ = just(Token::If)
838 .ignore_then(
839 expr_group
840 .clone()
841 .delimited_by(just(Token::ParenBegin), just(Token::ParenEnd)),
842 )
843 .padded_by(just(Token::LineBreak).repeated())
844 .then(expr_group.clone())
845 .then(
846 just(Token::Else)
847 .padded_by(just(Token::LineBreak).repeated())
848 .ignore_then(expr_group.clone())
849 .or_not(),
850 )
851 .map_with(move |((cond, then), opt_else), e| {
852 Expr::If(cond, then, opt_else).into_id(Location {
853 span: get_span(e.span()),
854 path: path.clone(),
855 })
856 })
857 .labelled("if");
858 let ctx = ctx.clone();
859 block
860 .or(if_)
861 .or(expr.clone())
863 .recover_with(via_parser(nested_delimiters(
866 Token::BlockBegin,
867 Token::BlockEnd,
868 [(Token::ParenBegin, Token::ParenEnd)],
869 move |span| Expr::Error.into_id(ctx.clone().gen_loc(span)),
870 )))
871 })
872 .boxed()
873}
874
875fn top_stage_parser<'src, I>(
876 ctx: ParseContext,
877) -> impl Parser<'src, I, (ProgramStatement, Location), ParseError<'src>> + Clone
878where
879 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
880{
881 let stages = one_of([Token::Macro, Token::Main]).map(move |token| match token {
882 Token::Macro => StageKind::Macro,
883 Token::Main => StageKind::Main,
884 _ => unreachable!(),
885 });
886 just(Token::Sharp)
887 .ignore_then(
888 just(Token::StageKwd)
889 .ignore_then(stages.delimited_by(just(Token::ParenBegin), just(Token::ParenEnd))),
890 )
891 .map_with(move |stage: StageKind, e| {
892 (
893 ProgramStatement::StageDeclaration { stage },
894 Location {
895 span: get_span(e.span()),
896 path: ctx.file_path.clone(),
897 },
898 )
899 })
900 .labelled("Stage Declaration")
901}
902
903fn toplevel_parser<'src, I>(
904 ctx: ParseContext,
905) -> impl Parser<'src, I, Program, ParseError<'src>> + Clone
906where
907 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
908{
909 let exprgroup = exprgroup_parser(ctx.clone());
910 let lvar =
911 lvar_parser_typed_with_default(ctx.clone(), exprgroup.clone()).try_map(|ty, span| {
912 TypedId::try_from(ty).map_err(|err| Rich::custom(span, err.to_string()))
913 });
914 let path = ctx.file_path.clone();
915
916 let fnparams = lvar
917 .clone()
918 .separated_by(breakable_comma())
919 .collect()
920 .delimited_by(just(Token::ParenBegin), just(Token::ParenEnd))
921 .map_with(move |params, e| {
922 (
923 params,
924 Location {
925 span: get_span(e.span()),
926 path: path.clone(),
927 },
928 )
929 })
930 .labelled("fnparams");
931 let path = ctx.file_path.clone();
932 let path2 = ctx.file_path.clone();
933 let function_s = just(Token::Function)
934 .ignore_then(ident_parser().clone().validate(|ident, e, emitter| {
935 if let Err(e) = validate_reserved_ident(ident, e.span()) {
936 emitter.emit(e);
937 }
938 ident
939 }))
940 .then(fnparams.clone())
941 .then(
942 just(Token::Arrow)
943 .ignore_then(type_parser(ctx.clone()))
944 .or_not(),
945 )
946 .then(
947 block_parser(exprgroup.clone(), ctx.clone())
948 .map(|e| match e.to_expr() {
949 Expr::Block(e) => e.unwrap(),
950 _ => e,
951 })
952 .recover_with(via_parser(nested_delimiters(
953 Token::BlockBegin,
954 Token::BlockEnd,
955 [(Token::ParenBegin, Token::ParenEnd)],
956 move |span| {
957 Expr::Error.into_id(Location {
958 span: get_span(span),
959 path: path.clone(),
960 })
961 },
962 ))),
963 )
964 .map_with(move |(((name, args), return_type), body), e| {
965 (
966 ProgramStatement::FnDefinition {
967 name,
968 args,
969 return_type,
970 body,
971 },
972 Location {
973 span: get_span(e.span()),
974 path: path2.clone(),
975 },
976 )
977 })
978 .labelled("function decl");
979 let path = ctx.file_path.clone();
980
981 let global_stmt = statement_parser(exprgroup.clone(), ctx.clone())
982 .map(|(s, span)| (ProgramStatement::GlobalStatement(s), span));
983 let import = just(Token::Include)
984 .ignore_then(
985 select! {Token::Str(s) => s.to_symbol()}
986 .delimited_by(just(Token::ParenBegin), just(Token::ParenEnd)),
987 )
988 .map_with(move |path_sym, e| {
989 (
990 ProgramStatement::Import(path_sym),
991 Location::new(get_span(e.span()), path.clone()),
992 )
993 });
994 let stage = top_stage_parser(ctx.clone());
995 let stmt = choice((function_s, global_stmt, import, stage))
996 .recover_with(skip_then_retry_until(
997 any().ignored(),
998 one_of([Token::LineBreak, Token::SemiColon])
999 .ignored()
1000 .or(end()),
1001 ))
1002 .labelled("toplevel statement");
1003 let separator = just(Token::LineBreak).or(just(Token::SemiColon)).repeated();
1004
1005 stmt.separated_by(separator.clone())
1006 .collect()
1007 .padded_by(separator)
1008 .then_ignore(end())
1009 .recover_with(skip_then_retry_until(
1010 any().ignored(),
1011 one_of([Token::LineBreak, Token::SemiColon])
1012 .ignored()
1013 .or(end()),
1014 ))
1015 .map(|stmts: Vec<(ProgramStatement, Location)>| Program {
1016 statements: stmts
1017 .into_iter()
1018 .map(|(s, loc)| (s, loc.span))
1019 .collect::<Vec<_>>(),
1020 })
1021 .boxed()
1022}
1023
1024fn parser<'src, I>(
1054 current_file: Option<PathBuf>,
1055) -> impl Parser<'src, I, Program, ParseError<'src>> + Clone
1056where
1057 I: ValueInput<'src, Token = Token, Span = SimpleSpan>,
1058{
1059 let ctx = ParseContext {
1060 file_path: current_file.unwrap_or_default(),
1061 };
1062 toplevel_parser(ctx)
1063}
1064
1065pub(crate) fn add_global_context(ast: ExprNodeId, file_path: PathBuf) -> ExprNodeId {
1066 let span = ast.to_span();
1067 let loc = Location {
1068 span: span.clone(),
1069 path: file_path,
1070 };
1071 let res = Expr::Let(
1072 TypedPattern::new(
1073 Pattern::Single(GLOBAL_LABEL.to_symbol()),
1074 Type::Unknown.into_id_with_location(loc.clone()),
1075 ),
1076 Expr::Lambda(vec![], None, ast).into_id(loc.clone()),
1077 None,
1078 );
1079 res.into_id(loc)
1080}
1081pub fn lex(
1082 src: &str,
1083 current_file: Option<PathBuf>,
1084) -> (
1085 Option<Vec<(Token, SimpleSpan)>>,
1086 Vec<Box<dyn ReportableError>>,
1087) {
1088 let (tokens, lex_errs) = lexer::lexer().parse(src).into_output_errors();
1089 let lex_errs = lex_errs.into_iter().map(|e| -> Box<dyn ReportableError> {
1090 Box::new(error::ParseError::<char>::new(
1091 e,
1092 current_file.clone().unwrap_or_default(),
1093 ))
1094 });
1095 (tokens, lex_errs.collect())
1096}
1097pub(super) fn convert_parse_errors<'src>(
1098 errs: &[Rich<'src, Token>],
1099) -> impl Iterator<Item = Box<dyn ReportableError>> {
1100 errs.iter().map(move |e| -> Box<dyn ReportableError> {
1101 Box::new(error::ParseError::new(e.clone(), Default::default()))
1102 })
1103}
1104
1105pub fn parse(
1106 src: &'_ str,
1107 current_file: Option<PathBuf>,
1108) -> (Program, Vec<Box<dyn ReportableError>>) {
1109 let (tokens, lex_errs) = lex(src, current_file.clone());
1110 if let Some(t) = tokens {
1111 let tokens_comment_filtered = t
1112 .into_iter()
1113 .filter_map(move |(tkn, span)| match tkn {
1114 Token::Comment(token::Comment::SingleLine(_)) => Some((Token::LineBreak, span)),
1115 Token::Comment(token::Comment::MultiLine(_)) => None,
1116 _ => Some((tkn.clone(), span)),
1117 })
1118 .collect::<Vec<_>>();
1119
1120 let (ast, errs) = parser(current_file.clone())
1121 .parse(
1122 Stream::from_iter(tokens_comment_filtered)
1123 .map((src.len()..src.len()).into(), |(t, s)| (t, s)),
1124 )
1125 .into_output_errors();
1126 let errs = convert_parse_errors(&errs)
1127 .chain(lex_errs)
1128 .collect::<Vec<_>>();
1129 (ast.unwrap_or_default(), errs)
1130 } else {
1131 (Program::default(), lex_errs)
1132 }
1133}
1134pub fn parse_to_expr(
1135 src: &str,
1136 current_file: Option<PathBuf>,
1137) -> (ExprNodeId, Vec<Box<dyn ReportableError>>) {
1138 let (prog, mut errs) = parse(src, current_file.clone());
1139 if prog.statements.is_empty() {
1140 return (Expr::Error.into_id_without_span(), errs);
1141 }
1142 let (expr, mut new_errs) = expr_from_program(prog, current_file.unwrap_or_default());
1143 errs.append(&mut new_errs);
1144 (expr, errs)
1145}