Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[superseded] Adding support for user-defined number suffixes #17020

Closed
wants to merge 11 commits into from
24 changes: 8 additions & 16 deletions compiler/lexer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -340,20 +340,12 @@ proc getNumber(L: var Lexer, result: var Token) =
L.bufpos = msgPos
lexMessage(L, msgKind, msg % t.literal)

proc cmpSuffix(L: Lexer, prev: int, endOfSuffix: int, chs: array[1, char]): bool =
if (endOfSuffix - prev) != 2:
proc cmpSuffix(L: Lexer, prev: int, endOfSuffix: int, chs: openArray[char]): bool {.inline.} =
if (endOfSuffix - prev - 1) != chs.len:
return false
return L.buf[prev + 1] == chs[0]

proc cmpSuffix(L: Lexer, prev: int, endOfSuffix: int, chs: array[2, char]): bool =
if (endOfSuffix - prev) != 3:
return false
return L.buf[prev + 1] == chs[0] and L.buf[prev + 2] == chs[1]

proc cmpSuffix(L: Lexer, prev: int, endOfSuffix: int, chs: array[3, char]): bool =
if (endOfSuffix - prev) != 4:
return false
return L.buf[prev + 1] == chs[0] and L.buf[prev + 2] == chs[1] and L.buf[prev + 3] == chs[2]
for i in 0..<chs.len:
if L.buf[prev + 1 + i] != chs[i]: return false
return true

var
startpos, endpos: int
Expand Down Expand Up @@ -425,16 +417,16 @@ proc getNumber(L: var Lexer, result: var Token) =

# Second stage, find out if there's a datatype suffix and handle it
var postPos = endpos
let suffixMarker = (L.buf[postPos] == '\'')
let suffixMarker = L.buf[postPos] == '\''
if suffixMarker:
inc(postPos)
if (L.buf[postPos] notin SymStartChars):
if L.buf[postPos] notin SymStartChars:
lexMessageLitNum(L, "invalid number suffix: '$1'", startpos)

# 2A: handle the builtin literal versions
var internalSuffix = false
var endOfSuffix = postPos
while L.buf[endOfSuffix] in SymStartChars + {'0'..'9'}:
while L.buf[endOfSuffix] in SymChars:
inc(endOfSuffix)
if L.buf[postPos] in {'f', 'F'}:
internalSuffix = true # tentatively found
Expand Down
11 changes: 8 additions & 3 deletions compiler/renderer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1188,9 +1188,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
gcomma(g, n, c)
put(g, tkBracketRi, "]")
of nkDotExpr:
gsub(g, n, 0)
put(g, tkDot, ".")
gsub(g, n, 1)
if n[1].kind == nkIdent and n[1].ident.s[0] == '\'': # TODO: add a re-usable helper function isUserLitteral
assert n[0].kind == nkStrLit
timotheecour marked this conversation as resolved.
Show resolved Hide resolved
put(g, tkStrNumLit, n[0].strVal)
gsub(g, n, 1)
else:
gsub(g, n, 0)
put(g, tkDot, ".")
gsub(g, n, 1)
of nkBind:
putWithSpace(g, tkBind, "bind")
gsub(g, n, 0)
Expand Down
3 changes: 3 additions & 0 deletions tests/lexer/tminushandling.nim
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ assertAST dedent """
Ident "uint"
IntLit -1""":
var x: uint = -1
template bad() =
x = 4 -1
doAssert not compiles(bad())

template main =
block: # check when a minus (-) is a negative sign for a literal
Expand Down
58 changes: 40 additions & 18 deletions tests/lexer/tstrnumlits.nim
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ assertAST dedent """
Ident "\'wrap"""":
-38383839292839283928392839283928392839283.928493849385935898243e-50000'wrap

proc `'wrap`(number: string): string = "[[" & number & "]]"
doAssert lispReprStr(-1'wrap) == """(DotExpr (StrLit "-1") (Ident "\'wrap"))"""

template main =
block: # basic suffix usage
proc `'wrap`(number: string): string =
result = "[[" & number & "]]"
template `'twrap`(number: string): untyped =
number.`'wrap`
proc extraContext(): string =
Expand All @@ -60,12 +61,12 @@ template main =
doAssert 1'wrap == "[[1]]"
doAssert -1'wrap == "[[-1]]":
"unable to resolve a negative integer-suffix pattern"
doAssert lispReprStr(-1'wrap) == """(DotExpr (StrLit "-1") (Ident "\'wrap"))"""
doAssert 12345.67890'wrap == "[[12345.67890]]"
doAssert 1'wrap*1'wrap == "[[1]]times[[1]]":
"unable to resolve an operator between two suffixed numeric literals"
doAssert 1'wrap+ -1'wrap == "[[1]]plus[[-1]]":
doAssert 1'wrap+ -1'wrap == "[[1]]plus[[-1]]": # will generate a compiler warning about inconsistent spacing
"unable to resolve a negative suffixed numeric literal following an operator"
doAssert 1'wrap + -1'wrap == "[[1]]plus[[-1]]"
doAssert 1'twrap == "[[1]]"
doAssert extraContext() == "[[22.40]]":
"unable to return a suffixed numeric literal by an implicit return"
Expand All @@ -78,18 +79,16 @@ template main =

block: # verify that the i64, f32, etc builtin suffixes still parse correctly
const expectedF32: float32 = 123.125
proc `'wrap`(number: string): string =
result = "[[" & number & "]]"
proc `'f9`(number: string): string = # proc starts with 'f' just like 'f32'
result = "[[" & number & "]]"
"[[" & number & "]]"
proc `'f32a`(number: string): string = # looks even more like 'f32'
result = "[[" & number & "]]"
"[[" & number & "]]"
proc `'d9`(number: string): string = # proc starts with 'd' just like the d suffix
result = "[[" & number & "]]"
"[[" & number & "]]"
proc `'i9`(number: string): string = # proc starts with 'i' just like 'i64'
result = "[[" & number & "]]"
"[[" & number & "]]"
proc `'u9`(number: string): string = # proc starts with 'u' just like 'u8'
result = "[[" & number & "]]"
"[[" & number & "]]"

doAssert 123.125f32 == expectedF32:
"failing to support non-quoted legacy f32 floating point suffix"
Expand All @@ -104,17 +103,11 @@ template main =
"failed to properly build AST for suffix that starts with u"
doAssert -128'i8 == (-128).int8

block: # extra parameter and case checks
proc `'wrap`(number: string): string =
result = "[[" & number & "]]"
template `'foo`(a: string, b: int): untyped = (a, b)

doAssert -12'foo(2) == ("-12", 2)
block: # case checks
doAssert 1E2 == 100:
"lexer not handling upper-case exponent"
doAssert 1.0E2 == 100.0
doAssert 1e2 == 100

doAssert 0xdeadBEEF'wrap == "[[0xdeadBEEF]]":
"lexer not maintaining original case"
doAssert 0.1E12'wrap == "[[0.1E12]]"
Expand All @@ -123,5 +116,34 @@ template main =
doAssert 0.0e-12'wrap == "[[0.0e-12]]"
doAssert 0e-12'wrap == "[[0e-12]]"

block: # macro and template usage
template `'foo`(a: string): untyped = (a, 2)
doAssert -12'foo == ("-12", 2)
template `'fooplus`(a: string, b: int): untyped = (a, b)
doAssert -12'fooplus(2) == ("-12", 2)
template `'fooplusopt`(a: string, b: int = 99): untyped = (a, b)
doAssert -12'fooplusopt(2) == ("-12", 2)
doAssert -12'fooplusopt() == ("-12", 99)
doAssert -12'fooplusopt == ("-12", 99)
macro `'bar`(a: static string): untyped =
var infix = newNimNode(nnkInfix)
infix.add newIdentNode("&")
infix.add newLit("got ")
infix.add newLit(a.repr)
result = newNimNode(nnkStmtList)
result.add infix
doAssert -12'bar == "got \"-12\""
macro deb(a): untyped = newLit(a.repr)
doAssert deb(-12'bar) == "-12'bar"
# macro metawrap(): untyped =
# func wrap1(a: string): string = "{" & a & "}"
# func `'wrap2`(a: string): string = "{" & a & "}"
# result = quote do:
# let a1 = wrap1"-128"
# let a2 = -128'wrap2
# metawrap()
# doAssert a1 == "{-128}"
# doAssert a2 == "{-128}"
timotheecour marked this conversation as resolved.
Show resolved Hide resolved

static: main()
main()