Commits

ZyX_I committed 66930a7

@/fwc: Added `either' check

Comments (0)

Files changed (4)

plugin/frawor/fwc/compiler.vim

 let s:constructor={}
 "▶1 Messages
 let s:_messages={
-            \'notypes' : 'Expected at least one type specification',
-            \'tooshort': 'Argument list is too short: '.
-            \            'expected at least %u, but got %u',
-            \'toolong' : 'Argument list is too long: '.
-            \            'expected at most %u, but got %u',
-            \'invlen'  : 'Invalid arguments length: expected %u, but got %u',
+            \   'notypes': 'Expected at least one type specification',
+            \  'tooshort': 'Argument list is too short: '.
+            \              'expected at least %u, but got %u',
+            \   'toolong': 'Argument list is too long: '.
+            \              'expected at most %u, but got %u',
+            \    'invlen': 'Invalid arguments length: expected %u, but got %u',
         \}
 call extend(s:_messages, map({
-            \ 'funcfail': 'custom function returned 0',
-            \ 'exprfail': 'custom expression returned 0',
-            \'typesfail': 'invalid type: expected one of %s, but got %s',
-            \ 'typefail': 'invalid type: expected %s, but got %s',
-            \    'nbool': 'invalid value: expected either 0 or 1, but got %s',
-            \    'nfunc': 'function %s is not callable',
-            \   'nsfunc': 'expected function name or '.
-            \             'callable function reference, but got %s',
-            \     'nreg': 'string `%s'' is not a valid regular expression (%s)',
-            \   'nmatch': "string `%s' does not match regular expression `%s'",
-            \  'keysmis': 'the following required keys are missing: %s',
-            \   'keymis': 'key `%s'' is missing',
-            \  'runfail': 'running argument failed with exception %s',
-            \ 'evalfail': 'evaluating argument failed with exception %s',
-            \   'nrange': '%s is not in range [%s, %s]',
-            \  'nindict': '%s is not in dictionary',
-            \  'ninlist': 'argument is not in list',
-            \'invlstlen': 'invalid list length: expected %u, but got %u',
+            \  'funcfail': 'custom function returned 0',
+            \  'exprfail': 'custom expression returned 0',
+            \ 'typesfail': 'invalid type: expected one of %s, but got %s',
+            \  'typefail': 'invalid type: expected %s, but got %s',
+            \     'nbool': 'invalid value: expected either 0 or 1, but got %s',
+            \     'nfunc': 'function %s is not callable',
+            \    'nsfunc': 'expected function name or '.
+            \              'callable function reference, but got %s',
+            \      'nreg': "string `%s' is not a valid regular expression (%s)",
+            \    'nmatch': "string `%s' does not match regular expression `%s'",
+            \   'keysmis': 'the following required keys are missing: %s',
+            \    'keymis': 'key `%s'' is missing',
+            \   'runfail': 'running argument failed with exception %s',
+            \  'evalfail': 'evaluating argument failed with exception %s',
+            \    'nrange': '%s is not in range [%s, %s]',
+            \   'nindict': '%s is not in dictionary',
+            \   'ninlist': 'argument is not in list',
+            \ 'invlstlen': 'invalid list length: expected %u, but got %u',
+            \'eitherfail': 'all alternatives failed',
         \}, '"Error while processing check %s for %s: ".v:val'))
 let s:_messages._types=['number', 'string', 'function reference', 'list',
             \           'dictionary']
             \  'redir': 'redir END',
         \}
 function s:constructor.close()
-    let command=self.l[-2][:(stridx(self.l[-2], ' ')-1)]
+    let command=matchstr(self.l[-2], '\S\+')
     if has_key(s:cmdends, command)
         return self.add(s:cmdends[command])
     else
     endif
     return self.add('call add(@$@messages, ['.string(a:msg).', '.
                 \                             join(args, ', ').'])',
-                \   'throw "CHECKFAILED"')
+                \   'throw "CHECKFAILED"').up()
 endfunction
 "▶1 nextthrow      :: condition::expr, throwargs + self → self + self
 function s:constructor.nextthrow(cond, ...)
-    return call(self.nextcond(a:cond).addthrow, a:000, self).up()
+    return call(self.nextcond(a:cond).addthrow, a:000, self)
 endfunction
 "▶1 getlenrange    :: adescr → (minlen, maxlen) + adescr
 function s:F.getlenrange(adescr)
         return self.argstr()
     endif
 endfunction
+"▶1 getlvarid      :: varname + self → varname + self
+function s:constructor.getlvarid(v)
+    return printf('@$@%s%X', a:v, len(self.stack))
+endfunction
 "▶1 argstr         :: [genString::Bool, [subscript]] + self → String
 "▶2 addargchunk    :: [chunk], chunk::String, literal::Bool → _ + chunks
 function s:F.addargchunk(chunks, chunk, literal)
     let i=0
     for arg in a:tuple[1]
         call add(self.subs, i)
-        call self.compilearg(arg, a:idx.'.'.i, a:default)
+        call self.compilearg(arg, a:idx.'(tuple).'.i, a:default)
         call remove(self.subs, -1)
         let i+=1
     endfor
 endfunction
 "▶1 addlist        :: listdesc, idx, defaultArgType + self → self + self
 function s:constructor.addlist(list, idx, default)
-    let conid=printf('%X', len(self.stack))
-    let largstr='@$@larg'.conid
-    let istr='@$@i'.conid
+    let largstr=self.getlvarid('larg')
+    let istr=self.getlvarid('i')
     call self.add('let '.largstr.'=len('.self.argstr().')',
                 \ 'let '.istr.'=0',
                 \ 'while '.istr.'<'.largstr)
                 \.deeper()
     call add(self.subs, [istr])
-    call self.compilearg(a:list[1], a:idx.'.list', a:default)
+    call self.compilearg(a:list[1], a:idx.'(list)', a:default)
     call self.add('let '.istr.'+=1')
     call remove(self.subs, -1)
     call self.up().close()
                         \                          self.getvar(desc[1]).', {})')
                         \.up().add('catch /.*/').deeper()
                         \.addthrow('runfail', a:idx, '@#@', 'v:exception')
-                        \.up().close()
+                        \.close()
         "▶3 `earg'
         elseif desc[0] is 'earg'
             call self.add('try')
                         \.deeper('let '.curargstr.'=eval('.curargstr.')')
                         \.up().add('catch /.*/').deeper()
                         \.addthrow('evalfail', a:idx, '@#@', 'v:exception')
-                        \.up().close()
+                        \.close()
         "▶3 `tuple'
         elseif desc[0] is 'tuple'
             call self.nextthrow('len('.curargstr.')!='.len(desc[1]),
                             \       'exprfail', idx, '@#@')
             "▶3 `either'
             elseif desc[0] is 'either'
-                " TODO
+                if addedcond
+                    call self.close()
+                    let addedcond=0
+                endif
+                let sucstr=self.getlvarid('succeeded')
+                let msglenstr=self.getlvarid('msglen')
+                let pmsglenstr=self.getlvarid('pmsglen')
+                call self.add('let '.sucstr.'=1',
+                            \ 'let '.msglenstr. '=len(@$@messages)',
+                            \ 'let '.pmsglenstr.'=len(@$@pmessages)')
+                if !empty(desc[1])
+                    call self.add('try').deeper()
+                                \.compilearg(desc[1][0], idx.'(either).0',
+                                \            'check')
+                                \.up()
+                                \.add('catch /^CHECKFAILED$/')
+                                \.deeper('let '.sucstr.'=0')
+                                \.up().close()
+                endif
+                let i=1
+                for arg in desc[1][1:]
+                    call self.add('if !'.sucstr)
+                                \.deeper('let '.sucstr.'=1',
+                                \        'try').deeper()
+                                \.compilearg(arg, idx.'(either).'.i, 'check')
+                                \.up()
+                                \.add('catch /^CHECKFAILED$/')
+                                \.deeper('let '.sucstr.'=0')
+                                \.up().close()
+                                \.up().close()
+                    let i+=1
+                endfor
+                call self.nextcond(sucstr)
+                            \.nextcond('len(@$@messages)>'.msglenstr)
+                                    \.add('call remove(@$@messages, '.
+                                    \                  msglenstr.', -1)')
+                            \.up().close()
+                            \.nextcond('len(@$@pmessages)>'.pmsglenstr)
+                                    \.add('call remove(@$@pmessages, '.
+                                    \                  pmsglenstr.', -1)')
+                            \.up().close()
+                        \.up()
+                        \.add('else').deeper()
+                            \.addthrow('eitherfail', idx, '@#@')
+                        \.close()
             "▶3 `tuple'
             elseif desc[0] is 'tuple'
                 call self.addtypecond([type([])], idx)
                 call self.addlist(desc, idx, 'check')
             "▶3 `dict'
             elseif desc[0] is 'dict'
+                let addedcond=1
+                call self.addtypecond([type({})], idx)
                 " TODO
             "▶3 `in'
             elseif desc[0] is 'in'
                             \       'nmatch', idx, '@#@', curargstr, regex)
             "▶3 `path'
             elseif desc[0] is 'path'
+                let addedcond=1
                 call self.addtypecond([type('')], idx)
                 " TODO
             "▶3 `type'
                         \.add('catch /.*/').deeper()
                           \.addthrow('nreg', idx, '@#@', curargstr,
                           \                  'v:exception')
-                        \.up().close()
+                        \.close()
             "▶3 `bool'
             elseif desc[0] is 'bool'
                 let addedcond=1
         call self.compilecheck(check, a:idx.'.'.(i+1-len(check)))
         call remove(check, 1, -1)
     endif
-    if type(self.subs[-1])==type(0)
-        let self.subs[-1]+=1
-    endif
     return self
 endfunction
 "▶1 compileadesc   :: adescr + self → self + self
         call s:F.getlenrange(a:adescr)
     endif
     if !has_key(a:adescr, 'checkedfor')
-        call self.lencheck(self.subs[0]+a:adescr.minimum,
-                    \      self.subs[0]+a:adescr.maximum)
+        call self.lencheck(self.subs[-1]+a:adescr.minimum,
+                    \      self.subs[-1]+a:adescr.maximum)
         let a:adescr.checkedfor=1
     endif
     if has_key(a:adescr, 'arg')
-        call map(a:adescr.arg, 'self.compilearg(v:val, v:key)')
+        for arg in a:adescr.arg
+            call self.compilearg(arg, self.subs[-1])
+            let self.subs[-1]+=1
+        endfor
     endif
     " TODO
     return self

plugin/frawor/fwc/parser.vim

 let s:args.check.eval=['expr']
 "▶3 check.either
 " Checks whether {argument} matches one of given specifications
-let s:args.check.either=['*check']
+let s:args.check.either=['*arg']
 "▶3 check.tuple
 " Checks whether {argument} is a list with a fixed length and each element 
 " matching given specification
 ::: Section <Pipes/Built-in pipes/in>
 plugin/frawor/fwc/compiler:ninlist
 plugin/frawor/fwc/compiler:ninlist
-::: Section <Pipes/Built-in pipes/Substitute>
+::: Section <Pipes/Built-in pipes/substitute>
 plugin/frawor/fwc/compiler:typefail
-::: Section <Pipes/Built-in pipes/Bool>
+::: Section <Pipes/Built-in pipes/bool>
 ::: Section <Pipes/Pipes composition>
 ::: Section <Checks/Function checks>
 plugin/frawor/fwc/compiler:funcfail
 plugin/frawor/fwc/compiler:typefail
 plugin/frawor/fwc/compiler:ninlist
 plugin/frawor/fwc/compiler:ninlist
+::: Section <Checks/Built-in checks/either>
+plugin/frawor/fwc/compiler:nbool
+plugin/frawor/fwc/compiler:typefail
+plugin/frawor/fwc/compiler:eitherfail
+plugin/frawor/fwc/compiler:nbool
 ::: Section <Checks/Built-in checks/list>
 plugin/frawor/fwc/compiler:typefail
 plugin/frawor/fwc/compiler:nreg
 plugin/frawor/fwc/compiler:nfunc
 plugin/frawor/fwc/compiler:nsfunc
 plugin/frawor/fwc/compiler:nsfunc
-::: Section <Checks/Built-in checks/Match>
+::: Section <Checks/Built-in checks/match>
 plugin/frawor/fwc/compiler:nmatch
 plugin/frawor/fwc/compiler:typefail
 plugin/frawor/fwc/compiler:nmatch
-::: Section <Checks/Built-in checks/Types>
+::: Section <Checks/Built-in checks/type>
 plugin/frawor/fwc/compiler:typefail
 plugin/frawor/fwc/compiler:typefail
 plugin/frawor/fwc/compiler:typefail

test/fwctests.dat

 ['in =["a",0]',            'filter'], ['0'],                0
 ['in =["a",0]',            'filter'], [0],                  [0]
 ['in =["a",0]~exact 0',    'filter'], [0],                  [0]
-#▶3 Substitute
+#▶3 substitute
 :let s:regex='\v.'
 :let s:replacement='def'
 :let s:flags='g'
 ['substitute/abc/"def"g',  'filter'], ['abcabc'],           ['defdef']
 ['substitute/abc/"def"""', 'filter'], ['abcabc'],           ['defabc']
 ['substitute$regex$replacement$flags', 'filter'], ['abc'],  ['defdefdef']
-#▶3 Bool
+#▶3 bool
 ['bool',                   'filter'], [[]],                 [0]
 ['bool',                   'filter'], [{}],                 [0]
 ['bool',                   'filter'], [""],                 [0]
 ['in =["a",0]',     'check'], [0],                  1
 ['_',               'check'], [[[[]]]],             1
 ['any',             'check'], [[[[]]]],             1
+#▶3 either
+['either bool, isreg',      'check'], ['abc'],         1
+['either bool, isreg',      'check'], [2],             0
+['either bool, isreg',      'check'], [0],             1
+['either bool, isreg bool', 'check'], [0, 2],          0
 #▶3 list
 ['list isreg',              'check'], ["['abc']"],     0
 ['list isreg',              'check'], [['abc']],       1
 # The following may throw an exception
 ['isfunc',          'check'], ['()'],               0
 ['isfunc',          'check'], ['g:[xx]'],           0
-#▶3 Match
+#▶3 match
 ['match/\vb@<!a/',  'check'], ['abc'],              1
 ['match/\vb@<!a/',  'check'], ['bac'],              0
 ['match/\vb@<!a/',  'check'], [0],                  0
 ['match$regex',     'check'], [''],                 0
 ['match$regex',     'check'], ['abc'],              1
-#▶3 Types
+#▶3 type
 ['type string',     'check'], [''],                 1
 ['type string',     'check'], [0],                  0
 ['type ""',         'check'], [''],                 1