source_layout_cases.py (7252B)
1 """Owner-relative layout oracles: authored values and complete located replies. 2 3 No tricu parser generates expectations. Contract adapters deliberately pass a 4 value through; these tests exercise annotated-head layout, not contract policy. 5 """ 6 from http_headers import LEAF, number, pair, tree_list 7 from source_grammar_models import Located, ast_apply, diagnostic, chars, reply, authored 8 9 10 IDENTITY = 'id x = x\n' 11 CONTRACTS = ('withContract contract value = contract value\n' 12 'matchResult failure success value = success value t\n' 13 'err message rest = t\nidentity x = x\n') 14 15 16 def semantic_cases(): 17 rows = [] 18 def add(label, source, expected=number(23)): 19 rows.append((label, source, expected)) 20 for label, body in [ 21 ('inline', 'id 23'), 22 ('inline-continuation', 'id\n 23'), 23 ('next-line-aligned', '\n id\n 23'), 24 ('next-line-deeper', '\n id\n 23'), 25 ('next-line-variable-indent', '\n id\n 23'), 26 ('comments', '\n id -- operand next\n\n -- comment\n 23'), 27 ('block-comment', '\n id |- ignored\n comment -|\n 23'), 28 ('grouped', '(id\n23)'), 29 ]: 30 add(label, IDENTITY + 'main = ' + body) 31 add('return-' + label, CONTRACTS + IDENTITY + 'main =@identity ' + body) 32 add('argument-' + label, CONTRACTS + IDENTITY + 'f x@identity = ' + body + '\nmain = f 99') 33 add('grouped-contract', CONTRACTS + IDENTITY + 'main =@(id\n identity)\n id\n 23') 34 add('application-not-nesting', 'first a b = a\nmain =\n first\n 23\n 24') 35 add('following-definition', 'main =\n id\n 23\nid x = x') 36 add('equations', 'pick t =\n id\n 23\npick _ = 24\nid x = x\nmain = pick t') 37 add('local-block', IDENTITY + 'main = let\n value =\n id\n 23\n other = 24\nin value') 38 add('local-inline', IDENTITY + 'main = let value =\n id\n 23\nin value') 39 add('local-body', IDENTITY + 'main = let value = 23 in\n id\n value') 40 add('where-block', IDENTITY + 'main = value where\n value =\n id\n 23\n other = 24') 41 add('where-inline', IDENTITY + 'main = value where value =\n id\n 23') 42 add('where-top-dedent', 'main = case t of\n t = value\n _ = 24\nwhere value = 23') 43 add('where-arm-aligned', 'main = case t of\n t = value\n where value = 23\n _ = 24') 44 add('pipe-top-dedent', 'main = case t of\n t = 24\n _ = 25\n|> (x : 23)') 45 add('pipe-arm-aligned', 'main = case t of\n t = 24\n |> (x : 23)\n _ = 25') 46 add('pipe-next-line', IDENTITY + 'main =\n 23\n |>\n id') 47 add('pipe-application', 'first a b = a\nmain =\n first\n 23\n <| 24') 48 add('case-body', IDENTITY + 'main = case t of\n t =\n id\n 23\n _ = 24') 49 add('case-nested', IDENTITY + 'main = case t of\n t = case t of\n t =\n id\n 23\n _ = 24\n _ = 25') 50 add('do-binding', IDENTITY + 'pass value next = next value\nmain = do pass\n x <-\n id\n 23\n x') 51 add('do-expression', IDENTITY + 'pass value next = next value\nmain = do pass\n id\n 99\n id\n 23') 52 add('do-nested', IDENTITY + 'pass value next = next value\nmain = do pass\n x <- do pass\n y <-\n id\n 23\n y\n x') 53 add('list-atoms', 'main = [\n 23\n 24\n]', tree_list([number(23), number(24)])) 54 add('list-group', IDENTITY + 'main = [(id\n 23)\n 24]', tree_list([number(23), number(24)])) 55 add('grouped-inline-local', IDENTITY + 'main = (let value =\nid\n23 in value)') 56 add('grouped-inline-where', IDENTITY + 'main = (value where value =\nid\n23)') 57 add('lambda-body', 'main = (x :\n x\n) 23') 58 add('local-shadow-boundary', 'value = 24\nmain = let\n value =\n id\n 23\n id x = x\nin value\nid x = x') 59 return rows 60 61 62 def parser_cases(): 63 rows = [] 64 # Full program ASTs distinguish application association, token spans and 65 # untouched sibling definitions; evaluations alone could hide those bugs. 66 for index, source in enumerate([ 67 'main = t t', 'main = t\n t', 'main =\n t\n t', 68 'main =\n t\n t', 'main =\n t\n t', 69 'main =\n t -- comment\n\n t', 70 'main =\n t\n t\nother = 24', 71 'main =\n t\n t\n t', 72 ]): 73 x = Located(source) 74 second = x.node(1, LEAF, source.rindex('t'), source.rindex('t')+1) if '--' in source else x.atom(1, 't', 2) 75 body = ast_apply(x.atom(1, 't'), second) 76 if source.endswith('\n t\n t\n t'): 77 body = ast_apply(body, x.atom(1, 't', 3)) 78 definitions = [pair(x.atom(2, 'main'), body)] 79 if 'other' in source: 80 definitions.append(pair(x.atom(2, 'other'), x.atom(6, '24'))) 81 rows.append(('ast-layout-' + str(index), source, 'parseGateProgram', 82 reply(True, pair(tree_list(definitions), LEAF), consumed=True))) 83 # Existing single-expression and delimiter protocols remain intentional. 84 x = Located('t\nx') 85 suffix = tree_list([x.token(6, LEAF, 1, 2), x.atom(2, 'x')]) 86 rows.append(('ast-top-separation', x.text, 'parseGateSource', 87 reply(False, diagnostic(2, LEAF, number(6), x.pos(1)), suffix, True))) 88 x = Located('(t\n t)') 89 rows.append(('ast-group', x.text, 'parseGateSource', reply(True, 90 x.node(5, ast_apply(x.atom(1, 't'), x.atom(1, 't', 2)), 0, len(x.text)), consumed=True))) 91 x = Located('[t\n t]') 92 rows.append(('ast-list', x.text, 'parseGateSource', reply(True, 93 x.node(8, tree_list([x.atom(1, 't'), x.atom(1, 't', 2)]), 0, len(x.text)), consumed=True))) 94 # Missing bodies cannot steal a following sibling token. Check complete 95 # diagnostics AND exact unconsumed located tokens / commitment flags. 96 for label, source, expected, kind in [ 97 ('missing-body', 'main =\n', 'definition body', None), 98 ('unindented-body', 'main =\nt', 'definition body', 1), 99 ('missing-arm-body', 'main = case t of\n _ =\n t', 'case arm body', 1), 100 ('nested-do-indent', 'main = do bind\n do bind\n t', 'indented do block', 1), 101 ('missing-do-action', 'main = do bind\n x <-\n t', 'do binding action', 1), 102 ('missing-local-value', 'main = let\n x =\n t', 'local binding value', 1), 103 ('missing-pipe-operand', 'main = t |>\nt', 'pipe operand', 1), 104 ]: 105 x = Located(source) 106 # The last authored token is the unconsumed sibling. 107 at = None if kind is None else source.rindex('t') 108 suffix = LEAF if at is None else tree_list([x.token(kind, chars('t'), at, at+1)]) 109 rows.append((label, source, 'parseGateProgram', reply(False, 110 diagnostic(40, chars(expected), LEAF if kind is None else number(kind), 111 LEAF if at is None else x.pos(at)), suffix, True))) 112 # Keep the pre-existing independent parser protocol controls, including 113 # consumed failures and malformed delimiters, against the rebuilt parser. 114 for label, operation, argument, expected in authored(): 115 if operation in ('expr', 'program'): 116 from local_control import decode_text 117 rows.append(('grammar-' + label, decode_text(argument), 118 'parseGateProgram' if operation == 'program' else 'parseGateSource', expected)) 119 return rows