Renderer bug fixes (#8804)
Fixes #8763: render bug: pure enums not handled correctly Fixes #8762: render bug: binary operators called with quotes rendered incorrectly FIxes #8761: render bug: inversion of operator priorities
This commit is contained in:
parent
36e6ca16d1
commit
c033ff990a
4 changed files with 151 additions and 51 deletions
|
|
@ -884,6 +884,42 @@ proc getOperator(L: var TLexer, tok: var TToken) =
|
||||||
if buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
|
if buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
|
||||||
tok.strongSpaceB = -1
|
tok.strongSpaceB = -1
|
||||||
|
|
||||||
|
proc getPrecedence*(tok: TToken, strongSpaces: bool): int =
|
||||||
|
## Calculates the precedence of the given token.
|
||||||
|
template considerStrongSpaces(x): untyped =
|
||||||
|
x + (if strongSpaces: 100 - tok.strongSpaceA.int*10 else: 0)
|
||||||
|
|
||||||
|
case tok.tokType
|
||||||
|
of tkOpr:
|
||||||
|
let L = tok.ident.s.len
|
||||||
|
let relevantChar = tok.ident.s[0]
|
||||||
|
|
||||||
|
# arrow like?
|
||||||
|
if L > 1 and tok.ident.s[L-1] == '>' and
|
||||||
|
tok.ident.s[L-2] in {'-', '~', '='}: return considerStrongSpaces(1)
|
||||||
|
|
||||||
|
template considerAsgn(value: untyped) =
|
||||||
|
result = if tok.ident.s[L-1] == '=': 1 else: value
|
||||||
|
|
||||||
|
case relevantChar
|
||||||
|
of '$', '^': considerAsgn(10)
|
||||||
|
of '*', '%', '/', '\\': considerAsgn(9)
|
||||||
|
of '~': result = 8
|
||||||
|
of '+', '-', '|': considerAsgn(8)
|
||||||
|
of '&': considerAsgn(7)
|
||||||
|
of '=', '<', '>', '!': result = 5
|
||||||
|
of '.': considerAsgn(6)
|
||||||
|
of '?': result = 2
|
||||||
|
else: considerAsgn(2)
|
||||||
|
of tkDiv, tkMod, tkShl, tkShr: result = 9
|
||||||
|
of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5
|
||||||
|
of tkDotDot: result = 6
|
||||||
|
of tkAnd: result = 4
|
||||||
|
of tkOr, tkXor, tkPtr, tkRef: result = 3
|
||||||
|
else: return -10
|
||||||
|
result = considerStrongSpaces(result)
|
||||||
|
|
||||||
|
|
||||||
proc newlineFollows*(L: TLexer): bool =
|
proc newlineFollows*(L: TLexer): bool =
|
||||||
var pos = L.bufpos
|
var pos = L.bufpos
|
||||||
var buf = L.buf
|
var buf = L.buf
|
||||||
|
|
@ -1249,3 +1285,14 @@ proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream;
|
||||||
result = tok.indent
|
result = tok.indent
|
||||||
if result > 0 or tok.tokType == tkEof: break
|
if result > 0 or tok.tokType == tkEof: break
|
||||||
closeLexer(lex)
|
closeLexer(lex)
|
||||||
|
|
||||||
|
proc getPrecedence*(ident: PIdent): int =
|
||||||
|
## assumes ident is binary operator already
|
||||||
|
var tok: TToken
|
||||||
|
initToken(tok)
|
||||||
|
tok.ident = ident
|
||||||
|
tok.tokType =
|
||||||
|
if tok.ident.id in ord(tokKeywordLow) - ord(tkSymbol) .. ord(tokKeywordHigh) - ord(tkSymbol):
|
||||||
|
TTokType(tok.ident.id + ord(tkSymbol))
|
||||||
|
else: tkOpr
|
||||||
|
getPrecedence(tok, false)
|
||||||
|
|
|
||||||
|
|
@ -252,41 +252,6 @@ proc isRightAssociative(tok: TToken): bool {.inline.} =
|
||||||
result = tok.tokType == tkOpr and tok.ident.s[0] == '^'
|
result = tok.tokType == tkOpr and tok.ident.s[0] == '^'
|
||||||
# or (let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>'))
|
# or (let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>'))
|
||||||
|
|
||||||
proc getPrecedence(tok: TToken, strongSpaces: bool): int =
|
|
||||||
## Calculates the precedence of the given token.
|
|
||||||
template considerStrongSpaces(x): untyped =
|
|
||||||
x + (if strongSpaces: 100 - tok.strongSpaceA.int*10 else: 0)
|
|
||||||
|
|
||||||
case tok.tokType
|
|
||||||
of tkOpr:
|
|
||||||
let L = tok.ident.s.len
|
|
||||||
let relevantChar = tok.ident.s[0]
|
|
||||||
|
|
||||||
# arrow like?
|
|
||||||
if L > 1 and tok.ident.s[L-1] == '>' and
|
|
||||||
tok.ident.s[L-2] in {'-', '~', '='}: return considerStrongSpaces(1)
|
|
||||||
|
|
||||||
template considerAsgn(value: untyped) =
|
|
||||||
result = if tok.ident.s[L-1] == '=': 1 else: value
|
|
||||||
|
|
||||||
case relevantChar
|
|
||||||
of '$', '^': considerAsgn(10)
|
|
||||||
of '*', '%', '/', '\\': considerAsgn(9)
|
|
||||||
of '~': result = 8
|
|
||||||
of '+', '-', '|': considerAsgn(8)
|
|
||||||
of '&': considerAsgn(7)
|
|
||||||
of '=', '<', '>', '!': result = 5
|
|
||||||
of '.': considerAsgn(6)
|
|
||||||
of '?': result = 2
|
|
||||||
else: considerAsgn(2)
|
|
||||||
of tkDiv, tkMod, tkShl, tkShr: result = 9
|
|
||||||
of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5
|
|
||||||
of tkDotDot: result = 6
|
|
||||||
of tkAnd: result = 4
|
|
||||||
of tkOr, tkXor, tkPtr, tkRef: result = 3
|
|
||||||
else: return -10
|
|
||||||
result = considerStrongSpaces(result)
|
|
||||||
|
|
||||||
proc isOperator(tok: TToken): bool =
|
proc isOperator(tok: TToken): bool =
|
||||||
## Determines if the given token is an operator type token.
|
## Determines if the given token is an operator type token.
|
||||||
tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
|
tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
|
||||||
|
|
|
||||||
|
|
@ -307,14 +307,19 @@ proc lsub(g: TSrcGen; n: PNode): int
|
||||||
proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
|
proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
|
||||||
proc skip(t: PType): PType =
|
proc skip(t: PType): PType =
|
||||||
result = t
|
result = t
|
||||||
while result.kind in {tyGenericInst, tyRange, tyVar, tyLent, tyDistinct,
|
while result != nil and result.kind in {tyGenericInst, tyRange, tyVar, tyLent, tyDistinct,
|
||||||
tyOrdinal, tyAlias, tySink}:
|
tyOrdinal, tyAlias, tySink}:
|
||||||
result = lastSon(result)
|
result = lastSon(result)
|
||||||
if n.typ != nil and n.typ.skip.kind in {tyBool, tyEnum}:
|
let typ = n.typ.skip
|
||||||
let enumfields = n.typ.skip.n
|
if typ != nil and typ.kind in {tyBool, tyEnum}:
|
||||||
|
if sfPure in typ.sym.flags:
|
||||||
|
result = typ.sym.name.s & '.'
|
||||||
|
let enumfields = typ.n
|
||||||
# we need a slow linear search because of enums with holes:
|
# we need a slow linear search because of enums with holes:
|
||||||
for e in items(enumfields):
|
for e in items(enumfields):
|
||||||
if e.sym.position == x: return e.sym.name.s
|
if e.sym.position == x:
|
||||||
|
result &= e.sym.name.s
|
||||||
|
return
|
||||||
|
|
||||||
if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8)
|
if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8)
|
||||||
elif nfBase8 in n.flags: result = "0o" & toOct(x, size * 3)
|
elif nfBase8 in n.flags: result = "0o" & toOct(x, size * 3)
|
||||||
|
|
@ -861,6 +866,47 @@ proc isBracket*(n: PNode): bool =
|
||||||
of nkSym: result = n.sym.name.s == "[]"
|
of nkSym: result = n.sym.name.s == "[]"
|
||||||
else: result = false
|
else: result = false
|
||||||
|
|
||||||
|
proc skipHiddenNodes(n: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
while result != nil:
|
||||||
|
if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and result.len > 1:
|
||||||
|
result = result[1]
|
||||||
|
elif result.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString} and
|
||||||
|
result.len > 0:
|
||||||
|
result = result[0]
|
||||||
|
else: break
|
||||||
|
|
||||||
|
proc accentedName(g: var TSrcGen, n: PNode) =
|
||||||
|
if n == nil: return
|
||||||
|
let isOperator =
|
||||||
|
if n.kind == nkIdent and n.ident.s.len > 0 and n.ident.s[0] in OpChars: true
|
||||||
|
elif n.kind == nkSym and n.sym.name.s.len > 0 and n.sym.name.s[0] in OpChars: true
|
||||||
|
else: false
|
||||||
|
|
||||||
|
if isOperator:
|
||||||
|
put(g, tkAccent, "`")
|
||||||
|
gident(g, n)
|
||||||
|
put(g, tkAccent, "`")
|
||||||
|
else:
|
||||||
|
gsub(g, n)
|
||||||
|
|
||||||
|
proc infixArgument(g: var TSrcGen, n: PNode, i: int) =
|
||||||
|
if i >= n.len: return
|
||||||
|
|
||||||
|
var needsParenthesis = false
|
||||||
|
let n_next = n[i].skipHiddenNodes
|
||||||
|
if n_next.kind == nkInfix:
|
||||||
|
if n_next[0].kind in {nkSym, nkIdent} and n[0].kind in {nkSym, nkIdent}:
|
||||||
|
let nextId = if n_next[0].kind == nkSym: n_next[0].sym.name else: n_next[0].ident
|
||||||
|
let nnId = if n[0].kind == nkSym: n[0].sym.name else: n[0].ident
|
||||||
|
if getPrecedence(nextId) < getPrecedence(nnId):
|
||||||
|
needsParenthesis = true
|
||||||
|
if needsParenthesis:
|
||||||
|
put(g, tkParLe, "(")
|
||||||
|
gsub(g, n, i)
|
||||||
|
if needsParenthesis:
|
||||||
|
put(g, tkParRi, ")")
|
||||||
|
|
||||||
proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||||
if isNil(n): return
|
if isNil(n): return
|
||||||
var
|
var
|
||||||
|
|
@ -896,7 +942,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||||
gcomma(g, n, 2)
|
gcomma(g, n, 2)
|
||||||
put(g, tkBracketRi, "]")
|
put(g, tkBracketRi, "]")
|
||||||
elif n.len > 1 and n.lastSon.kind == nkStmtList:
|
elif n.len > 1 and n.lastSon.kind == nkStmtList:
|
||||||
gsub(g, n[0])
|
accentedName(g, n[0])
|
||||||
if n.len > 2:
|
if n.len > 2:
|
||||||
put(g, tkParLe, "(")
|
put(g, tkParLe, "(")
|
||||||
gcomma(g, n, 1, -2)
|
gcomma(g, n, 1, -2)
|
||||||
|
|
@ -904,16 +950,16 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||||
put(g, tkColon, ":")
|
put(g, tkColon, ":")
|
||||||
gsub(g, n, n.len-1)
|
gsub(g, n, n.len-1)
|
||||||
else:
|
else:
|
||||||
if sonsLen(n) >= 1: gsub(g, n.sons[0])
|
if sonsLen(n) >= 1: accentedName(g, n[0])
|
||||||
put(g, tkParLe, "(")
|
put(g, tkParLe, "(")
|
||||||
gcomma(g, n, 1)
|
gcomma(g, n, 1)
|
||||||
put(g, tkParRi, ")")
|
put(g, tkParRi, ")")
|
||||||
of nkCallStrLit:
|
of nkCallStrLit:
|
||||||
gsub(g, n, 0)
|
if n.len > 0: accentedName(g, n[0])
|
||||||
if n.len > 1 and n.sons[1].kind == nkRStrLit:
|
if n.len > 1 and n.sons[1].kind == nkRStrLit:
|
||||||
put(g, tkRStrLit, '\"' & replace(n[1].strVal, "\"", "\"\"") & '\"')
|
put(g, tkRStrLit, '\"' & replace(n[1].strVal, "\"", "\"\"") & '\"')
|
||||||
else:
|
else:
|
||||||
gsub(g, n.sons[1])
|
gsub(g, n, 1)
|
||||||
of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv:
|
of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv:
|
||||||
if n.len >= 2:
|
if n.len >= 2:
|
||||||
gsub(g, n.sons[1])
|
gsub(g, n.sons[1])
|
||||||
|
|
@ -951,7 +997,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||||
gsub(g, n, 0)
|
gsub(g, n, 0)
|
||||||
gcomma(g, n, 1)
|
gcomma(g, n, 1)
|
||||||
of nkCommand:
|
of nkCommand:
|
||||||
gsub(g, n, 0)
|
accentedName(g, n[0])
|
||||||
put(g, tkSpaces, Space)
|
put(g, tkSpaces, Space)
|
||||||
gcomma(g, n, 1)
|
gcomma(g, n, 1)
|
||||||
of nkExprEqExpr, nkAsgn, nkFastAsgn:
|
of nkExprEqExpr, nkAsgn, nkFastAsgn:
|
||||||
|
|
@ -1064,14 +1110,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||||
putWithSpace(g, tkColon, ":")
|
putWithSpace(g, tkColon, ":")
|
||||||
gsub(g, n, 1)
|
gsub(g, n, 1)
|
||||||
of nkInfix:
|
of nkInfix:
|
||||||
gsub(g, n, 1)
|
infixArgument(g, n, 1)
|
||||||
put(g, tkSpaces, Space)
|
put(g, tkSpaces, Space)
|
||||||
gsub(g, n, 0) # binary operator
|
gsub(g, n, 0) # binary operator
|
||||||
if not fits(g, lsub(g, n.sons[2]) + lsub(g, n.sons[0]) + 1):
|
if not fits(g, lsub(g, n.sons[2]) + lsub(g, n.sons[0]) + 1):
|
||||||
optNL(g, g.indent + longIndentWid)
|
optNL(g, g.indent + longIndentWid)
|
||||||
else:
|
else:
|
||||||
put(g, tkSpaces, Space)
|
put(g, tkSpaces, Space)
|
||||||
gsub(g, n, 2)
|
infixArgument(g, n, 2)
|
||||||
of nkPrefix:
|
of nkPrefix:
|
||||||
gsub(g, n, 0)
|
gsub(g, n, 0)
|
||||||
if n.len > 1:
|
if n.len > 1:
|
||||||
|
|
@ -1079,10 +1125,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||||
elif n[0].kind == nkSym: n[0].sym.name
|
elif n[0].kind == nkSym: n[0].sym.name
|
||||||
elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
|
elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
|
||||||
else: nil
|
else: nil
|
||||||
var n_next = n[1]
|
let n_next = skipHiddenNodes(n[1])
|
||||||
while n_next.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref,
|
|
||||||
nkStringToCString, nkCStringToString} and n_next.len > 0:
|
|
||||||
n_next = n_next[0]
|
|
||||||
if n_next.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
|
if n_next.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
|
||||||
put(g, tkSpaces, Space)
|
put(g, tkSpaces, Space)
|
||||||
if n_next.kind == nkInfix:
|
if n_next.kind == nkInfix:
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,10 @@ macro repr_and_parse(fn: typed): typed =
|
||||||
echo fn_impl.repr
|
echo fn_impl.repr
|
||||||
result = parseStmt(fn_impl.repr)
|
result = parseStmt(fn_impl.repr)
|
||||||
|
|
||||||
|
macro repr_to_string(fn: typed): string =
|
||||||
|
let fn_impl = fn.getImpl
|
||||||
|
result = newStrLitNode(fn_impl.repr)
|
||||||
|
|
||||||
repr_and_parse(f)
|
repr_and_parse(f)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -70,8 +74,49 @@ proc test_cond_stmtlist(x, y: int): int =
|
||||||
result = x
|
result = x
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------
|
||||||
|
# bug #8762
|
||||||
|
proc t2(a, b: int): int =
|
||||||
|
`+`(a, b)
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------
|
||||||
|
# bug #8761
|
||||||
|
|
||||||
|
proc fn1(x, y: int):int =
|
||||||
|
2 * (x + y)
|
||||||
|
|
||||||
|
proc fn2(x, y: float): float =
|
||||||
|
(y + 2 * x) / (x - y)
|
||||||
|
|
||||||
|
proc fn3(x, y: int): bool =
|
||||||
|
(((x and 3) div 4) or (x mod (y xor -1))) == 0 or y notin [1,2]
|
||||||
|
|
||||||
|
static:
|
||||||
|
let fn1s = "proc fn1(x, y: int): int =\n result = 2 * (x + y)\n"
|
||||||
|
let fn2s = "proc fn2(x, y: float): float =\n result = (y + 2.0 * x) / (x - y)\n"
|
||||||
|
let fn3s = "proc fn3(x, y: int): bool =\n result = ((x and 3) div 4 or x mod (y xor -1)) == 0 or not contains([1, 2], y)\n"
|
||||||
|
doAssert fn1.repr_to_string == fn1s
|
||||||
|
doAssert fn2.repr_to_string == fn2s
|
||||||
|
doAssert fn3.repr_to_string == fn3s
|
||||||
|
|
||||||
|
#------------------------------------
|
||||||
|
# bug #8763
|
||||||
|
|
||||||
|
type
|
||||||
|
A {.pure.} = enum
|
||||||
|
X, Y
|
||||||
|
B {.pure.} = enum
|
||||||
|
X, Y
|
||||||
|
|
||||||
|
proc test_pure_enums(a: B) =
|
||||||
|
case a
|
||||||
|
of B.X: echo B.X
|
||||||
|
of B.Y: echo B.Y
|
||||||
|
|
||||||
repr_and_parse(one_if_proc)
|
repr_and_parse(one_if_proc)
|
||||||
repr_and_parse(test_block)
|
repr_and_parse(test_block)
|
||||||
repr_and_parse(test_cond_stmtlist)
|
repr_and_parse(test_cond_stmtlist)
|
||||||
|
repr_and_parse(t2)
|
||||||
|
repr_and_parse(test_pure_enums)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue