Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ type
nkBreakState, # special break statement for easier code generation
nkFuncDef, # a func
nkTupleConstr # a tuple constructor
nkCustomDefSection, # a custom definition section, eg `byAddr foo = bar`

TNodeKinds* = set[TNodeKind]

Expand Down
8 changes: 5 additions & 3 deletions compiler/lexer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type
tkTemplate,
tkTry, tkTuple, tkType, tkUsing,
tkVar, tkWhen, tkWhile, tkXor,
tkYield, # end of keywords
tkYield, tkByAddr, # end of keywords
tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit,
tkUIntLit, tkUInt8Lit, tkUInt16Lit, tkUInt32Lit, tkUInt64Lit,
tkFloatLit, tkFloat32Lit, tkFloat64Lit, tkFloat128Lit,
Expand All @@ -66,6 +66,8 @@ type

TTokTypes* = set[TTokType]

const customDefTokens* = {tkByAddr}

const
weakTokens = {tkComma, tkSemiColon, tkColon,
tkParRi, tkParDotRi, tkBracketRi, tkBracketDotRi,
Expand All @@ -90,7 +92,7 @@ const
"template",
"try", "tuple", "type", "using",
"var", "when", "while", "xor",
"yield",
"yield", specialWords[wByAddr],
"tkIntLit", "tkInt8Lit", "tkInt16Lit", "tkInt32Lit", "tkInt64Lit",
"tkUIntLit", "tkUInt8Lit", "tkUInt16Lit", "tkUInt32Lit", "tkUInt64Lit",
"tkFloatLit", "tkFloat32Lit", "tkFloat64Lit", "tkFloat128Lit",
Expand Down Expand Up @@ -1338,7 +1340,7 @@ proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream;
var prevToken = tkEof
while tok.tokType != tkEof:
rawGetTok(lex, tok)
if tok.indent > 0 and prevToken in {tkColon, tkEquals, tkType, tkConst, tkLet, tkVar, tkUsing}:
if tok.indent > 0 and prevToken in {tkColon, tkEquals, tkType, tkConst, tkLet, tkVar, tkUsing} + customDefTokens:
result = tok.indent
if result > 0: break
prevToken = tok.tokType
Expand Down
16 changes: 11 additions & 5 deletions compiler/parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ proc semiStmtList(p: var TParser, result: PNode) =

proc parsePar(p: var TParser): PNode =
#| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
#| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
#| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' | 'byaddr'
#| | 'when' | 'var' | 'mixin'
#| par = '(' optInd
#| ( &parKeyw complexOrSimpleStmt ^+ ';'
Expand All @@ -557,7 +557,7 @@ proc parsePar(p: var TParser): PNode =
if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase,
tkTry, tkDefer, tkFinally, tkExcept, tkFor, tkBlock,
tkConst, tkLet, tkWhen, tkVar, tkFor,
tkMixin}:
tkMixin} + customDefTokens:
# XXX 'bind' used to be an expression, so we exclude it here;
# tests/reject/tbind2 fails otherwise.
semiStmtList(p, result)
Expand Down Expand Up @@ -1793,9 +1793,11 @@ type
TDefParser = proc (p: var TParser): PNode {.nimcall.}

proc parseSection(p: var TParser, kind: TNodeKind,
defparser: TDefParser): PNode =
defparser: TDefParser, tokType = default(TTokType)): PNode =
#| section(RULE) = COMMENT? RULE / (IND{>} (RULE / COMMENT)^+IND{=} DED)
result = newNodeP(kind, p)
if kind == nkCustomDefSection:
result.add newIdentNodeP(p.lex.cache.getIdent(TokTypeToStr[tokType]), p)
if kind != nkTypeSection: getTok(p)
skipComment(p, result)
if realInd(p):
Expand Down Expand Up @@ -2199,10 +2201,11 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
#| | 'converter' routine
#| | 'type' section(typeDef)
#| | 'const' section(constant)
#| | ('let' | 'var' | 'using') section(variable)
#| | ('let' | 'var' | 'using' | 'byaddr') section(variable)
#| | bindStmt | mixinStmt)
#| / simpleStmt
case p.tok.tokType
let tokType = p.tok.tokType
case tokType
of tkIf: result = parseIfOrWhen(p, nkIfStmt)
of tkWhile: result = parseWhile(p)
of tkCase: result = parseCase(p)
Expand Down Expand Up @@ -2237,6 +2240,9 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
of tkLet:
prettySection:
result = parseSection(p, nkLetSection, parseVariable)
of customDefTokens:
prettySection:
result = parseSection(p, nkCustomDefSection, parseVariable, tokType)
of tkVar:
prettySection:
result = parseSection(p, nkVarSection, parseVariable)
Expand Down
30 changes: 22 additions & 8 deletions compiler/renderer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ when defined(nimHasUsed):
{.used.}

import
lexer, options, idents, strutils, ast, msgs, lineinfos
lexer, options, idents, strutils, ast, msgs, lineinfos, wordrecg

type
TRenderFlag* = enum
Expand Down Expand Up @@ -500,9 +500,14 @@ proc lsub(g: TSrcGen; n: PNode): int =
else:
result = len("enum")
of nkEnumFieldDef: result = lsons(g, n) + 3
of nkVarSection, nkLetSection:
of nkVarSection, nkLetSection, nkCustomDefSection:
if n.len > 1: result = MaxLineLen + 1
else: result = lsons(g, n) + len("var_")
else:
if n.kind == nkCustomDefSection:
let kw = whichKeyword(n[0].ident)
result = lsons(g, n, start = 1) + len(specialWords[kw]) + 1
else:
result = lsons(g, n) + len("var_")
of nkUsingStmt:
if n.len > 1: result = MaxLineLen + 1
else: result = lsons(g, n) + len("using_")
Expand Down Expand Up @@ -1344,21 +1349,30 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
initContext(a)
incl(a.flags, rfInConstExpr)
gsection(g, n, a, tkConst, "const")
of nkVarSection, nkLetSection, nkUsingStmt:
of nkVarSection, nkLetSection, nkCustomDefSection, nkUsingStmt:
if n.len == 0: return
var firstDefIndex = 0
if n.kind == nkVarSection: putWithSpace(g, tkVar, "var")
elif n.kind == nkLetSection: putWithSpace(g, tkLet, "let")
elif n.kind == nkCustomDefSection:
firstDefIndex = 1
let kw = whichKeyword(n[0].ident)
case kw
of wByAddr: # we can have more keywords like that, treated in same way
putWithSpace(g, tkByAddr, specialWords[wByAddr])
else:
doAssert false, $kw
else: putWithSpace(g, tkUsing, "using")
if n.len > 1:
if n.len > 1 + firstDefIndex:
gcoms(g)
indentNL(g)
for i in 0..<n.len:
for i in firstDefIndex..<n.len:
optNL(g)
gsub(g, n[i])
gcoms(g)
dedent(g)
else:
gsub(g, n[0])
gsub(g, n[firstDefIndex])
of nkReturnStmt:
putWithSpace(g, tkReturn, "return")
if n.len > 0 and n[0].kind == nkAsgn:
Expand Down Expand Up @@ -1561,7 +1575,7 @@ proc renderModule*(n: PNode, infile, outfile: string,
gsub(g, n[i])
optNL(g)
case n[i].kind
of nkTypeSection, nkConstSection, nkVarSection, nkLetSection,
of nkTypeSection, nkConstSection, nkVarSection, nkLetSection, nkCustomDefSection,
nkCommentStmt: putNL(g)
else: discard
gcoms(g)
Expand Down
9 changes: 9 additions & 0 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2742,6 +2742,15 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkVarSection: result = semVarOrLet(c, n, skVar)
of nkLetSection: result = semVarOrLet(c, n, skLet)
of nkConstSection: result = semConst(c, n)
of nkCustomDefSection:
# cleaner to let user code define it than hard code via `semTemplateDef(c, n)`
result = newTree(nkCall)
result.add newIdentNode(getIdent(c.cache, "byAddrImpl"), n.info)
result.add n
result.add n[1][0]
result.add n[1][1]
result.add n[1][2]
result = semExpr(c, result, flags)
of nkTypeSection: result = semTypeSection(c, n)
of nkDiscardStmt: result = semDiscard(c, n)
of nkWhileStmt: result = semWhile(c, n, flags)
Expand Down
5 changes: 3 additions & 2 deletions compiler/wordrecg.nim
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ type
wMacro, wMethod, wMixin, wMod, wNil,
wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn,
wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wUsing, wVar,
wWhen, wWhile, wXor, wYield,
wWhen, wWhile, wXor, wYield, wByAddr,
# end of keywords here

wColon, wColonColon, wEquals, wDot, wDotDot,
wStar, wMinus,
Expand Down Expand Up @@ -116,7 +117,7 @@ const
"shl", "shr", "static",
"template", "try", "tuple", "type", "using", "var",
"when", "while", "xor",
"yield",
"yield", "byaddr",

":", "::", "=", ".", "..",
"*", "-",
Expand Down
4 changes: 2 additions & 2 deletions doc/grammar.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ qualifiedIdent = symbol ('.' optInd symbol)?
setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}'
castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
| 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
| 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' | 'byaddr'
| 'when' | 'var' | 'mixin'
par = '(' optInd
( &parKeyw complexOrSimpleStmt ^+ ';'
Expand Down Expand Up @@ -200,7 +200,7 @@ complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
| 'converter' routine
| 'type' section(typeDef)
| 'const' section(constant)
| ('let' | 'var' | 'using') section(variable)
| ('let' | 'var' | 'using' | 'byaddr') section(variable)
| bindStmt | mixinStmt)
/ simpleStmt
stmt = (IND{>} complexOrSimpleStmt^+(IND{=} / ';') DED)
Expand Down
3 changes: 2 additions & 1 deletion lib/core/macros.nim
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ type
nnkState,
nnkBreakState,
nnkFuncDef,
nnkTupleConstr
nnkTupleConstr,
nnkCustomDefSection,

NimNodeKinds* = set[NimNodeKind]
NimTypeKind* = enum # some types are no longer used, see ast.nim
Expand Down
14 changes: 14 additions & 0 deletions lib/std/references.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
template byAddrImpl*(n, name, typ, exp): untyped =
## Defines a reference syntax for lvalue expressions, analog to C++ `auto& a = expr`.
## The expression is evaluated only once, and any side effects will only be
## evaluated once, at declaration time.
##
## Note: to use this, use `byaddr x = expr` (not `byAddrImpl`), see examples.
runnableExamples:
var x = @[1,2,3]
let x0=x[1]
byaddr x1=x[1]
x1+=10
doAssert type(x1) is int and x == @[1,12,3]
let myAddr = addr `exp`
template `name`: untyped = myAddr[]
34 changes: 34 additions & 0 deletions tests/stdlib/treferences.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import std/references

block:
var x = @[1,2,3]
let x0=x[1]
byaddr x1=x[1]
x1+=10
doAssert type(x1) is int and x == @[1,12,3]

byaddr
x2=x[1]
doAssert x2.addr == x1.addr

when false:
# this could be supported but would require a macro instead of a simple
# template in `byAddrImpl`
byaddr
x2=x[1]
x3=x[1]
doAssert x2.addr == x3.addr

import std/macros

macro dbg(a): string = newLit a.treeRepr
let s = dbg:
byaddr foo = bar
doAssert s == """
StmtList
CustomDefSection
Ident "byaddr"
IdentDefs
Ident "foo"
Empty
Ident "bar""""
2 changes: 1 addition & 1 deletion tools/grammar_nanny.nim
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import std / [strutils, sets]

import ".." / compiler / [
llstream, ast, lexer, options, msgs, idents,
llstream, lexer, options, msgs, idents,
lineinfos, pathutils]

proc checkGrammarFileImpl(cache: IdentCache, config: ConfigRef) =
Expand Down