proc bodies can be expressions with a type

This commit is contained in:
Araq 2012-09-22 22:43:36 +02:00
commit 759b3201ad
12 changed files with 75 additions and 13 deletions

View file

@ -176,6 +176,7 @@ type
nkFromStmt, # a from * import statement nkFromStmt, # a from * import statement
nkIncludeStmt, # an include statement nkIncludeStmt, # an include statement
nkBindStmt, # a bind statement nkBindStmt, # a bind statement
nkMixinStmt, # a mixin statement
nkCommentStmt, # a comment statement nkCommentStmt, # a comment statement
nkStmtListExpr, # a statement list followed by an expr; this is used nkStmtListExpr, # a statement list followed by an expr; this is used
# to allow powerful multi-line templates # to allow powerful multi-line templates

View file

@ -1461,8 +1461,8 @@ proc parseVariable(p: var TParser): PNode =
else: result = parseIdentColonEquals(p, {withPragma}) else: result = parseIdentColonEquals(p, {withPragma})
indAndComment(p, result) # special extension! indAndComment(p, result) # special extension!
proc parseBind(p: var TParser): PNode = proc parseBind(p: var TParser, k: TNodeKind): PNode =
result = newNodeP(nkBindStmt, p) result = newNodeP(k, p)
getTok(p) getTok(p)
optInd(p, result) optInd(p, result)
while true: while true:
@ -1523,7 +1523,8 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
of tkLet: result = parseSection(p, nkLetSection, parseVariable) of tkLet: result = parseSection(p, nkLetSection, parseVariable)
of tkWhen: result = parseIfOrWhen(p, nkWhenStmt) of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
of tkVar: result = parseSection(p, nkVarSection, parseVariable) of tkVar: result = parseSection(p, nkVarSection, parseVariable)
of tkBind: result = parseBind(p) of tkBind: result = parseBind(p, nkBindStmt)
of tkMixin: result = parseBind(p, nkMixinStmt)
else: result = simpleStmt(p) else: result = simpleStmt(p)
proc parseStmt(p: var TParser): PNode = proc parseStmt(p: var TParser): PNode =

View file

@ -386,6 +386,7 @@ proc lsub(n: PNode): int =
of nkDotExpr: result = lsons(n) + 1 of nkDotExpr: result = lsons(n) + 1
of nkBind: result = lsons(n) + len("bind_") of nkBind: result = lsons(n) + len("bind_")
of nkBindStmt: result = lcomma(n) + len("bind_") of nkBindStmt: result = lcomma(n) + len("bind_")
of nkMixinStmt: result = lcomma(n) + len("mixin_")
of nkCheckedFieldExpr: result = lsub(n.sons[0]) of nkCheckedFieldExpr: result = lsub(n.sons[0])
of nkLambda: result = lsons(n) + len("proc__=_") of nkLambda: result = lsons(n) + len("proc__=_")
of nkDo: result = lsons(n) + len("do__:_") of nkDo: result = lsons(n) + len("do__:_")
@ -1152,6 +1153,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
of nkBindStmt: of nkBindStmt:
putWithSpace(g, tkBind, "bind") putWithSpace(g, tkBind, "bind")
gcomma(g, n, c) gcomma(g, n, c)
of nkMixinStmt:
putWithSpace(g, tkMixin, "mixin")
gcomma(g, n, c)
of nkElifBranch: of nkElifBranch:
optNL(g) optNL(g)
putWithSpace(g, tkElif, "elif") putWithSpace(g, tkElif, "elif")

View file

@ -30,6 +30,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc semExprNoType(c: PContext, n: PNode): PNode proc semExprNoType(c: PContext, n: PNode): PNode
proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc semProcBody(c: PContext, n: PNode): PNode
proc fitNode(c: PContext, formal: PType, arg: PNode): PNode proc fitNode(c: PContext, formal: PType, arg: PNode): PNode
proc changeType(n: PNode, newType: PType) proc changeType(n: PNode, newType: PType)

View file

@ -706,11 +706,10 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode =
# and check 'arg' for semantics again: # and check 'arg' for semantics again:
addSon(result, semExpr(c, arg)) addSon(result, semExpr(c, arg))
proc semExprNoType(c: PContext, n: PNode): PNode = proc discardCheck(result: PNode) =
proc ImplicitelyDiscardable(n: PNode): bool {.inline.} = proc ImplicitelyDiscardable(n: PNode): bool {.inline.} =
result = isCallExpr(n) and n.sons[0].kind == nkSym and result = isCallExpr(n) and n.sons[0].kind == nkSym and
sfDiscardable in n.sons[0].sym.flags sfDiscardable in n.sons[0].sym.flags
result = semExpr(c, n, {efWantStmt})
if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}: if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
if result.kind == nkNilLit: if result.kind == nkNilLit:
# XXX too much work and fixing would break bootstrapping: # XXX too much work and fixing would break bootstrapping:
@ -718,7 +717,11 @@ proc semExprNoType(c: PContext, n: PNode): PNode =
result.typ = nil result.typ = nil
elif not ImplicitelyDiscardable(result) and result.typ.kind != tyError and elif not ImplicitelyDiscardable(result) and result.typ.kind != tyError and
gCmd != cmdInteractive: gCmd != cmdInteractive:
localError(n.info, errDiscardValue) localError(result.info, errDiscardValue)
proc semExprNoType(c: PContext, n: PNode): PNode =
result = semExpr(c, n, {efWantStmt})
discardCheck(result)
proc isTypeExpr(n: PNode): bool = proc isTypeExpr(n: PNode): bool =
case n.kind case n.kind
@ -1061,11 +1064,28 @@ proc SemReturn(c: PContext, n: PNode): PNode =
addSon(a, n.sons[0]) addSon(a, n.sons[0])
n.sons[0] = semAsgn(c, a) n.sons[0] = semAsgn(c, a)
# optimize away ``result = result``: # optimize away ``result = result``:
if n[0][1].kind == nkSym and n[0][1].sym.kind == skResult: if n[0][1].kind == nkSym and n[0][1].sym == c.p.resultSym:
n.sons[0] = ast.emptyNode n.sons[0] = ast.emptyNode
else: else:
LocalError(n.info, errNoReturnTypeDeclared) LocalError(n.info, errNoReturnTypeDeclared)
proc semProcBody(c: PContext, n: PNode): PNode =
openScope(c.tab)
result = semExpr(c, n)
if c.p.resultSym != nil and not isEmptyType(result.typ):
# transform ``expr`` to ``result = expr``, but not if the expr is already
# ``result``:
if result.kind == nkSym and result.sym == c.p.resultSym:
nil
else:
var a = newNodeI(nkAsgn, n.info, 2)
a.sons[0] = newSymNode(c.p.resultSym)
a.sons[1] = result
result = semAsgn(c, a)
else:
discardCheck(result)
closeScope(c.tab)
proc SemYieldVarResult(c: PContext, n: PNode, restype: PType) = proc SemYieldVarResult(c: PContext, n: PNode, restype: PType) =
var t = skipTypes(restype, {tyGenericInst}) var t = skipTypes(restype, {tyGenericInst})
case t.kind case t.kind

View file

@ -93,7 +93,7 @@ proc instantiateBody(c: PContext, n: PNode, result: PSym) =
var symMap: TIdTable var symMap: TIdTable
InitIdTable symMap InitIdTable symMap
freshGenSyms(b, result, symMap) freshGenSyms(b, result, symMap)
b = semStmtScope(c, b) b = semProcBody(c, b)
b = hloBody(c, b) b = hloBody(c, b)
n.sons[bodyPos] = transformBody(c.module, b, result) n.sons[bodyPos] = transformBody(c.module, b, result)
#echo "code instantiated ", result.name.s #echo "code instantiated ", result.name.s

View file

@ -767,7 +767,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
addResult(c, s.typ.sons[0], n.info, kind) addResult(c, s.typ.sons[0], n.info, kind)
if sfImportc notin s.flags: if sfImportc notin s.flags:
# no semantic checking for importc: # no semantic checking for importc:
let semBody = hloBody(c, semStmtScope(c, n.sons[bodyPos])) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
# unfortunately we cannot skip this step when in 'system.compiles' # unfortunately we cannot skip this step when in 'system.compiles'
# context as it may even be evaluated in 'system.compiles': # context as it may even be evaluated in 'system.compiles':
n.sons[bodyPos] = transformBody(c.module, semBody, s) n.sons[bodyPos] = transformBody(c.module, semBody, s)

View file

@ -90,6 +90,29 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var TIntSet): PNode =
else: else:
illFormedAst(a) illFormedAst(a)
result = newNodeI(nkNilLit, n.info) result = newNodeI(nkNilLit, n.info)
when false:
# not active before 0.9.0 is out
proc semMixinStmt(c: PContext, n: PNode, toMixin: var TIntSet): PNode =
for i in 0 .. < n.len:
var a = n.sons[i]
# If 'a' is an overloaded symbol, we used to use the first symbol
# as a 'witness' and use the fact that subsequent lookups will yield
# the same symbol!
# This is however not true anymore for hygienic templates as semantic
# processing for them changes the symbol table...
let s = QualifiedLookUp(c, a)
if s != nil:
# we need to mark all symbols:
let sc = symChoice(c, n, s, scForceOpen)
if sc.kind == nkSym:
toMixin.incl(sc.sym.id)
else:
for x in items(sc): toMixin.incl(x.sym.id)
else:
# do nothing: identifiers are already not bound:
nil
result = newNodeI(nkNilLit, n.info)
proc replaceIdentBySym(n: var PNode, s: PNode) = proc replaceIdentBySym(n: var PNode, s: PNode) =
case n.kind case n.kind

View file

@ -46,7 +46,7 @@ type
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt,
nnkIncludeStmt, nnkBindStmt, nnkIncludeStmt, nnkBindStmt, nnkMixinStmt,
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,

View file

@ -13,8 +13,11 @@ type
valueC, valueC,
valueD = (4, "abc") valueD = (4, "abc")
# test the new "proc body can be an expr" feature:
proc getValue: TMyEnum = valueD
# trick the optimizer with a variable: # trick the optimizer with a variable:
var x = valueD var x = getValue()
echo valueA, ord(valueA), valueB, ord(valueB), valueC, valueD, ord(valueD), x echo valueA, ord(valueA), valueB, ord(valueB), valueC, valueD, ord(valueD), x

View file

@ -1,8 +1,13 @@
version 0.9.0 version 0.9.0
============= =============
- make 'bind' default for templates and introduce 'mixin' - fix tirc capture of 'result'
version 0.9.2
=============
- make 'bind' default for templates and introduce 'mixin'
- implicit deref for parameter matching; overloading based on 'var T' - implicit deref for parameter matching; overloading based on 'var T'
- optimize genericAssign in the code generator - optimize genericAssign in the code generator
@ -173,4 +178,6 @@ Version 2 and beyond
case x with `=~` case x with `=~`
- implement ``partial`` pragma for partial evaluation - implement ``partial`` pragma for partial evaluation: easily done with AST
overloading

View file

@ -165,6 +165,8 @@ Language Additions
comment pieces don't have to align on the same column. comment pieces don't have to align on the same column.
- Enums can be annotated with ``pure`` so that their field names do not pollute - Enums can be annotated with ``pure`` so that their field names do not pollute
the current scope. the current scope.
- A proc body can consist of an expression that has a type. This is rewritten
to ``result = expression`` then.
2012-02-09 Version 0.8.14 released 2012-02-09 Version 0.8.14 released