Commits

ZyX_I committed 1031587

@/checks: Added args.arg.either, made it possible to supply empty strings for readstr, added nan to readflt, added readexpr, added getsubscr, added check for infinite loops, added more functions' descriptions

  • Participants
  • Parent commits 0d10751

Comments (0)

Files changed (1)

File plugin/frawor/checks.vim

 " Checks whether {argument} is a list with a fixed length and each element 
 " matching given specification
 let s:args.arg.tuple=['*arg']
+"▶2 arg.either
+" Checks whether {argument} matches one of given specifications
+let s:args.arg.either=['*arg']
 "▶2 arg.path
 " Checks whether {argument} is a path matching given specification
 let s:args.arg.path=['path']
 "▶2 scan.readstr    :: () + self → String + self(s)
 " Gets next double-quoted string. Backslash just escapes next character, no 
 " other translations are done
+" {str} :: '"' ( ( ! '\' | '"' ) | ( '\\' | '\"' ) )* '"'
 function s:F.scan.readstr()
     if !empty(self.ungot)
         call self.throw('int', 'strungetc')
     endif
-    let c=matchstr(self.s, '\v(\\.|[^\\"])+"')
+    let c=matchstr(self.s, '\v(\\.|[^\\"])*"')
     if empty(c)
         call self.throw('unmatchp', '"')
     endif
 endfunction
 "▶2 scan.readreg    :: endstr + self → String + self(s)
 " Gets the next regular expression. {endstr} determines border character
+" {reg} :: ( "\" . | ! "\" {endstr} ) {endstr}
+" {endstr} :: {char} \ {wordchar}
 function s:F.scan.readreg(endstr)
     if !empty(self.ungot)
         call self.throw('int', 'regungetc')
     let self.eos=empty(self.s)
     return c[:-2]
 endfunction
-"▶2 scan.readflt    :: () + self → String + self(s)
+"▶2 scan.readflt    :: () + self → String|0 + self(s)
+"  {flt} :: ( "+" | "-" ) ( "nan" | "inf" | {unum} )
+" {unum} :: {d}* "."? {d}* ( "e" ( "+" | "-" )? [0-9]+ )?
+"    {d} :: [0-9] | "_"
 function s:F.scan.readflt()
     if !empty(self.ungot)
         call self.throw('int', 'fltungetc')
     endif
     let self.s=substitute(self.s, '^\_s\+', '', '')
-    let c=matchstr(self.s, '\v\c^[+-]? *%(inf|[0-9_]*\.?[0-9_]*%(e[+-]?\d+)?)')
+    let c=matchstr(self.s,
+                \'\v\c^[+-]? *%(nan|inf|[0-9_]*\.?[0-9_]*%(e[+-]?\d+)?)')
     if empty(c)
-        return '0'
+        return 0
     endif
     let self.s=self.s[len(c):]
     let self.eos=empty(self.s)
                 \'\.\d\@!',        '.0',   '' ),
                 \'\v^[+-]?\d+e@=', '&.0',  '' )
 endfunction
+"▶2 scan.readexpr   :: () + self → String + self(s)
+let s:parens={'(': ')', '[': ']', '{': '}'}
+let s:revparens={}
+call map(copy(s:parens), 'extend(s:revparens, {v:val : v:key})')
+function s:F.scan.readexpr()
+    if !empty(self.ungot)
+        call self.throw('int', 'fltungetc')
+    endif
+    let self.s=substitute(self.s, '^\_s\+', '', '')
+    let c=""
+    let parens=[]
+    while !empty(self.s)
+        let chunk=matchstr(self.s, '\v^.{-}[''[\](){}"]')
+        let stopsym=""
+        if empty(chunk)
+            let chunk=self.s
+        else
+            let stopsym=chunk[-1:]
+        endif
+        if has_key(s:parens, stopsym)
+            call add(parens, s:parens[stopsym])
+        elseif has_key(s:revparens, stopsym)
+            let close=""
+            while !empty(parens) && parens[-1] isnot stopsym
+                let close.=remove(parens, -1)
+            endwhile
+            let c.=close
+            if empty(parens)
+                break
+            else
+                call remove(parens, -1)
+            endif
+        elseif stopsym is '"'
+            let string=matchstr(self.s, '\v(\\.|[^\\"])*"', len(chunk))
+            if empty(string)
+                call self.throw('unmatchp', '"')
+            else
+                let chunk.=string
+            endif
+        elseif stopsym is "'"
+            let string=matchstr(self.s, '\v(''''|[^''])*''', len(chunk))
+            if empty(string)
+                call self.throw('unmatchp', "'")
+            else
+                let chunk.=string
+            endif
+        endif
+        let self.s=self.s[len(chunk):]
+        let c.=chunk
+    endwhile
+    let c.=join(parens, "")
+    let self.eos=empty(self.s)
+    return c
+endfunction
 "▶2 scan.scanlist   :: farg + self → self
 " Scans a list of elements that looks either like "{e1}, {e2}", "({e1} {e2})" or 
 " "({e1}, {e2})" (last two can be combined).
 " (it really makes a difference only at the end of the string), * says that 
 " string should be scanned for a list of arguments with arbitrary length, not 
 " just for a single one
+" Input: {funcname} [arguments]?
+"        {funcname} :: {wordchar}+
+" Output: context(intfunc, {funcname}[, contexts])
 function s:F.scan.intfunc()
     let type=self.stack[-1][0]
-    if self.eos
+    if self.eos "▶3
         call self.throw('funcmis', type)
-    endif
+    endif       "▲3
     let c=self.readc()
     let func=s:F.getmatch(s:args[type], c)
-    if func is 0
+    if func is 0 "▶3
         call self.throw("ukfunc", c)
-    endif
+    endif        "▲3
     call self.addcon('intfunc', func)
     let fargs=get(s:args[type], func, [])
     for farg in fargs
         if farg[0] is '?'
             let farg=farg[1:]
-        elseif self.eos
+        elseif self.eos "▶3
             call self.throw('argmis', type.'.'.func)
-        endif
+        endif           "▲3
         if farg[0] is '*'
             call self.scanlist(farg[1:])
         else
 "▶2 scan.getone     :: &self!
 " Consumes a word and adds 1 to context if next word is "1", otherwise adds 0. 
 " If next word is "0", it is consumed.
+" Input: ( "0" | "1" )?
+" Output: add(0|1)
 function s:F.scan.getone()
     let c=self.readc()
     if c is '1'
 " Adds type number to the context.
 " Valid arguments: s[tring], n[umber], f[loat], d[ictionary], l[ist], fu[nction]
 "                   "", '',     -0,      .0,         {},        []
+" Input: {typedescr}
+" Output: add({typeNumber})
+"         {typeNumber}: any number described in |type()|
 let s:typechars={
             \'{': [type({}),  '}'],
             \'[': [type([]),  ']'],
 endfunction
 "▶2 scan.getstring  :: &self!
 " Gets either a string or variable name
-" {string} :: '"' ( ( ! '"' "\" ) | "\" ( '"' | "\" ) ) '"'
+" Input: {str}
+"      | "$" {var}
+"      | {wordchar}+
+"      | .
+" Output: add({string}) | *getvar
 function s:F.scan.getstring()
     let c=self.readc()
     if c is '"'
 endfunction
 "▶2 scan.getpath    :: &self!
 " Gets path specification:
-" {path} :: [( "d" | "f" )] [ "r" ] [( "w" | "W" | "p" )] [ "x" ]
-"           & ! ( "d" ) "r"
-"           & ! ( "d" ) [( "w" | "W" | "p" )] "x"
+" Input: [( "d" | "f" )] [ "r" ] [( "w" | "W" | "p" )] [ "x" ]
+"        & ! ( "d" ) "r"
+"        & ! ( "d" ) [( "w" | "W" | "p" )] "x"
 " 1. "d" for directory and "f" for regular file, otherwise both may be accepted
 " 2. "r" for readable file (not directory)
 " 3. "w" for writeable file or directory (unless "f" is specified),
 "        if you have directory /a and you can write to it, then path /a/b/c/d 
 "        will be accepted),
 " 4. "x" for executable file (not directory)
+" Output: add({pathspec})
 function s:F.scan.getpath()
     let c=self.readc()
     if c=~#'^[df]\=r\=[wWp]\=x\=$' && c!~#'^d\%(.\{,2}x\|r\)'
 endfunction
 "▶2 scan.getddescr  :: &self
 " Gets dictionary description:
-" {ddescr} :: "{" [{keydescr} {valuedescr}] "}"
-" {keydescr} :: {string} | "/" {regex} | {arg}
-" {valuedescr} :: {arg}
+" Input: "{" ({keydescr} {arg})* "}"
+"        {keydescr} :: {str}
+"                    | "/" {reg}(endstr=/)
+"                    | {arg}
+" Output: context(ddescr, [{keycon}])
+"         {keycon} :: context(eq,    String, {arg})
+"                   | context(regex, String, {arg})
+"                   | context(check, {arg},  {arg})
 function s:F.scan.getddescr()
     call self.addcon('ddescr')
-    call self.readc() " Skip `{'
+    if !self.eos
+        let c=self.readc()
+        if c isnot '{'
+            call self.ungetc(c)
+        endif
+    endif
     while !self.eos
         let c=self.readc()
         if c is '}'
     return self.conclose()
 endfunction
 "▶2 scan.getreg     :: &self
-" {regex} ::  "$" {variable}
-"           | {startchar} {reg} {endchar}
+" Gets regular expression
+" Input: "$" {var}
+"      | {startchar} {reg}(endchar={endchar})
+"           {startchar} :: ! ( {wordchar} | "$" )
+"             {endchar} :: pair({startchar}) | {startchar}
+" Output: context(regex, {var}|String)
 let s:pairs={
             \'(': ')',
             \'[': ']',
     return self.conclose()
 endfunction
 "▶2 scan.getnumber  :: &self
+" Get integer or floating point number (including ±inf and nan)
+" Input: {flt}
+"      | "$" {var}
+"      | ""
+" Output: context(inf, "+" | "-")
+"       | context(nan)
+"       | context(number, Number)
+"       | context(float, Float)
+"       | *getvar
 function s:F.scan.getnumber()
     let f=self.readflt()
-    if f[-3:] is 'inf'
-        return self.addcon('inf').conclose()
+    if f is 0
+        let c=self.readc()
+        if c is '$'
+            return self.getvar()
+        else
+            call self.ungetc(c)
+        endif
+    elseif f[-3:] is 'inf'
+        let sign=f[0]
+        if f[0] is 'i'
+            let sign='+'
+        endif
+        return self.addcon('inf', sign).conclose()
+    elseif f is 'nan'
+        return self.addcon('nan').conclose()
     endif
     let r=eval(f)
     if type(r) is type(0)
     endif
 endfunction
 "▶2 scan.getexpr    :: &self
-let s:parens={'(': ')', '[': ']', '{': '}'}
-let s:revparens={}
-call map(copy(s:parens), 'extend(s:revparens, {v:val : v:key})')
+" Input: {expr}
+" Output: context(expr, String)
 function s:F.scan.getexpr()
-    let r=""
-    call self.addcon('expr')
-    let parens=[]
+    return self.addcon('expr', self.readexpr()).conclose()
+endfunction
+"▶2 scan.getsubscr  :: &self
+" Input: ( "." ( "."? ( ":" {n} {n} | {subscript} ) )* )
+"        {subscript} :: {str}
+"                     | [0-9] {wordchar}*
+"                     | {wordchar}+
+"                {n} :: "-"? {wordchar}+
+" Output: context(subscript, (String|Number|context(Number, Number))*)
+function s:F.scan.getsubscr()
+    call self.addcon('subscript')
     while !self.eos
         let c=self.readc()
-        if has_key(s:parens, c)
-            call add(parens, s:parens[c])
-        elseif c is '"'
-            let c.=substitute(escape(self.readstr(),'\"'), "\n", '\\n', 'g').'"'
-        elseif has_key(s:revparens, c)
-            if empty(parens)
-                call self.ungetc(c)
-                break
-            elseif c is parens[-1]
-                call remove(parens, -1)
-            else
-                call self.throw('unmatchp', s:revparens[c])
-            endif
+        if c is '"'
+            call self.add(self.readstr())
+        elseif c=~#'^\d'
+            call self.add(+c)
+        elseif c=~#'^\w'
+            call self.add(c)
+        elseif c is ':'
+            call self.addcon()
+            " Start and end subscripts
+            for i in range(0, 1)
+                if self.eos
+                    call self.throw('subsmis')
+                endif
+                let v=self.readc()
+                if v is '-'
+                    if self.eos
+                        call self.throw('nonum')
+                    endif
+                    let v.=self.readc()
+                endif
+                call self.add(+v)
+            endfor
+            call self.conclose()
+        elseif c isnot '.'
+            call self.ungetc(c)
+            break
         endif
-        let r.=c
     endwhile
-    let r.=join(parens, "")
-    return self.add(r).conclose()
+    return self.conclose()
 endfunction
 "▶2 scan.getchvar   :: &self
+" Input: "<"* {subscr}?
+" Output: context(this, Number, {subscr}?)
 function s:F.scan.getchvar()
     call self.addcon('this', 0)
     if !self.eos
             endwhile
         endif
         if c is '.'
-            call self.addcon('subscript', [])
-            while !self.eos
-                let c=self.readc()
-                if c is '"'
-                    call add(self.l[-1], self.readstr())
-                elseif c=~#'^\d'
-                    call add(self.l[-1], +c)
-                elseif c=~#'^\w'
-                    call add(self.l[-1], c)
-                elseif c is ':'
-                    call self.addcon('splice')
-                    " Start and end subscripts
-                    for i in range(0, 1)
-                        if self.eos
-                            call self.throw('subsmis')
-                        endif
-                        let v=self.readc()
-                        if v is '-'
-                            if self.eos
-                                call self.throw('nonum')
-                            endif
-                            let v.=self.readc()
-                        endif
-                        call self.add(+v)
-                    endfor
-                    call self.conclose()
-                elseif c isnot '.'
-                    break
-                endif
-            endwhile
-            call self.conclose()
+            call self.getsubscr()
         else
             call self.ungetc(c)
         endif
     endif
     let c=self.readc()
     if c=~#'^\w'
-        call self.addcon('plugvar', c)
-        while !self.eos
-            let c=self.readc()
-            if c=~#'^\w'
-                let self.l[-1].=".".c
-            elseif c isnot '.'
-                call self.ungetc(c)
-                break
-            endif
-        endwhile
-        call self.conclose()
+        call self.addcon('plugvar').ungetc(c).getsubscr().conclose()
     elseif c is '@'
         return self.getchvar()
     elseif c is '='
     call self.addcon('msg', self.readc())
     if !self.eos
         let c=self.readc()
-        if self.c is '('
+        if c is '('
             while !self.eos
                 let c=self.readc()
                 if c is ')'
                 \    'o': copy(s:options),
                 \}
     call extend(s, s:F.scan, 'error')
-    " FIXME Redefine these functions with more verbose ones
+    " FIXME Redefine this function with more verbose one
     let s.throw=s:_f.throw
-    let s.warn=s:_f.warn
     call add(s.stack, s.tree)
     let s.l=s.stack[-1]
     call s.scanopts().add(s.o)
-    while !s.eos
+    let prevlen=0
+    while !s.eos && len(s.s)!=prevlen
         call s.scan()
+        let prevlen=len(s.s)
     endwhile
     return s.tree
 endfunction