Commits

ZyX_I committed 5521d01

@/fwc: Moved all `optional', `prefixes', `next' and `actions' sections-related code to @/fwc/topconstructs, some refactoring

Comments (0)

Files changed (10)

plugin/frawor/fwc/compiler.vim

             \exists('g:frawor__donotload')
     finish
 endif
-execute frawor#Setup('0.0', {'@/fwc/parser'     : '0.0',
-            \                '@/fwc/constructor': '0.0',
-            \                '@/fwc/intfuncs'   : '0.0',
-            \                '@/os'             : '0.0',
-            \                '@/decorators'     : '0.0'}, 1)
+execute frawor#Setup('0.0', {'@/fwc/parser'       : '0.0',
+            \                '@/fwc/constructor'  : '0.0',
+            \                '@/fwc/intfuncs'     : '0.0',
+            \                '@/fwc/topconstructs': '0.0',
+            \                '@/os'               : '0.0',
+            \                '@/decorators'       : '0.0'}, 1)
 let s:compiler={}
 let s:cf='CHECKFAILED'
 let s:cfstr=string(s:cf)
         endif
     endif
 endfunction
-"▶1 getsubs        :: [subscript] + self → String
-function s:compiler.getsubs(subscripts)
-    let r=''
-    for sub in a:subscripts
-        let tsub=type(sub)
-        if tsub==type('')
-            let r.=((sub=~#'\W')?('['.self.string(sub).']'):
-                        \        ('.'.sub))
-        elseif tsub==type(0)
-            let r.='['.sub.']'
-        elseif tsub==type([])
-            let r.='['.join(map(copy(sub), 'type(v:val)=='.type([]).'?'.
-                        \                       'self.getvar(v:val):'.
-                        \                       'v:val'), ':').']'
-        endif
-        unlet sub
-    endfor
-    return r
-endfunction
-"▶1 argstr         :: [genString::Bool, [subscript]] + self → String
-"▶2 addargchunk    :: chunks, chunk::String, literal::Bool → _ + chunks
-function s:F.addargchunk(chunks, chunk, literal)
-    if a:literal==(len(a:chunks)%2)
-        call add(a:chunks, a:chunk)
-    else
-        let a:chunks[-1].=a:chunk
-    endif
-endfunction
-"▶2 addstrsub      :: chunks, sub, self → _ + chunks
-function s:F.addstrsub(chunks, sub, self)
-    if type(a:sub)==type([])
-        call s:F.addargchunk(a:chunks, 'string('.a:self.getvar(a:sub).')', 1)
-    elseif type(a:sub)==type('')
-        call s:F.addargchunk(a:chunks, 'string('.a:sub.')', 1)
-    else
-        call s:F.addargchunk(a:chunks, a:sub, 0)
-    endif
-endfunction
-"▲2
-function s:compiler.argstr(...)
-    if get(a:000, 0, 0)
-        let chunks=[self.argbase]
-        for sub in get(a:000, 1, self.subs)
-            let tsub=type(sub)
-            if tsub==type('')
-                if sub=~#'^\w\+$'
-                    call s:F.addargchunk(chunks, '.'.sub, 0)
-                else
-                    call s:F.addargchunk(chunks, '['.self.string(sub).']', 0)
-                endif
-            elseif tsub==type(0)
-                call s:F.addargchunk(chunks, '['.sub.']', 0)
-            else
-                call s:F.addargchunk(chunks, '[', 0)
-                call s:F.addstrsub(chunks, sub[0], self)
-                if len(sub)>1
-                    call s:F.addargchunk(chunks, ':', 0)
-                    call s:F.addstrsub(chunks, sub[1], self)
-                endif
-                call s:F.addargchunk(chunks, ']', 0)
-            endif
-            unlet sub
-        endfor
-        return join(map(chunks, 'v:key%2 ? v:val : self.string(v:val)'), '.')
-    else
-        return self.argbase.(self.getsubs(get(a:000, 1, self.subs)))
-    endif
-endfunction
-"▶1 incrementsub   :: subscript, incby → subscript
-function s:F.incrementsub(sub, incby)
-    if type(a:sub)==type(0)
-        return a:sub+a:incby
-    elseif type(a:sub)==type([]) && len(a:sub)==1
-        let num=matchstr(a:sub[0], '[+-]\d\+$')
-        return [printf('%s%+i', a:sub[0][:-1-len(num)],
-                    \           str2nr(num)+a:incby)]
-    endif
-endfunction
-"▶1 incsub         :: &self([incby])
-function s:compiler.incsub(...)
-    if empty(self.subs)
-        return self
-    else
-        let self.subs[-1]=s:F.incrementsub(self.subs[-1], get(a:000, 0, 1))
-    endif
-    return self
-endfunction
-"▶1 fail           :: &self
-function s:compiler.fail()
-    let msgstatus=self.msgs.statuses[-1]
-    if msgstatus is 'return'
-        return self[self.failcal[0]](self.failcal[1]).up()
-    else
-        return self.throw(s:cfstr)
-    endif
-endfunction
-"▶1 pushms         :: &self(msgstatus)
-function s:compiler.pushms(status)
-    call add(self.msgs.statuses, a:status)
-    return self
-endfunction
-"▶1 popms          :: &self
-function s:compiler.popms()
-    call remove(self.msgs.statuses, -1)
-    return self
-endfunction
-"▶1 addthrow       :: msg::String, msgarg, needcurarg, ... + self → self + self
-function s:compiler.addthrow(msg, needcurarg, ...)
-    let args=self.string(a:msg).', '
-    if a:needcurarg
-        let args.=self.string(a:1).', '.self.argstr(1).', '.
-                    \join(a:000[1:], ', ')
-    else
-        let args.=join(a:000, ', ')
-    endif
-    if !empty(self.msgs.own) && !empty(self.msgs.own[-1])
-        let pargs=self.msgs.own[-1]
-    endif
-    let msgstatus=self.msgs.statuses[-1]
-    if msgstatus is 'return'
-        if exists('pargs')
-            call self.call('@%@.p._f.warn('.pargs.')')
-        endif
-        call self.call('@%@.F.warn('.args.')')
-    elseif msgstatus is 'throw'
-        if exists('pargs')
-            call self.call('add(@$@pmessages, ['.pargs.'])')
-        endif
-        call self.call('add(@$@messages, ['.args.'])')
-    elseif msgstatus is 'throwignore'
-        " Ignore and fail
-    endif
-    return self.fail()
-endfunction
-"▶1 nextthrow      :: condition::expr, throwargs + self → self + self
-function s:compiler.nextthrow(cond, ...)
-    return call(self.addif(a:cond).addthrow, [a:1, 1]+a:000[1:], self)
-endfunction
-"▶1 addsavemsgs    :: &self
-function s:compiler.addsavemsgs()
-    if self.msgs.statuses==#['return']
-        call add(self.msgs.savevars, [0, 0])
-        return self
-    else
-        let msglenstr=self.getlvarid('msglen')
-        let pmsglenstr=self.getlvarid('pmsglen')
-        call add(self.msgs.savevars, [msglenstr, pmsglenstr])
-        return   self.let(msglenstr,  'len(@$@messages)')
-                    \.let(pmsglenstr, 'len(@$@pmessages)')
-    endif
-endfunction
-"▶1 addrestmsgs    :: a:0::Bool + self → self + self
-function s:compiler.addrestmsgs(...)
-    let [msglenstr, pmsglenstr]=self.msgs.savevars[-1]
-    if !a:0
-        call remove(self.msgs.savevars, -1)
-    endif
-    return   self.if('len(@$@messages)>'.msglenstr)
-                    \.call('remove(@$@messages, '.msglenstr.', -1)')
-                \.up().if('len(@$@pmessages)>'.pmsglenstr)
-                    \.call('remove(@$@pmessages, '.pmsglenstr.', -1)')
-                \.endif()
-endfunction
-"▶1 witharg        :: (argbase[, [subscript]]) + self → self + self
-function s:compiler.witharg(newarg)
-    call add(self.preva, [self.argbase, self.subs])
-    let self.argbase=a:newarg[0]
-    let self.subs=get(a:newarg, 1, [])
-    return self
-endfunction
-"▶1 without        :: &self
-function s:compiler.without()
-    let [self.argbase, self.subs]=remove(self.preva, -1)
-    return self
-endfunction
-"▶1 getfunc        :: funccontext, split[, addarg, ...] + self → String
-function s:compiler.getfunc(func, split, ...)
-    if a:split
-        let r=[self.getvar(a:func[1])]
-    else
-        let r=self.getvar(a:func[1]).'('
-    endif
-    let args=[]
-    let added000=0
-    for arg in a:func[2:]
-        if a:0 && arg[0] is 'this'
-            let args+=a:000
-            let added000=1
-        else
-            call add(args, self.getvar(arg))
-        endif
-    endfor
-    if a:0 && !added000
-        let args+=a:000
-    endif
-    if a:split
-        let r+=args
-    else
-        let r.=join(args, ', ').')'
-    endif
-    return r
-endfunction
-"▶1 getfunstatvar  :: varname, varinit + self → varstr + self
-function s:compiler.getfunstatvar(name, init)
-    let id=printf('%x', self.vids[a:name])
-    let self.vids[a:name]+=1
-    if !has_key(self.vars, a:name)
-        let self.vars[a:name]={}
-    endif
-    let self.vars[a:name][id]=a:init
-    return '@%@'.self.getsubs([a:name, id])
-endfunction
-"▶1 getmatcher     :: matchercontext, ldstr, strstr + self → String
-function s:compiler.getmatcher(matcher, ldstr, strstr)
-    let mname=a:matcher[1][1]
-    if !has_key(s:_r.FWC_intfuncs[mname], 'matcher')
-        call self._throw('umatcher', mname)
-    endif
-    let r=self.getfunstatvar('matchers', s:_r.FWC_intfuncs[mname].matcher)
-                \.'('.a:ldstr.', '.a:strstr
-    if len(a:matcher[1])>3
-        let curargstr=self.argstr()
-        let r.=', '.join(map(a:matcher[1][2:-2],
-                    \        'type(v:val)=='.type([]).'?'.
-                    \           'self.getvar(v:val, 0, a:ldstr, a:strstr):'.
-                    \           'self.string(v:val)'),
-                    \    ', ')
-    endif
-    if self.type is 'check' || self.type is 'filter'
-        let r.=', '.a:matcher[1][-1].')'
-    else
-        let r.=', 2)'
-    endif
-    return r
-endfunction
-"▶1 getexpr        :: exprcontext[, curstr] + self → String
-function s:compiler.getexpr(expr, ...)
-    let curargstr=self.argstr()
-    let this=get(a:000, 0, curargstr)
-    return substitute(substitute(substitute(a:expr[1],
-                \'\V@@@', escape(self.argbase, '&~\'), 'g'),
-                \'\V@.@', escape(this,         '&~\'), 'g'),
-                \'\V@:@', escape(curargstr,    '&~\'), 'g')
-endfunction
-"▶1 getvar         :: varcontext[, splitsubs[, dotarg]] + self → String
-function s:compiler.getvar(var, ...)
-    let r=[]
-    let splitsubs=get(a:000, 0, 0)
-    if a:var[0] is 'plugvar'
-        let r=['@%@.p.'.a:var[1], a:var[2:]]
-    elseif a:var[0] is 'expr'
-        let r=[self.getexpr(a:var)]
-    elseif a:var[0] is 'string'
-        let r=[self.string(a:var[1])]
-    elseif a:var[0] is 'argument'
-        let r=[self.argbase, [self.subs[0]+a:var[1]]+a:var[2:]]
-    elseif a:var[0] is 'cur'
-        let r=[self.argbase, self.subs[:-1-a:var[1]]+a:var[2:]]
-    elseif a:var[0] is 'list'
-        let r=['['.join(map(a:var[1:], 'type(v:val)=='.type('').'?'.
-                    \                       'self.string(v:val):'.
-                    \                       'self.getvar(v:val)'), ', ').']']
-    elseif a:var[0] is 'evaluate'
-        let r=[eval(substitute(self.getvar(a:var[1]), '@%@', 'self.vars', 'g'))]
-        if type(r[0])!=type('')
-            let r[0]=self.string(r[0])
-        endif
-    elseif a:var[0] is 'func'
-        let r=[call(self.getfunc, [a:var, 0]+a:000[1:], self)]
-    elseif a:var[0] is 'this'
-        let r=[self.argbase, self.subs]
-    endif
-    return ((splitsubs)?(r):(r[0].((len(r)>1)?(self.getsubs(r[1])):(''))))
-endfunction
-"▶1 getlvarid      :: varname + self → varname
-function s:compiler.getlvarid(v)
-    return printf('@$@%s%X', a:v, len(self.stack))
-endfunction
-"▶1 compilemsg     :: msgcontext, _ + self → self + self
-function s:compiler.compilemsg(msg, idx)
-    if a:msg[1] is 0
-        call add(self.msgs.own, '')
-        return self
-    elseif a:msg[1] is 1
-        if !empty(self.msgs.own)
-            call remove(self.msgs.own, -1)
-        endif
-        return self
-    endif
-    let msg=[]
-    let curargstrstr=self.argstr(1)
-    for msgarg in a:msg[2:]
-        if msgarg[0] is 'curval'
-            call add(msg, self.argstr())
-        elseif msgarg[0] is 'curarg'
-            call add(msg, curargstrstr)
-        elseif msgarg[0] is 'curidx'
-            call add(msg, self.string(idx))
-        else
-            call add(msg, substitute(self.getvar(msgarg), '@#@',
-                        \            escape(curargstrstr, '\&~'), 'g'))
-        endif
-    endfor
-    let msgstr=self.string(a:msg[1]).', '.join(msg, ', ')
-    call add(self.msgs.own, msgstr)
-    return self
-endfunction
-"▶1 compilestring  :: {string} + self → self + self
-function s:compiler.compilestring(str)
-    if type(a:str)==type('')
-        return self.string(a:str)
-    endif
-    return self.getvar(a:str)
-endfunction
-"▶1 compilepipe    :: pipecontext, idx + self → self + self
-function s:compiler.compilepipe(pipe, idx)
-    let curargstr=self.argstr()
-    "▶2 `func' pipe
-    if a:pipe[1][0] is 'func'
-        call self.let(curargstr, self.getfunc(a:pipe[1],0, curargstr))
-        let self.typechanged=1
-    "▶2 `expr' pipe
-    elseif a:pipe[1][0] is 'expr'
-        call self.let(curargstr, self.getexpr(a:pipe[1], curargstr))
-        let self.typechanged=1
-    "▶2 Built-in pipes
-    elseif a:pipe[1][0] is 'intfunc'
-        let desc=a:pipe[1][1:]
-        if has_key(s:_r.FWC_intfuncs[desc[0]], 'pipe')
-            call call(s:_r.FWC_intfuncs[desc[0]].pipe, [desc,a:idx,'pipe'],self)
-        else
-            call call(s:_r.FWC_intfuncs[desc[0]].check,[desc,a:idx,'pipe'],self)
-        endif
-        if has_key(s:_r.FWC_intfuncs[desc[0]], 'optimize')
-            call call(s:_r.FWC_intfuncs[desc[0]].optimize, [a:idx], self)
-        endif
-    endif
-    "▲2
-    return self
-endfunction
-let s:compiler.compilefilter=s:compiler.compilepipe
-"▶1 addpathp       :: idx + self → self + self
-function s:compiler.addpathp(idx)
-    let curargstr=self.argstr()
-    let dirnamestr=self.getlvarid('dirname')
-    let prevdirstr=self.getlvarid('prevdir')
-    let foundstr=self.getlvarid('found')
-    call self.addif('!@%@.os.path.exists('.curargstr.')')
-                \.let(dirnamestr, '@%@.os.path.normpath('.curargstr.')')
-                \.let(prevdirstr, '""')
-                \.let(foundstr, 0)
-                \.while(dirnamestr.' isnot '.prevdirstr)
-                    \.addif('filewritable('.dirnamestr.')==2')
-                        \.let(foundstr, 1)
-                        \.break().up()
-                    \.addif('@%@.os.path.exists('.dirnamestr.')')
-                        \.break().up()
-                    \.let(prevdirstr, dirnamestr)
-                    \.let(dirnamestr, '@%@.os.path.dirname('.dirnamestr.')')
-                \.up()
-                \.nextthrow('!'.foundstr, 'nowrite', a:idx, curargstr)
-            \.up().up()
-    return self
-endfunction
-"▶1 addtypecond    :: types, idx + self → self + self
-function s:compiler.addtypecond(types, idx)
-    if self.o.onlystrings && self.argbase is '@@@'
-        if index(types, type(''))==-1
-            call self.nextthrow(1, 'nostrings', a:idx)
-        endif
-        return self
-    endif
-    let curargstr=self.argstr()
-    let typenames=map(copy(a:types), 's:_messages._types[v:val]')
-    if len(a:types)>=2
-        call self.nextthrow('index('.string(a:types).', '.
-                    \            'type('.curargstr.'))==-1',
-                    \       'typesfail', a:idx, string(join(typenames, '/')),
-                    \                    '@%@.m.types[type('.curargstr.')]')
-    elseif len(a:types)==1
-        call self.nextthrow('type('.curargstr.')!='.a:types[0],
-                    \       'typefail', a:idx, string(typenames[0]),
-                    \                   '@%@.m.types[type('.curargstr.')]')
-    else
-        call self._throw('notypes')
-    endif
-    return self
-endfunction
-"▶1 incidx         :: idx → idx
-function s:F.incidx(idx)
-    let subidxs=split(a:idx, '\V.')
-    let subidxs[-1]+=1
-    return join(subidxs, '.')
-endfunction
-"▶1 compilecheck   :: checkcontext, idx + self → self + self
-function s:compiler.compilecheck(check, idx)
-    let curargstr=self.argstr()
-    let check=a:check[1]
-    "▶2 `func' check
-    if check[0] is 'func'
-        call self.nextthrow(self.getfunc(check, 0, curargstr).' is 0',
-                    \       'funcfail', a:idx)
-    "▶2 `expr' check
-    elseif check[0] is 'expr'
-        call self.nextthrow(self.getexpr(check, curargstr).' is 0',
-                    \       'exprfail', a:idx)
-    "▶2 Built-in checks
-    else
-        let desc=check[1:]
-        let dname=desc[0]
-        if !has_key(s:_r.FWC_intfuncs[dname], 'check')
-            call self._throw('ucheck', dname)
-        endif
-        call call(s:_r.FWC_intfuncs[dname].check, [desc, a:idx, 'check'], self)
-        if has_key(s:_r.FWC_intfuncs[dname], 'optimize')
-            call call(s:_r.FWC_intfuncs[dname].optimize, [a:idx], self)
-        endif
-    endif
-    "▲2
-    return self
-endfunction
-"▶1 compilearg     :: argcontext, idx[, defcomptype] + self → self + self
-function s:compiler.compilearg(argcon, idx, ...)
-    let type=get(a:000, 0, self.type)
-    if a:argcon[0] is 'arg'
-        let arg=a:argcon[1:]
-    else
-        let arg=a:argcon
-    endif
-    let pmsgnum=len(self.msgs.own)
-    let msg=[]
-    let i=0
-    let savedonlystrings=self.o.onlystrings
-    for proc in arg
-        let i+=1
-        let compargs=[proc, a:idx.'.'.i]
-        let comptype=proc[0]
-        if comptype is 'intfunc'
-            let comptype=type
-            let compargs[0]=[comptype, compargs[0]]
-        elseif comptype is 'defval'
-            continue
-        endif
-        call call(self['compile'.comptype], compargs, self)
-        if self.typechanged
-            let self.o.onlystrings=0
-            let self.typechanged=0
-        endif
-    endfor
-    let self.o.onlystrings=savedonlystrings
-    if len(self.msgs.own)>pmsgnum
-        call remove(self.msgs.own, pmsgnum, -1)
-    endif
-    return self
-endfunction
 "▶1 getlenrange    :: adescr → (minlen, maxlen) + adescr
 function s:F.getlenrange(adescr)
     let minimum=0
     let a:adescr.maximum=maximum
     return [minimum, maximum]
 endfunction
+"▶1 fail           :: &self
+function s:compiler.fail()
+    let msgstatus=self.msgs.statuses[-1]
+    if msgstatus is 'return'
+        return self[self.failcal[0]](self.failcal[1]).up()
+    else
+        return self.throw(s:cfstr)
+    endif
+endfunction
+"▶1 pushms         :: &self(msgstatus)
+function s:compiler.pushms(status)
+    call add(self.msgs.statuses, a:status)
+    return self
+endfunction
+"▶1 popms          :: &self
+function s:compiler.popms()
+    call remove(self.msgs.statuses, -1)
+    return self
+endfunction
+"▶1 witharg        :: (argbase[, [subscript]]) + self → self + self
+function s:compiler.witharg(newarg)
+    call add(self.preva, [self.argbase, self.subs])
+    let self.argbase=a:newarg[0]
+    let self.subs=get(a:newarg, 1, [])
+    return self
+endfunction
+"▶1 without        :: &self
+function s:compiler.without()
+    let [self.argbase, self.subs]=remove(self.preva, -1)
+    return self
+endfunction
+"▶1 incrementsub   :: subscript, incby → subscript
+function s:F.incrementsub(sub, incby)
+    if type(a:sub)==type(0)
+        return a:sub+a:incby
+    elseif type(a:sub)==type([]) && len(a:sub)==1
+        let num=matchstr(a:sub[0], '[+-]\d\+$')
+        return [printf('%s%+i', a:sub[0][:-1-len(num)],
+                    \           str2nr(num)+a:incby)]
+    endif
+endfunction
+"▶1 incsub         :: &self([incby])
+function s:compiler.incsub(...)
+    if empty(self.subs)
+        return self
+    else
+        let self.subs[-1]=s:F.incrementsub(self.subs[-1], get(a:000, 0, 1))
+    endif
+    return self
+endfunction
+"▶1 argstr         :: [genString::Bool, [subscript]] + self → String
+"▶2 addargchunk    :: chunks, chunk::String, literal::Bool → _ + chunks
+function s:F.addargchunk(chunks, chunk, literal)
+    if a:literal==(len(a:chunks)%2)
+        call add(a:chunks, a:chunk)
+    else
+        let a:chunks[-1].=a:chunk
+    endif
+endfunction
+"▶2 addstrsub      :: chunks, sub, self → _ + chunks
+function s:F.addstrsub(chunks, sub, self)
+    if type(a:sub)==type([])
+        call s:F.addargchunk(a:chunks, 'string('.a:self.getvar(a:sub).')', 1)
+    elseif type(a:sub)==type('')
+        call s:F.addargchunk(a:chunks, 'string('.a:sub.')', 1)
+    else
+        call s:F.addargchunk(a:chunks, a:sub, 0)
+    endif
+endfunction
+"▲2
+function s:compiler.argstr(...)
+    if get(a:000, 0, 0)
+        let chunks=[self.argbase]
+        for sub in get(a:000, 1, self.subs)
+            let tsub=type(sub)
+            if tsub==type('')
+                if sub=~#'^\w\+$'
+                    call s:F.addargchunk(chunks, '.'.sub, 0)
+                else
+                    call s:F.addargchunk(chunks, '['.self.string(sub).']', 0)
+                endif
+            elseif tsub==type(0)
+                call s:F.addargchunk(chunks, '['.sub.']', 0)
+            else
+                call s:F.addargchunk(chunks, '[', 0)
+                call s:F.addstrsub(chunks, sub[0], self)
+                if len(sub)>1
+                    call s:F.addargchunk(chunks, ':', 0)
+                    call s:F.addstrsub(chunks, sub[1], self)
+                endif
+                call s:F.addargchunk(chunks, ']', 0)
+            endif
+            unlet sub
+        endfor
+        return join(map(chunks, 'v:key%2 ? v:val : self.string(v:val)'), '.')
+    else
+        return self.argbase.(self.getsubs(get(a:000, 1, self.subs)))
+    endif
+endfunction
+"▶1 getsub         :: subscript → string
+function s:compiler.getsub(subscript)
+    return ((type(a:subscript)==type([]))?(a:subscript[0]):(a:subscript))
+endfunction
+"▶1 getsubs        :: [subscript] + self → String
+function s:compiler.getsubs(subscripts)
+    let r=''
+    for sub in a:subscripts
+        let tsub=type(sub)
+        if tsub==type('')
+            let r.=((sub=~#'\W')?('['.self.string(sub).']'):
+                        \        ('.'.sub))
+        elseif tsub==type(0)
+            let r.='['.sub.']'
+        elseif tsub==type([])
+            let r.='['.join(map(copy(sub), 'type(v:val)=='.type([]).'?'.
+                        \                       'self.getvar(v:val):'.
+                        \                       'v:val'), ':').']'
+        endif
+        unlet sub
+    endfor
+    return r
+endfunction
+"▶1 getfunc        :: funccontext, split[, addarg, ...] + self → String
+function s:compiler.getfunc(func, split, ...)
+    if a:split
+        let r=[self.getvar(a:func[1])]
+    else
+        let r=self.getvar(a:func[1]).'('
+    endif
+    let args=[]
+    let added000=0
+    for arg in a:func[2:]
+        if a:0 && arg[0] is 'this'
+            let args+=a:000
+            let added000=1
+        else
+            call add(args, self.getvar(arg))
+        endif
+    endfor
+    if a:0 && !added000
+        let args+=a:000
+    endif
+    if a:split
+        let r+=args
+    else
+        let r.=join(args, ', ').')'
+    endif
+    return r
+endfunction
+"▶1 getmatcher     :: matchercontext, ldstr, strstr + self → String
+function s:compiler.getmatcher(matcher, ldstr, strstr)
+    let mname=a:matcher[1][1]
+    if !has_key(s:_r.FWC_intfuncs[mname], 'matcher')
+        call self._throw('umatcher', mname)
+    endif
+    let r=self.getfunstatvar('matchers', s:_r.FWC_intfuncs[mname].matcher)
+                \.'('.a:ldstr.', '.a:strstr
+    if len(a:matcher[1])>3
+        let curargstr=self.argstr()
+        let r.=', '.join(map(a:matcher[1][2:-2],
+                    \        'type(v:val)=='.type([]).'?'.
+                    \           'self.getvar(v:val, 0, a:ldstr, a:strstr):'.
+                    \           'self.string(v:val)'),
+                    \    ', ')
+    endif
+    if self.type is 'check' || self.type is 'filter'
+        let r.=', '.a:matcher[1][-1].')'
+    else
+        let r.=', 2)'
+    endif
+    return r
+endfunction
+"▶1 getexpr        :: exprcontext[, curstr] + self → String
+function s:compiler.getexpr(expr, ...)
+    let curargstr=self.argstr()
+    let this=get(a:000, 0, curargstr)
+    return substitute(substitute(substitute(a:expr[1],
+                \'\V@@@', escape(self.argbase, '&~\'), 'g'),
+                \'\V@.@', escape(this,         '&~\'), 'g'),
+                \'\V@:@', escape(curargstr,    '&~\'), 'g')
+endfunction
+"▶1 getvar         :: varcontext[, splitsubs[, dotarg]] + self → String
+function s:compiler.getvar(var, ...)
+    let r=[]
+    let splitsubs=get(a:000, 0, 0)
+    if a:var[0] is 'plugvar'
+        let r=['@%@.p.'.a:var[1], a:var[2:]]
+    elseif a:var[0] is 'expr'
+        let r=[self.getexpr(a:var)]
+    elseif a:var[0] is 'string'
+        let r=[self.string(a:var[1])]
+    elseif a:var[0] is 'argument'
+        let r=[self.argbase, [self.subs[0]+a:var[1]]+a:var[2:]]
+    elseif a:var[0] is 'cur'
+        let r=[self.argbase, self.subs[:-1-a:var[1]]+a:var[2:]]
+    elseif a:var[0] is 'list'
+        let r=['['.join(map(a:var[1:], 'type(v:val)=='.type('').'?'.
+                    \                       'self.string(v:val):'.
+                    \                       'self.getvar(v:val)'), ', ').']']
+    elseif a:var[0] is 'evaluate'
+        let r=[eval(substitute(self.getvar(a:var[1]), '@%@', 'self.vars', 'g'))]
+        if type(r[0])!=type('')
+            let r[0]=self.string(r[0])
+        endif
+    elseif a:var[0] is 'func'
+        let r=[call(self.getfunc, [a:var, 0]+a:000[1:], self)]
+    elseif a:var[0] is 'this'
+        let r=[self.argbase, self.subs]
+    endif
+    return ((splitsubs)?(r):(r[0].((len(r)>1)?(self.getsubs(r[1])):(''))))
+endfunction
+"▶1 getfunstatvar  :: varname, varinit + self → varstr + self
+function s:compiler.getfunstatvar(name, init)
+    let id=printf('%x', self.vids[a:name])
+    let self.vids[a:name]+=1
+    if !has_key(self.vars, a:name)
+        let self.vars[a:name]={}
+    endif
+    let self.vars[a:name][id]=a:init
+    return '@%@'.self.getsubs([a:name, id])
+endfunction
+"▶1 getlvarid      :: varname + self → varname
+function s:compiler.getlvarid(v)
+    return printf('@$@%s%X', a:v, len(self.stack))
+endfunction
+"▶1 getd           :: varname + self → varname + self
+function s:compiler.getd(var)
+    if !has_key(self.o, 'requiresd')
+        let self.o.requiresd=1
+    endif
+    return self.getlvarid('d.'.a:var)
+endfunction
 "▶1 getlargsstr    :: () + self → varname + self
 function s:compiler.getlargsstr()
     let key=string([self.argbase]+self.subs[:-2])[1:-2]
         return largsstr
     endif
 endfunction
-"▶1 getsub         :: subscript → string
-function s:F.getsub(subscript)
-    return ((type(a:subscript)==type([]))?(a:subscript[0]):(a:subscript))
+"▶1 getstring      :: {string} + self → self + self
+function s:compiler.getstring(str)
+    if type(a:str)==type('')
+        return self.string(a:str)
+    endif
+    return self.getvar(a:str)
+endfunction
+"▶1 addthrow       :: msg::String, msgarg, needcurarg, ... + self → self + self
+function s:compiler.addthrow(msg, needcurarg, ...)
+    let args=self.string(a:msg).', '
+    if a:needcurarg
+        let args.=self.string(a:1).', '.self.argstr(1).', '.
+                    \join(a:000[1:], ', ')
+    else
+        let args.=join(a:000, ', ')
+    endif
+    if !empty(self.msgs.own) && !empty(self.msgs.own[-1])
+        let pargs=self.msgs.own[-1]
+    endif
+    let msgstatus=self.msgs.statuses[-1]
+    if msgstatus is 'return'
+        if exists('pargs')
+            call self.call('@%@.p._f.warn('.pargs.')')
+        endif
+        call self.call('@%@.F.warn('.args.')')
+    elseif msgstatus is 'throw'
+        if exists('pargs')
+            call self.call('add(@$@pmessages, ['.pargs.'])')
+        endif
+        call self.call('add(@$@messages, ['.args.'])')
+    elseif msgstatus is 'throwignore'
+        " Ignore and fail
+    endif
+    return self.fail()
+endfunction
+"▶1 nextthrow      :: condition::expr, throwargs + self → self + self
+function s:compiler.nextthrow(cond, ...)
+    return call(self.addif(a:cond).addthrow, [a:1, 1]+a:000[1:], self)
+endfunction
+"▶1 addsavemsgs    :: &self
+function s:compiler.addsavemsgs()
+    if self.msgs.statuses==#['return']
+        call add(self.msgs.savevars, [0, 0])
+        return self
+    else
+        let msglenstr=self.getlvarid('msglen')
+        let pmsglenstr=self.getlvarid('pmsglen')
+        call add(self.msgs.savevars, [msglenstr, pmsglenstr])
+        return   self.let(msglenstr,  'len(@$@messages)')
+                    \.let(pmsglenstr, 'len(@$@pmessages)')
+    endif
+endfunction
+"▶1 addrestmsgs    :: a:0::Bool + self → self + self
+function s:compiler.addrestmsgs(...)
+    let [msglenstr, pmsglenstr]=self.msgs.savevars[-1]
+    if !a:0
+        call remove(self.msgs.savevars, -1)
+    endif
+    return   self.if('len(@$@messages)>'.msglenstr)
+                    \.call('remove(@$@messages, '.msglenstr.', -1)')
+                \.up().if('len(@$@pmessages)>'.pmsglenstr)
+                    \.call('remove(@$@pmessages, '.pmsglenstr.', -1)')
+                \.endif()
+endfunction
+"▶1 addtypecond    :: types, idx + self → self + self
+function s:compiler.addtypecond(types, idx)
+    if self.o.onlystrings && self.argbase is '@@@'
+        if index(types, type(''))==-1
+            call self.nextthrow(1, 'nostrings', a:idx)
+        endif
+        return self
+    endif
+    let curargstr=self.argstr()
+    let typenames=map(copy(a:types), 's:_messages._types[v:val]')
+    if len(a:types)>=2
+        call self.nextthrow('index('.string(a:types).', '.
+                    \            'type('.curargstr.'))==-1',
+                    \       'typesfail', a:idx, string(join(typenames, '/')),
+                    \                    '@%@.m.types[type('.curargstr.')]')
+    elseif len(a:types)==1
+        call self.nextthrow('type('.curargstr.')!='.a:types[0],
+                    \       'typefail', a:idx, string(typenames[0]),
+                    \                   '@%@.m.types[type('.curargstr.')]')
+    else
+        call self._throw('notypes')
+    endif
+    return self
 endfunction
 "▶1 addlencheck    :: minlen, maxlen + self → self + self
 function s:compiler.addlencheck(minimum, maximum)
     let largsstr=self.getlargsstr()
-    let minimum=s:F.getsub(s:F.incrementsub(self.subs[-1], a:minimum))
-    let maximum=s:F.getsub(s:F.incrementsub(self.subs[-1], a:maximum))
+    let minimum=self.getsub(s:F.incrementsub(self.subs[-1], a:minimum))
+    let maximum=self.getsub(s:F.incrementsub(self.subs[-1], a:maximum))
     if a:maximum==a:minimum
         call self.addif(largsstr.' isnot '.maximum)
                     \.addthrow('invlen', 0, minimum, largsstr)
     endif
     return self
 endfunction
-"▶1 getd           :: varname + self → varname + self
-function s:compiler.getd(var)
-    if !has_key(self.o, 'requiresd')
-        let self.o.requiresd=1
+"▶1 compilemsg     :: msgcontext, _ + self → self + self
+function s:compiler.compilemsg(msg, idx)
+    if a:msg[1] is 0
+        call add(self.msgs.own, '')
+        return self
+    elseif a:msg[1] is 1
+        if !empty(self.msgs.own)
+            call remove(self.msgs.own, -1)
+        endif
+        return self
     endif
-    return self.getlvarid('d.'.a:var)
+    let msg=[]
+    let curargstrstr=self.argstr(1)
+    for msgarg in a:msg[2:]
+        if msgarg[0] is 'curval'
+            call add(msg, self.argstr())
+        elseif msgarg[0] is 'curarg'
+            call add(msg, curargstrstr)
+        elseif msgarg[0] is 'curidx'
+            call add(msg, self.string(idx))
+        else
+            call add(msg, substitute(self.getvar(msgarg), '@#@',
+                        \            escape(curargstrstr, '\&~'), 'g'))
+        endif
+    endfor
+    let msgstr=self.string(a:msg[1]).', '.join(msg, ', ')
+    call add(self.msgs.own, msgstr)
+    return self
 endfunction
-"▶1 addprefix      :: preflist, prefix → _ + preflist
-function s:compiler.addprefix(preflist, prefix)
-    if index(a:preflist, a:prefix)!=-1
-        call self._throw('ambprefix', a:prefix)
-    endif
-    call add(a:preflist, a:prefix)
-endfunction
-"▶1 compoptional   :: adescr, idx, {rolvars}, {lvars} + self → {lvars}
-" {rolvars} :: caidxstr, largsstr, purgemax
-"   {lvars} :: nextsub, addedsavemsgs
-function s:compiler.compoptional(adescr, idx, caidxstr, largsstr, purgemax,
-            \                    nextsub, addedsavemsgs)
-    let addedsavemsgs=a:addedsavemsgs
-    " XXX nodefs will be still 0 when compiling next adescr. It is intentional.
-    let nodefs=empty(self.defvals)
-    let lopt=len(a:adescr.optional)
-    if lopt>1
-        let failstr=self.getlvarid('fail')
-        call self.let(failstr, 1)
-    endif
-    let addedsavemsgs=1
-    call self.addsavemsgs()
-                \.try()
-                    \.pushms('throw')
-                    \.compileadesc(a:adescr.optional[0],
-                    \              a:idx.'.0(optional)',
-                    \              (a:purgemax || a:adescr.maximum==-1))
-                    \.popms()
-                    \.addrestmsgs(1)
-    if lopt>1
-        call self.let(failstr, 0)
-    endif
-    let newsub=s:F.getsub(self.subs[-1])
-    if newsub isnot a:caidxstr
-        call self.let(a:caidxstr, newsub)
-    endif
-    call self.catch(s:cfreg)
-    if lopt==1 && has_key(a:adescr.optional[0], 'arg')
-        let defaults=reverse(map(filter(copy(a:adescr.optional[0].arg),
-                    \                   'exists("v:val[0][0]") && '.
-                    \                   'v:val[0][0] is "defval"'),
-                    \            'v:val[0][1]'))
-        let self.defvals+=defaults
-        if !empty(defaults) && nodefs
-            let base=self.argstr(0, self.subs[:-2])
-            for defvar in self.defvals
-                call self.call('insert('.base.', '.self.getvar(defvar).', '.
-                            \            a:caidxstr.')')
-            endfor
-            let ldefaults=len(self.defvals)
-            call     self.increment(a:caidxstr, ldefaults)
-                        \.increment(a:largsstr, ldefaults)
+"▶1 compilepipe    :: pipecontext, idx + self → self + self
+function s:compiler.compilepipe(pipe, idx)
+    let curargstr=self.argstr()
+    "▶2 `func' pipe
+    if a:pipe[1][0] is 'func'
+        call self.let(curargstr, self.getfunc(a:pipe[1],0, curargstr))
+        let self.typechanged=1
+    "▶2 `expr' pipe
+    elseif a:pipe[1][0] is 'expr'
+        call self.let(curargstr, self.getexpr(a:pipe[1], curargstr))
+        let self.typechanged=1
+    "▶2 Built-in pipes
+    elseif a:pipe[1][0] is 'intfunc'
+        let desc=a:pipe[1][1:]
+        if has_key(s:_r.FWC_intfuncs[desc[0]], 'pipe')
+            call call(s:_r.FWC_intfuncs[desc[0]].pipe, [desc,a:idx,'pipe'],self)
+        else
+            call call(s:_r.FWC_intfuncs[desc[0]].check,[desc,a:idx,'pipe'],self)
+        endif
+        if has_key(s:_r.FWC_intfuncs[desc[0]], 'optimize')
+            call call(s:_r.FWC_intfuncs[desc[0]].optimize, [a:idx], self)
         endif
     endif
-    call self.up()
-    let self.subs[-1]=[a:caidxstr]
-    if lopt>1
-        let i=1
-        for opt in a:adescr.optional[1:]
-            call self.if(failstr)
-                        \.try()
-                            \.pushms('throw')
-                            \.compileadesc(opt, a:idx.'.'.i.'(optional)')
-                            \.popms()
-                            \.addrestmsgs(1)
-                            \.let(failstr, 0)
-            let newsub=s:F.getsub(self.subs[-1])
-            if newsub isnot a:caidxstr
-                call self.let(a:caidxstr, newsub)
-            endif
-            call self.catch(s:cfreg)
-                \.endif()
-            let self.subs[-1]=[a:caidxstr]
-            let i+=1
-        endfor
-    endif
-    return [[a:caidxstr], addedsavemsgs]
+    "▲2
+    return self
 endfunction
-"▶1 compprefixes   :: {comp*}
-function s:compiler.compprefixes(adescr, idx, caidxstr, largsstr, purgemax,
-            \                    nextsub, addedsavemsgs)
-    "▶2 Define variables
-    let addedsavemsgs=a:addedsavemsgs
-    let nextsub=[a:caidxstr]
-    let preflist=[]
-    let plstr=self.getfunstatvar('prefixes', preflist)
-    let prefdictstr=self.getlvarid('prefdict')
-    let base=self.argstr(0, self.subs[:-2])
-    let astr=self.getlvarid('arg')
-    let idx=a:idx.'(prefixes)'
-    let defaults=filter(copy(a:adescr.prefixes),
-                \       'exists("v:val[2][0]") && v:val[2][0] isnot "arg"')
-    let lists=filter(copy(a:adescr.prefixes), 'v:val[1].list')
-    let haslist=!empty(lists)
-    let lastliststr=self.getlvarid('lastlist')
-    let addtry=(has_key(a:adescr, 'next') || has_key(a:adescr, 'actions'))
-    "▶2 Add messages saving if required
-    if addtry
-        if !addedsavemsgs
-            call self.addsavemsgs()
-            let addedsavemsgs=1
+let s:compiler.compilefilter=s:compiler.compilepipe
+"▶1 compilecheck   :: checkcontext, idx + self → self + self
+function s:compiler.compilecheck(check, idx)
+    let curargstr=self.argstr()
+    let check=a:check[1]
+    "▶2 `func' check
+    if check[0] is 'func'
+        call self.nextthrow(self.getfunc(check, 0, curargstr).' is 0',
+                    \       'funcfail', a:idx)
+    "▶2 `expr' check
+    elseif check[0] is 'expr'
+        call self.nextthrow(self.getexpr(check, curargstr).' is 0',
+                    \       'exprfail', a:idx)
+    "▶2 Built-in checks
+    else
+        let desc=check[1:]
+        let dname=desc[0]
+        if !has_key(s:_r.FWC_intfuncs[dname], 'check')
+            call self._throw('ucheck', dname)
         endif
-        call self.try().pushms('throw')
-    endif
-    "▶2 Initialize variables inside constructed function
-    call self.let(prefdictstr, '{}')
-                \.call('insert('.base.', '.prefdictstr.', '.
-                \                s:F.getsub(nextsub).')')
-                \.increment(a:caidxstr)
-                \.increment(a:largsstr)
-    "▶2 Add default values
-    for [prefix, prefopts, defval; dummylist] in defaults
-        call self.let(prefdictstr.self.getsubs([prefix]),
-                    \ self.getvar(defval))
-    endfor
-    "▲2
-    call self.while('len('.base.')>'.a:caidxstr)
-    "▶2 Get `astr' variable
-    if haslist
-        call self.if('type('.self.argstr().')=='.type(''))
-    elseif !self.o.onlystrings
-        call self.addtypecond([type('')], idx)
-    endif
-    call self.let(astr, self.getmatcher(get(a:adescr, 'prefixesmatcher',
-                \                           s:prefdefmatcher), plstr,
-                \                       self.argstr()))
-    let removestr='remove('.base.', '.a:caidxstr.')'
-    if addtry
-        let argorigstr=self.getd('argorig')
-    endif
-    if haslist
-        call self.up().else().let(astr, 0).up().endif()
-                    \.if(astr.' isnot 0')
-    else
-        call self.nextthrow(astr.' is 0', 'pnf', idx, self.argstr())
-    endif
-    call self.increment(a:largsstr, -1)
-    if addtry
-        call self.let(argorigstr, removestr)
-    else
-        call self.call(removestr)
-    endif
-    if haslist
-        call self.up().endif()
+        call call(s:_r.FWC_intfuncs[dname].check, [desc, a:idx, 'check'], self)
+        if has_key(s:_r.FWC_intfuncs[dname], 'optimize')
+            call call(s:_r.FWC_intfuncs[dname].optimize, [a:idx], self)
+        endif
     endif
     "▲2
-    for [prefix, prefopts; args] in a:adescr.prefixes
-        "▶2 Add prefix to prefix list
-        call self.addprefix(preflist, prefix)
-        if prefopts.alt
-            call self.addprefix(preflist, 'no'.prefix)
+    return self
+endfunction
+"▶1 compilearg     :: argcontext, idx[, defcomptype] + self → self + self
+function s:compiler.compilearg(argcon, idx, ...)
+    let type=get(a:000, 0, self.type)
+    if a:argcon[0] is 'arg'
+        let arg=a:argcon[1:]
+    else
+        let arg=a:argcon
+    endif
+    let pmsgnum=len(self.msgs.own)
+    let msg=[]
+    let i=0
+    let savedonlystrings=self.o.onlystrings
+    for proc in arg
+        let i+=1
+        let compargs=[proc, a:idx.'.'.i]
+        let comptype=proc[0]
+        if comptype is 'intfunc'
+            let comptype=type
+            let compargs[0]=[comptype, compargs[0]]
+        elseif comptype is 'defval'
+            continue
         endif
-        "▶2 Remove default value specification if any
-        let hasdefault=0
-        if !empty(args) && args[0][0] isnot 'arg'
-            let hasdefault=1
-            call remove(args, 0)
+        call call(self['compile'.comptype], compargs, self)
+        if self.typechanged
+            let self.o.onlystrings=0
+            let self.typechanged=0
         endif
-        "▲2
-        let prefstr=prefdictstr.self.getsubs([prefix])
-        let prefixstr=self.string(prefix)
-        "▶2 Construct prefix condition
-        let cond=astr.' is '.prefixstr
-        if prefopts.list
-            let cond.=' || ('.astr.' is 0 && '.
-                        \   lastliststr.' is '.prefixstr.')'
-        endif
-        call self.addif(cond)
-        "▶2 Process prefix arguments
-        for i in range(1, prefopts.argnum)
-            call self.compilearg(args[i-1], idx.self.string(prefix))
-                        \.incsub()
-        endfor
-        "▶2 Move prefix arguments to prefix dictionary
-        if prefopts.argnum>0
-            call self.increment(a:largsstr, -prefopts.argnum)
-        endif
-        if prefopts.list
-            let removestr='remove('.base.', '.a:caidxstr.', '.
-                        \           a:caidxstr.'+'.(prefopts.argnum-1).')'
-            let cond='has_key('.prefdictstr.', '.prefixstr.')'
-            if hasdefault
-                let cond.=' && type('.prefstr.')=='.type([])
-            endif
-            call self.if(cond)
-                        \.increment(prefstr, removestr)
-                    \.else()
-                        \.let(prefstr, removestr)
-                    \.up()
-            call self.let(lastliststr, prefixstr)
-        else
-            if haslist
-                call self.let(lastliststr, 0)
-            endif
-            if prefopts.argnum==1
-                call self.let(prefstr, 'remove('.base.', '.a:caidxstr.')')
-            elseif prefopts.argnum>0
-                call self.let(prefstr,
-                            \'remove('.base.', '.a:caidxstr.', '.
-                            \        a:caidxstr.'+'.(prefopts.argnum-1).')')
-            else
-                call self.let(prefstr, 1)
-            endif
-        endif
-        "▲2
-        let self.subs[-1]=[a:caidxstr]
-        call self.up()
-        "▶2 Process `no{prefix}'
-        if prefopts.alt
-            call self.addif(astr.' is '.self.string('no'.prefix))
-                        \.let(prefstr, 0)
-                        \.up()
-        endif
-        "▲2
     endfor
-    call self.up()
-    if addtry
-        call self.unlet(argorigstr).up().up().addrestmsgs(1).popms()
-            \.catch(s:cfreg)
-                \.if('exists('.string(argorigstr).')')
-                    \.call('insert('.base.', '.argorigstr.', '.a:caidxstr.')')
-                    \.increment(a:largsstr)
-                \.up()
-            \.up()
-    else
-        call self.up()
+    let self.o.onlystrings=savedonlystrings
+    if len(self.msgs.own)>pmsgnum
+        call remove(self.msgs.own, pmsgnum, -1)
     endif
-    return [[a:caidxstr], addedsavemsgs]
+    return self
 endfunction
-"▶1 compnext       :: {comp*}
-function s:compiler.compnext(adescr, idx, caidxstr, largsstr, purgemax,
-            \                nextsub, addedsavemsgs)
-    let addedsavemsgs=a:addedsavemsgs
-    if has_key(a:adescr, 'actions')
-        if !addedsavemsgs
-            call self.addsavemsgs()
-            let addedsavemsgs=1
-        endif
-        call self.try().pushms('throw')
-    endif
-    call self.while(a:caidxstr.'<'.a:largsstr)
-                \.compilearg(a:adescr.next[0], a:idx.'.(next)')
-                \.increment(a:caidxstr)
-                \.up()
-    if has_key(a:adescr, 'actions')
-        call self.addrestmsgs(1).catch(s:cfreg).popms().up()
-    endif
-    return [[a:caidxstr], addedsavemsgs]
-endfunction
-"▶1 compactions    :: {comp*}
-function s:compiler.compactions(adescr, idx, caidxstr, largsstr, purgemax,
-            \                   nextsub, addedsavemsgs)
-    let actionstr=self.getlvarid('action')
-    let actions=filter(copy(a:adescr.actions), 'v:val[0] isnot 0')
-    let noact=get(filter(copy(a:adescr.actions), 'v:val[0] is 0'), 0, 0)
-    let fsastr=self.getfunstatvar('actions', map(copy(actions), 'v:val[0]'))
-    if noact isnot 0
-        call self.try().pushms('throw')
-    endif
-    let idx=a:idx.'(actions)'
-    if !self.o.onlystrings
-        call self.addtypecond([type('')], idx)
-    endif
-    let curargstr=self.argstr()
-    call self.let(actionstr, self.getmatcher(get(a:adescr, 'actionsmatcher',
-                \                                s:actdefmatcher),
-                \                            fsastr, curargstr))
-                \.nextthrow(actionstr.' is 0', 'anf', idx, curargstr)
-                \.let(curargstr, actionstr)
-    unlet curargstr
-    call self.incsub()
-    let astartsub=copy(self.subs[-1])
-    for action in actions
-        call self.addif(actionstr.' is '.self.string(action[0]))
-        if len(action)>1
-            call self.compileadesc(action[1],idx.'.'.self.string(action[0]))
-        endif
-        call self.let(a:caidxstr, s:F.getsub(self.subs[-1]))
-                    \.up()
-        let self.subs[-1]=copy(astartsub)
-    endfor
-    if noact isnot 0
-        call self.popms().catch(s:cfreg)
-        call self.incsub(-1)
-        if len(noact)>1
-            call self.compileadesc(noact[1], idx.'.nullact')
-                        \.let(a:caidxstr, s:F.getsub(self.subs[-1]))
-        endif
-        let self.subs[-1]=copy(astartsub)
-        call self.up().up()
-    endif
-    let self.subs[-1]=[a:caidxstr]
-    return [[a:caidxstr], a:addedsavemsgs]
-endfunction
-"▶1 compileadesc   :: adescr[, idx[, purgemax]] + self → self + self
-let s:actdefmatcher=['matcher', ['intfunc', 'start', 0, 0]]
-let s:prefdefmatcher=s:actdefmatcher
-function s:compiler.compileadesc(adescr, ...)
+"▶1 compadescr     :: adescr[, idx[, purgemax]] + self → self + self
+function s:compiler.compadescr(adescr, ...)
     let purgemax=get(a:000, 1, 0)
     "▶2 Length checks
     if !empty(self.subs)
     "▲2
     let addedsavemsgs=0
     let caidxstr=self.getlvarid('caidx')
-    let oldsub=s:F.getsub(nextsub)
+    let oldsub=self.getsub(nextsub)
     if oldsub isnot caidxstr
         call self.let(caidxstr, oldsub)
     endif
     let largsstr=self.getlargsstr()
     let idx=get(a:000, 0, '')
     "▶2 Following keys (optional, prefixes, next, actions)
-    for key in ['optional', 'prefixes', 'next', 'actions']
+    for key in s:_r.FWC_topconstructs._order
         if has_key(a:adescr, key)
             let [newnextsub, addedsavemsgs]=
-                        \call(self['comp'.key],
+                        \call(s:_r.FWC_topconstructs[key].compile,
                         \     [a:adescr, idx, caidxstr, largsstr, purgemax,
                         \      nextsub, addedsavemsgs],
                         \     self)
     " XXX a:0 is checked here
     if !a:0 && type(self.subs[-1])==type([])
         let largsstr=self.getlargsstr()
-        let proclen=s:F.getsub(self.subs[-1])
+        let proclen=self.getsub(self.subs[-1])
         call self.addif(proclen.' isnot '.largsstr)
                     \.addthrow('lennmatch', 0, proclen, largsstr)
     endif
     "▲2
     return self
 endfunction
-"▶1 compilestr     :: vars, String, type, doreturn → [String]
-function s:F.compilestr(vars, string, type, doreturn)
+"▶1 compstr        :: vars, String, type, doreturn → [String]
+function s:F.compstr(vars, string, type, doreturn)
     "▶2 Setup self
     let t=extend(s:_r.new_constructor(), {
                 \   'type': a:type,
             call add(t.subs, 0)
         endif
         if t.type is 'check' || t.type is 'filter'
-            call t.compileadesc(tree[0])
+            call t.compadescr(tree[0])
         else
         endif
         call t.up().finally()
     let s:lastid+=1
     let s:vars[id]=s:F.createvars(a:plugdict)
     call add(a:fdict.ids, id)
-    let [opts, lines]=s:F.compilestr(s:vars[id], a:string, a:type, 1)
+    let [opts, lines]=s:F.compstr(s:vars[id], a:string, a:type, 1)
     if opts.only
         call insert(lines, 'let d={"arg": a:args}')
     elseif has_key(opts, 'requiresd')
     "▲2
     let vars=s:F.createvars(a:plugdict)
     let a='"FWCfail", '.self.string(a:fname).', '.self.string(a:plugdict.id)
-    let [opts, lines]=s:F.compilestr(vars, a:arg[0], a:arg[1], a)
+    let [opts, lines]=s:F.compstr(vars, a:arg[0], a:arg[1], a)
     if opts.only
         call map(lines, 'substitute(v:val, "@@@", "@$@args", "g")')
         call insert(lines, 'let @$@d={"args": @@@}')

plugin/frawor/fwc/intfuncs.vim

                 \((type(a:desc[1][1])==type(''))?
                 \       (self.string(a:desc[1][1])):
                 \       (self.getvar(a:desc[1][1]))).', '.
-                \join(map(a:desc[2:], 'self.compilestring(v:val)'), ', ').')')
+                \join(map(a:desc[2:], 'self.getstring(v:val)'), ', ').')')
 endfunction
 "▶1 `haskey'
 " Checks whether {argument} is a dictionary with given keys
 "▶1 `path'
 " Checks whether {argument} is a path matching given specification
 let s:r.path={'args': ['get']}
+"▶2 addpathp       :: idx + self → self + self
+function s:r.path.addpathp(idx)
+    let curargstr=self.argstr()
+    let dirnamestr=self.getlvarid('dirname')
+    let prevdirstr=self.getlvarid('prevdir')
+    let foundstr=self.getlvarid('found')
+    call self.addif('!@%@.os.path.exists('.curargstr.')')
+                \.let(dirnamestr, '@%@.os.path.normpath('.curargstr.')')
+                \.let(prevdirstr, '""')
+                \.let(foundstr, 0)
+                \.while(dirnamestr.' isnot '.prevdirstr)
+                    \.addif('filewritable('.dirnamestr.')==2')
+                        \.let(foundstr, 1)
+                        \.break().up()
+                    \.addif('@%@.os.path.exists('.dirnamestr.')')
+                        \.break().up()
+                    \.let(prevdirstr, dirnamestr)
+                    \.let(dirnamestr, '@%@.os.path.dirname('.dirnamestr.')')
+                \.up()
+                \.nextthrow('!'.foundstr, 'nowrite', a:idx, curargstr)
+            \.up().up()
+    return self
+endfunction
 "▶2 getpath    :: &self!
 " Gets path specification:
 " Input: [df]? "r"? [wWp]? "x"?
     endif
     return self
 endfunction
-"▲2
+"▶2 check
 function s:r.path.check(desc, idx, type)
     let curargstr=self.argstr()
     call self.addtypecond([type('')], a:idx)
                         \      'nowrite', a:idx, curargstr)
             let spec=spec[1:]
         elseif spec[0] is 'p'
-            call self.addpathp(a:idx)
+            call call(s:r.path.addpathp, [a:idx], self)
                         \.nextthrow('!@%@.os.path.isdir('.
                         \                             curargstr.')',
                         \           'isdir', a:idx, curargstr)
                         \      'nowrite', a:idx, curargstr)
             let spec=spec[1:]
         elseif spec[0] is 'p'
-            call self.addpathp(a:idx)
+            call call(s:r.path.addpathp, [a:idx], self)
             let spec=spec[1:]
         endif
         if spec is 'x'

plugin/frawor/fwc/parser.vim

     finish
 endif
 execute frawor#Setup('0.0', {'@/resources': '0.0',
-            \             '@/fwc/intfuncs': '0.0'}, 1)
+            \             '@/fwc/intfuncs': '0.0',
+            \        '@/fwc/topconstructs': '0.0'}, 1)
 let s:parser={}
 "▶1 Define messages
 let s:_messages={
     endif
     return self.conclose()
 endfunction
-"▶1 scanopt    :: &self
-" Input: {arg}* "]"?
-" Output: context(optional, {arg}*)
-function s:parser.scanopt()
-    call self.addcon('optional')
-    let prevlen=-1
-    while self.len && self.len!=prevlen
-        let prevlen=self.len
-        let c=self.readc()
-        if c is ']'
-            break
-        else
-            call self.ungetc(c).scan()
-        endif
-    endwhile
-    return self.conclose()
-endfunction
-"▶1 scanpref   :: &self
-" Input: {omtchr} ({prefdescr} ( "-" | {arg} )* )* "}"?
-"        {prefdescr} :: {prefopts}? ( {str} | {wordchar}+ )
-"         {prefopts} :: ( "?" | "!" | "*" | "+" {wordchar}+ )* ( ":" {var} )?
-" Output: context(prefixes[, {matcher}], {prefix}*)
-"           {prefix} :: context(prefix, String, {prefopts}[, {var}][, {arg}*])
-"         {prefopts} :: { "alt": Bool, "list": Bool, "opt": Bool,
-"                         "argnum": UInt }
-let s:defprefopts={'alt': 0, 'list': 0, 'opt': 0, 'argnum': 1}
-function s:parser.scanpref()
-    call self.addcon('prefixes').getomtchr()
-    let prevlen=-1
-    while self.len && prevlen!=self.len
-        let prevlen=self.len
-        let c=self.readc()
-        if !exists('pref')
-            if !exists('prefopts')
-                let prefopts=copy(s:defprefopts)
-                let prefopts.argnum=1
-            endif
-            if c=~#'^\w'
-                let pref=c
-            elseif c is '"'
-                let c=self.readstr()
-                let pref=c
-            elseif c is "'"
-                let c=self.readsstr()
-                let pref=c
-            elseif c is '+'
-                let prefopts.argnum=+self.readc()
-            elseif c is '?'
-                let prefopts.opt=1
-            elseif c is '!'
-                let prefopts.alt=1
-                let prefopts.argnum=0
-            elseif c is '*'
-                let prefopts.list=1
-            elseif c is '}'
-                break
-            else
-                call self.ungetc(c)
-                break
-            endif
-        else
-            call self.addcon('prefix', pref, prefopts)
-            if c is ':'
-                let prefopts.opt=1
-                call self.getvar()
-                let c=self.readc()
-            endif
-            if c is '-'
-                let prefopts.argnum=0
-            else
-                call self.ungetc(c)
-            endif
-            let argnum=prefopts.argnum
-            while argnum
-                call self.scan()
-                let argnum-=1
-            endwhile
-            unlet pref prefopts
-            call self.conclose()
-        endif
-    endwhile
-    return self.conclose()
-endfunction
-"▶1 scanact    :: &self
-" Input: {omtchr} ( {actdescr} ( "-" | "(" {arg}* ")"? | {arg} ) )* ">"?
-"        {actdescr} :: {str}
-"                    | {wordchar}+
-" Output: context(actions[, {matcher}],
-"                 (context(action, 0|String|{arg}, {arg}*))*)
-function s:parser.scanact()
-    call self.addcon('actions').getomtchr()
-    let hasaction=0
-    let prevlen=-1
-    while self.len && prevlen!=self.len
-        let prevlen=self.len
-        let c=self.readc()
-        if !hasaction
-            let hasaction=1
-            if c is '>'
-                let hasaction=0
-                break
-            elseif c is '"'
-                call self.addcon('action', self.readstr())
-            elseif c is "'"
-                call self.addcon('action', self.readsstr())
-            elseif c is '-'
-                call self.addcon('action', 0)
-            elseif c=~#'^\w'
-                call self.addcon('action', c)
-            else
-                call self.throw('invact', c)
-            endif
-        else
-            let hasaction=0
-            if c is '('
-                while self.len
-                    let c=self.readc()
-                    if c is ')'
-                        break
-                    endif
-                    call self.ungetc(c).scan()
-                endwhile
-            elseif c isnot '>' && c isnot '-'
-                call self.ungetc(c).scan()
-            endif
-            call self.conclose()
-        endif
-    endwhile
-    if hasaction
-        call self.conclose()
-    endif
-    return self.conclose()
-endfunction
 "▶1 scan       :: &self
 " Input: "[" {opt}  ⎫
 "      | "{" {pref} ⎬ (only at toplevel or in context(action or optional))
     let type=get(self.l, 0, '')
     if type is 'top' || type is 'optional'
                 \|| (type is 'action' && len(self.l)>1)
-        if c is '['
-            return self.scanopt()
-        elseif c is '{'
-            return self.scanpref()
-        elseif c is '<'
-            return self.scanact()
-        elseif c is '+'
-            return self.addcon('next').scan().conclose()
+        if has_key(s:_r.FWC_topconstructs._chars, c)
+            return call(s:_r.FWC_topconstructs[s:_r.FWC_topconstructs._chars[c]]
+                        \.scan, [], self)
         endif
     endif
     "▲2

plugin/frawor/fwc/topconstructs.vim

+"▶1 Header
+scriptencoding utf-8
+if exists('s:_pluginloaded') || exists('g:fraworOptions._donotload') ||
+            \exists('g:frawor__donotload')
+    finish
+endif
+execute frawor#Setup('0.0', {'@/resources': '0.0'}, 1)
+let s:r={}
+let s:cf='CHECKFAILED'
+let s:cfreg='\v^'.s:cf.'$'
+let s:actdefmatcher=['matcher', ['intfunc', 'start', 0, 0]]
+let s:prefdefmatcher=s:actdefmatcher
+"▶1 addprefix      :: preflist, prefix → _ + preflist
+function s:F.addprefix(preflist, prefix)
+    if index(a:preflist, a:prefix)!=-1
+        call self._throw('ambprefix', a:prefix)
+    endif
+    call add(a:preflist, a:prefix)
+endfunction
+"▶1 optional
+let s:r.optional={'char': '['}
+"▶2 scan
+" Input: {arg}* "]"?
+" Output: context(optional, {arg}*)
+function s:r.optional.scan()
+    call self.addcon('optional')
+    let prevlen=-1
+    while self.len && self.len!=prevlen
+        let prevlen=self.len
+        let c=self.readc()
+        if c is ']'
+            break
+        else
+            call self.ungetc(c).scan()
+        endif
+    endwhile
+    return self.conclose()
+endfunction
+"▶2 compile :: descr, idx, {rolvars}, {lvars} + self → {lvars}
+" {rolvars} :: caidxstr, largsstr, purgemax
+"   {lvars} :: nextsub, addedsavemsgs
+function s:r.optional.compile(adescr, idx, caidxstr, largsstr, purgemax,
+            \                 nextsub, addedsavemsgs)
+    let addedsavemsgs=a:addedsavemsgs
+    " XXX nodefs will be still 1 when compiling next adescr. It is intentional.
+    let nodefs=empty(self.defvals)
+    let lopt=len(a:adescr.optional)
+    if lopt>1
+        let failstr=self.getlvarid('fail')
+        call self.let(failstr, 1)
+    endif
+    let addedsavemsgs=1
+    call self.addsavemsgs()
+                \.try()
+                    \.pushms('throw')
+                    \.compadescr(a:adescr.optional[0],
+                    \            a:idx.'.0(optional)',
+                    \            (a:purgemax || a:adescr.maximum==-1))
+                    \.popms()
+                    \.addrestmsgs(1)
+    if lopt>1
+        call self.let(failstr, 0)
+    endif
+    let newsub=self.getsub(self.subs[-1])
+    if newsub isnot a:caidxstr
+        call self.let(a:caidxstr, newsub)
+    endif
+    call self.catch(s:cfreg)
+    if lopt==1 && has_key(a:adescr.optional[0], 'arg')
+        let defaults=reverse(map(filter(copy(a:adescr.optional[0].arg),
+                    \                   'exists("v:val[0][0]") && '.
+                    \                   'v:val[0][0] is "defval"'),
+                    \            'v:val[0][1]'))
+        let self.defvals+=defaults
+        if !empty(defaults) && nodefs
+            let base=self.argstr(0, self.subs[:-2])
+            for defvar in self.defvals
+                call self.call('insert('.base.', '.self.getvar(defvar).', '.
+                            \            a:caidxstr.')')
+            endfor
+            let ldefaults=len(self.defvals)
+            call     self.increment(a:caidxstr, ldefaults)
+                        \.increment(a:largsstr, ldefaults)
+        endif
+    endif
+    call self.up()
+    let self.subs[-1]=[a:caidxstr]
+    if lopt>1
+        let i=1
+        for opt in a:adescr.optional[1:]
+            call self.if(failstr)
+                        \.try()
+                            \.pushms('throw')
+                            \.compadescr(opt, a:idx.'.'.i.'(optional)')
+                            \.popms()
+                            \.addrestmsgs(1)
+                            \.let(failstr, 0)
+            let newsub=self.getsub(self.subs[-1])
+            if newsub isnot a:caidxstr
+                call self.let(a:caidxstr, newsub)
+            endif
+            call self.catch(s:cfreg)
+                \.endif()
+            let self.subs[-1]=[a:caidxstr]
+            let i+=1
+        endfor
+    endif
+    return [[a:caidxstr], addedsavemsgs]
+endfunction
+"▶1 prefixes
+let s:r.prefixes={'char': '{'}
+"▶2 scan
+" Input: {omtchr} ({prefdescr} ( "-" | {arg} )* )* "}"?
+"        {prefdescr} :: {prefopts}? ( {str} | {wordchar}+ )
+"         {prefopts} :: ( "?" | "!" | "*" | "+" {wordchar}+ )* ( ":" {var} )?
+" Output: context(prefixes[, {matcher}], {prefix}*)
+"           {prefix} :: context(prefix, String, {prefopts}[, {var}][, {arg}*])
+"         {prefopts} :: { "alt": Bool, "list": Bool, "opt": Bool,
+"                         "argnum": UInt }
+let s:defprefopts={'alt': 0, 'list': 0, 'opt': 0, 'argnum': 1}
+function s:r.prefixes.scan()
+    call self.addcon('prefixes').getomtchr()
+    let prevlen=-1
+    while self.len && prevlen!=self.len
+        let prevlen=self.len
+        let c=self.readc()
+        if !exists('pref')
+            if !exists('prefopts')
+                let prefopts=copy(s:defprefopts)
+                let prefopts.argnum=1
+            endif
+            if c=~#'^\w'
+                let pref=c
+            elseif c is '"'
+                let c=self.readstr()
+                let pref=c
+            elseif c is "'"
+                let c=self.readsstr()
+                let pref=c
+            elseif c is '+'
+                let prefopts.argnum=+self.readc()
+            elseif c is '?'
+                let prefopts.opt=1
+            elseif c is '!'
+                let prefopts.alt=1
+                let prefopts.argnum=0
+            elseif c is '*'
+                let prefopts.list=1
+            elseif c is '}'
+                break
+            else
+                call self.ungetc(c)
+                break
+            endif
+        else
+            call self.addcon('prefix', pref, prefopts)
+            if c is ':'
+                let prefopts.opt=1
+                call self.getvar()
+                let c=self.readc()
+            endif
+            if c is '-'
+                let prefopts.argnum=0
+            else
+                call self.ungetc(c)
+            endif
+            let argnum=prefopts.argnum
+            while argnum
+                call self.scan()
+                let argnum-=1
+            endwhile
+            unlet pref prefopts
+            call self.conclose()
+        endif
+    endwhile
+    return self.conclose()
+endfunction
+"▶2 compile :: descr, idx, {rolvars}, {lvars} + self → {lvars}
+function s:r.prefixes.compile(adescr, idx, caidxstr, largsstr, purgemax,
+            \                 nextsub, addedsavemsgs)
+    "▶3 Define variables
+    let addedsavemsgs=a:addedsavemsgs
+    let nextsub=[a:caidxstr]
+    let preflist=[]
+    let plstr=self.getfunstatvar('prefixes', preflist)
+    let prefdictstr=self.getlvarid('prefdict')
+    let base=self.argstr(0, self.subs[:-2])
+    let astr=self.getlvarid('arg')
+    let idx=a:idx.'(prefixes)'
+    let defaults=filter(copy(a:adescr.prefixes),
+                \       'exists("v:val[2][0]") && v:val[2][0] isnot "arg"')
+    let lists=filter(copy(a:adescr.prefixes), 'v:val[1].list')
+    let haslist=!empty(lists)
+    let lastliststr=self.getlvarid('lastlist')
+    let addtry=(has_key(a:adescr, 'next') || has_key(a:adescr, 'actions'))
+    "▶3 Add messages saving if required
+    if addtry
+        if !addedsavemsgs
+            call self.addsavemsgs()
+            let addedsavemsgs=1
+        endif
+        call self.try().pushms('throw')
+    endif
+    "▶3 Initialize variables inside constructed function
+    call self.let(prefdictstr, '{}')
+                \.call('insert('.base.', '.prefdictstr.', '.
+                \                self.getsub(nextsub).')')
+                \.increment(a:caidxstr)
+                \.increment(a:largsstr)
+    "▶3 Add default values
+    for [prefix, prefopts, defval; dummylist] in defaults
+        call self.let(prefdictstr.self.getsubs([prefix]),
+                    \ self.getvar(defval))
+    endfor
+    "▲3
+    call self.while('len('.base.')>'.a:caidxstr)
+    "▶3 Get `astr' variable
+    if haslist
+        call self.if('type('.self.argstr().')=='.type(''))
+    elseif !self.o.onlystrings
+        call self.addtypecond([type('')], idx)
+    endif
+    call self.let(astr, self.getmatcher(get(a:adescr, 'prefixesmatcher',
+                \                           s:prefdefmatcher), plstr,
+                \                       self.argstr()))
+    let removestr='remove('.base.', '.a:caidxstr.')'
+    if addtry
+        let argorigstr=self.getd('argorig')
+    endif
+    if haslist
+        call self.up().else().let(astr, 0).up().endif()
+                    \.if(astr.' isnot 0')
+    else
+        call self.nextthrow(astr.' is 0', 'pnf', idx, self.argstr())
+    endif
+    call self.increment(a:largsstr, -1)
+    if addtry
+        call self.let(argorigstr, removestr)
+    else
+        call self.call(removestr)
+    endif
+    if haslist
+        call self.up().endif()
+    endif
+    "▲3
+    for [prefix, prefopts; args] in a:adescr.prefixes
+        "▶3 Add prefix to prefix list
+        call s:F.addprefix(preflist, prefix)
+        if prefopts.alt
+            call s:F.addprefix(preflist, 'no'.prefix)
+        endif
+        "▶3 Remove default value specification if any
+        let hasdefault=0
+        if !empty(args) && args[0][0] isnot 'arg'
+            let hasdefault=1
+            call remove(args, 0)
+        endif
+        "▲3
+        let prefstr=prefdictstr.self.getsubs([prefix])
+        let prefixstr=self.string(prefix)
+        "▶3 Construct prefix condition
+        let cond=astr.' is '.prefixstr
+        if prefopts.list
+            let cond.=' || ('.astr.' is 0 && '.
+                        \   lastliststr.' is '.prefixstr.')'
+        endif
+        call self.addif(cond)
+        "▶3 Process prefix arguments
+        for i in range(1, prefopts.argnum)
+            call self.compilearg(args[i-1], idx.self.string(prefix))
+                        \.incsub()
+        endfor
+        "▶3 Move prefix arguments to prefix dictionary
+        if prefopts.argnum>0
+            call self.increment(a:largsstr, -prefopts.argnum)
+        endif
+        if prefopts.list
+            let removestr='remove('.base.', '.a:caidxstr.', '.
+                        \           a:caidxstr.'+'.(prefopts.argnum-1).')'
+            let cond='has_key('.prefdictstr.', '.prefixstr.')'
+            if hasdefault
+                let cond.=' && type('.prefstr.')=='.type([])
+            endif
+            call self.if(cond)
+                        \.increment(prefstr, removestr)
+                    \.else()
+                        \.let(prefstr, removestr)
+                    \.up()
+            call self.let(lastliststr, prefixstr)
+        else
+            if haslist
+                call self.let(lastliststr, 0)
+            endif
+            if prefopts.argnum==1
+                call self.let(prefstr, 'remove('.base.', '.a:caidxstr.')')
+            elseif prefopts.argnum>0
+                call self.let(prefstr,
+                            \'remove('.base.', '.a:caidxstr.', '.
+                            \        a:caidxstr.'+'.(prefopts.argnum-1).')')
+            else
+                call self.let(prefstr, 1)
+            endif
+        endif
+        "▲3
+        let self.subs[-1]=[a:caidxstr]
+        call self.up()
+        "▶3 Process `no{prefix}'
+        if prefopts.alt
+            call self.addif(astr.' is '.self.string('no'.prefix))
+                        \.let(prefstr, 0)
+                        \.up()
+        endif
+        "▲3
+    endfor
+    call self.up()
+    if addtry
+        call self.unlet(argorigstr).up().up().addrestmsgs(1).popms()
+            \.catch(s:cfreg)
+                \.if('exists('.string(argorigstr).')')
+                    \.call('insert('.base.', '.argorigstr.', '.a:caidxstr.')')
+                    \.increment(a:largsstr)
+                \.up()
+            \.up()
+    else
+        call self.up()
+    endif
+    return [[a:caidxstr], addedsavemsgs]
+endfunction
+"▶1 next
+let s:r.next={'char': '+'}
+"▶2 scan
+function s:r.next.scan()
+    return self.addcon('next').scan().conclose()
+endfunction
+"▶2 compile :: descr, idx, {rolvars}, {lvars} + self → {lvars}
+function s:r.next.compile(adescr, idx, caidxstr, largsstr, purgemax,
+            \             nextsub, addedsavemsgs)
+    let addedsavemsgs=a:addedsavemsgs
+    if has_key(a:adescr, 'actions')
+        if !addedsavemsgs
+            call self.addsavemsgs()
+            let addedsavemsgs=1
+        endif
+        call self.try().pushms('throw')
+    endif
+    call self.while(a:caidxstr.'<'.a:largsstr)
+                \.compilearg(a:adescr.next[0], a:idx.'.(next)')
+                \.increment(a:caidxstr)
+                \.up()
+    if has_key(a:adescr, 'actions')
+        call self.addrestmsgs(1).catch(s:cfreg).popms().up()
+    endif
+    return [[a:caidxstr], addedsavemsgs]
+endfunction
+"▶1 actions
+let s:r.actions={'char': '<'}
+"▶2 scan
+" Input: {omtchr} ( {actdescr} ( "-" | "(" {arg}* ")"? | {arg} ) )* ">"?
+"        {actdescr} :: {str}
+"                    | {wordchar}+
+" Output: context(actions[, {matcher}],
+"                 (context(action, 0|String|{arg}, {arg}*))*)
+function s:r.actions.scan()
+    call self.addcon('actions').getomtchr()
+    let hasaction=0
+    let prevlen=-1
+    while self.len && prevlen!=self.len
+        let prevlen=self.len
+        let c=self.readc()
+        if !hasaction
+            let hasaction=1
+            if c is '>'
+                let hasaction=0
+                break
+            elseif c is '"'
+                call self.addcon('action', self.readstr())
+            elseif c is "'"
+                call self.addcon('action', self.readsstr())
+            elseif c is '-'
+                call self.addcon('action', 0)
+            elseif c=~#'^\w'
+                call self.addcon('action', c)
+            else
+                call self.throw('invact', c)
+            endif
+        else
+            let hasaction=0
+            if c is '('
+                while self.len
+                    let c=self.readc()
+                    if c is ')'
+                        break
+                    endif
+                    call self.ungetc(c).scan()
+                endwhile
+            elseif c isnot '>' && c isnot '-'
+                call self.ungetc(c).scan()
+            endif
+            call self.conclose()
+        endif
+    endwhile
+    if hasaction
+        call self.conclose()
+    endif
+    return self.conclose()
+endfunction
+"▶2 compile :: descr, idx, {rolvars}, {lvars} + self → {lvars}
+function s:r.actions.compile(adescr, idx, caidxstr, largsstr, purgemax,
+            \                nextsub, addedsavemsgs)
+    let actionstr=self.getlvarid('action')
+    let actions=filter(copy(a:adescr.actions), 'v:val[0] isnot 0')
+    let noact=get(filter(copy(a:adescr.actions), 'v:val[0] is 0'), 0, 0)
+    let fsastr=self.getfunstatvar('actions', map(copy(actions), 'v:val[0]'))
+    if noact isnot 0
+        call self.try().pushms('throw')
+    endif
+    let idx=a:idx.'(actions)'
+    if !self.o.onlystrings
+        call self.addtypecond([type('')], idx)
+    endif
+    let curargstr=self.argstr()
+    call self.let(actionstr, self.getmatcher(get(a:adescr, 'actionsmatcher',
+                \                                s:actdefmatcher),
+                \                            fsastr, curargstr))
+                \.nextthrow(actionstr.' is 0', 'anf', idx, curargstr)
+                \.let(curargstr, actionstr)
+    unlet curargstr
+    call self.incsub()
+    let astartsub=copy(self.subs[-1])
+    for action in actions
+        call self.addif(actionstr.' is '.self.string(action[0]))
+        if len(action)>1
+            call self.compadescr(action[1],idx.'.'.self.string(action[0]))
+        endif
+        call self.let(a:caidxstr, self.getsub(self.subs[-1]))
+                    \.up()
+        let self.subs[-1]=copy(astartsub)
+    endfor
+    if noact isnot 0
+        call self.popms().catch(s:cfreg)
+        call self.incsub(-1)
+        if len(noact)>1
+            call self.compadescr(noact[1], idx.'.nullact')
+                        \.let(a:caidxstr, self.getsub(self.subs[-1]))
+        endif
+        let self.subs[-1]=copy(astartsub)
+        call self.up().up()
+    endif
+    let self.subs[-1]=[a:caidxstr]
+    return [[a:caidxstr], a:addedsavemsgs]
+endfunction
+"▶1 _order
+let s:r._order=['optional', 'prefixes', 'next', 'actions']
+let s:r._chars={}
+call map(filter(copy(s:r), 'v:key[0] isnot "_"'),
+            \'extend(s:r._chars, {v:val.char : v:key})')
+"▶1 Register resource
+call s:_f.postresource('FWC_topconstructs', s:r, 1)
+"▶1
+call frawor#Lockvar(s:, '')
+" vim: fmr=▶,▲ sw=4 ts=4 sts=4 et tw=80

test/global-functions-reload.ok

 1      echo 1
    endfunction
 
-   function <SNR>24_testFunction()
+   function <SNR>25_testFunction()
 1      echo 2
    endfunction
 
 1      echo 1
    endfunction
 
-   function <SNR>24_testFunction()
+   function <SNR>25_testFunction()
 1      echo 2
    endfunction