1//! Parser for the j language (§3.2–3.4), including the two layout rules.
3use crate::ast::{Expr, Item, Pattern, Source, TypeExpr};
4use crate::lex::{lex, LexError, SpTok, Tok};
6use std::collections::{BTreeSet, HashMap, HashSet};
10pub struct ParseError {
16 fn new(msg: impl Into<String>, line: usize) -> Self {
24impl From<LexError> for ParseError {
25 fn from(e: LexError) -> Self {
34 /// the text being parsed, of which each lambda keeps its span (§5.2)
38 /// Names in scope (top-level + builtins) for the no-shadowing rule;
39 /// in config.j the builtins alone (§4.2).
40 outer_names: Rc<BTreeSet<String>>,
41 /// Names bound by the lambdas and `let` blocks around the current
42 /// position, for the same rule (§4.2). Since shadowing is refused, no
43 /// name is in here twice, and a construct removes the names it added
44 /// when its scope ends.
45 locals: HashSet<String>,
46 /// How many local binders have been parsed, and for each name the
47 /// ordinal and line of the latest one: the `let` check that `locals`
48 /// cannot make (see `parse_let`).
50 last_binder: HashMap<String, (usize, usize)>,
51 /// set when an operator expression ends with a trailing operator before
52 /// `)`, i.e. a left section
53 section_op: Option<&'static str>,
54 /// levels of recursive descent currently open, bounded by `MAX_DEPTH`
56 /// how many levels high (see `MAX_DEPTH`) the expression the last parse
57 /// function returned is, which each leaves here for its caller
61/// How many levels deep an expression may be (§3.4): a leaf is one level, and
62/// any other node one more than its highest part, or two more for a
63/// parenthesis (or section) and for the braces of a record or update. Deeper
64/// input is a parse error (§1.4) instead of an overflow of the worker
65/// thread's stack (main.rs). The recursive descent opens at most one level of
66/// its own (`nested`) per level of the expression around the point it has
67/// reached, each taking up to about 6 KB of stack in a debug build (a
68/// parenthesis takes two) and 1.3 KB in a release build, so the deepest parse
69/// takes under half of the worker's 512 MB. The passes over the tree after the
70/// parse, and dropping it, recurse on its height at about 1.2 KB a level.
71const MAX_DEPTH: usize = 40_000;
73/// fixity table (§3.2): (precedence, right-assoc)
74fn fixity(op: &str) -> Option<(u8, bool)> {
78 "+" | "-" => (6, false),
79 "++" | "::" => (5, true),
80 "==" | "/=" | "<" | "<=" | ">" | ">=" => (4, false), // non-assoc
87/// non-associative operators cannot be chained
88fn is_nonassoc(op: &str) -> bool {
89 matches!(op, "==" | "/=" | "<" | "<=" | ">" | ">=")
93 pub fn new(src: &str, outer_names: Rc<BTreeSet<String>>) -> Result<Self, ParseError> {
99 locals: HashSet::new(),
101 last_binder: HashMap::new(),
108 /// Run `f` one level deeper in the recursive descent. Every cycle of
109 /// mutually recursive parse functions passes through a call made here,
110 /// which is what bounds the parser's stack use. Each call is matched by a
111 /// level of the expression around what `f` parses, so on input that
112 /// parses, this bound is reached no sooner than `within`'s: it is what
113 /// stops input that never closes, like `((((…`.
116 f: impl FnOnce(&mut Self) -> Result<T, ParseError>,
117 ) -> Result<T, ParseError> {
118 if self.depth >= MAX_DEPTH {
119 return Err(ParseError::new("nested too deeply", self.line()));
127 /// `h`, the height of an expression just built, while it is within
128 /// `MAX_DEPTH`. Every node is checked here as it is built, since a loop
129 /// builds a left-associative chain (`a + b + …`, `f x y …`, `r.a.b …`,
130 /// `r { … } { … }`) as tall as it is long without recursing, and the
131 /// passes over the tree after the parse, and dropping it, recurse on its
133 fn within(&self, h: usize) -> Result<usize, ParseError> {
135 return Err(ParseError::new("nested too deeply", self.line()));
140 fn peek(&self) -> &Tok {
141 &self.toks[self.pos].tok
143 fn line(&self) -> usize {
144 self.toks[self.pos].line
146 fn col(&self) -> usize {
147 self.toks[self.pos].col
149 fn bump(&mut self) -> Tok {
150 let t = self.toks[self.pos].tok.clone();
151 if self.pos < self.toks.len() - 1 {
156 fn skip_newlines(&mut self) {
157 while matches!(self.peek(), Tok::Newline) {
161 fn expect(&mut self, t: &Tok) -> Result<(), ParseError> {
162 if self.peek() == t {
167 format!("expected {}, found {}", t.describe(), self.peek().describe()),
173 // ------------------------------------------------------------------
174 // top-level: config := item* (layout rule 1)
175 // ------------------------------------------------------------------
176 pub fn parse_config(&mut self) -> Result<Vec<Item>, ParseError> {
177 let mut items = Vec::new();
178 self.skip_newlines();
179 while !matches!(self.peek(), Tok::Eof) {
181 return Err(ParseError::new(
182 "top-level items must begin in column 1",
186 let item = self.parse_item()?;
188 // after an item, expect newline(s) or eof
189 self.skip_newlines();
194 /// One item. Under rule 1 (§3.3) a line starting past column 1 continues
195 /// it, so a line break before its `=` or `:` is skipped when the next
196 /// line starts past column 1, like one inside its type or expression.
197 fn parse_item(&mut self) -> Result<Item, ParseError> {
198 match self.peek().clone() {
199 Tok::TypeName(name) => {
200 // typedecl: TYPENAME '=' type
202 self.skip_layout_newlines(1);
203 self.expect(&Tok::Equals)?;
204 let ty = self.parse_type()?;
205 Ok(Item::TypeDecl(name, ty))
207 Tok::Ident(name) => {
208 let line = self.line();
210 self.skip_layout_newlines(1);
211 match self.peek().clone() {
214 let ty = self.parse_type()?;
215 Ok(Item::Signature(name, ty, line))
219 // inside nothing, so it opens no level (see `nested`)
220 let e = self.parse_expr_inner(1)?;
221 Ok(Item::Definition(name, Rc::new(e), line))
223 other => Err(ParseError::new(
224 format!("expected `:` or `=` after `{}`, found {}", name, other.describe()),
230 // operator signature: (op) : type
231 let line = self.line();
233 let op = match self.bump() {
236 return Err(ParseError::new(
237 format!("expected operator, found {}", other.describe()),
242 self.expect(&Tok::RParen)?;
243 self.skip_layout_newlines(1);
244 self.expect(&Tok::Colon)?;
245 let ty = self.parse_type()?;
246 Ok(Item::Signature(op.to_string(), ty, line))
248 other => Err(ParseError::new(
249 format!("unexpected {} at top level", other.describe()),
255 // ------------------------------------------------------------------
257 // ------------------------------------------------------------------
258 fn parse_type(&mut self) -> Result<TypeExpr, ParseError> {
259 self.nested(Self::parse_type_inner)
262 /// Types occur only in top-level items, so their layout limit is always
263 /// rule 1's column 1 (§3.3): a line break may fall anywhere in a type,
264 /// before or after `->` alike, when the next line starts past column 1.
265 fn parse_type_inner(&mut self) -> Result<TypeExpr, ParseError> {
266 self.skip_layout_newlines(1);
267 let lhs = self.parse_atype()?;
268 self.skip_layout_newlines(1);
269 if matches!(self.peek(), Tok::Arrow) {
271 let rhs = self.parse_type()?;
272 Ok(TypeExpr::Fun(Rc::new(lhs), Rc::new(rhs)))
278 fn parse_atype(&mut self) -> Result<TypeExpr, ParseError> {
279 match self.peek().clone() {
280 Tok::TypeName(n) => {
290 let t = self.parse_type()?;
291 self.expect(&Tok::RBracket)?;
292 Ok(TypeExpr::List(Rc::new(t)))
296 let mut fields = Vec::new();
298 self.skip_layout_newlines(1);
299 if matches!(self.peek(), Tok::RBrace) {
303 let name = match self.bump() {
306 return Err(ParseError::new(
307 format!("expected field name, found {}", other.describe()),
312 self.skip_layout_newlines(1);
313 self.expect(&Tok::Colon)?;
314 let t = self.parse_type()?;
315 fields.push((name, Rc::new(t)));
316 self.skip_layout_newlines(1);
326 return Err(ParseError::new(
327 format!("expected `,` or `}}`, found {}", other.describe()),
333 Ok(TypeExpr::Record(fields))
337 let t = self.parse_type()?;
338 self.expect(&Tok::RParen)?;
341 other => Err(ParseError::new(
342 format!("expected a type, found {}", other.describe()),
348 // ------------------------------------------------------------------
350 // ------------------------------------------------------------------
352 /// Parse a full expression. `min_col` is the layout column limit: the
353 /// expression stops at a Newline whose following token is in a column
354 /// <= min_col (it belongs to an outer construct).
355 pub fn parse_expr(&mut self, min_col: usize) -> Result<Expr, ParseError> {
356 self.nested(|p| p.parse_expr_inner(min_col))
359 /// Each construct here and in `parse_atom_inner` is parsed by a function
360 /// of its own: nested input stacks a frame of every function on its path
361 /// for each level (see `MAX_DEPTH`), and in a debug build a function's
362 /// frame holds the temporaries of all its arms.
363 fn parse_expr_inner(&mut self, min_col: usize) -> Result<Expr, ParseError> {
364 self.skip_layout_newlines(min_col);
365 match self.peek().clone() {
366 Tok::Backslash => self.parse_lambda(min_col),
367 Tok::If => self.parse_if(min_col),
368 Tok::Let => self.parse_let(min_col),
369 _ => self.parse_or(min_col),
373 fn parse_lambda(&mut self, min_col: usize) -> Result<Expr, ParseError> {
374 let src_start = self.toks[self.pos].clone();
376 let mut params = Vec::new();
378 match self.peek().clone() {
380 // in scope from here on, so a later parameter
381 // may not reuse it either
382 self.check_shadow(&n)?;
383 self.locals.insert(n.clone());
384 self.note_binder(&n, self.line());
385 params.push(Pattern::Var(n));
389 params.push(Pattern::Wildcard);
397 return Err(ParseError::new(
399 "expected a parameter or `->`, found {}",
407 if params.is_empty() {
408 return Err(ParseError::new("lambda needs at least one parameter", self.line()));
410 let body = self.parse_expr(min_col)?;
411 self.height = self.within(self.height + 1)?;
413 if let Pattern::Var(n) = p {
414 self.locals.remove(n);
417 let src = self.source_slice(&src_start);
418 Ok(Expr::Lambda(params, Rc::new(body), src))
421 fn parse_if(&mut self, min_col: usize) -> Result<Expr, ParseError> {
423 let c = self.parse_expr(min_col)?;
424 let mut h = self.height;
425 self.skip_layout_newlines(min_col);
426 self.expect(&Tok::Then)?;
427 let t = self.parse_expr(min_col)?;
428 h = h.max(self.height);
429 self.skip_layout_newlines(min_col);
430 self.expect(&Tok::Else)?;
431 let e = self.parse_expr(min_col)?;
432 self.height = self.within(h.max(self.height) + 1)?;
433 Ok(Expr::If(Rc::new(c), Rc::new(t), Rc::new(e)))
436 /// skip Newlines whose next real token is in a column > min_col
437 fn skip_layout_newlines(&mut self, min_col: usize) {
439 if !matches!(self.peek(), Tok::Newline) {
442 // lookahead past consecutive newlines
443 let mut p = self.pos;
444 while matches!(self.toks[p].tok, Tok::Newline) {
447 if matches!(self.toks[p].tok, Tok::Eof) {
450 if self.toks[p].col > min_col {
458 fn parse_let(&mut self, min_col: usize) -> Result<Expr, ParseError> {
460 // skip newlines to find the block column
461 let mut p = self.pos;
462 while matches!(self.toks[p].tok, Tok::Newline) {
465 let block_col = self.toks[p].col;
467 if block_col <= min_col {
468 return Err(ParseError::new(
469 "let bindings must be indented",
473 let mut bindings: Vec<(String, Rc<Expr>)> = Vec::new();
474 let mut names: Vec<(String, usize)> = Vec::new();
475 let first_binder = self.binders;
478 // binding: ident '=' expr, expr limited to block_col
479 let name = match self.peek().clone() {
482 return Err(ParseError::new(
483 format!("expected a binding name, found {}", other.describe()),
488 if names.iter().any(|(n, _)| *n == name) {
489 return Err(ParseError::new(
490 format!("`{}` is bound twice in this let", name),
494 self.check_shadow(&name)?;
495 // in scope from here on: in its own expression, the later ones
496 // and the body (§4.1)
497 self.locals.insert(name.clone());
498 names.push((name.clone(), self.line()));
500 self.expect(&Tok::Equals)?;
501 let e = self.parse_expr(block_col)?;
502 h = h.max(self.height);
503 bindings.push((name, Rc::new(e)));
504 // separator: `;` or newline at exactly block_col, then `in` ends
505 match self.peek().clone() {
508 self.skip_layout_newlines(block_col - 1); // any continuation
509 if matches!(self.peek(), Tok::In) {
520 // find next real token
521 let mut q = self.pos;
522 while matches!(self.toks[q].tok, Tok::Newline) {
525 let next_col = self.toks[q].col;
526 if matches!(self.toks[q].tok, Tok::In) {
527 // like any token that begins a line, an `in` at or
528 // left of the limit belongs to an outer construct
529 // (§3.3): a new top-level item or an outer let's
530 // binding line, never this let
531 if next_col <= min_col {
532 return Err(ParseError::new(
533 format!("`in` must be indented past column {}", min_col),
541 if next_col == block_col {
545 if next_col < block_col {
546 // block ends without `in`? `in` must appear on an
547 // outer line; treat as error unless next is In handled above
548 if matches!(self.toks[q].tok, Tok::In) {
553 // The `in` could be at a smaller column: look for it
554 return Err(ParseError::new(
555 "expected `in` or another binding",
559 // deeper indentation continues the expression; but the
560 // expression parser should have consumed it. Error.
561 return Err(ParseError::new(
562 "unexpected indentation in let block",
567 return Err(ParseError::new(
568 format!("expected `;`, newline, or `in`, found {}", other.describe()),
574 // A binding is in scope in the expressions before it too (§4.1), but
575 // it was not yet in `locals` while they were parsed: a binder in one
576 // of them that reuses its name is caught here instead.
577 for (n, _) in &names {
578 if let Some(&(k, line)) = self.last_binder.get(n) {
579 if k >= first_binder {
580 return Err(shadowed(n, line));
584 for (n, line) in &names {
585 self.note_binder(n, *line);
587 let body = self.parse_expr(min_col)?;
588 self.height = self.within(h.max(self.height) + 1)?;
589 for (n, _) in &names {
590 self.locals.remove(n);
592 Ok(Expr::Let(bindings, Rc::new(body)))
595 /// §4.2: a binder may not reuse a top-level or builtin name, nor one
596 /// bound by an enclosing lambda or `let`.
597 fn check_shadow(&self, name: &str) -> Result<(), ParseError> {
598 if self.outer_names.contains(name) || self.locals.contains(name) {
599 return Err(shadowed(name, self.line()));
604 /// Record a local binder for the check at the end of each enclosing
605 /// `let` block's bindings.
606 fn note_binder(&mut self, name: &str, line: usize) {
607 self.last_binder.insert(name.to_string(), (self.binders, line));
611 fn parse_or(&mut self, min_col: usize) -> Result<Expr, ParseError> {
612 // `or` is infixl 0 and extends as far right as possible
613 let mut lhs = self.parse_opexpr(1, min_col)?;
614 let mut h = self.height;
616 self.skip_layout_newlines(min_col);
617 if matches!(self.peek(), Tok::Or) {
619 let rhs = self.nested(|p| p.parse_or(min_col))?;
620 h = self.within(h.max(self.height) + 1)?;
621 lhs = Expr::Or(Rc::new(lhs), Rc::new(rhs));
630 /// precedence-climbing over the fixity table
631 fn parse_opexpr(&mut self, min_prec: u8, min_col: usize) -> Result<Expr, ParseError> {
632 let mut lhs = self.parse_app(min_col)?;
633 let mut h = self.height;
635 self.skip_op_newline(min_col);
636 let op: &'static str = match self.peek() {
640 let (prec, right) = match fixity(op) {
648 // left section: `(e op)` — a trailing operator before `)`
649 if matches!(self.peek(), Tok::RParen) {
650 self.section_op = Some(op);
654 let next_min = if right { prec } else { prec + 1 };
655 let rhs = self.nested(|p| p.parse_opexpr(next_min, min_col))?;
656 // non-assoc check: if lhs is the same operator, reject chaining
658 // a non-associative operator cannot be chained; explicit
659 // parentheses break the chain
660 if let Expr::BinOp(o2, _, _) = &lhs {
662 return Err(ParseError::new(
663 format!("`{}` is non-associative and cannot be chained", op),
668 if let Expr::BinOp(o2, _, _) = &rhs {
669 if is_nonassoc(o2) && *o2 == op {
670 return Err(ParseError::new(
671 format!("`{}` is non-associative and cannot be chained", op),
677 h = self.within(h.max(self.height) + 1)?;
678 lhs = Expr::BinOp(op, Rc::new(lhs), Rc::new(rhs));
684 /// allow an operator at the start of a continuation line (column > min_col)
685 fn skip_op_newline(&mut self, min_col: usize) {
686 if !matches!(self.peek(), Tok::Newline) {
689 let mut p = self.pos;
690 while matches!(self.toks[p].tok, Tok::Newline) {
693 if matches!(self.toks[p].tok, Tok::Op(_)) && self.toks[p].col > min_col {
698 fn starts_atom(t: &Tok) -> bool {
718 fn parse_app(&mut self, min_col: usize) -> Result<Expr, ParseError> {
719 let mut e = self.parse_postfix(min_col)?;
720 let mut h = self.height;
722 // application across a newline requires deeper indentation
723 if matches!(self.peek(), Tok::Newline) {
724 let mut p = self.pos;
725 while matches!(self.toks[p].tok, Tok::Newline) {
728 if Self::starts_atom(&self.toks[p].tok) && self.toks[p].col > min_col {
734 if Self::starts_atom(self.peek()) {
735 let arg = self.parse_postfix(min_col)?;
736 h = self.within(h.max(self.height) + 1)?;
737 e = Expr::App(Rc::new(e), Rc::new(arg));
746 fn parse_postfix(&mut self, min_col: usize) -> Result<Expr, ParseError> {
747 let mut e = self.parse_atom(min_col)?;
748 let mut h = self.height;
750 match self.peek().clone() {
751 Tok::Selector(f) => {
753 h = self.within(h + 1)?;
754 e = Expr::Select(Rc::new(e), f);
757 // record update (or a record literal argument? no —
758 // postfix binds tighter than application, so `{` after an
759 // atom is an update)
761 // two levels, like a record literal's braces, so a second
762 // level of the descent to match (see `MAX_DEPTH`)
763 let fields = self.nested(|p| p.parse_fields(min_col, true))?;
764 h = self.within(h.max(self.height) + 2)?;
765 e = Expr::Update(Rc::new(e), fields);
767 // a Newline ends the chain too: a selector or `{` that
768 // begins a line is the next postfix, not this one's (§3.3)
776 /// parse `ident = expr, ...` inside braces. `update` controls the error message.
781 ) -> Result<Vec<(String, Rc<Expr>)>, ParseError> {
782 let mut fields: Vec<(String, Rc<Expr>)> = Vec::new();
783 self.skip_layout_newlines(min_col);
784 if matches!(self.peek(), Tok::RBrace) {
786 // nothing inside the braces
790 // the height of the highest field
793 let name = match self.bump() {
796 return Err(ParseError::new(
797 format!("expected a field name, found {}", other.describe()),
802 if fields.iter().any(|(n, _)| *n == name) {
803 return Err(ParseError::new(
804 format!("duplicate field `{}`", name),
808 self.expect(&Tok::Equals)?;
809 let v = self.parse_expr(min_col)?;
810 h = h.max(self.height);
811 fields.push((name, Rc::new(v)));
812 self.skip_layout_newlines(min_col);
816 self.skip_layout_newlines(min_col);
823 return Err(ParseError::new(
824 format!("expected `,` or `}}`, found {}", other.describe()),
834 fn parse_atom(&mut self, min_col: usize) -> Result<Expr, ParseError> {
835 self.nested(|p| p.parse_atom_inner(min_col))
838 fn parse_atom_inner(&mut self, min_col: usize) -> Result<Expr, ParseError> {
839 // one level, unless an arm below builds more around what it parses
841 match self.peek().clone() {
846 Tok::TypeName(n) => {
848 Ok(Expr::TypeName(n))
866 Tok::LabelLit(s) => {
868 Ok(Expr::LabelLit(s))
880 Ok(Expr::Bool(false))
882 Tok::Selector(f) => {
884 Ok(Expr::SelectorFun(f))
886 Tok::LBracket => self.parse_list(min_col),
889 let fields = self.parse_fields(min_col, false)?;
890 // the braces are two levels, as they open two of the
891 // descent: this atom and the field (see `MAX_DEPTH`)
892 self.height = self.within(self.height + 2)?;
893 Ok(Expr::Record(fields))
895 Tok::LParen => self.parse_paren(min_col),
896 other => Err(ParseError::new(
897 format!("unexpected {}", other.describe()),
903 fn parse_list(&mut self, min_col: usize) -> Result<Expr, ParseError> {
905 let mut elems = Vec::new();
908 self.skip_layout_newlines(min_col);
909 if matches!(self.peek(), Tok::RBracket) {
913 let e = self.parse_postfix(min_col)?;
914 h = h.max(self.height);
915 elems.push(Rc::new(e));
917 self.height = self.within(h + 1)?;
918 Ok(Expr::List(elems))
921 fn parse_paren(&mut self, min_col: usize) -> Result<Expr, ParseError> {
922 let open = self.toks[self.pos].clone();
924 // (op) | (op expr) | (expr op) | (expr)
925 if let Tok::Op(o) = self.peek().clone() {
927 self.skip_layout_newlines(min_col);
928 if matches!(self.peek(), Tok::RParen) {
930 return Ok(Expr::Var(o.to_string()));
932 // right section: (op e) = \x -> x op e
933 let e = self.parse_expr(min_col)?;
934 // `(op e op)` is no production (§3.4); unchecked, the
935 // trailing operator would be dropped here and left set
936 // for the next `(e)` to take as a left section
937 if let Some(o2) = self.section_op.take() {
938 return Err(ParseError::new(
940 "a section cannot have operators on both sides (`{}` and `{}`)",
946 self.expect(&Tok::RParen)?;
947 // a lambda around an operator: two levels
948 self.height = self.within(self.height + 2)?;
949 let var = fresh_var();
950 let body = Expr::BinOp(
952 Rc::new(Expr::Var(var.clone())),
955 return Ok(Expr::Lambda(
956 vec![Pattern::Var(var)],
958 self.source_slice(&open),
961 let first = self.parse_expr(min_col)?;
962 // a parenthesis is two levels, as is a section's lambda
963 // around its operator
964 self.height = self.within(self.height + 2)?;
965 self.skip_layout_newlines(min_col);
966 if let Some(o) = self.section_op.take() {
967 // left section: (e op) = \x -> e op x
968 self.expect(&Tok::RParen)?;
969 let var = fresh_var();
970 let body = Expr::BinOp(
973 Rc::new(Expr::Var(var.clone())),
975 return Ok(Expr::Lambda(
976 vec![Pattern::Var(var)],
978 self.source_slice(&open),
981 self.expect(&Tok::RParen)?;
982 Ok(Expr::Paren(Rc::new(first)))
985 /// The source of a lambda or section, from its first token `start` to
986 /// the last token consumed, for its rendering (§5.2: exactly as
987 /// written). Line breaks the layout rules skipped looking for more of it
988 /// are not part of it, nor is a comment after its last token.
989 fn source_slice(&self, start: &SpTok) -> Source {
990 let mut last = self.pos - 1;
991 while matches!(self.toks[last].tok, Tok::Newline) {
994 Source::new(self.src.clone(), start.start, self.toks[last].end)
997 /// Parse a single command-line expression. Layout rule 1 does not apply.
998 pub fn parse_cli_expr(&mut self) -> Result<Expr, ParseError> {
999 self.skip_newlines();
1000 // inside nothing, so it opens no level (see `nested`)
1001 let e = self.parse_expr_inner(0)?;
1002 self.skip_newlines();
1003 if !matches!(self.peek(), Tok::Eof) {
1004 return Err(ParseError::new(
1005 format!("unexpected {} after the expression", self.peek().describe()),
1013fn shadowed(name: &str, line: usize) -> ParseError {
1014 ParseError::new(format!("`{}` is already bound and may not be shadowed", name), line)
1017use std::sync::atomic::{AtomicUsize, Ordering};
1018static FRESH: AtomicUsize = AtomicUsize::new(0);
1019fn fresh_var() -> String {
1020 format!("$sec{}", FRESH.fetch_add(1, Ordering::Relaxed))
1023pub fn parse_config(src: &str, outer_names: Rc<BTreeSet<String>>) -> Result<Vec<Item>, ParseError> {
1024 let mut p = Parser::new(src, outer_names)?;
1028pub fn parse_expr(src: &str, outer_names: Rc<BTreeSet<String>>) -> Result<Expr, ParseError> {
1029 let mut p = Parser::new(src, outer_names)?;
1033/// Re-render a lambda as source text (used for display of closures).
1034pub fn render_lambda(params: &[Pattern], body: &Expr) -> String {
1035 let ps: Vec<String> = params
1038 Pattern::Var(n) => n.clone(),
1039 Pattern::Wildcard => "_".into(),
1042 format!("\\{} -> {}", ps.join(" "), render_expr(body))
1045pub fn render_expr(e: &Expr) -> String {
1047 Expr::Var(n) => n.clone(),
1048 Expr::TypeName(n) => n.clone(),
1049 Expr::Int(n) => n.to_string(),
1050 Expr::Text(s) => format!("{:?}", s),
1051 Expr::IdLit(s) => format!("@{}", s),
1052 Expr::Id(s) => format!("@{}", s),
1053 Expr::NewId => "@".into(),
1054 Expr::LabelLit(s) => format!("%{}", s),
1055 Expr::PathLit(p) => {
1059 format!("./{}", p.join("/"))
1062 Expr::Bool(b) => b.to_string(),
1063 Expr::Lambda(ps, b, _) => render_lambda(ps, b),
1064 Expr::If(c, t, f) => format!(
1065 "if {} then {} else {}",
1070 Expr::Let(bs, b) => {
1071 let parts: Vec<String> = bs
1073 .map(|(n, e)| format!("{} = {}", n, render_expr(e)))
1075 format!("let {} in {}", parts.join("; "), render_expr(b))
1077 Expr::App(f, x) => {
1078 let fs = match f.as_ref() {
1079 Expr::App(_, _) => format!("({})", render_expr(f)),
1080 Expr::Paren(_) => render_expr(f),
1081 _ => render_expr(f),
1083 format!("{} ({})", fs, render_expr(x))
1085 Expr::BinOp(o, a, b) => format!("({} {} {})", render_expr(a), o, render_expr(b)),
1086 Expr::Or(a, b) => format!("({} or {})", render_expr(a), render_expr(b)),
1087 Expr::Select(e, f) => format!("{}.{}", render_expr(e), f),
1088 Expr::Update(e, fs) => {
1089 let parts: Vec<String> = fs
1091 .map(|(n, v)| format!("{} = {}", n, render_expr(v)))
1093 format!("{} {{ {} }}", render_expr(e), parts.join(", "))
1095 Expr::Record(fs) => {
1096 let parts: Vec<String> = fs
1098 .map(|(n, v)| format!("{} = {}", n, render_expr(v)))
1100 format!("{{ {} }}", parts.join(", "))
1103 let parts: Vec<String> = es.iter().map(|e| render_expr(e)).collect();
1104 format!("[{}]", parts.join(" "))
1106 Expr::SelectorFun(f) => format!(".{}", f),
1107 Expr::Paren(e) => render_expr(e),
1108 Expr::Crash => "crash".into(),
1112/// helper used by tests
1113pub fn int(n: i64) -> Expr {
1114 Expr::Int(BigInt::from(n))
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list