proc bodies can be expressions with a type

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

View File

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

View File

@@ -1461,8 +1461,8 @@ proc parseVariable(p: var TParser): PNode =
else: result = parseIdentColonEquals(p, {withPragma})
indAndComment(p, result) # special extension!
proc parseBind(p: var TParser): PNode =
result = newNodeP(nkBindStmt, p)
proc parseBind(p: var TParser, k: TNodeKind): PNode =
result = newNodeP(k, p)
getTok(p)
optInd(p, result)
while true:
@@ -1523,7 +1523,8 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
of tkLet: result = parseSection(p, nkLetSection, parseVariable)
of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
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)
proc parseStmt(p: var TParser): PNode =

View File

@@ -386,6 +386,7 @@ proc lsub(n: PNode): int =
of nkDotExpr: result = lsons(n) + 1
of nkBind: result = lsons(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 nkLambda: result = lsons(n) + len("proc__=_")
of nkDo: result = lsons(n) + len("do__:_")
@@ -1152,6 +1153,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
of nkBindStmt:
putWithSpace(g, tkBind, "bind")
gcomma(g, n, c)
of nkMixinStmt:
putWithSpace(g, tkMixin, "mixin")
gcomma(g, n, c)
of nkElifBranch:
optNL(g)
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 semExprNoType(c: PContext, n: PNode): 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 changeType(n: PNode, newType: PType)

View File

@@ -706,11 +706,10 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode =
# and check 'arg' for semantics again:
addSon(result, semExpr(c, arg))
proc semExprNoType(c: PContext, n: PNode): PNode =
proc discardCheck(result: PNode) =
proc ImplicitelyDiscardable(n: PNode): bool {.inline.} =
result = isCallExpr(n) and n.sons[0].kind == nkSym and
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.kind == nkNilLit:
# XXX too much work and fixing would break bootstrapping:
@@ -718,7 +717,11 @@ proc semExprNoType(c: PContext, n: PNode): PNode =
result.typ = nil
elif not ImplicitelyDiscardable(result) and result.typ.kind != tyError and
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 =
case n.kind
@@ -1061,11 +1064,28 @@ proc SemReturn(c: PContext, n: PNode): PNode =
addSon(a, n.sons[0])
n.sons[0] = semAsgn(c, a)
# 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
else:
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) =
var t = skipTypes(restype, {tyGenericInst})
case t.kind

View File

@@ -93,7 +93,7 @@ proc instantiateBody(c: PContext, n: PNode, result: PSym) =
var symMap: TIdTable
InitIdTable symMap
freshGenSyms(b, result, symMap)
b = semStmtScope(c, b)
b = semProcBody(c, b)
b = hloBody(c, b)
n.sons[bodyPos] = transformBody(c.module, b, result)
#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)
if sfImportc notin s.flags:
# 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'
# context as it may even be evaluated in 'system.compiles':
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:
illFormedAst(a)
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) =
case n.kind

View File

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

View File

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

View File

@@ -1,8 +1,13 @@
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'
- optimize genericAssign in the code generator
@@ -173,4 +178,6 @@ Version 2 and beyond
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.
- Enums can be annotated with ``pure`` so that their field names do not pollute
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