docgen understands and ignores *when false*
This commit is contained in:
parent
8ee63f9836
commit
adf13aaea3
18 changed files with 1194 additions and 79 deletions
|
|
@ -22,6 +22,10 @@ __TINYC__
|
|||
#ifndef NIMBASE_H
|
||||
#define NIMBASE_H
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# define _GNU_SOURCE 1
|
||||
#endif
|
||||
|
||||
#if !defined(__TINYC__)
|
||||
# include <math.h>
|
||||
#else
|
||||
|
|
@ -29,7 +33,6 @@ __TINYC__
|
|||
# define GCC_MAJOR 4
|
||||
# define __GNUC_MINOR__ 4
|
||||
# define __GNUC_PATCHLEVEL__ 5 */
|
||||
|
||||
# define __DECLSPEC_SUPPORTED 1
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements helper procs for CGI applictions. Example:
|
||||
## This module implements helper procs for CGI applications. Example:
|
||||
##
|
||||
## .. code-block:: Nimrod
|
||||
##
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
## writeln(stdout, "your password: " & myData["password"])
|
||||
## writeln(stdout, "</body></html>")
|
||||
|
||||
import strutils, os, strtabs
|
||||
import strutils, os, strtabs, cookies
|
||||
|
||||
proc URLencode*(s: string): string =
|
||||
## Encodes a value to be HTTP safe: This means that characters in the set
|
||||
|
|
@ -355,32 +355,16 @@ proc setCookie*(name, value: string) =
|
|||
write(stdout, "Set-Cookie: ", name, "=", value, "\n")
|
||||
|
||||
var
|
||||
cookies: PStringTable = nil
|
||||
|
||||
proc parseCookies(s: string): PStringTable =
|
||||
result = newStringTable(modeCaseInsensitive)
|
||||
var i = 0
|
||||
while true:
|
||||
while s[i] == ' ' or s[i] == '\t': inc(i)
|
||||
var keystart = i
|
||||
while s[i] != '=' and s[i] != '\0': inc(i)
|
||||
var keyend = i-1
|
||||
if s[i] == '\0': break
|
||||
inc(i) # skip '='
|
||||
var valstart = i
|
||||
while s[i] != ';' and s[i] != '\0': inc(i)
|
||||
result[copy(s, keystart, keyend)] = copy(s, valstart, i-1)
|
||||
if s[i] == '\0': break
|
||||
inc(i) # skip ';'
|
||||
gcookies: PStringTable = nil
|
||||
|
||||
proc getCookie*(name: string): string =
|
||||
## Gets a cookie. If no cookie of `name` exists, "" is returned.
|
||||
if cookies == nil: cookies = parseCookies(getHttpCookie())
|
||||
result = cookies[name]
|
||||
if gcookies == nil: gcookies = parseCookies(getHttpCookie())
|
||||
result = gcookies[name]
|
||||
|
||||
proc existsCookie*(name: string): bool =
|
||||
## Checks if a cookie of `name` exists.
|
||||
if cookies == nil: cookies = parseCookies(getHttpCookie())
|
||||
result = hasKey(cookies, name)
|
||||
if gcookies == nil: gcookies = parseCookies(getHttpCookie())
|
||||
result = hasKey(gcookies, name)
|
||||
|
||||
|
||||
|
|
|
|||
30
lib/pure/cookies.nim
Normal file
30
lib/pure/cookies.nim
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements helper procs for parsing Cookies.
|
||||
|
||||
import strtabs
|
||||
|
||||
proc parseCookies*(s: string): PStringTable =
|
||||
## parses cookies into a string table.
|
||||
result = newStringTable(modeCaseInsensitive)
|
||||
var i = 0
|
||||
while true:
|
||||
while s[i] == ' ' or s[i] == '\t': inc(i)
|
||||
var keystart = i
|
||||
while s[i] != '=' and s[i] != '\0': inc(i)
|
||||
var keyend = i-1
|
||||
if s[i] == '\0': break
|
||||
inc(i) # skip '='
|
||||
var valstart = i
|
||||
while s[i] != ';' and s[i] != '\0': inc(i)
|
||||
result[copy(s, keystart, keyend)] = copy(s, valstart, i-1)
|
||||
if s[i] == '\0': break
|
||||
inc(i) # skip ';'
|
||||
|
||||
|
|
@ -136,9 +136,12 @@ proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) =
|
|||
if meth == reqPost:
|
||||
# get from client and post to CGI program:
|
||||
var buf = alloc(contentLength)
|
||||
if recv(client, buf, contentLength) != contentLength: OSError()
|
||||
if recv(client, buf, contentLength) != contentLength:
|
||||
dealloc(buf)
|
||||
OSError()
|
||||
var inp = process.inputStream
|
||||
inp.writeData(inp, buf, contentLength)
|
||||
dealloc(buf)
|
||||
|
||||
var outp = process.outputStream
|
||||
while running(process) or not outp.atEnd(outp):
|
||||
|
|
@ -153,10 +156,21 @@ proc acceptRequest(client: TSocket) =
|
|||
var query = ""
|
||||
var buf = ""
|
||||
discard recvLine(client, buf)
|
||||
var path = ""
|
||||
var data = buf.split()
|
||||
var meth = reqGet
|
||||
|
||||
var q = find(data[1], '?')
|
||||
|
||||
# extract path
|
||||
if q >= 0:
|
||||
# strip "?..." from path, this may be found in both POST and GET
|
||||
path = "." & data[1].copy(0, q-1)
|
||||
else:
|
||||
path = "." & data[1]
|
||||
# path starts with "/", by adding "." in front of it we serve files from cwd
|
||||
|
||||
if cmpIgnoreCase(data[0], "GET") == 0:
|
||||
var q = find(data[1], '?')
|
||||
if q >= 0:
|
||||
cgi = true
|
||||
query = data[1].copy(q+1)
|
||||
|
|
@ -166,7 +180,6 @@ proc acceptRequest(client: TSocket) =
|
|||
else:
|
||||
unimplemented(client)
|
||||
|
||||
var path = data[1]
|
||||
if path[path.len-1] == '/' or existsDir(path):
|
||||
path = path / "index.html"
|
||||
|
||||
|
|
@ -221,7 +234,7 @@ proc next*(s: var TServer) =
|
|||
var buf = ""
|
||||
discard recvLine(s.client, buf)
|
||||
var data = buf.split()
|
||||
if cmpIgnoreCase(data[0], "GET") == 0:
|
||||
if cmpIgnoreCase(data[0], "GET") == 0 or cmpIgnoreCase(data[0], "POST") == 0:
|
||||
var q = find(data[1], '?')
|
||||
if q >= 0:
|
||||
s.query = data[1].copy(q+1)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,11 @@ type
|
|||
pkAny, ## any character (.)
|
||||
pkAnyRune, ## any Unicode character (_)
|
||||
pkNewLine, ## CR-LF, LF, CR
|
||||
pkLetter, ## Unicode letter
|
||||
pkLower, ## Unicode lower case letter
|
||||
pkUpper, ## Unicode upper case letter
|
||||
pkTitle, ## Unicode title character
|
||||
pkWhitespace, ## Unicode whitespace character
|
||||
pkTerminal,
|
||||
pkTerminalIgnoreCase,
|
||||
pkTerminalIgnoreStyle,
|
||||
|
|
@ -71,7 +76,7 @@ type
|
|||
rule: TNode ## the rule that the symbol refers to
|
||||
TNode {.final.} = object
|
||||
case kind: TPegKind
|
||||
of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine: nil
|
||||
of pkEmpty..pkWhitespace: nil
|
||||
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
|
||||
of pkChar, pkGreedyRepChar: ch: char
|
||||
of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
|
||||
|
|
@ -196,6 +201,7 @@ proc `@`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsSearch".} =
|
|||
|
||||
proc `@@`*(a: TPeg): TPeg {.noSideEffect, rtl,
|
||||
extern: "npgegsCapturedSearch".} =
|
||||
## constructs a "captured search" for the PEG `a`
|
||||
result.kind = pkCapturedSearch
|
||||
result.sons = @[a]
|
||||
|
||||
|
|
@ -237,6 +243,27 @@ proc newLine*: TPeg {.inline.} =
|
|||
## constructs the PEG `newline`:idx: (``\n``)
|
||||
result.kind = pkNewline
|
||||
|
||||
proc UnicodeLetter*: TPeg {.inline.} =
|
||||
## constructs the PEG ``\letter`` which matches any Unicode letter.
|
||||
result.kind = pkLetter
|
||||
|
||||
proc UnicodeLower*: TPeg {.inline.} =
|
||||
## constructs the PEG ``\lower`` which matches any Unicode lowercase letter.
|
||||
result.kind = pkLower
|
||||
|
||||
proc UnicodeUpper*: TPeg {.inline.} =
|
||||
## constructs the PEG ``\upper`` which matches any Unicode lowercase letter.
|
||||
result.kind = pkUpper
|
||||
|
||||
proc UnicodeTitle*: TPeg {.inline.} =
|
||||
## constructs the PEG ``\title`` which matches any Unicode title letter.
|
||||
result.kind = pkTitle
|
||||
|
||||
proc UnicodeWhitespace*: TPeg {.inline.} =
|
||||
## constructs the PEG ``\white`` which matches any Unicode
|
||||
## whitespace character.
|
||||
result.kind = pkWhitespace
|
||||
|
||||
proc capture*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsCapture".} =
|
||||
## constructs a capture with the PEG `a`
|
||||
result.kind = pkCapture
|
||||
|
|
@ -267,8 +294,8 @@ proc spaceCost(n: TPeg): int =
|
|||
case n.kind
|
||||
of pkEmpty: nil
|
||||
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
|
||||
pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, pkAny, pkAnyRune,
|
||||
pkNewLine, pkGreedyAny:
|
||||
pkGreedyRepChar, pkCharChoice, pkGreedyRepSet,
|
||||
pkAny..pkWhitespace, pkGreedyAny:
|
||||
result = 1
|
||||
of pkNonTerminal:
|
||||
# we cannot inline a rule with a non-terminal
|
||||
|
|
@ -379,6 +406,12 @@ proc toStrAux(r: TPeg, res: var string) =
|
|||
of pkEmpty: add(res, "()")
|
||||
of pkAny: add(res, '.')
|
||||
of pkAnyRune: add(res, '_')
|
||||
of pkLetter: add(res, "\\letter")
|
||||
of pkLower: add(res, "\\lower")
|
||||
of pkUpper: add(res, "\\upper")
|
||||
of pkTitle: add(res, "\\title")
|
||||
of pkWhitespace: add(res, "\\white")
|
||||
|
||||
of pkNewline: add(res, "\\n")
|
||||
of pkTerminal: add(res, singleQuoteEsc(r.term))
|
||||
of pkTerminalIgnoreCase:
|
||||
|
|
@ -460,10 +493,15 @@ proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} =
|
|||
# --------------------- core engine -------------------------------------------
|
||||
|
||||
type
|
||||
TMatchClosure {.final.} = object
|
||||
TCaptures* {.final.} = object ## contains the captured substrings.
|
||||
matches: array[0..maxSubpatterns-1, tuple[first, last: int]]
|
||||
ml: int
|
||||
|
||||
proc bounds*(c: TCaptures,
|
||||
i: range[0..maxSubpatterns-1]): tuple[first, last: int] =
|
||||
## returns the bounds ``[first..last]`` of the `i`'th capture.
|
||||
result = c.matches[i]
|
||||
|
||||
when not useUnicode:
|
||||
type
|
||||
TRune = char
|
||||
|
|
@ -472,9 +510,17 @@ when not useUnicode:
|
|||
inc(i)
|
||||
template runeLenAt(s, i: expr): expr = 1
|
||||
|
||||
proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
|
||||
## this implements a simple PEG interpreter. Thanks to superoperators it
|
||||
## has competitive performance nevertheless.
|
||||
proc isAlpha(a: char): bool {.inline.} = return a in {'a'..'z','A'..'Z'}
|
||||
proc isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'}
|
||||
proc isLower(a: char): bool {.inline.} = return a in {'a'..'z'}
|
||||
proc isTitle(a: char): bool {.inline.} = return false
|
||||
proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'}
|
||||
|
||||
proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {.
|
||||
nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## low-level matching proc that implements the PEG interpreter. Use this
|
||||
## for maximum efficiency (every other PEG operation ends up calling this
|
||||
## proc).
|
||||
## Returns -1 if it does not match, else the length of the match
|
||||
case p.kind
|
||||
of pkEmpty: result = 0 # match of length 0
|
||||
|
|
@ -486,6 +532,51 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
|
|||
result = runeLenAt(s, start)
|
||||
else:
|
||||
result = -1
|
||||
of pkLetter:
|
||||
if s[start] != '\0':
|
||||
var a: TRune
|
||||
result = start
|
||||
fastRuneAt(s, result, a)
|
||||
if isAlpha(a): dec(result, start)
|
||||
else: result = -1
|
||||
else:
|
||||
result = -1
|
||||
of pkLower:
|
||||
if s[start] != '\0':
|
||||
var a: TRune
|
||||
result = start
|
||||
fastRuneAt(s, result, a)
|
||||
if isLower(a): dec(result, start)
|
||||
else: result = -1
|
||||
else:
|
||||
result = -1
|
||||
of pkUpper:
|
||||
if s[start] != '\0':
|
||||
var a: TRune
|
||||
result = start
|
||||
fastRuneAt(s, result, a)
|
||||
if isUpper(a): dec(result, start)
|
||||
else: result = -1
|
||||
else:
|
||||
result = -1
|
||||
of pkTitle:
|
||||
if s[start] != '\0':
|
||||
var a: TRune
|
||||
result = start
|
||||
fastRuneAt(s, result, a)
|
||||
if isTitle(a): dec(result, start)
|
||||
else: result = -1
|
||||
else:
|
||||
result = -1
|
||||
of pkWhitespace:
|
||||
if s[start] != '\0':
|
||||
var a: TRune
|
||||
result = start
|
||||
fastRuneAt(s, result, a)
|
||||
if isWhitespace(a): dec(result, start)
|
||||
else: result = -1
|
||||
else:
|
||||
result = -1
|
||||
of pkGreedyAny:
|
||||
result = len(s) - start
|
||||
of pkNewLine:
|
||||
|
|
@ -537,14 +628,14 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
|
|||
of pkNonTerminal:
|
||||
var oldMl = c.ml
|
||||
when false: echo "enter: ", p.nt.name
|
||||
result = m(s, p.nt.rule, start, c)
|
||||
result = rawMatch(s, p.nt.rule, start, c)
|
||||
when false: echo "leave: ", p.nt.name
|
||||
if result < 0: c.ml = oldMl
|
||||
of pkSequence:
|
||||
var oldMl = c.ml
|
||||
result = 0
|
||||
for i in 0..high(p.sons):
|
||||
var x = m(s, p.sons[i], start+result, c)
|
||||
var x = rawMatch(s, p.sons[i], start+result, c)
|
||||
if x < 0:
|
||||
c.ml = oldMl
|
||||
result = -1
|
||||
|
|
@ -553,14 +644,14 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
|
|||
of pkOrderedChoice:
|
||||
var oldMl = c.ml
|
||||
for i in 0..high(p.sons):
|
||||
result = m(s, p.sons[i], start, c)
|
||||
result = rawMatch(s, p.sons[i], start, c)
|
||||
if result >= 0: break
|
||||
c.ml = oldMl
|
||||
of pkSearch:
|
||||
var oldMl = c.ml
|
||||
result = 0
|
||||
while start+result < s.len:
|
||||
var x = m(s, p.sons[0], start+result, c)
|
||||
var x = rawMatch(s, p.sons[0], start+result, c)
|
||||
if x >= 0:
|
||||
inc(result, x)
|
||||
return
|
||||
|
|
@ -572,7 +663,7 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
|
|||
inc(c.ml)
|
||||
result = 0
|
||||
while start+result < s.len:
|
||||
var x = m(s, p.sons[0], start+result, c)
|
||||
var x = rawMatch(s, p.sons[0], start+result, c)
|
||||
if x >= 0:
|
||||
if idx < maxSubpatterns:
|
||||
c.matches[idx] = (start, start+result-1)
|
||||
|
|
@ -585,7 +676,7 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
|
|||
of pkGreedyRep:
|
||||
result = 0
|
||||
while true:
|
||||
var x = m(s, p.sons[0], start+result, c)
|
||||
var x = rawMatch(s, p.sons[0], start+result, c)
|
||||
# if x == 0, we have an endless loop; so the correct behaviour would be
|
||||
# not to break. But endless loops can be easily introduced:
|
||||
# ``(comment / \w*)*`` is such an example. Breaking for x == 0 does the
|
||||
|
|
@ -600,15 +691,15 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
|
|||
result = 0
|
||||
while contains(p.charChoice^, s[start+result]): inc(result)
|
||||
of pkOption:
|
||||
result = max(0, m(s, p.sons[0], start, c))
|
||||
result = max(0, rawMatch(s, p.sons[0], start, c))
|
||||
of pkAndPredicate:
|
||||
var oldMl = c.ml
|
||||
result = m(s, p.sons[0], start, c)
|
||||
result = rawMatch(s, p.sons[0], start, c)
|
||||
if result >= 0: result = 0 # do not consume anything
|
||||
else: c.ml = oldMl
|
||||
of pkNotPredicate:
|
||||
var oldMl = c.ml
|
||||
result = m(s, p.sons[0], start, c)
|
||||
result = rawMatch(s, p.sons[0], start, c)
|
||||
if result < 0: result = 0
|
||||
else:
|
||||
c.ml = oldMl
|
||||
|
|
@ -616,7 +707,7 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
|
|||
of pkCapture:
|
||||
var idx = c.ml # reserve a slot for the subpattern
|
||||
inc(c.ml)
|
||||
result = m(s, p.sons[0], start, c)
|
||||
result = rawMatch(s, p.sons[0], start, c)
|
||||
if result >= 0:
|
||||
if idx < maxSubpatterns:
|
||||
c.matches[idx] = (start, start+result-1)
|
||||
|
|
@ -629,7 +720,7 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
|
|||
var n: TPeg
|
||||
n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef))
|
||||
n.term = s.copy(a, b)
|
||||
result = m(s, n, start, c)
|
||||
result = rawMatch(s, n, start, c)
|
||||
of pkRule, pkList: assert false
|
||||
|
||||
proc match*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||
|
|
@ -638,8 +729,8 @@ proc match*(s: string, pattern: TPeg, matches: var openarray[string],
|
|||
## the captured substrings in the array ``matches``. If it does not
|
||||
## match, nothing is written into ``matches`` and ``false`` is
|
||||
## returned.
|
||||
var c: TMatchClosure
|
||||
result = m(s, pattern, start, c) == len(s) -start
|
||||
var c: TCaptures
|
||||
result = rawMatch(s, pattern, start, c) == len(s) -start
|
||||
if result:
|
||||
for i in 0..c.ml-1:
|
||||
matches[i] = copy(s, c.matches[i][0], c.matches[i][1])
|
||||
|
|
@ -647,8 +738,8 @@ proc match*(s: string, pattern: TPeg, matches: var openarray[string],
|
|||
proc match*(s: string, pattern: TPeg,
|
||||
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
|
||||
var c: TMatchClosure
|
||||
result = m(s, pattern, start, c) == len(s)-start
|
||||
var c: TCaptures
|
||||
result = rawMatch(s, pattern, start, c) == len(s)-start
|
||||
|
||||
proc matchLen*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||
start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||
|
|
@ -656,8 +747,8 @@ proc matchLen*(s: string, pattern: TPeg, matches: var openarray[string],
|
|||
## if there is no match, -1 is returned. Note that a match length
|
||||
## of zero can happen. It's possible that a suffix of `s` remains
|
||||
## that does not belong to the match.
|
||||
var c: TMatchClosure
|
||||
result = m(s, pattern, start, c)
|
||||
var c: TCaptures
|
||||
result = rawMatch(s, pattern, start, c)
|
||||
if result >= 0:
|
||||
for i in 0..c.ml-1:
|
||||
matches[i] = copy(s, c.matches[i][0], c.matches[i][1])
|
||||
|
|
@ -668,8 +759,8 @@ proc matchLen*(s: string, pattern: TPeg,
|
|||
## if there is no match, -1 is returned. Note that a match length
|
||||
## of zero can happen. It's possible that a suffix of `s` remains
|
||||
## that does not belong to the match.
|
||||
var c: TMatchClosure
|
||||
result = m(s, pattern, start, c)
|
||||
var c: TCaptures
|
||||
result = rawMatch(s, pattern, start, c)
|
||||
|
||||
proc find*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||
start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||
|
|
@ -681,6 +772,18 @@ proc find*(s: string, pattern: TPeg, matches: var openarray[string],
|
|||
return -1
|
||||
# could also use the pattern here: (!P .)* P
|
||||
|
||||
proc findBounds*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||
start = 0): tuple[first, last: int] {.
|
||||
nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||
## returns the starting position and end position of ``pattern`` in ``s``
|
||||
## and the captured
|
||||
## substrings in the array ``matches``. If it does not match, nothing
|
||||
## is written into ``matches`` and (-1,0) is returned.
|
||||
for i in start .. s.len-1:
|
||||
var L = matchLen(s, pattern, matches, i)
|
||||
if L >= 0: return (i, i+L-1)
|
||||
return (-1, 0)
|
||||
|
||||
proc find*(s: string, pattern: TPeg,
|
||||
start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
|
||||
## returns the starting position of ``pattern`` in ``s``. If it does not
|
||||
|
|
@ -1351,6 +1454,11 @@ proc primary(p: var TPegParser): TPeg =
|
|||
of "a": result = charset({'a'..'z', 'A'..'Z'})
|
||||
of "A": result = charset({'\1'..'\xff'} - {'a'..'z', 'A'..'Z'})
|
||||
of "ident": result = pegs.ident
|
||||
of "letter": result = UnicodeLetter()
|
||||
of "upper": result = UnicodeUpper()
|
||||
of "lower": result = UnicodeLower()
|
||||
of "title": result = UnicodeTitle()
|
||||
of "white": result = UnicodeWhitespace()
|
||||
else: pegError(p, "unknown built-in: " & p.tok.literal)
|
||||
getTok(p)
|
||||
of tkEscaped:
|
||||
|
|
@ -1439,9 +1547,12 @@ proc rawParse(p: var TPegParser): TPeg =
|
|||
elif ntUsed notin nt.flags and i > 0:
|
||||
pegError(p, "unused rule: " & nt.name, nt.line, nt.col)
|
||||
|
||||
proc parsePeg*(input: string, filename = "pattern", line = 1, col = 0): TPeg =
|
||||
proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): TPeg =
|
||||
## constructs a TPeg object from `pattern`. `filename`, `line`, `col` are
|
||||
## used for error messages, but they only provide start offsets. `parsePeg`
|
||||
## keeps track of line and column numbers within `pattern`.
|
||||
var p: TPegParser
|
||||
init(TPegLexer(p), input, filename, line, col)
|
||||
init(TPegLexer(p), pattern, filename, line, col)
|
||||
p.tok.kind = tkInvalid
|
||||
p.tok.modifier = modNone
|
||||
p.tok.literal = ""
|
||||
|
|
@ -1505,9 +1616,9 @@ when isMainModule:
|
|||
expr.rule = sequence(capture(ident), *sequence(
|
||||
nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr)))
|
||||
|
||||
var c: TMatchClosure
|
||||
var c: TCaptures
|
||||
var s = "a+b + c +d+e+f"
|
||||
assert m(s, expr.rule, 0, c) == len(s)
|
||||
assert rawMatch(s, expr.rule, 0, c) == len(s)
|
||||
var a = ""
|
||||
for i in 0..c.ml-1:
|
||||
a.add(copy(s, c.matches[i][0], c.matches[i][1]))
|
||||
|
|
@ -1559,4 +1670,10 @@ when isMainModule:
|
|||
else:
|
||||
assert false
|
||||
|
||||
|
||||
assert match("eine übersicht und außerdem", peg"(\letter \white*)+")
|
||||
# ß is not a lower cased letter?!
|
||||
assert match("eine übersicht und auerdem", peg"(\lower \white*)+")
|
||||
assert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+")
|
||||
assert(not match("456678", peg"(\letter)+"))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
## This module implements a simple portable type-safe sockets layer.
|
||||
|
||||
import os
|
||||
import os, parseutils
|
||||
|
||||
when defined(Windows):
|
||||
import winlean
|
||||
|
|
@ -146,18 +146,66 @@ proc listen*(socket: TSocket, attempts = 5) =
|
|||
## listens to socket.
|
||||
if listen(cint(socket), cint(attempts)) < 0'i32: OSError()
|
||||
|
||||
proc bindAddr*(socket: TSocket, port = TPort(0)) =
|
||||
## binds a port number to a socket.
|
||||
proc invalidIp4(s: string) {.noreturn, noinline.} =
|
||||
raise newException(EInvalidValue, "invalid ip4 address: " & s)
|
||||
|
||||
proc parseIp4*(s: string): int32 =
|
||||
## parses an IP version 4 in dotted decimal form like "a.b.c.d".
|
||||
## Raises EInvalidValue in case of an error.
|
||||
var a, b, c, d: int
|
||||
var i = 0
|
||||
var j = parseInt(s, a, i)
|
||||
if j <= 0: invalidIp4(s)
|
||||
inc(i, j)
|
||||
if s[i] == '.': inc(i)
|
||||
else: invalidIp4(s)
|
||||
j = parseInt(s, b, i)
|
||||
if j <= 0: invalidIp4(s)
|
||||
inc(i, j)
|
||||
if s[i] == '.': inc(i)
|
||||
else: invalidIp4(s)
|
||||
j = parseInt(s, c, i)
|
||||
if j <= 0: invalidIp4(s)
|
||||
inc(i, j)
|
||||
if s[i] == '.': inc(i)
|
||||
else: invalidIp4(s)
|
||||
j = parseInt(s, d, i)
|
||||
if j <= 0: invalidIp4(s)
|
||||
inc(i, j)
|
||||
if s[i] != '\0': invalidIp4(s)
|
||||
result = int32(a shl 24 or b shl 16 or c shl 8 or d)
|
||||
|
||||
proc bindAddr*(socket: TSocket, port = TPort(0), address = "") =
|
||||
## binds an address/port number to a socket.
|
||||
## Use address string in dotted decimal form like "a.b.c.d"
|
||||
## or leave "" for any address.
|
||||
var name: Tsockaddr_in
|
||||
when defined(Windows):
|
||||
name.sin_family = int16(ord(AF_INET))
|
||||
else:
|
||||
name.sin_family = posix.AF_INET
|
||||
name.sin_port = sockets.htons(int16(port))
|
||||
name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
|
||||
if address == "":
|
||||
name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
|
||||
else:
|
||||
name.sin_addr.s_addr = parseIp4(address)
|
||||
if bindSocket(cint(socket), cast[ptr TSockAddr](addr(name)),
|
||||
sizeof(name)) < 0'i32:
|
||||
OSError()
|
||||
|
||||
when false:
|
||||
proc bindAddr*(socket: TSocket, port = TPort(0)) =
|
||||
## binds a port number to a socket.
|
||||
var name: Tsockaddr_in
|
||||
when defined(Windows):
|
||||
name.sin_family = int16(ord(AF_INET))
|
||||
else:
|
||||
name.sin_family = posix.AF_INET
|
||||
name.sin_port = sockets.htons(int16(port))
|
||||
name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
|
||||
if bindSocket(cint(socket), cast[ptr TSockAddr](addr(name)),
|
||||
sizeof(name)) < 0'i32:
|
||||
OSError()
|
||||
|
||||
proc getSockName*(socket: TSocket): TPort =
|
||||
## returns the socket's associated port number.
|
||||
|
|
@ -409,6 +457,33 @@ proc send*(socket: TSocket, data: string) =
|
|||
## sends data to a socket.
|
||||
if send(socket, cstring(data), data.len) != data.len: OSError()
|
||||
|
||||
when defined(Windows):
|
||||
const
|
||||
SOCKET_ERROR = -1
|
||||
IOCPARM_MASK = 127
|
||||
IOC_IN = int(-2147483648)
|
||||
FIONBIO = int(IOC_IN or ((sizeof(int) and IOCPARM_MASK) shl 16) or
|
||||
(102 shl 8) or 126)
|
||||
|
||||
proc ioctlsocket(s: TWinSocket, cmd: clong,
|
||||
argptr: ptr clong): cint {.
|
||||
stdcall, importc:"ioctlsocket", dynlib: "ws2_32.dll".}
|
||||
|
||||
proc setBlocking*(s: TSocket, blocking: bool) =
|
||||
## sets blocking mode on socket
|
||||
when defined(Windows):
|
||||
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
|
||||
if SOCKET_ERROR == ioctlsocket(TWinSocket(s), FIONBIO, addr(mode)):
|
||||
OSError()
|
||||
else: # BSD sockets
|
||||
var x: int = fcntl(cint(s), F_GETFL, 0)
|
||||
if x == -1:
|
||||
OSError()
|
||||
else:
|
||||
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
|
||||
if fcntl(cint(s), F_SETFL, mode) == -1:
|
||||
OSError()
|
||||
|
||||
when defined(Windows):
|
||||
var wsa: TWSADATA
|
||||
if WSAStartup(0x0101'i16, wsa) != 0: OSError()
|
||||
|
|
|
|||
|
|
@ -62,10 +62,7 @@ proc popCurrentException {.compilerRtl, inl.} =
|
|||
# some platforms have native support for stack traces:
|
||||
const
|
||||
nativeStackTrace = (defined(macosx) or defined(linux)) and
|
||||
not nimrodStackTrace and false
|
||||
|
||||
# `nativeStackTrace` does not work for me --> deactivated for now. Maybe for
|
||||
# the next release version.
|
||||
not nimrodStackTrace
|
||||
|
||||
when nativeStacktrace:
|
||||
type
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue