#
# puppetManifest.ctags --- multitable regex parser for Puppet's manifest file
#
#  Copyright (c) 2017, Red Hat, Inc.
#  Copyright (c) 2017, Masatake YAMATO
#
#  Author: Masatake YAMATO <yamato@redhat.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.
#
#
# References:
#
# - https://github.com/puppetlabs/puppet-specifications/blob/master/language/intro.md
# - https://docs.puppet.com/puppet/5.1/lang_visual_index.html
#
--langdef=PuppetManifest{_autoFQTag}
--map-PuppetManifest=+.pp

#
# Kinds
#
--kinddef-PuppetManifest=c,class,classes
--kinddef-PuppetManifest=d,definition,definitions
--kinddef-PuppetManifest=n,node,nodes
--kinddef-PuppetManifest=r,resource,resources
--kinddef-PuppetManifest=v,variable,variables
--kinddef-PuppetManifest=p,param,parameters
--kinddef-PuppetManifest=V,vresource,virtual resources
--kinddef-PuppetManifest=t,type,type aliases

#
# Separators
#
--_scopesep-PuppetManifest=/c:::
--_scopesep-PuppetManifest=*/*:::

#
# Prelude
#
--_prelude-PuppetManifest={{
    % spec:dict<num,kind> TRYMAKETAG tag:int true
    % spec:dict<num,kind> TRYMAKETAG false
    /trymaketag {
        {
            {
                1 index _matchstr {
                    % key value str
                    3 -1 roll
                    % value str key
                    _matchloc
                    % value str loc
                    3 -1 roll exch
                    % str value loc
                    _tag _commit
                    stop
                } {
                    pop pop
                } ifelse
            } forall
         } stopped
    } def
}}

#
# Tables
#
--_tabledef-PuppetManifest=main
--_tabledef-PuppetManifest=separator
--_tabledef-PuppetManifest=any
--_tabledef-PuppetManifest=ignoreWhiteSpace
--_tabledef-PuppetManifest=end
--_tabledef-PuppetManifest=endWithPop

--_tabledef-PuppetManifest=ssliteral
--_tabledef-PuppetManifest=dsliteral

--_tabledef-PuppetManifest=comment

--_tabledef-PuppetManifest=blockStart
--_tabledef-PuppetManifest=blockHead
--_tabledef-PuppetManifest=blockHeadPopAtLast
--_tabledef-PuppetManifest=block

--_tabledef-PuppetManifest=classStart
#--_tabledef-PuppetManifest=resourceBlock{args=end}
--_tabledef-PuppetManifest=resourceBlock

--_tabledef-PuppetManifest=skipLiteral
--_tabledef-PuppetManifest=skipBlock
--_tabledef-PuppetManifest=skipArray
--_tabledef-PuppetManifest=skipArgs
--_tabledef-PuppetManifest=skipCollector

--_tabledef-PuppetManifest=signature
--_tabledef-PuppetManifest=skipDefaultValue

--_tabledef-PuppetManifest=var

--_tabledef-PuppetManifest=defineStart
--_tabledef-PuppetManifest=caseStart
--_tabledef-PuppetManifest=ifStart

--_tabledef-PuppetManifest=nodeStart

--_tabledef-PuppetManifest=typeStart

#
# Utilities
#
--_mtable-regex-PuppetManifest=separator/[a-zA-Z0-9]//
--_mtable-regex-PuppetManifest=separator///{tleave}

--_mtable-regex-PuppetManifest=any/.//
--_mtable-regex-PuppetManifest=ignoreWhiteSpace/[ \t\n]+//
--_mtable-regex-PuppetManifest=end///{tleave}
--_mtable-regex-PuppetManifest=endWithPop///{tleave}{scope=pop}

--_mtable-regex-PuppetManifest=ssliteral/[^']*'//{tleave}
--_mtable-regex-PuppetManifest=ssliteral/[^']+//
--_mtable-regex-PuppetManifest=dsliteral/[^"]*"//{tleave}
--_mtable-regex-PuppetManifest=dsliteral/[^"]+//

#
# comment
#
--_tabledef-PuppetManifest=comment_multiline
--_tabledef-PuppetManifest=comment_oneline
--_mtable-regex-PuppetManifest=comment/\/\*//{tenter=comment_multiline}
--_mtable-regex-PuppetManifest=comment/\#//{tenter=comment_oneline}
--_mtable-regex-PuppetManifest=comment_multiline/\*\///{tleave}
--_mtable-extend-PuppetManifest=comment_multiline+any
--_mtable-regex-PuppetManifest=comment_oneline/\n//{tleave}
--_mtable-extend-PuppetManifest=comment_oneline+any

#
# skipLiteral
#
--_mtable-regex-PuppetManifest=skipLiteral/'//{tenter=ssliteral}
--_mtable-regex-PuppetManifest=skipLiteral/"//{tenter=dsliteral}

#
# skipBlock
#
--_mtable-extend-PuppetManifest=skipBlock+comment
--_mtable-extend-PuppetManifest=skipBlock+skipLiteral
--_mtable-regex-PuppetManifest=skipBlock/\{//{tenter=skipBlock}
--_mtable-regex-PuppetManifest=skipBlock/\}//{tleave}
--_mtable-extend-PuppetManifest=skipBlock+any

#
# skipArray
#
--_mtable-extend-PuppetManifest=skipArray+comment
--_mtable-extend-PuppetManifest=skipArray+skipLiteral
--_mtable-regex-PuppetManifest=skipArray/\[//{tenter=skipArray}
--_mtable-regex-PuppetManifest=skipArray/\]//{tleave}
--_mtable-extend-PuppetManifest=skipArray+any

#
# skipArgs
#
--_mtable-extend-PuppetManifest=skipArgs+comment
--_mtable-extend-PuppetManifest=skipArgs+skipLiteral
--_mtable-regex-PuppetManifest=skipArgs/\(//{tenter=skipArgs}
--_mtable-regex-PuppetManifest=skipArgs/\)//{tleave}
--_mtable-extend-PuppetManifest=skipArgs+any

#
# signature
#
--_mtable-extend-PuppetManifest=signature+comment
--_mtable-extend-PuppetManifest=signature+skipLiteral
--_mtable-extend-PuppetManifest=signature+ignoreWhiteSpace
--_mtable-regex-PuppetManifest=signature/\)//{tleave}{{
    %
    % fill signature
    %
    dup ?, eq { pop } if
    ?) _buildstring _scopetop {
        exch signature:
    } {
        % something wrong
        pop
    } ifelse
}}
--_mtable-regex-PuppetManifest=signature/\$([a-zA-Z][_a-zA-Z0-9:]*)[ \t]*([=,])[ \t]*/\1/p/{scope=ref}{{
    % push the name of parameter for filling the signature field of definition
    \2 0 get ?= eq {
       /skipDefaultValue _tenter
    } if
    ?$ \1 ?,
}}
--_mtable-extend-PuppetManifest=signature+any

#
# skipDefaultValue
#
--_mtable-extend-PuppetManifest=skipDefaultValue+comment
--_mtable-extend-PuppetManifest=skipDefaultValue+skipLiteral
--_mtable-regex-PuppetManifest=skipDefaultValue/\[//{tenter=skipArray}
--_mtable-regex-PuppetManifest=skipDefaultValue/\{//{tenter=skipBlock}
--_mtable-regex-PuppetManifest=skipDefaultValue/\(//{tenter=skipArgs}
--_mtable-regex-PuppetManifest=skipDefaultValue/,//{tleave}
--_mtable-regex-PuppetManifest=skipDefaultValue/\)//{tleave}{_advanceTo=0start}
--_mtable-extend-PuppetManifest=skipDefaultValue+any

#
# skipCollector
#
--_mtable-extend-PuppetManifest=skipCollector+comment
--_mtable-extend-PuppetManifest=skipCollector+skipLiteral
--_mtable-regex-PuppetManifest=skipCollector/<<?\|//{tenter=skipCollector}
--_mtable-regex-PuppetManifest=skipCollector/\|>>?//{tleave}
--_mtable-extend-PuppetManifest=skipCollector+any



#
# block
#
--_mtable-regex-PuppetManifest=blockStart/(@)?(::[a-zA-Z0-9:_]+|[a-zA-Z][a-zA-Z0-9:_]*)[ \t\n]*\{//{tenter=resourceBlock}{{
    \1 _isstring {
       /vresource
    } {
       /resource
    } ifelse \2 true
    % kind:name type:string true
}}
--_mtable-regex-PuppetManifest=blockStart/class[ \t\n]+//{tenter=classStart}
--_mtable-regex-PuppetManifest=blockStart/define[ \t\n]+//{tenter=defineStart}
--_mtable-regex-PuppetManifest=blockStart/case[ \t\n]+//{tenter=caseStart}
--_mtable-regex-PuppetManifest=blockStart/(if|elsif|else|unless)[ \t\n]+//{tenter=ifStart}
--_mtable-regex-PuppetManifest=blockStart/node[ \t\n]+//{tenter=nodeStart}
--_mtable-regex-PuppetManifest=blockStart/type[ \t\n]+//{tenter=typeStart}
--_mtable-regex-PuppetManifest=blockStart/\$//{tenter=var}


--_mtable-extend-PuppetManifest=blockHead+comment
--_mtable-extend-PuppetManifest=blockHead+ignoreWhiteSpace
--_mtable-regex-PuppetManifest=blockHead/\{//{tenter=block,end}
--_mtable-extend-PuppetManifest=blockHead+any

--_mtable-extend-PuppetManifest=blockHeadPopAtLast+comment
--_mtable-regex-PuppetManifest=blockHeadPopAtLast/\{//{tenter=block,endWithPop}
--_mtable-extend-PuppetManifest=blockHeadPopAtLast+ignoreWhiteSpace
--_mtable-regex-PuppetManifest=blockHeadPopAtLast/inherits[ \t\n]+(::[a-z][_a-zA-Z0-9:]*|[a-z][_a-zA-Z0-9:]*)[ \t\n]*//{{
    _scopetop {
        dup :kind /class eq {
           \1 inherits:
        } {
           pop
        } ifelse
    } if
}}
--_mtable-extend-PuppetManifest=blockHeadPopAtLast+any

--_mtable-extend-PuppetManifest=block+ignoreWhiteSpace
--_mtable-extend-PuppetManifest=block+blockStart
--_mtable-regex-PuppetManifest=block/<<?\|//{tenter=skipCollector}

# Following stack manipulation for handling following input.
# A {
#     "a": b => c;
#     "d": e => f;
# }
# B {
#     "g": h => i;
#     "j": k => k /* THIS COMMENT CANNOT BE IGNORED WELL */
# }
--_mtable-regex-PuppetManifest=block/;?[ \t\n]*\}//{tleave}
--_mtable-regex-PuppetManifest=block/;//{tjump=resourceBlock}{scope=pop}{{
    false
}}
#--_mtable-regex-PuppetManifest=block/;//{tleave}{scope=pop}
--_mtable-regex-PuppetManifest=block/://
--_mtable-extend-PuppetManifest=block+comment
--_mtable-extend-PuppetManifest=block+skipLiteral
--_mtable-regex-PuppetManifest=block/\{//{tenter=block}
--_mtable-regex-PuppetManifest=block/.//{tenter=separator}

#
# main
#
--_mtable-extend-PuppetManifest=main+comment
--_mtable-extend-PuppetManifest=main+ignoreWhiteSpace
--_mtable-extend-PuppetManifest=main+skipLiteral
--_mtable-regex-PuppetManifest=main/<<?\|//{tenter=skipCollector}
--_mtable-regex-PuppetManifest=main/\$//{tenter=var}
--_mtable-extend-PuppetManifest=main+blockStart
--_mtable-regex-PuppetManifest=main/\(//{tenter=skipArgs}
--_mtable-regex-PuppetManifest=main/\{//{tenter=skipBlock}
--_mtable-regex-PuppetManifest=main/.//{tenter=separator}

#
# class
--_mtable-extend-PuppetManifest=classStart+comment
--_mtable-regex-PuppetManifest=classStart/(::[a-z][_a-zA-Z0-9:]*|[a-z][_a-zA-Z0-9:]*)[ \t\n]*(\(|\{|inherits[ \t\n]+(::[a-z][_a-zA-Z0-9:]*|[a-z][_a-zA-Z0-9:]*))[ \t\n]*/\1/c/{scope=push}{{
    \3 _isstring {
        . exch inherits:
        /blockHead /endWithPop _tentercont
    } {
        \2 0 get ?( eq {
            % for gathering signature
            mark ?(
            % {tenter=signature,blockHeadPopAtLast}
            /signature /blockHeadPopAtLast _tentercont
        } {
            @2 _advanceto
            /blockHead /endWithPop _tentercont
        } ifelse
    } ifelse
}}

#
# resource
#
--_tabledef-PuppetManifest=resourceName
--_tabledef-PuppetManifest=resourceNameInArray
--_tabledef-PuppetManifest=resourceBody
--_tabledef-PuppetManifest=resourceArray
--_tabledef-PuppetManifest=resourceCollector

# resourceBlock expects data on optscript:
#
# false RESOURCEBLOCK -
# kind:name type:string true RESOURCEBLOCK -
#
--_mtable-extend-PuppetManifest=resourceBlock+ignoreWhiteSpace
--_mtable-regex-PuppetManifest=resourceBlock/\}//{tleave}{{
    { pop pop } if
}}
--_mtable-regex-PuppetManifest=resourceBlock/'//{tenter=resourceName}{_advanceTo=0start}
--_mtable-regex-PuppetManifest=resourceBlock/"//{tenter=resourceName}{_advanceTo=0start}
--_mtable-regex-PuppetManifest=resourceBlock/\[//{tenter=resourceArray}
--_mtable-regex-PuppetManifest=resourceBlock/\{//{tenter=block}
--_mtable-extend-PuppetManifest=resourceBlock+comment
--_mtable-regex-PuppetManifest=resourceBlock/.//{tenter=resourceBody}{scope=push}{placeholder}

# resourceName expects data on optscript:
#
# false RESOURCENAME false
# kind:name type:string true RESOURCENAME kind:name type:string true
#
--_mtable-regex-PuppetManifest=resourceName/'([^']+)'|"([^"]+)"//{tenter=resourceBody,end}{{
    dup {
        1 index (typename) exch [ 3 1 roll ]
        % kind:name type:string true [ (typename) type:string ]
        <<
        4 index
        % kind:name type:string true [ (typename) type:string ] << kind:name
        1 exch dup 2 exch
        >>
        % kind:name type:string true [ (typename) type:string ] << 1 kind:name 2 kind:name >>
        trymaketag
        % kind:name type:string true [ (typename) type:string ] tag:int true %
        % kind:name type:string true [ (typename) type:string ] false
        {
            dup _scopepush
            exch typeref:
        } {
            pop
        } ifelse
    } {
        << 1 /resource 2 /resource >> trymaketag {
           _scopepush
        } if
    } ifelse
}}
--_mtable-regex-PuppetManifest=resourceName///{tquit}

# resourceNameInArray expects data on optscript:
#
# false RESOURCENAMEINARRAY false
# kind:name type:string true RESOURCENAMEINARRAY kind:name type:string true
#
--_mtable-regex-PuppetManifest=resourceNameInArray/'([^']+)'|"([^"]+)"///{tleave}{{
    dup {
        % do the same as resourceName
        1 index (typename) exch [ 3 1 roll ]
        << 4 index 1 exch dup 2 exch >> trymaketag
        {
            dup _scoperef
            exch typeref:
        } {
            pop
        } ifelse

    } {
        << 1 /resource 2 /resource >> trymaketag {
           _scoperef
        } if
    } ifelse
}}
--_mtable-regex-PuppetManifest=resourceNameInArray////{tquit}

# --_mtable-extend-PuppetManifest=resourceBody+ignoreWhiteSpace
#
# Next pattern is just for optimization.
#
--_mtable-regex-PuppetManifest=resourceBody/[^\/#{'";}<]+//
--_mtable-regex-PuppetManifest=resourceBody/'//{tenter=ssliteral}
--_mtable-regex-PuppetManifest=resourceBody/"//{tenter=dsliteral}
--_mtable-regex-PuppetManifest=resourceBody/\}//{tleave}{_advanceTo=0start}{scope=pop}
--_mtable-regex-PuppetManifest=resourceBody/\{//{tenter=skipBlock}
--_mtable-regex-PuppetManifest=resourceBody/;//{tleave}{scope=pop}
--_mtable-regex-PuppetManifest=resourceBody/<<?\|//{tenter=skipCollector}
--_mtable-extend-PuppetManifest=resourceBody+comment
--_mtable-extend-PuppetManifest=resourceBody+any

--_mtable-extend-PuppetManifest=resourceArray+comment
--_mtable-extend-PuppetManifest=resourceArray+ignoreWhiteSpace
--_mtable-regex-PuppetManifest=resourceArray/['"]//{tenter=resourceNameInArray}{_advanceTo=0start}
--_mtable-regex-PuppetManifest=resourceArray/\]//{tleave}
--_mtable-extend-PuppetManifest=resourceArray+any

#
# var
#
--_tabledef-PuppetManifest=varexpr
--_mtable-regex-PuppetManifest=var/(::[a-zA-Z0-9_:]+|[a-zA-Z_][a-zA-Z0-9_:]*)[ \t\n]*=/\1/v/{tenter=varexpr,end}{{
    \1 0 get ?: eq not {
       . _scoperef
    } if
}}

--_mtable-extend-PuppetManifest=varexpr+comment
--_mtable-regex-PuppetManifest=varexpr/'//{tenter=ssliteral,end}
--_mtable-regex-PuppetManifest=varexpr/"//{tenter=dsliteral,end}
--_mtable-regex-PuppetManifest=varexpr/\[//{tenter=skipArray,end}
--_mtable-regex-PuppetManifest=varexpr/\{//{tenter=skipBlock,end}
--_mtable-regex-PuppetManifest=varexpr/\(//{tenter=skipArgs,end}
--_mtable-regex-PuppetManifest=varexpr/\$[a-zA-Z:][0-9a-zA-Z:]*//
--_mtable-regex-PuppetManifest=varexpr/[0-9]+(\.[0-9]+(e([+-][0-9]+)))?//
--_mtable-regex-PuppetManifest=varexpr/[a-zA-Z0-9:][0-9a-zA-Z:]*//
--_mtable-regex-PuppetManifest=varexpr/[ \t]//
--_mtable-regex-PuppetManifest=varexpr/\n//{tleave}
--_mtable-extend-PuppetManifest=varexpr+any

#
# define
#
--_mtable-extend-PuppetManifest=defineStart+comment
--_mtable-regex-PuppetManifest=defineStart/([a-z][_a-zA-Z0-9:]*)[ \n\t]*([({])/\1/d/{scope=push}{{
    \2 0 get ?( eq {
       % for gathering signature
       mark ?(
       % {tenter=signature,blockHeadPopAtLast}
       /signature /blockHeadPopAtLast _tentercont
    } {
       /block /endWithPop _tentercont
    } ifelse
}}

#
# case
#
--_tabledef-PuppetManifest=caseBlock

--_mtable-extend-PuppetManifest=caseStart+comment
--_mtable-extend-PuppetManifest=caseStart+ignoreWhiteSpace
--_mtable-regex-PuppetManifest=caseStart/\{//{tenter=caseBlock}
--_mtable-regex-PuppetManifest=caseStart/}//{tleave}
--_mtable-extend-PuppetManifest=caseStart+any

--_mtable-extend-PuppetManifest=caseBlock+comment
--_mtable-extend-PuppetManifest=caseBlock+ignoreWhiteSpace
--_mtable-extend-PuppetManifest=caseBlock+skipLiteral
--_mtable-regex-PuppetManifest=caseBlock/://{tenter=blockHead}
--_mtable-regex-PuppetManifest=caseBlock/}//{tleave}{_advanceTo=0start}
--_mtable-extend-PuppetManifest=caseBlock+any

#
# if/elsif/else
#
--_mtable-extend-PuppetManifest=ifStart+comment
--_mtable-extend-PuppetManifest=ifStart+ignoreWhiteSpace
--_mtable-extend-PuppetManifest=ifStart+skipLiteral
--_mtable-regex-PuppetManifest=ifStart/\{//{tenter=block,end}
--_mtable-extend-PuppetManifest=ifStart+any

#
# node
#
--_mtable-extend-PuppetManifest=nodeStart+comment
--_mtable-extend-PuppetManifest=nodeStart+ignoreWhiteSpace
--_mtable-regex-PuppetManifest=nodeStart/'([^']+)'/\1/n/
--_mtable-regex-PuppetManifest=nodeStart/"([^"]+)"/\1/n/
--_mtable-regex-PuppetManifest=nodeStart/\{///{tenter=block,end}
--_mtable-extend-PuppetManifest=nodeStart+any

#
# type
#
--_mtable-extend-PuppetManifest=typeStart+comment
--_mtable-extend-PuppetManifest=typeStart+ignoreWhiteSpace
--_mtable-regex-PuppetManifest=typeStart/([a-zA-Z][a-zA-Z0-9]*::[a-zA-Z][a-zA-Z0-9]+)[ \t\n]*=[ \t\n]*/\1/t/{tenter=varexpr,end}
--_mtable-extend-PuppetManifest=typeStart+any
