wsz
2025-06-10 4be5e6f4ae34bcb7cb47f05816421459bbfaedf1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
--------------------------------------------------------------------------------
-- Copyright (c) 2006-2013 Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
--     Fabien Fleutot - API and implementation
--
--------------------------------------------------------------------------------
 
local unpack = table.unpack or unpack
 
local gg    = require 'metalua.grammar.generator'
 
return function(M)
    local _M = gg.future(M)
    M.lexer :add '->'
    local A = { }
    local _A = gg.future(A)
    M.annot = A
 
    -- Type identifier: Lua keywords such as `"nil"` allowed.
    function M.annot.tid(lx)
        local w = lx :next()
        local t = w.tag
        if t=='Keyword' and w[1] :match '^[%a_][%w_]*$' or w.tag=='Id'
        then return {tag='TId'; lineinfo=w.lineinfo; w[1]}
        else return gg.parse_error (lx, 'tid expected') end
    end
 
    local field_types = { var='TVar'; const='TConst';
                          currently='TCurrently'; field='TField' }
 
    -- TODO check lineinfo
    function M.annot.tf(lx)
        local tk = lx:next()
        local w = tk[1]
        local tag = field_types[w]
        if not tag then error ('Invalid field type '..w)
        elseif tag=='TField' then return {tag='TField'} else
            local te = M.te(lx)
            return {tag=tag; te}
        end
    end
 
    M.annot.tebar_content = gg.list{
        name        = 'tebar content',
        primary     = _A.te,
        separators  = { ",", ";" },
        terminators = ")" }
 
    M.annot.tebar = gg.multisequence{
        name = 'annot.tebar',
        --{ '*', builder = 'TDynbar' }, -- maybe not user-available
        { '(', _A.tebar_content, ')',
          builder = function(x) return x[1] end },
        { _A.te }
    }
 
    M.annot.te = gg.multisequence{
        name = 'annot.te',
        { _A.tid, builder=function(x) return x[1] end },
        { '*', builder = 'TDyn' },
        { "[",
          gg.list{
              primary = gg.sequence{
                  _M.expr, "=", _A.tf,
                  builder = 'TPair'
              },
              separators  = { ",", ";" },
              terminators = { "]", "|" } },
          gg.onkeyword{ "|", _A.tf },
          "]",
          builder = function(x)
              local fields, other = unpack(x)
              return { tag='TTable', other or {tag='TField'}, fields }
          end }, -- "[ ... ]"
        { '(', _A.tebar_content, ')', '->', '(', _A.tebar_content, ')',
          builder = function(x)
               local p, r = unpack(x)
               return {tag='TFunction', p, r }
           end } }
 
    M.annot.ts = gg.multisequence{
        name = 'annot.ts',
        { 'return', _A.tebar_content, builder='TReturn' },
        { _A.tid, builder = function(x)
              if x[1][1]=='pass' then return {tag='TPass'}
              else error "Bad statement type" end
          end } }
 
-- TODO: add parsers for statements:
-- #return tebar
-- #alias = te
-- #ell = tf
--[[
    M.annot.stat_annot = gg.sequence{
        gg.list{ primary=_A.tid, separators='.' },
        '=',
        XXX??,
        builder = 'Annot' }
--]]
 
    return M.annot
end