tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

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