Commits

ZyX_I  committed cd60dca

@/fwc: Added `key' check, partially implemented `key' pipe

  • Participants
  • Parent commits ca75239

Comments (0)

Files changed (4)

File plugin/frawor/fwc/compiler.vim

             \  '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',
         \}, '"Error while processing check %u for %s: ".v:val'))
 let s:_messages._types=['number', 'string', 'function reference', 'list',
             \           'dictionary']
                 \"^''\\.", '',            ''),
                 \"\\.''$", '',            '')
 endfunction
+"▶1 cleanup        :: list::[_, {arg}*] → + list
+function s:F.cleanup(list)
+    if len(a:list)>1
+        let args=remove(a:list, 1, -1)
+        let sq={}
+        call add(a:list, sq)
+        for arg in args
+            if !has_key(sq, arg[0])
+                let sq[arg[0]]=[]
+            endif
+            if arg[0] is 'actions' || arg[0] is 'prefixes'
+                if get(get(arg, 1, []), 0, '') is 'matcher'
+                    let sq[arg[0].'matcher']=remove(arg, 1)[1:]
+                endif
+                call extend(sq[arg[0]], filter(map(arg[1:], 'v:val[1:]'),
+                            \                  '!empty(v:val)'))
+            elseif arg[0] is 'next'
+                if len(arg[1:][0])>1
+                    call add(sq[arg[0]], arg[1:][0][1:])
+                endif
+            elseif arg[0] is 'optional'
+                if len(arg)>1
+                    call add(sq[arg[0]], arg)
+                endif
+            else
+                if len(arg)>1
+                    call add(sq[arg[0]], arg[1:])
+                endif
+            endif
+        endfor
+        call filter(sq, '!empty(v:val)')
+        for key in filter(['actions', 'optional'], 'has_key(sq, v:val)')
+            call map(copy(sq[key]), 's:F.cleanup(v:val)')
+            call filter(sq[key], '!empty(v:val)')
+            if empty(sq[key])
+                call remove(sq, key)
+            endif
+        endfor
+        if has_key(sq, 'optional')
+            call map(sq.optional, 'v:val[1]')
+        endif
+    endif
+endfunction
 "▶1 add            :: item, ... + self → self + self
 function s:constructor.add(...)
     let self.l+=a:000
 function s:constructor.nextthrow(cond, ...)
     return call(self.nextcond(a:cond).addthrow, a:000, self).up()
 endfunction
-"▶1 cleanup        :: list::[_, {arg}*] → + list
-function s:F.cleanup(list)
-    if len(a:list)>1
-        let args=remove(a:list, 1, -1)
-        let sq={}
-        call add(a:list, sq)
-        for arg in args
-            if !has_key(sq, arg[0])
-                let sq[arg[0]]=[]
-            endif
-            if arg[0] is 'actions' || arg[0] is 'prefixes'
-                if get(get(arg, 1, []), 0, '') is 'matcher'
-                    let sq[arg[0].'matcher']=remove(arg, 1)[1:]
-                endif
-                call extend(sq[arg[0]], filter(map(arg[1:], 'v:val[1:]'),
-                            \                  '!empty(v:val)'))
-            elseif arg[0] is 'next'
-                if len(arg[1:][0])>1
-                    call add(sq[arg[0]], arg[1:][0][1:])
-                endif
-            elseif arg[0] is 'optional'
-                if len(arg)>1
-                    call add(sq[arg[0]], arg)
-                endif
-            else
-                if len(arg)>1
-                    call add(sq[arg[0]], arg[1:])
-                endif
-            endif
-        endfor
-        call filter(sq, '!empty(v:val)')
-        for key in filter(['actions', 'optional'], 'has_key(sq, v:val)')
-            call map(copy(sq[key]), 's:F.cleanup(v:val)')
-            call filter(sq[key], '!empty(v:val)')
-            if empty(sq[key])
-                call remove(sq, key)
-            endif
-        endfor
-        if has_key(sq, 'optional')
-            call map(sq.optional, 'v:val[1]')
-        endif
-    endif
-endfunction
 "▶1 getlenrange    :: adescr → (minlen, maxlen) + adescr
 function s:F.getlenrange(adescr)
     let minimum=0
         elseif desc[0] is 'in'
             " TODO
         elseif desc[0] is 'key'
-            " TODO
+            if len(desc)==2 || (desc[2][1][0] is 'intfunc' &&
+                        \       desc[2][1][1] is 'exact' &&
+                        \       desc[2][1][2] is 0)
+                call self.compilecheck(['check', a:pipe[1]], a:idx)
+            else
+                call self.addtypecond([type('')], idx)
+                " TODO
+            endif
         elseif desc[0] is 'take'
             " TODO
         elseif desc[0] is 'func' || desc[0] is 'eval'
     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:constructor.compilecheck(check, idx)
     let curargstr=self.argstr()
                 endif
             "▶3 `key'
             elseif desc[0] is 'key'
+                let addedcond=1
+                call self.addtypecond([type('')], idx)
+                call self.nextthrow('!has_key('.self.getvar(desc[1]).', '.
+                            \                   curargstr.')',
+                            \       'nindict', idx, '@#@', curargstr)
+            elseif desc[0] is 'in'
                 " TODO
             "▶3 `haskey'
             elseif desc[0] is 'haskey'
 "▶1 compileadesc   :: adescr + self → self + self
 let s:pipechecks={
             \'substitute': ['intfunc', 'type',   [type('')]],
-            \        'in': ['intfunc', 'type',   [type('')]],
-            \       'key': ['intfunc', 'type',   [type('')]],
             \      'take': ['intfunc', 'type',   [type('')]],
             \       'run': ['intfunc', 'isfunc', 0         ],
             \      'earg': ['intfunc', 'type',   [type('')]],
             \                        'init': {'ids': []}})
 "▶1
 " TODO implement recursion protection
+" TODO implement onlystrings option (disables type checks)
 call frawor#Lockvar(s:, 'lastid,vars')
 " vim: fmr=▶,▲ sw=4 ts=4 sts=4 et tw=80

File plugin/frawor/fwc/parser.vim

 " Runs substitute on {argument}
 let s:args.pipe.substitute=['reg', 'string', 'string']
 "▶3 pipe.in
-" Picks up first element from {var}::List that matches {argument}
+" Picks up first element from {var}::List that matches {argument}. If {matcher} 
+" is absent then {argument} may be of any type. In other case it should be 
+" string.
 let s:args.pipe.in=['var', '?omtchr']
 "▶3 pipe.key
 " Picks up first key from {var}::Dictionary that matches {argument}
 " Input: ( "~" {intfunc} )?
 " Output: context(matcher, {intfunc})?
 function s:parser.getomtchr()
-    let c=self.readc()
-    if c is '~'
-        return self.getmatcher()
+    if self.len
+        let c=self.readc()
+        if c is '~'
+            return self.getmatcher()
+        endif
+        return self.ungetc(c)
     endif
-    return self.ungetc(c)
+    return self
 endfunction
 "▶1 scanfie    :: contextName + self → self + self
 " Input: {intfunc}
     return self.conclose()
 endfunction
 "▶1 scanopts   :: &self
-" Input: "-" "("? ( "no"? ( "only" | "recursive" ) )* ")"
+" Input: "-" "("? ( "no"? ( "only" | "recursive" | "onlystrings" ) )* ")"
 " Output: add(<self.o>)
-let s:options={'only': 0, 'recursive': 0}
+let s:options={'only': 0, 'recursive': 0, 'onlystrings': 0}
 function s:parser.scanopts()
     let c=self.readc()
     let self.o=copy(s:options)

File test/fwccheck.ok

 plugin/frawor/fwc/compiler:runfail
 plugin/frawor/fwc/compiler:evalfail
 plugin/frawor/fwc/compiler:nsfunc
+plugin/frawor/fwc/compiler:nindict
+plugin/frawor/fwc/compiler:typefail
 ::: Section <Pipes/Built-in pipes/Substitute>
 plugin/frawor/fwc/compiler:typefail
 ::: Section <Pipes/Built-in pipes/Bool>
 plugin/frawor/fwc/compiler:nbool
 plugin/frawor/fwc/compiler:typefail
 plugin/frawor/fwc/compiler:nreg
+plugin/frawor/fwc/compiler:nindict
+plugin/frawor/fwc/compiler:typefail
 ::: Section <Checks/Built-in checks/range>
 plugin/frawor/fwc/compiler:typefail
 plugin/frawor/fwc/compiler:nrange

File test/fwctests.dat

 ['earg',                   'filter'], ['('],                0
 ['isfunc',                 'filter'], [0],                  0
 ['isfunc',                 'filter'], [function("tr")],     [function("tr")]
+['key={"abc": 1}~exact',   'filter'], ['abc'],              ['abc']
+['key={"abc": 1}',         'filter'], ['abc'],              ['abc']
+['key={"abc": 1}',         'filter'], ['ab'],               0
+['key={"abc": 1}',         'filter'], [0],                  0
 #▶3 Substitute
 :let s:regex='\v.'
 :let s:replacement='def'
 ['isreg',           'check'], [0],                  0
 ['isreg',           'check'], ['\(abc\)'],          1
 ['isreg',           'check'], ['\(abc'],            0
+['key={"abc": 1}',  'check'], ['abc'],              1
+['key={"abc": 1}',  'check'], ['ab'],               0
+['key={"abc": 1}',  'check'], [0],                  0
 ['_',               'check'], [[[[]]]],             1
 ['any',             'check'], [[[[]]]],             1
 #▶3 range