* fix RFC #341: dot-like operators are now parsed with same precedence as `.` * fixup * [skip ci] address comment in changelog * address comment * update grammmar * add manual entry * fixup * -d:nimPreviewDotLikeOps * address comment to unblock PR: move nimPreviewDotLikeOps out of config/config.nims
This commit is contained in:
parent
3d1bba04ab
commit
3aa16c1de0
7 changed files with 74 additions and 5 deletions
|
|
@ -104,6 +104,10 @@
|
||||||
- In `std/dom`, `Interval` is now a `ref object`, same as `Timeout`. Definitions of `setTimeout`,
|
- In `std/dom`, `Interval` is now a `ref object`, same as `Timeout`. Definitions of `setTimeout`,
|
||||||
`clearTimeout`, `setInterval`, `clearInterval` were updated.
|
`clearTimeout`, `setInterval`, `clearInterval` were updated.
|
||||||
|
|
||||||
|
- With `-d:nimPreviewDotLikeOps` (default in devel), dot-like operators (operators starting with `.`, but not with `..`)
|
||||||
|
now have the same precedence as `.`, so that `a.?b.c` is now parsed as `(a.?b).c` instead of `a.?(b.c)`.
|
||||||
|
A warning is generated when a dot-like operator is used without `-d:nimPreviewDotLikeOps`.
|
||||||
|
|
||||||
- The allocator for Nintendo Switch, which was nonfunctional because
|
- The allocator for Nintendo Switch, which was nonfunctional because
|
||||||
of breaking changes in libnx, was removed, in favour of the new `-d:nimAllocPagesViaMalloc` option.
|
of breaking changes in libnx, was removed, in favour of the new `-d:nimAllocPagesViaMalloc` option.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ type
|
||||||
warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape",
|
warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape",
|
||||||
warnXIsNeverRead = "XIsNeverRead", warnXmightNotBeenInit = "XmightNotBeenInit",
|
warnXIsNeverRead = "XIsNeverRead", warnXmightNotBeenInit = "XmightNotBeenInit",
|
||||||
warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated",
|
warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated",
|
||||||
|
warnDotLikeOps = "DotLikeOps",
|
||||||
warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic",
|
warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic",
|
||||||
warnRstRedefinitionOfLabel = "RedefinitionOfLabel",
|
warnRstRedefinitionOfLabel = "RedefinitionOfLabel",
|
||||||
warnRstUnknownSubstitutionX = "UnknownSubstitutionX",
|
warnRstUnknownSubstitutionX = "UnknownSubstitutionX",
|
||||||
|
|
@ -116,6 +117,7 @@ const
|
||||||
warnXmightNotBeenInit: "'$1' might not have been initialized",
|
warnXmightNotBeenInit: "'$1' might not have been initialized",
|
||||||
warnDeprecated: "$1",
|
warnDeprecated: "$1",
|
||||||
warnConfigDeprecated: "config file '$1' is deprecated",
|
warnConfigDeprecated: "config file '$1' is deprecated",
|
||||||
|
warnDotLikeOps: "$1",
|
||||||
warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
|
warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
|
||||||
warnUnknownMagic: "unknown magic '$1' might crash the compiler",
|
warnUnknownMagic: "unknown magic '$1' might crash the compiler",
|
||||||
warnRstRedefinitionOfLabel: "redefinition of label '$1'",
|
warnRstRedefinitionOfLabel: "redefinition of label '$1'",
|
||||||
|
|
|
||||||
|
|
@ -466,6 +466,17 @@ proc dotExpr(p: var Parser, a: PNode): PNode =
|
||||||
exprColonEqExprListAux(p, tkParRi, y)
|
exprColonEqExprListAux(p, tkParRi, y)
|
||||||
result = y
|
result = y
|
||||||
|
|
||||||
|
proc dotLikeExpr(p: var Parser, a: PNode): PNode =
|
||||||
|
#| dotLikeExpr = expr DOTLIKEOP optInd symbol
|
||||||
|
var info = p.parLineInfo
|
||||||
|
result = newNodeI(nkInfix, info)
|
||||||
|
optInd(p, result)
|
||||||
|
var opNode = newIdentNodeP(p.tok.ident, p)
|
||||||
|
getTok(p)
|
||||||
|
result.add(opNode)
|
||||||
|
result.add(a)
|
||||||
|
result.add(parseSymbol(p, smAfterDot))
|
||||||
|
|
||||||
proc qualifiedIdent(p: var Parser): PNode =
|
proc qualifiedIdent(p: var Parser): PNode =
|
||||||
#| qualifiedIdent = symbol ('.' optInd symbol)?
|
#| qualifiedIdent = symbol ('.' optInd symbol)?
|
||||||
result = parseSymbol(p)
|
result = parseSymbol(p)
|
||||||
|
|
@ -788,10 +799,15 @@ proc commandExpr(p: var Parser; r: PNode; mode: PrimaryMode): PNode =
|
||||||
p.hasProgress = false
|
p.hasProgress = false
|
||||||
result.add commandParam(p, isFirstParam, mode)
|
result.add commandParam(p, isFirstParam, mode)
|
||||||
|
|
||||||
|
proc isDotLike(tok: Token): bool =
|
||||||
|
result = tok.tokType == tkOpr and tok.ident.s.len > 1 and
|
||||||
|
tok.ident.s[0] == '.' and tok.ident.s[1] != '.'
|
||||||
|
|
||||||
proc primarySuffix(p: var Parser, r: PNode,
|
proc primarySuffix(p: var Parser, r: PNode,
|
||||||
baseIndent: int, mode: PrimaryMode): PNode =
|
baseIndent: int, mode: PrimaryMode): PNode =
|
||||||
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')'
|
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')'
|
||||||
#| | '.' optInd symbol generalizedLit?
|
#| | '.' optInd symbol generalizedLit?
|
||||||
|
#| | DOTLIKEOP optInd symbol generalizedLit?
|
||||||
#| | '[' optInd exprColonEqExprList optPar ']'
|
#| | '[' optInd exprColonEqExprList optPar ']'
|
||||||
#| | '{' optInd exprColonEqExprList optPar '}'
|
#| | '{' optInd exprColonEqExprList optPar '}'
|
||||||
#| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
|
#| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
|
||||||
|
|
@ -840,6 +856,14 @@ proc primarySuffix(p: var Parser, r: PNode,
|
||||||
# `foo ref` or `foo ptr`. Unfortunately, these two are also
|
# `foo ref` or `foo ptr`. Unfortunately, these two are also
|
||||||
# used as infix operators for the memory regions feature and
|
# used as infix operators for the memory regions feature and
|
||||||
# the current parsing rules don't play well here.
|
# the current parsing rules don't play well here.
|
||||||
|
let isDotLike2 = p.tok.isDotLike
|
||||||
|
if isDotLike2 and p.lex.config.isDefined("nimPreviewDotLikeOps"):
|
||||||
|
# synchronize with `tkDot` branch
|
||||||
|
result = dotLikeExpr(p, result)
|
||||||
|
result = parseGStrLit(p, result)
|
||||||
|
else:
|
||||||
|
if isDotLike2:
|
||||||
|
parMessage(p, warnDotLikeOps, "dot-like operators will be parsed differently with `-d:nimPreviewDotLikeOps`")
|
||||||
if p.inPragma == 0 and (isUnary(p.tok) or p.tok.tokType notin {tkOpr, tkDotDot}):
|
if p.inPragma == 0 and (isUnary(p.tok) or p.tok.tokType notin {tkOpr, tkDotDot}):
|
||||||
# actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
|
# actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
|
||||||
# solution, but pragmas.nim can't handle that
|
# solution, but pragmas.nim can't handle that
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ exprList = expr ^+ comma
|
||||||
exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)?
|
exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)?
|
||||||
dotExpr = expr '.' optInd (symbol | '[:' exprList ']')
|
dotExpr = expr '.' optInd (symbol | '[:' exprList ']')
|
||||||
explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )?
|
explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )?
|
||||||
|
dotLikeExpr = expr DOTLIKEOP optInd symbol
|
||||||
qualifiedIdent = symbol ('.' optInd symbol)?
|
qualifiedIdent = symbol ('.' optInd symbol)?
|
||||||
setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}'
|
setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}'
|
||||||
castExpr = 'cast' ('[' optInd typeDesc optPar ']' '(' optInd expr optPar ')') /
|
castExpr = 'cast' ('[' optInd typeDesc optPar ']' '(' optInd expr optPar ')') /
|
||||||
|
|
@ -56,6 +57,7 @@ tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
|
||||||
arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
|
arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
|
||||||
primarySuffix = '(' (exprColonEqExpr comma?)* ')'
|
primarySuffix = '(' (exprColonEqExpr comma?)* ')'
|
||||||
| '.' optInd symbol generalizedLit?
|
| '.' optInd symbol generalizedLit?
|
||||||
|
| DOTLIKEOP optInd symbol generalizedLit?
|
||||||
| '[' optInd exprColonEqExprList optPar ']'
|
| '[' optInd exprColonEqExprList optPar ']'
|
||||||
| '{' optInd exprColonEqExprList optPar '}'
|
| '{' optInd exprColonEqExprList optPar '}'
|
||||||
| &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
|
| &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
|
||||||
|
|
|
||||||
|
|
@ -748,6 +748,7 @@ has the second-lowest precedence.
|
||||||
|
|
||||||
Otherwise, precedence is determined by the first character.
|
Otherwise, precedence is determined by the first character.
|
||||||
|
|
||||||
|
|
||||||
================ ======================================================= ================== ===============
|
================ ======================================================= ================== ===============
|
||||||
Precedence level Operators First character Terminal symbol
|
Precedence level Operators First character Terminal symbol
|
||||||
================ ======================================================= ================== ===============
|
================ ======================================================= ================== ===============
|
||||||
|
|
@ -783,6 +784,14 @@ of a call or whether it is parsed as a tuple constructor:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
echo (1, 2) # pass the tuple (1, 2) to echo
|
echo (1, 2) # pass the tuple (1, 2) to echo
|
||||||
|
|
||||||
|
Dot-like operators
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Terminal symbol in the grammar: `DOTLIKEOP`.
|
||||||
|
|
||||||
|
Dot-like operators are operators starting with `.`, but not with `..`, for e.g. `.?`;
|
||||||
|
they have the same precedence as `.`, so that `a.?b.c` is parsed as `(a.?b).c` instead of `a.?(b.c)`.
|
||||||
|
|
||||||
|
|
||||||
Grammar
|
Grammar
|
||||||
-------
|
-------
|
||||||
|
|
|
||||||
|
|
@ -36,3 +36,4 @@ switch("define", "nimExperimentalAsyncjsThen")
|
||||||
switch("define", "nimExperimentalLinenoiseExtra")
|
switch("define", "nimExperimentalLinenoiseExtra")
|
||||||
|
|
||||||
switch("define", "nimPreviewFloatRoundtrip")
|
switch("define", "nimPreviewFloatRoundtrip")
|
||||||
|
switch("define", "nimPreviewDotLikeOps")
|
||||||
|
|
|
||||||
27
tests/misc/trfc_341.nim
Normal file
27
tests/misc/trfc_341.nim
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
# test for https://github.com/nim-lang/RFCs/issues/341
|
||||||
|
import std/json
|
||||||
|
import std/jsonutils
|
||||||
|
import std/macros
|
||||||
|
|
||||||
|
macro fn1(a: untyped): string = newLit a.lispRepr
|
||||||
|
|
||||||
|
doAssert fn1(a.?b.c) == """(DotExpr (Infix (Ident ".?") (Ident "a") (Ident "b")) (Ident "c"))"""
|
||||||
|
|
||||||
|
template `.?`(a: JsonNode, b: untyped{ident}): JsonNode =
|
||||||
|
a[astToStr(b)]
|
||||||
|
|
||||||
|
proc identity[T](a: T): T = a
|
||||||
|
proc timesTwo[T](a: T): T = a * 2
|
||||||
|
|
||||||
|
template main =
|
||||||
|
let a = (a1: 1, a2: "abc", a3: (a4: 2.5))
|
||||||
|
let j = a.toJson
|
||||||
|
doAssert j.?a1.getInt == 1
|
||||||
|
doAssert j.?a3.?a4.getFloat == 2.5
|
||||||
|
doAssert j.?a3.?a4.getFloat.timesTwo == 5.0
|
||||||
|
doAssert j.?a3.identity.?a4.getFloat.timesTwo == 5.0
|
||||||
|
doAssert j.identity.?a3.identity.?a4.identity.getFloat.timesTwo == 5.0
|
||||||
|
doAssert j.identity.?a3.?a4.identity.getFloat.timesTwo == 5.0
|
||||||
|
|
||||||
|
static: main()
|
||||||
|
main()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue