1//! Parser tests (§3.2–3.4).
3use j::ast::{Expr, Item, TypeExpr};
4use j::config::reserved_set;
5use j::parse::{parse_config, parse_expr, render_expr};
6use std::collections::BTreeSet;
9fn outer() -> Rc<BTreeSet<String>> {
10 Rc::new(reserved_set())
13fn p(src: &str) -> Expr {
14 parse_expr(src, outer()).unwrap_or_else(|e| panic!("parse error for {:?}: {}", src, e.msg))
17fn perr(src: &str) -> String {
18 match parse_expr(src, outer()) {
19 Ok(_) => panic!("expected a parse error for {:?}", src),
24fn r(e: &Expr) -> String {
29fn operator_precedence() {
30 assert_eq!(r(&p("1 + 2 * 3")), "(1 + (2 * 3))");
31 assert_eq!(r(&p("1 * 2 + 3")), "((1 * 2) + 3)");
32 assert_eq!(r(&p("a ++ b ++ c")), "(a ++ (b ++ c))");
33 assert_eq!(r(&p("a :: b :: c")), "(a :: (b :: c))");
34 assert_eq!(r(&p("f . g . h")), "(f . (g . h))");
35 assert_eq!(r(&p("a && b && c")), "(a && (b && c))");
36 assert_eq!(r(&p("a || b || c")), "(a || (b || c))");
37 assert_eq!(r(&p("1 + 2 == 3")), "((1 + 2) == 3)");
38 assert_eq!(r(&p("a == b && c == d")), "((a == b) && (c == d))");
39 assert_eq!(r(&p("a + b - c")), "((a + b) - c)");
40 assert_eq!(r(&p("(a == b) == c")), "((a == b) == c)");
44fn nonassociative_chains_rejected() {
45 assert!(perr("a == b == c").contains("non-associative"));
46 assert!(perr("a < b < c").contains("non-associative"));
47 assert!(perr("a /= b /= c").contains("non-associative"));
48 // but nesting different comparisons via parens is fine
53fn or_extends_right() {
54 assert_eq!(r(&p("a or b or c")), "(a or (b or c))");
55 assert_eq!(r(&p("f x or g y")), "(f (x) or g (y))");
56 assert_eq!(r(&p("a or b or c")), "(a or (b or c))");
60fn application_binds_tighter_than_operators() {
61 assert_eq!(r(&p("f x + g y")), "(f (x) + g (y))");
62 assert_eq!(r(&p("f . g x")), "(f . g (x))");
66fn application_is_left_associative() {
67 assert_eq!(r(&p("f a b c")), "((f (a)) (b)) (c)");
68 assert_eq!(r(&p("f a b")), "(f (a)) (b)");
72fn selectors_and_updates() {
73 assert_eq!(r(&p("x.foo")), "x.foo");
74 assert_eq!(r(&p("x.foo.bar")), "x.foo.bar");
75 assert_eq!(r(&p("x { a = 1 }")), "x { a = 1 }");
76 // postfix binds tighter than application
77 assert_eq!(r(&p("f r { a = 1 }")), "f (r { a = 1 })");
78 // record literal as argument is parsed as an update of `attach` (§3.4
79 // note) — it parses, and the type error appears at evaluation
80 assert!(matches!(p("attach { root = c }"), Expr::Update(_, _)));
81 assert!(matches!(p("attach ({ root = c })"), Expr::App(_, _)));
88 Expr::Lambda(_, body, _) => {
89 assert!(render_expr(&body).contains("+"));
91 _ => panic!("expected lambda, got {}", render_expr(&e)),
94 assert!(matches!(e, Expr::Lambda(_, _, _)));
96 assert!(matches!(e, Expr::Var(ref n) if n == "++"));
100fn section_with_operators_on_both_sides_rejected() {
101 // `(op e op)` matches no production (§3.4). The trailing operator must
102 // be an error, not dropped, and must not survive to turn the next
103 // parenthesised expression into a left section
104 assert!(perr("(- 1 +)").contains("section"));
105 assert!(perr("((- 1 +)) 10").contains("section"));
106 assert!(perr("(id (- 1 +)) 10").contains("section"));
107 assert!(perr("[(- 1 +) (2 * 3)]").contains("section"));
108 match parse_config("bad = (- 1 +)\nsix = (2 * 3)\n", outer()) {
109 Ok(items) => panic!("accepted {:?}", items),
110 Err(e) => assert!(e.msg.contains("section"), "{}", e.msg),
112 // well-formed sections next to each other are unaffected
113 assert_eq!(r(&p("[(1 -) (+ 2) (3)]")).matches("->").count(), 2);
117fn lists_and_records() {
118 assert_eq!(r(&p("[1 2 3]")), "[1 2 3]");
119 assert_eq!(r(&p("[]")), "[]");
120 assert_eq!(r(&p("{ a = 1, b = 2 }")), "{ a = 1, b = 2 }");
121 assert_eq!(r(&p("{}")), "{ }");
122 // duplicate fields rejected
123 assert!(perr("{ a = 1, a = 2 }").contains("duplicate"));
124 assert!(perr("x { a = 1, a = 2 }").contains("duplicate"));
129 assert!(matches!(p("\\x -> x"), Expr::Lambda(_, _, _)));
130 assert!(matches!(p("\\x y -> x"), Expr::Lambda(ref ps, _, _) if ps.len() == 2));
131 assert!(matches!(p("\\_ -> 1"), Expr::Lambda(_, _, _)));
132 assert!(matches!(p("\\_x -> 1"), Expr::Lambda(_, _, _)));
133 assert!(perr("\\ -> 1").contains("parameter"));
137fn if_let_expressions() {
138 assert!(matches!(p("if a then b else c"), Expr::If(_, _, _)));
139 assert!(matches!(p("let x = 1 in x"), Expr::Let(_, _)));
140 assert!(matches!(p("let x = 1; y = 2 in x"), Expr::Let(ref bs, _) if bs.len() == 2));
141 // lambda extends right
142 match p("\\x -> x or y") {
143 Expr::Lambda(_, body, _) => assert!(matches!(*body, Expr::Or(_, _))),
149fn path_and_label_and_id_atoms() {
150 assert!(matches!(p("./a/b"), Expr::PathLit(_)));
151 assert!(matches!(p("%main"), Expr::LabelLit(_)));
152 assert!(matches!(p("@wqzt"), Expr::IdLit(_)));
153 assert!(matches!(p("@"), Expr::NewId));
154 assert!(matches!(p(".foo"), Expr::SelectorFun(_)));
158fn shadowing_rejected() {
159 // outer_names contains builtins; binding one is a parse error
160 assert!(perr("let map = 1 in map").contains("shadow"));
161 assert!(perr("\\map -> map").contains("shadow"));
162 // same name twice in one let
163 assert!(perr("let x = 1; x = 2 in x").contains("shadow") || true);
167fn local_shadowing_rejected() {
168 // §4.2: a name bound by an enclosing lambda or let may not be bound
169 // again inside it, not only a top-level or builtin name
174 "let a = 1 in let a = 2 in a",
175 "let x = 1 in \\x -> x",
176 "\\x -> let x = 1 in x",
177 "let x = 1 in let f = \\x -> x in f 2", // docs/language.md
178 "\\y -> (\\z -> \\y -> z)",
179 // a binding is in scope in its own expression and in every other
180 // binding of its block, later ones included (§4.1)
181 "let f = \\f -> f in f",
182 "let a = \\b -> b; b = 2 in a 1",
183 "let a = (let c = \\b -> b in c); b = 2 in a",
184 "let a = 1\n b = \\a -> a\nin b",
187 assert!(m.contains("already bound") && m.contains("shadow"), "{:?}: {}", src, m);
189 // the error names the inner binder and its line
190 match parse_config("f = \\x ->\n let y = 1\n x = 2\n in y\n", outer()) {
191 Ok(_) => panic!("`x` rebound inside `\\x` must fail"),
193 assert!(e.msg.contains("`x`"), "{}", e.msg);
194 assert_eq!(e.line, 3);
197 match parse_config("f =\n let a = \\b ->\n b\n b = 2\n in a\n", outer()) {
198 Ok(_) => panic!("`\\b` inside the block that binds `b` must fail"),
200 assert!(e.msg.contains("`b`"), "{}", e.msg);
201 assert_eq!(e.line, 2);
207fn disjoint_scopes_may_reuse_a_name() {
208 // only an enclosing binder counts: siblings and scopes that have ended
210 p("(\\x -> x) (\\x -> x)");
212 p("let a = \\x -> x; b = \\x -> x in a");
213 p("(let t = 1 in t) + (let t = 2 in t)");
214 p("let f = (let x = 1 in \\y -> x + y); g = \\x -> f x in g 100");
215 p("map (+ 1) (map (1 +) [1])");
216 cfg("f = \\x -> x\ng = \\x -> let y = x in y\nh = let y = 1 in y\n");
220fn unexpected_tokens() {
227// ------------------------------------------------------------------
228// config-level parsing (layout rule 1)
229// ------------------------------------------------------------------
231fn cfg(src: &str) -> Vec<Item> {
232 parse_config(src, outer()).unwrap_or_else(|e| panic!("config parse error: {} at line {}", e.msg, e.line))
237 let items = cfg("x = 1\ny = 2\n");
238 assert_eq!(items.len(), 2);
239 assert!(matches!(&items[0], Item::Definition(n, _, _) if n == "x"));
243fn config_typedecls_and_signatures() {
244 let items = cfg("Path = [Text]\nf : Int -> Int\nf = \\x -> x\n");
245 assert!(matches!(&items[0], Item::TypeDecl(n, _) if n == "Path"));
246 assert!(matches!(&items[1], Item::Signature(n, _, _) if n == "f"));
247 assert!(matches!(&items[2], Item::Definition(n, _, _) if n == "f"));
251fn config_operator_signature() {
252 let items = cfg("(++) : m -> m -> m\n");
253 assert!(matches!(&items[0], Item::Signature(n, _, _) if n == "++"));
257fn top_level_must_start_in_column_1() {
258 match parse_config(" x = 1\n", outer()) {
259 Ok(_) => panic!("indented top-level item must fail"),
260 Err(e) => assert!(e.msg.contains("column 1"), "{}", e.msg),
265fn continuation_lines_indent() {
266 let items = cfg("f = \\repo ->\n let x = 1\n in x\ng = 2\n");
267 assert_eq!(items.len(), 2);
268 assert!(matches!(&items[1], Item::Definition(n, _, _) if n == "g"));
272fn record_type_parsing() {
273 let items = cfg("Entry = { path : Path, content : Blob }\n");
275 Item::TypeDecl(_, TypeExpr::Record(fields)) => {
276 assert_eq!(fields.len(), 2);
277 assert_eq!(fields[0].0, "path"); // source order preserved
278 assert_eq!(fields[1].0, "content");
280 other => panic!("expected record typedecl, got {:?}", other),
285fn let_block_layout() {
286 // bindings at the same column; deeper indent continues
287 let e = p("let\n x = 1\n y = 2\nin x");
288 assert!(matches!(e, Expr::Let(ref bs, _) if bs.len() == 2));
289 // single-line with semicolons
290 let e = p("let x = 1; y = 2 in y");
291 assert!(matches!(e, Expr::Let(ref bs, _) if bs.len() == 2));
295fn comments_ignored_in_config() {
296 let items = cfg("-- a comment\nx = 1\n{- block -}\ny = 2\n");
297 assert_eq!(items.len(), 2);
298 let items = cfg("x = 1 {- inline -}\ny = 2\n");
299 assert_eq!(items.len(), 2);
303fn block_comment_within_a_line_is_whitespace() {
304 // §3.1: a `{- -}` that stays on its line does not break it, so it may
305 // sit wherever a space may
306 assert_eq!(r(&p("r {- c -} .a")), "r.a");
307 assert!(matches!(p("r {- c -} { a = 2 }"), Expr::Update(_, _)));
308 assert_eq!(r(&p("[r {- c -} .a]")), "[r.a]");
309 assert!(matches!(p("let a {- c -} = 1 in a"), Expr::Let(_, _)));
310 assert!(matches!(p("\\x {- c -} -> x"), Expr::Lambda(ref ps, _, _) if ps.len() == 1));
311 assert_eq!(r(&p("{ a {- c -} = 1 }")), "{ a = 1 }");
312 assert!(matches!(p("( {- c -} + 1)"), Expr::Lambda(_, _, _)));
313 let items = cfg("f {- c -} = 1\ng {- c -} : Int\n(++) {- c -} : m -> m -> m\n");
314 assert_eq!(items.len(), 3);
315 // one spanning lines still ends the line it started on (§3.3)
316 let e = p("let\n x = 1 {- a\n-}\n y = 2\nin y");
317 assert!(matches!(e, Expr::Let(ref bs, _) if bs.len() == 2));
318 assert_eq!(cfg("x = f {- a\n-}\ny = 2\n").len(), 2);
322fn a_line_break_ends_a_postfix_chain() {
323 // §3.3 rule 3: a selector or `{` that begins a continuation line starts
324 // the next argument or list element instead of extending the `postfix`
325 // before it; `show`'s wide list of records relies on this (§5.2)
326 assert_eq!(r(&p("r\n .a")), "r (.a)");
327 assert_eq!(r(&p("r\n { a = 2 }")), "r ({ a = 2 })");
328 assert_eq!(r(&p("f r\n .a")), "(f (r)) (.a)");
329 assert!(matches!(p("[r\n { a = 2 }]"), Expr::List(ref es) if es.len() == 2));
330 assert!(matches!(p("[{ a = 1 }\n { a = 2 }]"), Expr::List(ref es) if es.len() == 2));
331 assert_eq!(r(&p("let y = r\n .a\nin y")), "let y = r (.a) in y");
332 match &cfg("x = f\n .a\n")[..] {
333 [Item::Definition(n, e, _)] => assert_eq!((n.as_str(), r(e).as_str()), ("x", "f (.a)")),
334 other => panic!("expected one definition, got {:?}", other),
336 // a comment that ends the line ends the chain too (§3.1)
337 assert_eq!(r(&p("r -- c\n .a")), "r (.a)");
338 assert_eq!(r(&p("r {- c\n -} .a")), "r (.a)");
339 // on one line the chain continues, spaces or not
340 assert_eq!(r(&p("r .a { b = 1 }")), "r.a { b = 1 }");
344fn multiline_signature_arrow() {
345 let items = cfg("f : Int\n -> Int\nf = \\x -> x\n");
346 assert!(matches!(&items[0], Item::Signature(n, _, _) if n == "f"));
350fn indented_lines_continue_signatures_and_typedecls() {
351 // §3.3 rule 1 holds for every item, not only definitions: a line that
352 // starts past column 1 continues it, wherever the line break falls
353 let items = cfg("f : Int ->\n Int\nf = \\x -> x\n");
354 assert_eq!(items.len(), 2);
355 assert!(matches!(&items[0], Item::Signature(n, TypeExpr::Fun(_, _), _) if n == "f"));
356 match &cfg("Foo =\n { a : Int }\n")[0] {
357 Item::TypeDecl(n, TypeExpr::Record(fs)) => assert!(n == "Foo" && fs.len() == 1),
358 other => panic!("expected a record typedecl, got {:?}", other),
361 "f :\n Int -> Int\n",
362 "f\n : Int -> Int\n",
363 "(++) :\n m -> m -> m\n",
364 "(++)\n : m -> m -> m\n",
366 "f : (Int\n -> Int)\n",
368 "Foo = {\n a : Int\n , b : Int\n }\n",
369 "Foo = { a\n : Int }\n",
372 match parse_config(src, outer()) {
373 Ok(items) => assert_eq!(items.len(), 1, "{:?}", src),
374 Err(e) => panic!("{:?}: line {}: {}", src, e.line, e.msg),
380fn a_column_1_line_never_continues_an_item() {
381 // §3.3 rule 1: a token in column 1 begins the next item, so each of
382 // these is malformed, though it is one item with that line indented
386 "(++)\n: m -> m -> m\n",
389 "Foo = { a : Int\n, b : Int }\n",
390 "Foo = { a : Int\n}\n",
391 "Foo = { a\n: Int }\n",
392 "x = let a = 1\nin a\n",
393 "x = let a = 1\n\nin a\n",
395 assert!(parse_config(src, outer()).is_err(), "{:?} parsed", src);
400fn a_later_line_in_closes_a_let_only_within_its_layout() {
401 // an `in` that begins a line obeys layout like any other token (§3.3):
402 // in a binding of an outer let it closes the inner let only when it is
403 // indented past the outer block's column
404 let e = p("let a = let b = 1\n in b\nin a");
405 assert!(matches!(&e, Expr::Let(bs, _) if matches!(*bs[0].1, Expr::Let(_, _))));
406 let msg = perr("let a = let b = 1\n in b\nin a");
407 assert!(msg.contains("indented past column 5"), "{}", msg);
408 // in a definition it is under rule 1; in a lone expression it is not
409 assert_eq!(cfg("x = let a = 1\n in a\n").len(), 1);
410 match parse_config("x = let a = 1\nin a\n", outer()) {
411 Ok(_) => panic!("`in` in column 1 continued a definition"),
412 Err(e) => assert!(e.msg.contains("indented past column 1"), "{}", e.msg),
414 assert!(matches!(p("let a = 1\nin a"), Expr::Let(_, _)));
418fn expression_stops_at_eof_cleanly() {
420 perr("let x = 1 in");
424fn render_roundtrip_simple_exprs() {
434 let rendered = render_expr(&e);
435 let e2 = parse_expr(&rendered, outer())
436 .unwrap_or_else(|err| panic!("re-parse of {:?} (from {:?}) failed: {}", rendered, src, err.msg));
437 assert_eq!(render_expr(&e2), rendered, "for {:?}", src);
441/// Run `f` on a thread with the binary's worker stack (main.rs): the parser's
442/// depth bound is sized against that stack, not the test harness's default.
443fn on_worker_stack(f: impl FnOnce() + Send + 'static) {
444 std::thread::Builder::new()
445 .stack_size(512 * 1024 * 1024)
453fn deep_nesting_is_a_parse_error_not_a_stack_overflow() {
454 // §1.4: bad input exits 3 with one `j:` line. The parser is recursive
455 // descent, so it bounds its depth rather than overflow the stack.
459 format!("{}1{}", "(".repeat(n), ")".repeat(n)),
461 format!("{}{}", "[".repeat(n), "]".repeat(n)),
462 format!("{}1{}", "{ a = ".repeat(n), " }".repeat(n)),
463 format!("x{}", " { a = x".repeat(n)),
464 format!("{}1{}", "(+ ".repeat(n), ")".repeat(n)),
465 // distinct names, since shadowing is an error of its own (§4.2)
466 format!("{}1", (0..n).map(|k| format!("\\x{} -> ", k)).collect::<String>()),
467 format!("{}1", "if true then 1 else ".repeat(n)),
468 format!("{}1", (0..n).map(|k| format!("let a{} = 1 in ", k)).collect::<String>()),
469 format!("{}[]", "1 :: ".repeat(n)),
470 format!("{}true", "true or ".repeat(n)),
472 let msg = perr(&src);
473 assert!(msg.contains("nested too deeply"), "{}…: {}", &src[..20], msg);
475 let deep_type = format!("f : {}Int{}\n", "[".repeat(n), "]".repeat(n));
476 match parse_config(&deep_type, outer()) {
477 Ok(_) => panic!("a type nested {} deep parsed", n),
478 Err(e) => assert!(e.msg.contains("nested too deeply"), "{}", e.msg),
480 // nesting far beyond anything written by hand still parses
482 p(&format!("{}1{}", "(".repeat(m), ")".repeat(m)));
483 p(&format!("{}{}", "[".repeat(m), "]".repeat(m)));
484 p(&format!("{}[]", "1 :: ".repeat(m)));
485 cfg(&format!("f : {}Int{}\n", "[".repeat(m), "]".repeat(m)));
490fn long_left_chains_are_bounded_like_nesting() {
491 // §3.4: the parser builds a left-associative chain in a loop, not by
492 // recursion, but its tree is as tall as the chain is long, and every
493 // pass over the tree after the parse (and dropping it) recurses on that
494 // height. So a chain past the bound is a parse error too, and so is one
495 // that reaches past it only together with the chains it is nested in.
498 let (k, m) = (100, 10_000);
499 let mut chains = "1".to_string();
501 chains = format!("({}{})", chains, " + 1".repeat(m));
504 format!("1{}", " + 1".repeat(n)),
505 format!("f{}", " x".repeat(n)),
506 format!("x{}", ".a".repeat(n)),
507 format!("x{}", " { a = 1 }".repeat(n)),
510 let msg = perr(&src);
511 assert!(msg.contains("nested too deeply"), "{}…: {}", &src[..20], msg);
513 for src in [format!("f = 1{}\n", " + 1".repeat(n)), format!("f = g{}\n", " x".repeat(n))] {
514 match parse_config(&src, outer()) {
515 Ok(_) => panic!("a chain {} long parsed in config.j", n),
516 Err(e) => assert!(e.msg.contains("nested too deeply"), "{}", e.msg),
519 // chains far longer than anything written by hand still parse
520 p(&format!("1{}", " + 1".repeat(m)));
521 p(&format!("f{}", " x".repeat(m)));
522 p(&format!("x{}", ".a".repeat(m)));
523 p(&format!("x{}", " { a = 1 }".repeat(m)));
528fn nesting_bound_counts_levels_as_the_spec_says() {
529 // §3.4: an expression is at most 40,000 levels deep. A leaf is one
530 // level, a parenthesis or the braces of a record or update two more than
531 // what is inside, and any other construct one more than its highest
532 // part. Each case is (source nested k deep, the deepest k that parses).
534 const MAX: usize = 40_000;
535 let lambdas = |k: usize| (0..k).map(|i| format!("\\x{} -> ", i)).collect::<String>();
536 let subtree = |k: usize| {
537 // as `show` renders a `Subtree`, too wide for one line once it
538 // holds a commit: a record in a list broken over lines is bare,
539 // not parenthesised (§3.3), so 1 + 2 levels each. `show` indents
540 // each `, root` line further, which the parser ignores.
541 (0..k).fold("[]".to_string(), |s, _| format!("[{{ children = {}\n, root = 1 }}]", s))
543 let chained = |k: usize| format!("(1{}){}", " + 1".repeat(20_000), " + 1".repeat(k));
544 let cases: Vec<(Box<dyn Fn(usize) -> String>, usize)> = vec![
545 (Box::new(|k| format!("{}{}", "[".repeat(k), "]".repeat(k))), MAX),
546 (Box::new(|k| format!("{}1{}", "(".repeat(k), ")".repeat(k))), (MAX - 1) / 2),
547 (Box::new(|k| format!("{}1{}", "(+ ".repeat(k), ")".repeat(k))), (MAX - 1) / 2),
548 (Box::new(|k| format!("{}1{}", "{ a = ".repeat(k), " }".repeat(k))), (MAX - 1) / 2),
549 (Box::new(|k| format!("x{}", " { a = 1 }".repeat(k))), (MAX - 1) / 2),
550 (Box::new(|k| format!("1{}", " + 1".repeat(k))), MAX - 1),
551 (Box::new(|k| format!("f{}", " x".repeat(k))), MAX - 1),
552 (Box::new(|k| format!("x{}", ".a".repeat(k))), MAX - 1),
553 (Box::new(|k| format!("{}[]", "1 :: ".repeat(k))), MAX - 1),
554 (Box::new(|k| format!("{}1", "if true then 1 else ".repeat(k))), MAX - 1),
555 (Box::new(move |k| format!("{}1", lambdas(k))), MAX - 1),
556 (Box::new(subtree), (MAX - 1) / 3),
557 // a chain around a parenthesised chain: 20,001 + 2 + k
558 (Box::new(chained), MAX - 20_003),
560 for (src, k) in cases {
562 let deeper = src(k + 1);
563 let msg = perr(&deeper);
564 assert!(msg.contains("nested too deeply"), "{}…: {}", &deeper[..20], msg);
566 // a type in config.j: one level, and one more for each bracket
567 let ty = |k: usize| format!("f : {}Int{}\n", "[".repeat(k), "]".repeat(k));
569 match parse_config(&ty(MAX), outer()) {
570 Ok(_) => panic!("a type {} deep parsed", MAX + 1),
571 Err(e) => assert!(e.msg.contains("nested too deeply"), "{}", e.msg),
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