Initial implementation of the parseExpr, parseStmt, getAst macro routines:
proc parseExpr*(s: string) : expr {.magic: "ParseExprToAst".}
## Compiles the passed string to its AST representation
## Expects a single expression
proc parseStmt*(s: string) : stmt {.magic: "ParseStmtToAst".}
## Compiles the passed string to its AST representation
## Expects one or more statements
proc getAst*(macroOrTemplate: expr): expr {.magic: "ExpandMacroToAst".}
## Obtains the AST nodes returned from a macro or template invocation
## example:
## macro FooMacro() =
## var ast = getAst(BarTemplate())
Handling of the node.toYaml magic moved to the evaluation engine.
This commit is contained in:
parent
d541815e4b
commit
91351e5996
6 changed files with 144 additions and 37 deletions
|
|
@ -57,10 +57,10 @@ type
|
||||||
nkStrLit, # a string literal ""
|
nkStrLit, # a string literal ""
|
||||||
nkRStrLit, # a raw string literal r""
|
nkRStrLit, # a raw string literal r""
|
||||||
nkTripleStrLit, # a triple string literal """
|
nkTripleStrLit, # a triple string literal """
|
||||||
nkMetaNode, # difficult to explain; represents itself
|
|
||||||
# (used for macros)
|
|
||||||
nkNilLit, # the nil literal
|
nkNilLit, # the nil literal
|
||||||
# end of atoms
|
# end of atoms
|
||||||
|
nkMetaNode, # difficult to explain; represents itself
|
||||||
|
# (used for macros)
|
||||||
nkDotCall, # used to temporarily flag a nkCall node;
|
nkDotCall, # used to temporarily flag a nkCall node;
|
||||||
# this is used
|
# this is used
|
||||||
# for transforming ``s.len`` to ``len(s)``
|
# for transforming ``s.len`` to ``len(s)``
|
||||||
|
|
@ -312,7 +312,8 @@ type
|
||||||
|
|
||||||
TMagic* = enum # symbols that require compiler magic:
|
TMagic* = enum # symbols that require compiler magic:
|
||||||
mNone, mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mIs, mOf,
|
mNone, mDefined, mDefinedInScope, mLow, mHigh, mSizeOf, mIs, mOf,
|
||||||
mEcho, mAstToYaml, mShallowCopy, mSlurp,
|
mEcho, mShallowCopy, mSlurp,
|
||||||
|
mAstToYaml, mParseExprToAst, mParseStmtToAst, mExpandMacroToAst,
|
||||||
mUnaryLt, mSucc,
|
mUnaryLt, mSucc,
|
||||||
mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
|
mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
|
||||||
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref,
|
mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref,
|
||||||
|
|
@ -610,6 +611,23 @@ proc copyTree*(src: PNode): PNode
|
||||||
|
|
||||||
proc discardSons*(father: PNode)
|
proc discardSons*(father: PNode)
|
||||||
|
|
||||||
|
proc len*(n: PNode): int {.inline.} =
|
||||||
|
if isNil(n.sons): result = 0
|
||||||
|
else: result = len(n.sons)
|
||||||
|
|
||||||
|
proc safeLen*(n: PNode): int {.inline.} =
|
||||||
|
## works even for leaves.
|
||||||
|
if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0
|
||||||
|
else: result = len(n.sons)
|
||||||
|
|
||||||
|
proc add*(father, son: PNode) =
|
||||||
|
assert son != nil
|
||||||
|
if isNil(father.sons): father.sons = @[]
|
||||||
|
add(father.sons, son)
|
||||||
|
|
||||||
|
proc `[]`*(n: PNode, i: int): PNode {.inline.} =
|
||||||
|
result = n.sons[i]
|
||||||
|
|
||||||
var emptyNode* = newNode(nkEmpty)
|
var emptyNode* = newNode(nkEmpty)
|
||||||
# There is a single empty node that is shared! Do not overwrite it!
|
# There is a single empty node that is shared! Do not overwrite it!
|
||||||
|
|
||||||
|
|
@ -754,6 +772,10 @@ proc newNodeIT(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
|
||||||
result.info = info
|
result.info = info
|
||||||
result.typ = typ
|
result.typ = typ
|
||||||
|
|
||||||
|
proc newMetaNodeIT*(tree: PNode, info: TLineInfo, typ: PType): PNode =
|
||||||
|
result = newNodeIT(nkMetaNode, info, typ)
|
||||||
|
result.add(tree)
|
||||||
|
|
||||||
proc NewType(kind: TTypeKind, owner: PSym): PType =
|
proc NewType(kind: TTypeKind, owner: PSym): PType =
|
||||||
new(result)
|
new(result)
|
||||||
result.kind = kind
|
result.kind = kind
|
||||||
|
|
@ -866,23 +888,6 @@ proc sonsLen(n: PNode): int =
|
||||||
if isNil(n.sons): result = 0
|
if isNil(n.sons): result = 0
|
||||||
else: result = len(n.sons)
|
else: result = len(n.sons)
|
||||||
|
|
||||||
proc len*(n: PNode): int {.inline.} =
|
|
||||||
if isNil(n.sons): result = 0
|
|
||||||
else: result = len(n.sons)
|
|
||||||
|
|
||||||
proc safeLen*(n: PNode): int {.inline.} =
|
|
||||||
## works even for leaves.
|
|
||||||
if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0
|
|
||||||
else: result = len(n.sons)
|
|
||||||
|
|
||||||
proc add*(father, son: PNode) =
|
|
||||||
assert son != nil
|
|
||||||
if isNil(father.sons): father.sons = @[]
|
|
||||||
add(father.sons, son)
|
|
||||||
|
|
||||||
proc `[]`*(n: PNode, i: int): PNode {.inline.} =
|
|
||||||
result = n.sons[i]
|
|
||||||
|
|
||||||
proc newSons(father: PNode, length: int) =
|
proc newSons(father: PNode, length: int) =
|
||||||
if isNil(father.sons): father.sons = @[]
|
if isNil(father.sons): father.sons = @[]
|
||||||
setlen(father.sons, len(father.sons) + length)
|
setlen(father.sons, len(father.sons) + length)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets,
|
strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets,
|
||||||
msgs, os, condsyms, idents, renderer, types, passes, semfold, transf
|
msgs, os, condsyms, idents, renderer, types, passes, semfold, transf,
|
||||||
|
ropes
|
||||||
|
|
||||||
type
|
type
|
||||||
PStackFrame* = ref TStackFrame
|
PStackFrame* = ref TStackFrame
|
||||||
|
|
@ -981,6 +982,9 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
|
||||||
if (a == b) or
|
if (a == b) or
|
||||||
(b.kind in {nkNilLit, nkEmpty}) and (a.kind in {nkNilLit, nkEmpty}):
|
(b.kind in {nkNilLit, nkEmpty}) and (a.kind in {nkNilLit, nkEmpty}):
|
||||||
result.intVal = 1
|
result.intVal = 1
|
||||||
|
of mAstToYaml:
|
||||||
|
var ast = evalAux(c, n.sons[1], {efLValue})
|
||||||
|
result = newStrNode(nkStrLit, ast.treeToYaml.ropeToStr)
|
||||||
of mNHint:
|
of mNHint:
|
||||||
result = evalAux(c, n.sons[1], {})
|
result = evalAux(c, n.sons[1], {})
|
||||||
if isSpecial(result): return
|
if isSpecial(result): return
|
||||||
|
|
@ -1034,6 +1038,9 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
||||||
dec(gNestedEvals)
|
dec(gNestedEvals)
|
||||||
if gNestedEvals <= 0: stackTrace(c, n, errTooManyIterations)
|
if gNestedEvals <= 0: stackTrace(c, n, errTooManyIterations)
|
||||||
case n.kind # atoms:
|
case n.kind # atoms:
|
||||||
|
of nkMetaNode:
|
||||||
|
result = copyTree(n.sons[0])
|
||||||
|
result.typ = n.typ
|
||||||
of nkEmpty: result = n
|
of nkEmpty: result = n
|
||||||
of nkSym: result = evalSym(c, n, flags)
|
of nkSym: result = evalSym(c, n, flags)
|
||||||
of nkType..nkNilLit: result = copyNode(n) # end of atoms
|
of nkType..nkNilLit: result = copyNode(n) # end of atoms
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,11 @@ proc closeParser*(p: var TParser)
|
||||||
proc parseTopLevelStmt*(p: var TParser): PNode
|
proc parseTopLevelStmt*(p: var TParser): PNode
|
||||||
# implements an iterator. Returns the next top-level statement or
|
# implements an iterator. Returns the next top-level statement or
|
||||||
# emtyNode if end of stream.
|
# emtyNode if end of stream.
|
||||||
|
|
||||||
|
proc parseString*(s: string, filename: string = "", line: int = 0): PNode
|
||||||
|
# filename and line could be set optionally, when the string originates
|
||||||
|
# from a certain source file. This way, the compiler could generate
|
||||||
|
# correct error messages referring to the original source.
|
||||||
|
|
||||||
# helpers for the other parsers
|
# helpers for the other parsers
|
||||||
proc getPrecedence*(tok: TToken): int
|
proc getPrecedence*(tok: TToken): int
|
||||||
|
|
@ -1369,3 +1374,13 @@ proc parseTopLevelStmt(p: var TParser): PNode =
|
||||||
result = complexOrSimpleStmt(p)
|
result = complexOrSimpleStmt(p)
|
||||||
if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
proc parseString(s: string, filename: string = "", line: int = 0): PNode =
|
||||||
|
var stream = LLStreamOpen(s)
|
||||||
|
stream.lineOffset = line
|
||||||
|
|
||||||
|
var parser : TParser
|
||||||
|
OpenParser(parser, filename, stream)
|
||||||
|
|
||||||
|
result = parser.parseAll
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -385,6 +385,10 @@ proc isAssignable(c: PContext, n: PNode): TAssignableResult =
|
||||||
else:
|
else:
|
||||||
nil
|
nil
|
||||||
|
|
||||||
|
proc isCallExpr(n: PNode): bool =
|
||||||
|
result = n.kind in {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand,
|
||||||
|
nkCallStrLit}
|
||||||
|
|
||||||
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
|
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
|
||||||
if n.kind == nkHiddenDeref:
|
if n.kind == nkHiddenDeref:
|
||||||
checkSonsLen(n, 1)
|
checkSonsLen(n, 1)
|
||||||
|
|
@ -884,16 +888,66 @@ proc setMs(n: PNode, s: PSym): PNode =
|
||||||
n.sons[0] = newSymNode(s)
|
n.sons[0] = newSymNode(s)
|
||||||
n.sons[0].info = n.info
|
n.sons[0].info = n.info
|
||||||
|
|
||||||
proc semAstToYaml(c: PContext, n: PNode): PNode =
|
proc expectStringArg(c: PContext, n: PNode, i: int): PNode =
|
||||||
result = newStrNode(nkStrLit, n.treeToYaml.ropeToStr)
|
result = c.semConstExpr(c, n.sons[i+1])
|
||||||
result.typ = getSysType(tyString)
|
|
||||||
result.info = n.info
|
if result.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
|
||||||
|
GlobalError(result.info, errStringLiteralExpected)
|
||||||
|
|
||||||
|
# The lexer marks multi-line strings as residing at the line where they are closed
|
||||||
|
# This function returns the line where the string begins
|
||||||
|
# Maybe the lexer should mark both the beginning and the end of expressions, then
|
||||||
|
# this function could be removed
|
||||||
|
proc stringStartingLine(s: PNode): int =
|
||||||
|
var totalLines = 0
|
||||||
|
for ln in splitLines(s.strVal): inc totalLines
|
||||||
|
|
||||||
|
result = s.info.line - totalLines
|
||||||
|
|
||||||
|
proc semParseExprToAst(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
|
if sonsLen(n) == 2:
|
||||||
|
var code = expectStringArg(c, n, 0)
|
||||||
|
var ast = parseString(code.strVal, code.info.toFilename, code.stringStartingLine)
|
||||||
|
|
||||||
|
if sonsLen(ast) != 1:
|
||||||
|
GlobalError(code.info, errExprExpected, "multiple statements")
|
||||||
|
|
||||||
|
result = newMetaNodeIT(ast.sons[0], code.info, newTypeS(tyExpr, c))
|
||||||
|
else:
|
||||||
|
result = semDirectOp(c, n, flags)
|
||||||
|
|
||||||
|
proc semParseStmtToAst(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
|
if sonsLen(n) == 2:
|
||||||
|
var code = expectStringArg(c, n, 0)
|
||||||
|
var ast = parseString(code.strVal, code.info.toFilename, code.stringStartingLine)
|
||||||
|
|
||||||
|
result = newMetaNodeIT(ast, code.info, newTypeS(tyStmt, c))
|
||||||
|
else:
|
||||||
|
result = semDirectOp(c, n, flags)
|
||||||
|
|
||||||
|
proc semExpandMacroToAst(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
|
if sonsLen(n) == 2 and isCallExpr(n.sons[1]):
|
||||||
|
var macroCall = n.sons[1]
|
||||||
|
|
||||||
|
var s = qualifiedLookup(c, macroCall.sons[0], {checkUndeclared})
|
||||||
|
if s == nil:
|
||||||
|
GlobalError(n.info, errUndeclaredIdentifier, macroCall.sons[0].strVal)
|
||||||
|
|
||||||
|
var expanded : Pnode
|
||||||
|
|
||||||
|
case s.kind
|
||||||
|
of skMacro: expanded = semMacroExpr(c, macroCall, s, false)
|
||||||
|
of skTemplate: expanded = semTemplateExpr(c, macroCall, s, false)
|
||||||
|
else: GlobalError(n.info, errXisNoMacroOrTemplate, s.name.s)
|
||||||
|
|
||||||
|
var macroRetType = newTypeS(s.typ.sons[0].kind, c)
|
||||||
|
result = newMetaNodeIT(expanded, n.info, macroRetType)
|
||||||
|
else:
|
||||||
|
GlobalError(n.info, errXisNoMacroOrTemplate, n.renderTree)
|
||||||
|
|
||||||
proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
proc semSlurp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
if sonsLen(n) == 2:
|
if sonsLen(n) == 2:
|
||||||
var a = c.semConstExpr(c, n.sons[1])
|
var a = expectStringArg(c, n, 0)
|
||||||
if a.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
|
|
||||||
GlobalError(a.info, errStringLiteralExpected)
|
|
||||||
try:
|
try:
|
||||||
var content = readFile(a.strVal)
|
var content = readFile(a.strVal)
|
||||||
result = newStrNode(nkStrLit, content)
|
result = newStrNode(nkStrLit, content)
|
||||||
|
|
@ -927,7 +981,9 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||||
else:
|
else:
|
||||||
result = semDirectOp(c, n, flags)
|
result = semDirectOp(c, n, flags)
|
||||||
of mSlurp: result = semSlurp(c, n, flags)
|
of mSlurp: result = semSlurp(c, n, flags)
|
||||||
of mAstToYaml: result = semAstToYaml(c, n)
|
of mParseExprToAst: result = semParseExprToAst(c, n, flags)
|
||||||
|
of mParseStmtToAst: result = semParseStmtToAst(c, n, flags)
|
||||||
|
of mExpandMacroToAst: result = semExpandMacroToAst(c, n, flags)
|
||||||
else: result = semDirectOp(c, n, flags)
|
else: result = semDirectOp(c, n, flags)
|
||||||
|
|
||||||
proc semIfExpr(c: PContext, n: PNode): PNode =
|
proc semIfExpr(c: PContext, n: PNode): PNode =
|
||||||
|
|
@ -1085,10 +1141,6 @@ proc semBlockExpr(c: PContext, n: PNode): PNode =
|
||||||
closeScope(c.tab)
|
closeScope(c.tab)
|
||||||
Dec(c.p.nestedBlockCounter)
|
Dec(c.p.nestedBlockCounter)
|
||||||
|
|
||||||
proc isCallExpr(n: PNode): bool =
|
|
||||||
result = n.kind in {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand,
|
|
||||||
nkCallStrLit}
|
|
||||||
|
|
||||||
proc semMacroStmt(c: PContext, n: PNode, semCheck = true): PNode =
|
proc semMacroStmt(c: PContext, n: PNode, semCheck = true): PNode =
|
||||||
checkMinSonsLen(n, 2)
|
checkMinSonsLen(n, 2)
|
||||||
var a: PNode
|
var a: PNode
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ type
|
||||||
nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit,
|
nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit,
|
||||||
nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkFloatLit,
|
nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkFloatLit,
|
||||||
nnkFloat32Lit, nnkFloat64Lit, nnkStrLit, nnkRStrLit,
|
nnkFloat32Lit, nnkFloat64Lit, nnkStrLit, nnkRStrLit,
|
||||||
nnkTripleStrLit, nnkMetaNode, nnkNilLit, nnkDotCall,
|
nnkTripleStrLit, nnkNilLit, nnkMetaNode, nnkDotCall,
|
||||||
nnkCommand, nnkCall, nnkCallStrLit, nnkExprEqExpr,
|
nnkCommand, nnkCall, nnkCallStrLit, nnkExprEqExpr,
|
||||||
nnkExprColonExpr, nnkIdentDefs, nnkVarTuple, nnkInfix,
|
nnkExprColonExpr, nnkIdentDefs, nnkVarTuple, nnkInfix,
|
||||||
nnkPrefix, nnkPostfix, nnkPar, nnkCurly,
|
nnkPrefix, nnkPostfix, nnkPar, nnkCurly,
|
||||||
|
|
@ -211,12 +211,26 @@ proc prettyPrint*(n: PNimrodNode): string {.compileTime.} =
|
||||||
|
|
||||||
add(result, ")")
|
add(result, ")")
|
||||||
|
|
||||||
proc toYaml*(n: PNimrodNode) {.magic: "AstToYaml".}
|
proc toYaml*(n: PNimrodNode): string {.magic: "AstToYaml".}
|
||||||
## Converts the AST `n` to an YAML string
|
## Converts the AST `n` to an YAML string
|
||||||
##
|
##
|
||||||
## Provides more detailed, potentially harder to digest information
|
## Provides more detailed, potentially harder to digest information
|
||||||
## than `prettyPrint`
|
## than `prettyPrint`
|
||||||
|
|
||||||
|
proc parseExpr*(s: string) : expr {.magic: "ParseExprToAst".}
|
||||||
|
## Compiles the passed string to its AST representation
|
||||||
|
## Expects a single expression
|
||||||
|
|
||||||
|
proc parseStmt*(s: string) : stmt {.magic: "ParseStmtToAst".}
|
||||||
|
## Compiles the passed string to its AST representation
|
||||||
|
## Expects one or more statements
|
||||||
|
|
||||||
|
proc getAst*(macroOrTemplate: expr): expr {.magic: "ExpandMacroToAst".}
|
||||||
|
## Obtains the AST nodes returned from a macro or template invocation
|
||||||
|
## example:
|
||||||
|
## macro FooMacro() =
|
||||||
|
## var ast = getAst(BarTemplate())
|
||||||
|
|
||||||
proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
|
proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
|
||||||
## checks that `n` is of kind `k`. If this is not the case,
|
## checks that `n` is of kind `k`. If this is not the case,
|
||||||
## compilation aborts with an error message. This is useful for writing
|
## compilation aborts with an error message. This is useful for writing
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,27 @@
|
||||||
# Dump the contents of a PNimrodNode
|
# Dump the contents of a PNimrodNode
|
||||||
|
|
||||||
import macros
|
import macros
|
||||||
|
|
||||||
|
template plus(a, b: expr): expr =
|
||||||
|
a + b
|
||||||
|
|
||||||
|
macro call(e: expr): expr =
|
||||||
|
return newCall("foo", newStrLitNode("bar"))
|
||||||
|
|
||||||
macro dumpAST(n: stmt): stmt =
|
macro dumpAST(n: stmt): stmt =
|
||||||
# dump AST as a side-effect and return the inner node
|
# dump AST as a side-effect and return the inner node
|
||||||
echo n.prettyPrint
|
echo n.prettyPrint
|
||||||
echo n.toYaml
|
echo n.toYaml
|
||||||
|
|
||||||
|
var plusAst = getAst(plus(1, 2))
|
||||||
|
echo plusAst.prettyPrint
|
||||||
|
|
||||||
|
var callAst = getAst(call())
|
||||||
|
echo callAst.prettyPrint
|
||||||
|
|
||||||
|
var e = parseExpr("foo(bar + baz)")
|
||||||
|
echo e.prettyPrint
|
||||||
|
|
||||||
result = n[1]
|
result = n[1]
|
||||||
|
|
||||||
dumpAST:
|
dumpAST:
|
||||||
|
|
@ -15,4 +30,3 @@ dumpAST:
|
||||||
|
|
||||||
proc sub(x, y: int): int = return x - y
|
proc sub(x, y: int): int = return x - y
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue