[backport] run nimpretty on parsers

This commit is contained in:
narimiran 2019-09-27 11:01:51 +02:00
commit b17ed2ca9c
11 changed files with 207 additions and 193 deletions

View file

@ -52,7 +52,7 @@
import strutils, streams, parsexml, xmltree, unicode, strtabs import strutils, streams, parsexml, xmltree, unicode, strtabs
type type
HtmlTag* = enum ## list of all supported HTML tags; order will always be HtmlTag* = enum ## list of all supported HTML tags; order will always be
## alphabetically ## alphabetically
tagUnknown, ## unknown HTML element tagUnknown, ## unknown HTML element
tagA, ## the HTML ``a`` element tagA, ## the HTML ``a`` element
@ -1945,7 +1945,8 @@ proc untilElementEnd(x: var XmlParser, result: XmlNode,
adderr(expected(x, result)) adderr(expected(x, result))
# this seems to do better match error corrections in browsers: # this seems to do better match error corrections in browsers:
while x.kind in {xmlElementEnd, xmlWhitespace}: while x.kind in {xmlElementEnd, xmlWhitespace}:
if x.kind == xmlElementEnd and cmpIgnoreCase(x.elemName, result.tag) == 0: if x.kind == xmlElementEnd and cmpIgnoreCase(x.elemName,
result.tag) == 0:
break break
next(x) next(x)
next(x) next(x)

View file

@ -406,7 +406,7 @@ macro `%*`*(x: untyped): untyped =
## `%` for every element. ## `%` for every element.
result = toJson(x) result = toJson(x)
proc `==`* (a, b: JsonNode): bool = proc `==`*(a, b: JsonNode): bool =
## Check two nodes for equality ## Check two nodes for equality
if a.isNil: if a.isNil:
if b.isNil: return true if b.isNil: return true
@ -428,13 +428,13 @@ proc `==`* (a, b: JsonNode): bool =
of JArray: of JArray:
result = a.elems == b.elems result = a.elems == b.elems
of JObject: of JObject:
# we cannot use OrderedTable's equality here as # we cannot use OrderedTable's equality here as
# the order does not matter for equality here. # the order does not matter for equality here.
if a.fields.len != b.fields.len: return false if a.fields.len != b.fields.len: return false
for key, val in a.fields: for key, val in a.fields:
if not b.fields.hasKey(key): return false if not b.fields.hasKey(key): return false
if b.fields[key] != val: return false if b.fields[key] != val: return false
result = true result = true
proc hash*(n: OrderedTable[string, JsonNode]): Hash {.noSideEffect.} proc hash*(n: OrderedTable[string, JsonNode]): Hash {.noSideEffect.}
@ -502,7 +502,8 @@ proc contains*(node: JsonNode, val: JsonNode): bool =
assert(node.kind == JArray) assert(node.kind == JArray)
find(node.elems, val) >= 0 find(node.elems, val) >= 0
proc existsKey*(node: JsonNode, key: string): bool {.deprecated: "use 'hasKey' instead".} = proc existsKey*(node: JsonNode, key: string): bool {.
deprecated: "use 'hasKey' instead".} =
node.hasKey(key) node.hasKey(key)
proc `{}`*(node: JsonNode, keys: varargs[string]): JsonNode = proc `{}`*(node: JsonNode, keys: varargs[string]): JsonNode =
@ -538,7 +539,8 @@ proc getOrDefault*(node: JsonNode, key: string): JsonNode =
if not isNil(node) and node.kind == JObject: if not isNil(node) and node.kind == JObject:
result = node.fields.getOrDefault(key) result = node.fields.getOrDefault(key)
template simpleGetOrDefault*{`{}`(node, [key])}(node: JsonNode, key: string): JsonNode = node.getOrDefault(key) template simpleGetOrDefault*{`{}`(node, [key])}(node: JsonNode,
key: string): JsonNode = node.getOrDefault(key)
proc `{}=`*(node: JsonNode, keys: varargs[string], value: JsonNode) = proc `{}=`*(node: JsonNode, keys: varargs[string], value: JsonNode) =
## Traverses the node and tries to set the value at the given location ## Traverses the node and tries to set the value at the given location
@ -691,7 +693,7 @@ proc pretty*(node: JsonNode, indent = 2): string =
## Similar to prettyprint in Python. ## Similar to prettyprint in Python.
runnableExamples: runnableExamples:
let j = %* {"name": "Isaac", "books": ["Robot Dreams"], let j = %* {"name": "Isaac", "books": ["Robot Dreams"],
"details": {"age":35, "pi":3.1415}} "details": {"age": 35, "pi": 3.1415}}
doAssert pretty(j) == """ doAssert pretty(j) == """
{ {
"name": "Isaac", "name": "Isaac",
@ -721,14 +723,14 @@ proc toUgly*(result: var string, node: JsonNode) =
result.add "[" result.add "["
for child in node.elems: for child in node.elems:
if comma: result.add "," if comma: result.add ","
else: comma = true else: comma = true
result.toUgly child result.toUgly child
result.add "]" result.add "]"
of JObject: of JObject:
result.add "{" result.add "{"
for key, value in pairs(node.fields): for key, value in pairs(node.fields):
if comma: result.add "," if comma: result.add ","
else: comma = true else: comma = true
key.escapeJson(result) key.escapeJson(result)
result.add ":" result.add ":"
result.toUgly value result.toUgly value
@ -1331,7 +1333,8 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
( (
var map = `tableInit`[`tableKeyType`, `tableValueType`](); var map = `tableInit`[`tableKeyType`, `tableValueType`]();
verifyJsonKind(`jsonNode`, {JObject}, astToStr(`jsonNode`)); verifyJsonKind(`jsonNode`, {JObject}, astToStr(`jsonNode`));
for `forLoopKey` in keys(`jsonNode`.fields): map[`forLoopKey`] = `constructorNode`; for `forLoopKey` in keys(`jsonNode`.fields): map[
`forLoopKey`] = `constructorNode`;
map map
) )
of "ref": of "ref":
@ -1374,7 +1377,8 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
( (
var list: `typeSym`; var list: `typeSym`;
verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`)); verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
for `forLoopI` in 0 ..< `jsonNode`.len: list[`forLoopI`] =`constructorNode`; for `forLoopI` in 0 ..< `jsonNode`.len: list[
`forLoopI`] = `constructorNode`;
list list
) )
of "tuple": of "tuple":
@ -1640,11 +1644,11 @@ when isMainModule:
except: except:
doAssert(false, "IndexError thrown for valid index") doAssert(false, "IndexError thrown for valid index")
doAssert(testJson{"b"}.getStr()=="asd", "Couldn't fetch a singly nested key with {}") doAssert(testJson{"b"}.getStr() == "asd", "Couldn't fetch a singly nested key with {}")
doAssert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil") doAssert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil")
doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil") doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil") doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
doAssert(testJson{"a"}==parseJson"[1, 2, 3, 4]", "Didn't return a non-JObject when there was one to be found") doAssert(testJson{"a"} == parseJson"[1, 2, 3, 4]", "Didn't return a non-JObject when there was one to be found")
doAssert(isNil(parseJson("[1, 2, 3]"){"foo"}), "Indexing directly into a list should return nil") doAssert(isNil(parseJson("[1, 2, 3]"){"foo"}), "Indexing directly into a list should return nil")
# Generator: # Generator:
@ -1669,10 +1673,10 @@ when isMainModule:
const hisAge = 31 const hisAge = 31
var j3 = %* var j3 = %*
[ { "name": "John" [ {"name": "John"
, "age": herAge , "age": herAge
} }
, { "name": "Susan" , {"name": "Susan"
, "age": hisAge , "age": hisAge
} }
] ]
@ -1708,7 +1712,8 @@ when isMainModule:
except IndexError: doAssert(true) except IndexError: doAssert(true)
var parsed2 = parseFile("tests/testdata/jsontest2.json") var parsed2 = parseFile("tests/testdata/jsontest2.json")
doAssert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}") doAssert(parsed2{"repository", "description"}.str ==
"IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}")
doAssert escapeJsonUnquoted("\10Foo🎃barÄ") == "\\nFoo🎃barÄ" doAssert escapeJsonUnquoted("\10Foo🎃barÄ") == "\\nFoo🎃barÄ"
doAssert escapeJsonUnquoted("\0\7\20") == "\\u0000\\u0007\\u0014" # for #7887 doAssert escapeJsonUnquoted("\0\7\20") == "\\u0000\\u0007\\u0014" # for #7887
@ -1752,15 +1757,15 @@ when isMainModule:
# Generate constructors for range[T] types # Generate constructors for range[T] types
block: block:
type type
Q1 = range[0'u8 .. 50'u8] Q1 = range[0'u8 .. 50'u8]
Q2 = range[0'u16 .. 50'u16] Q2 = range[0'u16 .. 50'u16]
Q3 = range[0'u32 .. 50'u32] Q3 = range[0'u32 .. 50'u32]
Q4 = range[0'i8 .. 50'i8] Q4 = range[0'i8 .. 50'i8]
Q5 = range[0'i16 .. 50'i16] Q5 = range[0'i16 .. 50'i16]
Q6 = range[0'i32 .. 50'i32] Q6 = range[0'i32 .. 50'i32]
Q7 = range[0'f32 .. 50'f32] Q7 = range[0'f32 .. 50'f32]
Q8 = range[0'f64 .. 50'f64] Q8 = range[0'f64 .. 50'f64]
Q9 = range[0 .. 50] Q9 = range[0 .. 50]
X = object X = object
m1: Q1 m1: Q1

View file

@ -15,7 +15,7 @@ import
strutils, streams strutils, streams
const const
EndOfFile* = '\0' ## end of file marker EndOfFile* = '\0' ## end of file marker
NewLines* = {'\c', '\L'} NewLines* = {'\c', '\L'}
# Buffer handling: # Buffer handling:
@ -27,13 +27,13 @@ const
type type
BaseLexer* = object of RootObj ## the base lexer. Inherit your lexer from BaseLexer* = object of RootObj ## the base lexer. Inherit your lexer from
## this object. ## this object.
bufpos*: int ## the current position within the buffer bufpos*: int ## the current position within the buffer
buf*: string ## the buffer itself buf*: string ## the buffer itself
input: Stream ## the input stream input: Stream ## the input stream
lineNumber*: int ## the current line number lineNumber*: int ## the current line number
sentinel: int sentinel: int
lineStart: int # index of last line start in buffer lineStart: int # index of last line start in buffer
offsetBase*: int # use ``offsetBase + bufpos`` to get the offset offsetBase*: int # use ``offsetBase + bufpos`` to get the offset
refillChars: set[char] refillChars: set[char]
proc close*(L: var BaseLexer) = proc close*(L: var BaseLexer) =
@ -65,11 +65,11 @@ proc fillBuffer(L: var BaseLexer) =
charsRead = L.input.readDataStr(L.buf, toCopy ..< toCopy + L.sentinel + 1) charsRead = L.input.readDataStr(L.buf, toCopy ..< toCopy + L.sentinel + 1)
s = toCopy + charsRead s = toCopy + charsRead
if charsRead < L.sentinel + 1: if charsRead < L.sentinel + 1:
L.buf[s] = EndOfFile # set end marker L.buf[s] = EndOfFile # set end marker
L.sentinel = s L.sentinel = s
else: else:
# compute sentinel: # compute sentinel:
dec(s) # BUGFIX (valgrind) dec(s) # BUGFIX (valgrind)
while true: while true:
assert(s < L.buf.len) assert(s < L.buf.len)
while s >= 0 and L.buf[s] notin L.refillChars: dec(s) while s >= 0 and L.buf[s] notin L.refillChars: dec(s)
@ -92,7 +92,7 @@ proc fillBuffer(L: var BaseLexer) =
proc fillBaseLexer(L: var BaseLexer, pos: int): int = proc fillBaseLexer(L: var BaseLexer, pos: int): int =
assert(pos <= L.sentinel) assert(pos <= L.sentinel)
if pos < L.sentinel: if pos < L.sentinel:
result = pos + 1 # nothing to do result = pos + 1 # nothing to do
else: else:
fillBuffer(L) fillBuffer(L)
L.offsetBase += pos L.offsetBase += pos
@ -142,7 +142,7 @@ proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192;
L.buf = newString(bufLen) L.buf = newString(bufLen)
L.sentinel = bufLen - 1 L.sentinel = bufLen - 1
L.lineStart = 0 L.lineStart = 0
L.lineNumber = 1 # lines start at 1 L.lineNumber = 1 # lines start at 1
fillBuffer(L) fillBuffer(L)
skipUtf8Bom(L) skipUtf8Bom(L)

View file

@ -115,34 +115,34 @@ include "system/inclrtl"
type type
CfgEventKind* = enum ## enumeration of all events that may occur when parsing CfgEventKind* = enum ## enumeration of all events that may occur when parsing
cfgEof, ## end of file reached cfgEof, ## end of file reached
cfgSectionStart, ## a ``[section]`` has been parsed cfgSectionStart, ## a ``[section]`` has been parsed
cfgKeyValuePair, ## a ``key=value`` pair has been detected cfgKeyValuePair, ## a ``key=value`` pair has been detected
cfgOption, ## a ``--key=value`` command line option cfgOption, ## a ``--key=value`` command line option
cfgError ## an error occurred during parsing cfgError ## an error occurred during parsing
CfgEvent* = object of RootObj ## describes a parsing event CfgEvent* = object of RootObj ## describes a parsing event
case kind*: CfgEventKind ## the kind of the event case kind*: CfgEventKind ## the kind of the event
of cfgEof: nil of cfgEof: nil
of cfgSectionStart: of cfgSectionStart:
section*: string ## `section` contains the name of the section*: string ## `section` contains the name of the
## parsed section start (syntax: ``[section]``) ## parsed section start (syntax: ``[section]``)
of cfgKeyValuePair, cfgOption: of cfgKeyValuePair, cfgOption:
key*, value*: string ## contains the (key, value) pair if an option key*, value*: string ## contains the (key, value) pair if an option
## of the form ``--key: value`` or an ordinary ## of the form ``--key: value`` or an ordinary
## ``key= value`` pair has been parsed. ## ``key= value`` pair has been parsed.
## ``value==""`` if it was not specified in the ## ``value==""`` if it was not specified in the
## configuration file. ## configuration file.
of cfgError: ## the parser encountered an error: `msg` of cfgError: ## the parser encountered an error: `msg`
msg*: string ## contains the error message. No exceptions msg*: string ## contains the error message. No exceptions
## are thrown if a parse error occurs. ## are thrown if a parse error occurs.
TokKind = enum TokKind = enum
tkInvalid, tkEof, tkInvalid, tkEof,
tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash
Token = object # a token Token = object # a token
kind: TokKind # the type of the token kind: TokKind # the type of the token
literal: string # the parsed (string) literal literal: string # the parsed (string) literal
CfgParser* = object of BaseLexer ## the parser object. CfgParser* = object of BaseLexer ## the parser object.
tok: Token tok: Token
@ -203,7 +203,7 @@ proc handleDecChars(c: var CfgParser, xi: var int) =
inc(c.bufpos) inc(c.bufpos)
proc getEscapedChar(c: var CfgParser, tok: var Token) = proc getEscapedChar(c: var CfgParser, tok: var Token) =
inc(c.bufpos) # skip '\' inc(c.bufpos) # skip '\'
case c.buf[c.bufpos] case c.buf[c.bufpos]
of 'n', 'N': of 'n', 'N':
add(tok.literal, "\n") add(tok.literal, "\n")
@ -258,11 +258,11 @@ proc handleCRLF(c: var CfgParser, pos: int): int =
else: result = pos else: result = pos
proc getString(c: var CfgParser, tok: var Token, rawMode: bool) = proc getString(c: var CfgParser, tok: var Token, rawMode: bool) =
var pos = c.bufpos + 1 # skip " var pos = c.bufpos + 1 # skip "
tok.kind = tkSymbol tok.kind = tkSymbol
if (c.buf[pos] == '"') and (c.buf[pos + 1] == '"'): if (c.buf[pos] == '"') and (c.buf[pos + 1] == '"'):
# long string literal: # long string literal:
inc(pos, 2) # skip "" inc(pos, 2) # skip ""
# skip leading newline: # skip leading newline:
pos = handleCRLF(c, pos) pos = handleCRLF(c, pos)
while true: while true:
@ -280,13 +280,13 @@ proc getString(c: var CfgParser, tok: var Token, rawMode: bool) =
else: else:
add(tok.literal, c.buf[pos]) add(tok.literal, c.buf[pos])
inc(pos) inc(pos)
c.bufpos = pos + 3 # skip the three """ c.bufpos = pos + 3 # skip the three """
else: else:
# ordinary string literal # ordinary string literal
while true: while true:
var ch = c.buf[pos] var ch = c.buf[pos]
if ch == '"': if ch == '"':
inc(pos) # skip '"' inc(pos) # skip '"'
break break
if ch in {'\c', '\L', lexbase.EndOfFile}: if ch in {'\c', '\L', lexbase.EndOfFile}:
tok.kind = tkInvalid tok.kind = tkInvalid
@ -320,7 +320,7 @@ proc skip(c: var CfgParser) =
of '\c', '\L': of '\c', '\L':
pos = handleCRLF(c, pos) pos = handleCRLF(c, pos)
else: else:
break # EndOfFile also leaves the loop break # EndOfFile also leaves the loop
c.bufpos = pos c.bufpos = pos
proc rawGetTok(c: var CfgParser, tok: var Token) = proc rawGetTok(c: var CfgParser, tok: var Token) =
@ -370,13 +370,13 @@ proc errorStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
## returns a properly formatted error message containing current line and ## returns a properly formatted error message containing current line and
## column information. ## column information.
result = `%`("$1($2, $3) Error: $4", result = `%`("$1($2, $3) Error: $4",
[c.filename, $getLine(c), $getColumn(c), msg]) [c.filename, $getLine(c), $getColumn(c), msg])
proc warningStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} = proc warningStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
## returns a properly formatted warning message containing current line and ## returns a properly formatted warning message containing current line and
## column information. ## column information.
result = `%`("$1($2, $3) Warning: $4", result = `%`("$1($2, $3) Warning: $4",
[c.filename, $getLine(c), $getColumn(c), msg]) [c.filename, $getLine(c), $getColumn(c), msg])
proc ignoreMsg*(c: CfgParser, e: CfgEvent): string {.rtl, extern: "npc$1".} = proc ignoreMsg*(c: CfgParser, e: CfgEvent): string {.rtl, extern: "npc$1".} =
## returns a properly formatted warning message containing that ## returns a properly formatted warning message containing that

View file

@ -71,12 +71,12 @@ import
type type
CsvRow* = seq[string] ## A row in a CSV file. CsvRow* = seq[string] ## A row in a CSV file.
CsvParser* = object of BaseLexer ## The parser object. CsvParser* = object of BaseLexer ## The parser object.
## ##
## It consists of two public fields: ## It consists of two public fields:
## * `row` is the current row ## * `row` is the current row
## * `headers` are the columns that are defined in the csv file ## * `headers` are the columns that are defined in the csv file
## (read using `readHeaderRow <#readHeaderRow,CsvParser>`_). ## (read using `readHeaderRow <#readHeaderRow,CsvParser>`_).
## Used with `rowEntry <#rowEntry,CsvParser,string>`_). ## Used with `rowEntry <#rowEntry,CsvParser,string>`_).
row*: CsvRow row*: CsvRow
filename: string filename: string
sep, quote, esc: char sep, quote, esc: char

View file

@ -15,21 +15,21 @@ import
strutils, lexbase, streams, unicode strutils, lexbase, streams, unicode
type type
JsonEventKind* = enum ## enumeration of all events that may occur when parsing JsonEventKind* = enum ## enumeration of all events that may occur when parsing
jsonError, ## an error occurred during parsing jsonError, ## an error occurred during parsing
jsonEof, ## end of file reached jsonEof, ## end of file reached
jsonString, ## a string literal jsonString, ## a string literal
jsonInt, ## an integer literal jsonInt, ## an integer literal
jsonFloat, ## a float literal jsonFloat, ## a float literal
jsonTrue, ## the value ``true`` jsonTrue, ## the value ``true``
jsonFalse, ## the value ``false`` jsonFalse, ## the value ``false``
jsonNull, ## the value ``null`` jsonNull, ## the value ``null``
jsonObjectStart, ## start of an object: the ``{`` token jsonObjectStart, ## start of an object: the ``{`` token
jsonObjectEnd, ## end of an object: the ``}`` token jsonObjectEnd, ## end of an object: the ``}`` token
jsonArrayStart, ## start of an array: the ``[`` token jsonArrayStart, ## start of an array: the ``[`` token
jsonArrayEnd ## start of an array: the ``]`` token jsonArrayEnd ## start of an array: the ``]`` token
TokKind* = enum # must be synchronized with TJsonEventKind! TokKind* = enum # must be synchronized with TJsonEventKind!
tkError, tkError,
tkEof, tkEof,
tkString, tkString,
@ -45,18 +45,18 @@ type
tkColon, tkColon,
tkComma tkComma
JsonError* = enum ## enumeration that lists all errors that can occur JsonError* = enum ## enumeration that lists all errors that can occur
errNone, ## no error errNone, ## no error
errInvalidToken, ## invalid token errInvalidToken, ## invalid token
errStringExpected, ## string expected errStringExpected, ## string expected
errColonExpected, ## ``:`` expected errColonExpected, ## ``:`` expected
errCommaExpected, ## ``,`` expected errCommaExpected, ## ``,`` expected
errBracketRiExpected, ## ``]`` expected errBracketRiExpected, ## ``]`` expected
errCurlyRiExpected, ## ``}`` expected errCurlyRiExpected, ## ``}`` expected
errQuoteExpected, ## ``"`` or ``'`` expected errQuoteExpected, ## ``"`` or ``'`` expected
errEOC_Expected, ## ``*/`` expected errEOC_Expected, ## ``*/`` expected
errEofExpected, ## EOF expected errEofExpected, ## EOF expected
errExprExpected ## expr expected errExprExpected ## expr expected
ParserState = enum ParserState = enum
stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma, stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma,

View file

@ -155,11 +155,11 @@ import
os, strutils os, strutils
type type
CmdLineKind* = enum ## The detected command line token. CmdLineKind* = enum ## The detected command line token.
cmdEnd, ## End of command line reached cmdEnd, ## End of command line reached
cmdArgument, ## An argument such as a filename cmdArgument, ## An argument such as a filename
cmdLongOption, ## A long option such as --option cmdLongOption, ## A long option such as --option
cmdShortOption ## A short option such as -c cmdShortOption ## A short option such as -c
OptParser* = object of RootObj ## \ OptParser* = object of RootObj ## \
## Implementation of the command line parser. ## Implementation of the command line parser.
## ##
@ -172,10 +172,10 @@ type
longNoVal: seq[string] longNoVal: seq[string]
cmds: seq[string] cmds: seq[string]
idx: int idx: int
kind*: CmdLineKind ## The detected command line token kind*: CmdLineKind ## The detected command line token
key*, val*: TaintedString ## Key and value pair; the key is the option key*, val*: TaintedString ## Key and value pair; the key is the option
## or the argument, and the value is not "" if ## or the argument, and the value is not "" if
## the option was given a value ## the option was given a value
proc parseWord(s: string, i: int, w: var string, proc parseWord(s: string, i: int, w: var string,
delim: set[char] = {'\t', ' '}): int = delim: set[char] = {'\t', ' '}): int =
@ -197,7 +197,7 @@ when declared(os.paramCount):
# we cannot provide this for NimRtl creation on Posix, because we can't # we cannot provide this for NimRtl creation on Posix, because we can't
# access the command line arguments then! # access the command line arguments then!
proc initOptParser*(cmdline = "", shortNoVal: set[char]={}, proc initOptParser*(cmdline = "", shortNoVal: set[char] = {},
longNoVal: seq[string] = @[]; longNoVal: seq[string] = @[];
allowWhitespaceAfterColon = true): OptParser = allowWhitespaceAfterColon = true): OptParser =
## Initializes the command line parser. ## Initializes the command line parser.
@ -235,7 +235,7 @@ when declared(os.paramCount):
result.key = TaintedString"" result.key = TaintedString""
result.val = TaintedString"" result.val = TaintedString""
proc initOptParser*(cmdline: seq[TaintedString], shortNoVal: set[char]={}, proc initOptParser*(cmdline: seq[TaintedString], shortNoVal: set[char] = {},
longNoVal: seq[string] = @[]; longNoVal: seq[string] = @[];
allowWhitespaceAfterColon = true): OptParser = allowWhitespaceAfterColon = true): OptParser =
## Initializes the command line parser. ## Initializes the command line parser.
@ -345,7 +345,8 @@ proc next*(p: var OptParser) {.rtl, extern: "npo$1".} =
inc(i) inc(i)
while i < p.cmds[p.idx].len and p.cmds[p.idx][i] in {'\t', ' '}: inc(i) while i < p.cmds[p.idx].len and p.cmds[p.idx][i] in {'\t', ' '}: inc(i)
# if we're at the end, use the next command line option: # if we're at the end, use the next command line option:
if i >= p.cmds[p.idx].len and p.idx < p.cmds.len and p.allowWhitespaceAfterColon: if i >= p.cmds[p.idx].len and p.idx < p.cmds.len and
p.allowWhitespaceAfterColon:
inc p.idx inc p.idx
i = 0 i = 0
if p.idx < p.cmds.len: if p.idx < p.cmds.len:
@ -403,7 +404,8 @@ proc remainingArgs*(p: OptParser): seq[TaintedString] {.rtl, extern: "npo$1".} =
result = @[] result = @[]
for i in p.idx..<p.cmds.len: result.add TaintedString(p.cmds[i]) for i in p.idx..<p.cmds.len: result.add TaintedString(p.cmds[i])
iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key, val: TaintedString] = iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key,
val: TaintedString] =
## Convenience iterator for iterating over the given ## Convenience iterator for iterating over the given
## `OptParser<#OptParser>`_. ## `OptParser<#OptParser>`_.
## ##
@ -443,7 +445,7 @@ iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key, val: TaintedSt
when declared(initOptParser): when declared(initOptParser):
iterator getopt*(cmdline: seq[TaintedString] = commandLineParams(), iterator getopt*(cmdline: seq[TaintedString] = commandLineParams(),
shortNoVal: set[char]={}, longNoVal: seq[string] = @[]): shortNoVal: set[char] = {}, longNoVal: seq[string] = @[]):
tuple[kind: CmdLineKind, key, val: TaintedString] = tuple[kind: CmdLineKind, key, val: TaintedString] =
## Convenience iterator for iterating over command line arguments. ## Convenience iterator for iterating over command line arguments.
## ##
@ -484,7 +486,8 @@ when declared(initOptParser):
## if filename == "": ## if filename == "":
## # no filename has been written, so we show the help ## # no filename has been written, so we show the help
## writeHelp() ## writeHelp()
var p = initOptParser(cmdline, shortNoVal=shortNoVal, longNoVal=longNoVal) var p = initOptParser(cmdline, shortNoVal = shortNoVal,
longNoVal = longNoVal)
while true: while true:
next(p) next(p)
if p.kind == cmdEnd: break if p.kind == cmdEnd: break

View file

@ -18,12 +18,12 @@ import
# ------------------- scanner ------------------------------------------------- # ------------------- scanner -------------------------------------------------
type type
TokKind = enum ## enumeration of all SQL tokens TokKind = enum ## enumeration of all SQL tokens
tkInvalid, ## invalid token tkInvalid, ## invalid token
tkEof, ## end of file reached tkEof, ## end of file reached
tkIdentifier, ## abc tkIdentifier, ## abc
tkQuotedIdentifier, ## "abc" tkQuotedIdentifier, ## "abc"
tkStringConstant, ## 'abc' tkStringConstant, ## 'abc'
tkEscapeConstant, ## e'abc' tkEscapeConstant, ## e'abc'
tkDollarQuotedConstant, ## $tag$abc$tag$ tkDollarQuotedConstant, ## $tag$abc$tag$
tkBitStringConstant, ## B'00011' tkBitStringConstant, ## B'00011'
@ -40,9 +40,9 @@ type
tkBracketRi, ## ']' tkBracketRi, ## ']'
tkDot ## '.' tkDot ## '.'
Token = object # a token Token = object # a token
kind: TokKind # the type of the token kind: TokKind # the type of the token
literal: string # the parsed (string) literal literal: string # the parsed (string) literal
SqlLexer* = object of BaseLexer ## the parser object. SqlLexer* = object of BaseLexer ## the parser object.
filename: string filename: string
@ -162,7 +162,7 @@ proc skip(c: var SqlLexer) =
break break
of '/': of '/':
if c.buf[pos+1] == '*': if c.buf[pos+1] == '*':
inc(pos,2) inc(pos, 2)
while true: while true:
case c.buf[pos] case c.buf[pos]
of '\0': break of '\0': break
@ -186,7 +186,7 @@ proc skip(c: var SqlLexer) =
of '\c', '\L': of '\c', '\L':
pos = handleCRLF(c, pos) pos = handleCRLF(c, pos)
else: else:
break # EndOfFile also leaves the loop break # EndOfFile also leaves the loop
c.bufpos = pos c.bufpos = pos
proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) = proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) =
@ -264,12 +264,13 @@ proc getSymbol(c: var SqlLexer, tok: var Token) =
while true: while true:
add(tok.literal, c.buf[pos]) add(tok.literal, c.buf[pos])
inc(pos) inc(pos)
if c.buf[pos] notin {'a'..'z','A'..'Z','0'..'9','_','$', '\128'..'\255'}: if c.buf[pos] notin {'a'..'z', 'A'..'Z', '0'..'9', '_', '$',
'\128'..'\255'}:
break break
c.bufpos = pos c.bufpos = pos
tok.kind = tkIdentifier tok.kind = tkIdentifier
proc getQuotedIdentifier(c: var SqlLexer, tok: var Token, quote='\"') = proc getQuotedIdentifier(c: var SqlLexer, tok: var Token, quote = '\"') =
var pos = c.bufpos + 1 var pos = c.bufpos + 1
tok.kind = tkQuotedIdentifier tok.kind = tkQuotedIdentifier
while true: while true:
@ -403,7 +404,7 @@ proc getTok(c: var SqlLexer, tok: var Token) =
of 'x', 'X': of 'x', 'X':
if c.buf[c.bufpos + 1] == '\'': if c.buf[c.bufpos + 1] == '\'':
tok.kind = tkHexStringConstant tok.kind = tkHexStringConstant
getBitHexString(c, tok, {'a'..'f','A'..'F','0'..'9'}) getBitHexString(c, tok, {'a'..'f', 'A'..'F', '0'..'9'})
else: else:
getSymbol(c, tok) getSymbol(c, tok)
of '$': getDollarString(c, tok) of '$': getDollarString(c, tok)
@ -504,7 +505,7 @@ type
nkConstraint, nkConstraint,
nkUnique, nkUnique,
nkIdentity, nkIdentity,
nkColumnDef, ## name, datatype, constraints nkColumnDef, ## name, datatype, constraints
nkInsert, nkInsert,
nkUpdate, nkUpdate,
nkDelete, nkDelete,
@ -543,14 +544,14 @@ const
type type
SqlParseError* = object of ValueError ## Invalid SQL encountered SqlParseError* = object of ValueError ## Invalid SQL encountered
SqlNode* = ref SqlNodeObj ## an SQL abstract syntax tree node SqlNode* = ref SqlNodeObj ## an SQL abstract syntax tree node
SqlNodeObj* = object ## an SQL abstract syntax tree node SqlNodeObj* = object ## an SQL abstract syntax tree node
case kind*: SqlNodeKind ## kind of syntax tree case kind*: SqlNodeKind ## kind of syntax tree
of LiteralNodes: of LiteralNodes:
strVal*: string ## AST leaf: the identifier, numeric literal strVal*: string ## AST leaf: the identifier, numeric literal
## string literal, etc. ## string literal, etc.
else: else:
sons*: seq[SqlNode] ## the node's children sons*: seq[SqlNode] ## the node's children
SqlParser* = object of SqlLexer ## SQL parser object SqlParser* = object of SqlLexer ## SQL parser object
tok: Token tok: Token
@ -714,7 +715,8 @@ proc identOrLiteral(p: var SqlParser): SqlNode =
getTok(p) # we must consume a token here to prevent endless loops! getTok(p) # we must consume a token here to prevent endless loops!
proc primary(p: var SqlParser): SqlNode = proc primary(p: var SqlParser): SqlNode =
if (p.tok.kind == tkOperator and (p.tok.literal == "+" or p.tok.literal == "-")) or isKeyw(p, "not"): if (p.tok.kind == tkOperator and (p.tok.literal == "+" or p.tok.literal ==
"-")) or isKeyw(p, "not"):
result = newNode(nkPrefix) result = newNode(nkPrefix)
result.add(newNode(nkIdent, p.tok.literal)) result.add(newNode(nkIdent, p.tok.literal))
getTok(p) getTok(p)
@ -1439,7 +1441,7 @@ proc ra(n: SqlNode, s: var SqlWriter) =
s.addKeyw("enum") s.addKeyw("enum")
rs(n, s) rs(n, s)
proc renderSQL*(n: SqlNode, upperCase=false): string = proc renderSQL*(n: SqlNode, upperCase = false): string =
## Converts an SQL abstract syntax tree to its string representation. ## Converts an SQL abstract syntax tree to its string representation.
var s: SqlWriter var s: SqlWriter
s.buffer = "" s.buffer = ""
@ -1493,7 +1495,7 @@ when not defined(js):
finally: finally:
close(p) close(p)
proc parseSQL*(input: string, filename=""): SqlNode = proc parseSQL*(input: string, filename = ""): SqlNode =
## parses the SQL from `input` into an AST and returns the AST. ## parses the SQL from `input` into an AST and returns the AST.
## `filename` is only used for error messages. ## `filename` is only used for error messages.
## Syntax errors raise an `SqlParseError` exception. ## Syntax errors raise an `SqlParseError` exception.

View file

@ -48,9 +48,9 @@
## * `other parsers<lib.html#pure-libraries-parsers>`_ for other parsers ## * `other parsers<lib.html#pure-libraries-parsers>`_ for other parsers
{.deadCodeElim: on.} # dce option deprecated {.deadCodeElim: on.} # dce option deprecated
{.push debugger:off .} # the user does not want to trace a part {.push debugger: off.} # the user does not want to trace a part
# of the standard library! # of the standard library!
include "system/inclrtl" include "system/inclrtl"
@ -64,8 +64,8 @@ const
proc toLower(c: char): char {.inline.} = proc toLower(c: char): char {.inline.} =
result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0, maxLen = 0): int proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0,
{.noSideEffect.} = maxLen = 0): int {.noSideEffect.} =
## Parses a binary number and stores its value in ``number``. ## Parses a binary number and stores its value in ``number``.
## ##
## Returns the number of the parsed characters or 0 in case of an error. ## Returns the number of the parsed characters or 0 in case of an error.
@ -111,8 +111,8 @@ proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0, maxLen = 0):
number = output number = output
result = i - start result = i - start
proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0, maxLen = 0): int proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0,
{.noSideEffect.} = maxLen = 0): int {.noSideEffect.} =
## Parses an octal number and stores its value in ``number``. ## Parses an octal number and stores its value in ``number``.
## ##
## Returns the number of the parsed characters or 0 in case of an error. ## Returns the number of the parsed characters or 0 in case of an error.
@ -158,8 +158,8 @@ proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0, maxLen = 0):
number = output number = output
result = i - start result = i - start
proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0, maxLen = 0): int proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0,
{.noSideEffect.} = maxLen = 0): int {.noSideEffect.} =
## Parses a hexadecimal number and stores its value in ``number``. ## Parses a hexadecimal number and stores its value in ``number``.
## ##
## Returns the number of the parsed characters or 0 in case of an error. ## Returns the number of the parsed characters or 0 in case of an error.
@ -468,7 +468,7 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
number = int(res) number = int(res)
proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {. proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {.
raises: [].}= raises: [].} =
## Parses a natural number into ``b``. This cannot raise an overflow ## Parses a natural number into ``b``. This cannot raise an overflow
## error. ``high(int)`` is returned for an overflow. ## error. ``high(int)`` is returned for an overflow.
## The number of processed character is returned. ## The number of processed character is returned.
@ -573,13 +573,13 @@ proc parseFloat*(s: string, number: var float, start = 0): int {.
number = bf number = bf
type type
InterpolatedKind* = enum ## Describes for `interpolatedFragments` InterpolatedKind* = enum ## Describes for `interpolatedFragments`
## which part of the interpolated string is ## which part of the interpolated string is
## yielded; for example in "str$$$var${expr}" ## yielded; for example in "str$$$var${expr}"
ikStr, ## ``str`` part of the interpolated string ikStr, ## ``str`` part of the interpolated string
ikDollar, ## escaped ``$`` part of the interpolated string ikDollar, ## escaped ``$`` part of the interpolated string
ikVar, ## ``var`` part of the interpolated string ikVar, ## ``var`` part of the interpolated string
ikExpr ## ``expr`` part of the interpolated string ikExpr ## ``expr`` part of the interpolated string
iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind, iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
value: string] = value: string] =
@ -649,7 +649,7 @@ when isMainModule:
import sequtils import sequtils
let input = "$test{} $this is ${an{ example}} " let input = "$test{} $this is ${an{ example}} "
let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"), let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"),
(ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")] (ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")]
doAssert toSeq(interpolatedFragments(input)) == expected doAssert toSeq(interpolatedFragments(input)) == expected
var value = 0 var value = 0

View file

@ -155,41 +155,41 @@ import
type type
XmlEventKind* = enum ## enumeration of all events that may occur when parsing XmlEventKind* = enum ## enumeration of all events that may occur when parsing
xmlError, ## an error occurred during parsing xmlError, ## an error occurred during parsing
xmlEof, ## end of file reached xmlEof, ## end of file reached
xmlCharData, ## character data xmlCharData, ## character data
xmlWhitespace, ## whitespace has been parsed xmlWhitespace, ## whitespace has been parsed
xmlComment, ## a comment has been parsed xmlComment, ## a comment has been parsed
xmlPI, ## processing instruction (``<?name something ?>``) xmlPI, ## processing instruction (``<?name something ?>``)
xmlElementStart, ## ``<elem>`` xmlElementStart, ## ``<elem>``
xmlElementEnd, ## ``</elem>`` xmlElementEnd, ## ``</elem>``
xmlElementOpen, ## ``<elem xmlElementOpen, ## ``<elem
xmlAttribute, ## ``key = "value"`` pair xmlAttribute, ## ``key = "value"`` pair
xmlElementClose, ## ``>`` xmlElementClose, ## ``>``
xmlCData, ## ``<![CDATA[`` ... data ... ``]]>`` xmlCData, ## ``<![CDATA[`` ... data ... ``]]>``
xmlEntity, ## &entity; xmlEntity, ## &entity;
xmlSpecial ## ``<! ... data ... >`` xmlSpecial ## ``<! ... data ... >``
XmlErrorKind* = enum ## enumeration that lists all errors that can occur XmlErrorKind* = enum ## enumeration that lists all errors that can occur
errNone, ## no error errNone, ## no error
errEndOfCDataExpected, ## ``]]>`` expected errEndOfCDataExpected, ## ``]]>`` expected
errNameExpected, ## name expected errNameExpected, ## name expected
errSemicolonExpected, ## ``;`` expected errSemicolonExpected, ## ``;`` expected
errQmGtExpected, ## ``?>`` expected errQmGtExpected, ## ``?>`` expected
errGtExpected, ## ``>`` expected errGtExpected, ## ``>`` expected
errEqExpected, ## ``=`` expected errEqExpected, ## ``=`` expected
errQuoteExpected, ## ``"`` or ``'`` expected errQuoteExpected, ## ``"`` or ``'`` expected
errEndOfCommentExpected ## ``-->`` expected errEndOfCommentExpected ## ``-->`` expected
errAttributeValueExpected ## non-empty attribute value expected errAttributeValueExpected ## non-empty attribute value expected
ParserState = enum ParserState = enum
stateStart, stateNormal, stateAttr, stateEmptyElementTag, stateError stateStart, stateNormal, stateAttr, stateEmptyElementTag, stateError
XmlParseOption* = enum ## options for the XML parser XmlParseOption* = enum ## options for the XML parser
reportWhitespace, ## report whitespace reportWhitespace, ## report whitespace
reportComments ## report comments reportComments ## report comments
allowUnquotedAttribs ## allow unquoted attribute values (for HTML) allowUnquotedAttribs ## allow unquoted attribute values (for HTML)
allowEmptyAttribs ## allow empty attributes (without explicit value) allowEmptyAttribs ## allow empty attributes (without explicit value)
XmlParser* = object of BaseLexer ## the parser object. XmlParser* = object of BaseLexer ## the parser object.
a, b, c: string a, b, c: string
@ -399,7 +399,7 @@ proc parseComment(my: var XmlParser) =
my.bufpos = pos my.bufpos = pos
my.kind = xmlComment my.kind = xmlComment
proc parseWhitespace(my: var XmlParser, skip=false) = proc parseWhitespace(my: var XmlParser, skip = false) =
var pos = my.bufpos var pos = my.bufpos
while true: while true:
case my.buf[pos] case my.buf[pos]
@ -562,7 +562,7 @@ proc parseTag(my: var XmlParser) =
my.kind = xmlCharData my.kind = xmlCharData
add(my.a, '<') add(my.a, '<')
return return
parseWhitespace(my, skip=true) parseWhitespace(my, skip = true)
if my.buf[my.bufpos] in NameStartChar: if my.buf[my.bufpos] in NameStartChar:
# an attribute follows: # an attribute follows:
my.kind = xmlElementOpen my.kind = xmlElementOpen
@ -588,7 +588,7 @@ proc parseEndTag(my: var XmlParser) =
my.bufpos = lexbase.handleRefillChar(my, my.bufpos+1) my.bufpos = lexbase.handleRefillChar(my, my.bufpos+1)
#inc(my.bufpos, 2) #inc(my.bufpos, 2)
parseName(my, my.a) parseName(my, my.a)
parseWhitespace(my, skip=true) parseWhitespace(my, skip = true)
if my.buf[my.bufpos] == '>': if my.buf[my.bufpos] == '>':
inc(my.bufpos) inc(my.bufpos)
else: else:
@ -606,7 +606,7 @@ proc parseAttribute(my: var XmlParser) =
return return
let startPos = my.bufpos let startPos = my.bufpos
parseWhitespace(my, skip=true) parseWhitespace(my, skip = true)
if my.buf[my.bufpos] != '=': if my.buf[my.bufpos] != '=':
if allowEmptyAttribs notin my.options or if allowEmptyAttribs notin my.options or
(my.buf[my.bufpos] != '>' and my.bufpos == startPos): (my.buf[my.bufpos] != '>' and my.bufpos == startPos):
@ -614,7 +614,7 @@ proc parseAttribute(my: var XmlParser) =
return return
inc(my.bufpos) inc(my.bufpos)
parseWhitespace(my, skip=true) parseWhitespace(my, skip = true)
var pos = my.bufpos var pos = my.bufpos
if my.buf[pos] in {'\'', '"'}: if my.buf[pos] in {'\'', '"'}:
@ -678,7 +678,7 @@ proc parseAttribute(my: var XmlParser) =
add(my.b, my.buf[pos]) add(my.b, my.buf[pos])
inc pos inc pos
my.bufpos = pos my.bufpos = pos
parseWhitespace(my, skip=true) parseWhitespace(my, skip = true)
proc parseCharData(my: var XmlParser) = proc parseCharData(my: var XmlParser) =
var pos = my.bufpos var pos = my.bufpos
@ -711,8 +711,9 @@ proc rawGetTok(my: var XmlParser) =
of '/': of '/':
parseEndTag(my) parseEndTag(my)
of '!': of '!':
if my.buf[pos+2] == '[' and my.buf[pos+3] == 'C' and my.buf[pos+4] == 'D' and if my.buf[pos+2] == '[' and my.buf[pos+3] == 'C' and
my.buf[pos+5] == 'A' and my.buf[pos+6] == 'T' and my.buf[pos+7] == 'A' and my.buf[pos+4] == 'D' and my.buf[pos+5] == 'A' and
my.buf[pos+6] == 'T' and my.buf[pos+7] == 'A' and
my.buf[pos+8] == '[': my.buf[pos+8] == '[':
parseCDATA(my) parseCDATA(my)
elif my.buf[pos+2] == '-' and my.buf[pos+3] == '-': elif my.buf[pos+2] == '-' and my.buf[pos+3] == '-':
@ -742,7 +743,8 @@ proc getTok(my: var XmlParser) =
of xmlComment: of xmlComment:
if my.options.contains(reportComments): break if my.options.contains(reportComments): break
of xmlWhitespace: of xmlWhitespace:
if my.options.contains(reportWhitespace) or lastKind in {xmlCharData, xmlComment, xmlEntity}: if my.options.contains(reportWhitespace) or lastKind in {xmlCharData,
xmlComment, xmlEntity}:
break break
else: break else: break

View file

@ -59,7 +59,7 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
of xmlError: of xmlError:
errors.add(errorMsg(x)) errors.add(errorMsg(x))
next(x) next(x)
of xmlElementStart: ## ``<elem>`` of xmlElementStart: ## ``<elem>``
result = newElement(x.elementName) result = newElement(x.elementName)
next(x) next(x)
untilElementEnd(x, result, errors) untilElementEnd(x, result, errors)
@ -169,4 +169,5 @@ when isMainModule:
block bug1518: block bug1518:
var err: seq[string] = @[] var err: seq[string] = @[]
assert $parsexml(newStringStream"<tag>One &amp; two</tag>", "temp.xml", err) == "<tag>One &amp; two</tag>" assert $parsexml(newStringStream"<tag>One &amp; two</tag>", "temp.xml",
err) == "<tag>One &amp; two</tag>"