implements a type API for macros

This commit is contained in:
Araq 2015-02-24 00:13:15 +01:00
commit 752052e903
7 changed files with 194 additions and 34 deletions

View file

@ -945,6 +945,13 @@ template `{}`*(n: PNode, i: int): expr = n[i -| n]
template `{}=`*(n: PNode, i: int, s: PNode): stmt = template `{}=`*(n: PNode, i: int, s: PNode): stmt =
n.sons[i -| n] = s n.sons[i -| n] = s
when defined(useNodeIds):
const nodeIdToDebug* = -1 # 884953 # 612794
#612840 # 612905 # 614635 # 614637 # 614641
# 423408
#429107 # 430443 # 441048 # 441090 # 441153
var gNodeId: int
proc newNode*(kind: TNodeKind): PNode = proc newNode*(kind: TNodeKind): PNode =
new(result) new(result)
result.kind = kind result.kind = kind
@ -1061,13 +1068,6 @@ proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
proc discardSons*(father: PNode) = proc discardSons*(father: PNode) =
father.sons = nil father.sons = nil
when defined(useNodeIds):
const nodeIdToDebug* = -1 # 884953 # 612794
#612840 # 612905 # 614635 # 614637 # 614641
# 423408
#429107 # 430443 # 441048 # 441090 # 441153
var gNodeId: int
proc withInfo*(n: PNode, info: TLineInfo): PNode = proc withInfo*(n: PNode, info: TLineInfo): PNode =
n.info = info n.info = info
return n return n

View file

@ -332,10 +332,15 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
if result.typ.kind != tyGenericParam: if result.typ.kind != tyGenericParam:
# XXX get rid of this hack! # XXX get rid of this hack!
var oldInfo = n.info var oldInfo = n.info
when defined(useNodeIds):
let oldId = n.id
reset(n[]) reset(n[])
when defined(useNodeIds):
n.id = oldId
n.kind = nkSym n.kind = nkSym
n.sym = result n.sym = result
n.info = oldInfo n.info = oldInfo
n.typ = result.typ
else: else:
localError(n.info, errIdentifierExpected) localError(n.info, errIdentifierExpected)
result = errorSym(c, n) result = errorSym(c, n)
@ -1179,11 +1184,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
var typeExpr = semExpr(c, n) var typeExpr = semExpr(c, n)
if typeExpr.typ.kind != tyTypeDesc: if typeExpr.typ.kind != tyTypeDesc:
localError(n.info, errTypeExpected) localError(n.info, errTypeExpected)
return errorType(c) result = errorType(c)
else:
result = typeExpr.typ.base result = typeExpr.typ.base
if result.isMetaType: if result.isMetaType:
var preprocessed = semGenericStmt(c, n) var preprocessed = semGenericStmt(c, n)
return makeTypeFromExpr(c, preprocessed) result = makeTypeFromExpr(c, preprocessed)
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
var s = semTypeIdent(c, n) var s = semTypeIdent(c, n)
if s.typ == nil: if s.typ == nil:
@ -1254,6 +1260,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
else: else:
localError(n.info, errTypeExpected) localError(n.info, errTypeExpected)
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
n.typ = result
proc setMagicType(m: PSym, kind: TTypeKind, size: int) = proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
m.typ.kind = kind m.typ.kind = kind

View file

@ -123,8 +123,12 @@ proc createStrKeepNode(x: var TFullReg) =
if x.node.isNil: if x.node.isNil:
x.node = newNode(nkStrLit) x.node = newNode(nkStrLit)
elif x.node.kind == nkNilLit: elif x.node.kind == nkNilLit:
when defined(useNodeIds):
let id = x.node.id
system.reset(x.node[]) system.reset(x.node[])
x.node.kind = nkStrLit x.node.kind = nkStrLit
when defined(useNodeIds):
x.node.id = id
elif x.node.kind notin {nkStrLit..nkTripleStrLit} or elif x.node.kind notin {nkStrLit..nkTripleStrLit} or
nfAllConst in x.node.flags: nfAllConst in x.node.flags:
# XXX this is hacky; tests/txmlgen triggers it: # XXX this is hacky; tests/txmlgen triggers it:
@ -1133,7 +1137,21 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
else: else:
stackTrace(c, tos, pc, errFieldXNotFound, "ident") stackTrace(c, tos, pc, errFieldXNotFound, "ident")
of opcNGetType: of opcNGetType:
internalError(c.debug[pc], "unknown opcode " & $instr.opcode) let rb = instr.regB
let rc = instr.regC
if rc == 0:
ensureKind(rkNode)
if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
regs[ra].node = opMapTypeToAst(regs[rb].node.typ, c.debug[pc])
else:
stackTrace(c, tos, pc, errGenerated, "node has no type")
else:
# typeKind opcode:
ensureKind(rkInt)
if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
regs[ra].intVal = ord(regs[rb].node.typ.kind)
#else:
# stackTrace(c, tos, pc, errGenerated, "node has no type")
of opcNStrVal: of opcNStrVal:
decodeB(rkNode) decodeB(rkNode)
createStr regs[ra] createStr regs[ra]

View file

@ -1,13 +1,13 @@
# #
# #
# The Nim Compiler # The Nim Compiler
# (c) Copyright 2013 Andreas Rumpf # (c) Copyright 2015 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
import ast, types, msgs, osproc, streams, options import ast, types, msgs, osproc, streams, options, idents
proc readOutput(p: Process): string = proc readOutput(p: Process): string =
result = "" result = ""
@ -19,11 +19,14 @@ proc readOutput(p: Process): string =
discard p.waitForExit discard p.waitForExit
proc opGorge*(cmd, input: string): string = proc opGorge*(cmd, input: string): string =
try:
var p = startProcess(cmd, options={poEvalCommand}) var p = startProcess(cmd, options={poEvalCommand})
if input.len != 0: if input.len != 0:
p.inputStream.write(input) p.inputStream.write(input)
p.inputStream.close() p.inputStream.close()
result = p.readOutput result = p.readOutput
except IOError, OSError:
result = ""
proc opSlurp*(file: string, info: TLineInfo, module: PSym): string = proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
try: try:
@ -36,3 +39,111 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
except IOError: except IOError:
localError(info, errCannotOpenFile, file) localError(info, errCannotOpenFile, file)
result = "" result = ""
proc atomicTypeX(name: string; t: PType; info: TLineInfo): PNode =
let sym = newSym(skType, getIdent(name), t.owner, info)
result = newSymNode(sym)
result.typ = t
proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode
proc mapTypeToBracket(name: string; t: PType; info: TLineInfo): PNode =
result = newNodeIT(nkBracketExpr, info, t)
result.add atomicTypeX(name, t, info)
for i in 0 .. < t.len:
result.add mapTypeToAst(t.sons[i], info)
proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
template atomicType(name): expr = atomicTypeX(name, t, info)
case t.kind
of tyNone: result = atomicType("none")
of tyBool: result = atomicType("bool")
of tyChar: result = atomicType("char")
of tyNil: result = atomicType("nil")
of tyExpr: result = atomicType("expr")
of tyStmt: result = atomicType("stmt")
of tyEmpty: result = atomicType"void"
of tyArrayConstr, tyArray:
result = newNodeIT(nkBracketExpr, info, t)
result.add atomicType("array")
result.add mapTypeToAst(t.sons[0], info)
result.add mapTypeToAst(t.sons[1], info)
of tyTypeDesc:
if t.base != nil:
result = newNodeIT(nkBracketExpr, info, t)
result.add atomicType("typeDesc")
result.add mapTypeToAst(t.base, info)
else:
result = atomicType"typeDesc"
of tyGenericInvocation:
result = newNodeIT(nkBracketExpr, info, t)
for i in 0 .. < t.len:
result.add mapTypeToAst(t.sons[i], info)
of tyGenericInst, tyGenericBody, tyOrdinal, tyUserTypeClassInst:
result = mapTypeToAst(t.lastSon, info)
of tyGenericParam, tyDistinct, tyForward: result = atomicType(t.sym.name.s)
of tyObject:
if allowRecursion:
result = newNodeIT(nkObjectTy, info, t)
if t.sons[0] == nil:
result.add ast.emptyNode
else:
result.add mapTypeToAst(t.sons[0], info)
result.add copyTree(t.n)
else:
result = atomicType(t.sym.name.s)
of tyEnum:
result = newNodeIT(nkEnumTy, info, t)
result.add copyTree(t.n)
of tyTuple: result = mapTypeToBracket("tuple", t, info)
of tySet: result = mapTypeToBracket("set", t, info)
of tyPtr: result = mapTypeToBracket("ptr", t, info)
of tyRef: result = mapTypeToBracket("ref", t, info)
of tyVar: result = mapTypeToBracket("var", t, info)
of tySequence: result = mapTypeToBracket("sequence", t, info)
of tyProc: result = mapTypeToBracket("proc", t, info)
of tyOpenArray: result = mapTypeToBracket("openArray", t, info)
of tyRange:
result = newNodeIT(nkBracketExpr, info, t)
result.add atomicType("range")
result.add t.n.sons[0].copyTree
result.add t.n.sons[1].copyTree
of tyPointer: result = atomicType"pointer"
of tyString: result = atomicType"string"
of tyCString: result = atomicType"cstring"
of tyInt: result = atomicType"int"
of tyInt8: result = atomicType"int8"
of tyInt16: result = atomicType"int16"
of tyInt32: result = atomicType"int32"
of tyInt64: result = atomicType"int64"
of tyFloat: result = atomicType"float"
of tyFloat32: result = atomicType"float32"
of tyFloat64: result = atomicType"float64"
of tyFloat128: result = atomicType"float128"
of tyUInt: result = atomicType"uint"
of tyUInt8: result = atomicType"uint8"
of tyUInt16: result = atomicType"uint16"
of tyUInt32: result = atomicType"uint32"
of tyUInt64: result = atomicType"uint64"
of tyBigNum: result = atomicType"bignum"
of tyConst: result = mapTypeToBracket("const", t, info)
of tyMutable: result = mapTypeToBracket("mutable", t, info)
of tyVarargs: result = mapTypeToBracket("varargs", t, info)
of tyIter: result = mapTypeToBracket("iter", t, info)
of tyProxy: result = atomicType"error"
of tyBuiltInTypeClass: result = mapTypeToBracket("builtinTypeClass", t, info)
of tyUserTypeClass: result = mapTypeToBracket("userTypeClass", t, info)
of tyCompositeTypeClass: result = mapTypeToBracket("compositeTypeClass", t, info)
of tyAnd: result = mapTypeToBracket("and", t, info)
of tyOr: result = mapTypeToBracket("or", t, info)
of tyNot: result = mapTypeToBracket("not", t, info)
of tyAnything: result = atomicType"anything"
of tyStatic, tyFromExpr, tyFieldAccessor:
result = newNodeIT(nkBracketExpr, info, t)
result.add atomicType("static")
if t.n != nil:
result.add t.n.copyTree
proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode =
result = mapTypeToAst(t, info, true)

View file

@ -950,7 +950,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
of mNFloatVal: genUnaryABC(c, n, dest, opcNFloatVal) of mNFloatVal: genUnaryABC(c, n, dest, opcNFloatVal)
of mNSymbol: genUnaryABC(c, n, dest, opcNSymbol) of mNSymbol: genUnaryABC(c, n, dest, opcNSymbol)
of mNIdent: genUnaryABC(c, n, dest, opcNIdent) of mNIdent: genUnaryABC(c, n, dest, opcNIdent)
of mNGetType: genUnaryABC(c, n, dest, opcNGetType) of mNGetType:
let tmp = c.genx(n.sons[1])
if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opcNGetType, dest, tmp, if n[0].sym.name.s == "typeKind": 1 else: 0)
c.freeTemp(tmp)
#genUnaryABC(c, n, dest, opcNGetType)
of mNStrVal: genUnaryABC(c, n, dest, opcNStrVal) of mNStrVal: genUnaryABC(c, n, dest, opcNStrVal)
of mNSetIntVal: of mNSetIntVal:
unused(n, dest) unused(n, dest)

View file

@ -83,7 +83,13 @@ type
ntySequence, ntyProc, ntyPointer, ntyOpenArray, ntySequence, ntyProc, ntyPointer, ntyOpenArray,
ntyString, ntyCString, ntyForward, ntyInt, ntyString, ntyCString, ntyForward, ntyInt,
ntyInt8, ntyInt16, ntyInt32, ntyInt64, ntyInt8, ntyInt16, ntyInt32, ntyInt64,
ntyFloat, ntyFloat32, ntyFloat64, ntyFloat128 ntyFloat, ntyFloat32, ntyFloat64, ntyFloat128,
ntyUInt, ntyUInt8, ntyUInt16, ntyUInt32, ntyUInt64,
ntyBigNum,
ntyConst, ntyMutable, ntyVarargs,
ntyIter,
ntyError
TNimTypeKinds* {.deprecated.} = set[NimTypeKind] TNimTypeKinds* {.deprecated.} = set[NimTypeKind]
NimSymKind* = enum NimSymKind* = enum
nskUnknown, nskConditional, nskDynLib, nskParam, nskUnknown, nskConditional, nskDynLib, nskParam,
@ -100,7 +106,7 @@ type
NimIdent* = object of RootObj NimIdent* = object of RootObj
## represents a Nim identifier in the AST ## represents a Nim identifier in the AST
NimSymObj {.final.} = object # hidden NimSymObj = object # hidden
NimSym* = ref NimSymObj NimSym* = ref NimSymObj
## represents a Nim *symbol* in the compiler; a *symbol* is a looked-up ## represents a Nim *symbol* in the compiler; a *symbol* is a looked-up
## *ident*. ## *ident*.
@ -125,16 +131,16 @@ proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.}
## constructs an identifier from the string `s` ## constructs an identifier from the string `s`
proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.} proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.}
## converts a Nimrod identifier to a string ## converts a Nim identifier to a string
proc `$`*(s: NimSym): string {.magic: "IdentToStr", noSideEffect.} proc `$`*(s: NimSym): string {.magic: "IdentToStr", noSideEffect.}
## converts a Nimrod symbol to a string ## converts a Nim symbol to a string
proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect.} proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect.}
## compares two Nimrod identifiers ## compares two Nim identifiers
proc `==`*(a, b: PNimrodNode): bool {.magic: "EqNimrodNode", noSideEffect.} proc `==`*(a, b: PNimrodNode): bool {.magic: "EqNimrodNode", noSideEffect.}
## compares two Nimrod nodes ## compares two Nim nodes
proc len*(n: PNimrodNode): int {.magic: "NLen", noSideEffect.} proc len*(n: PNimrodNode): int {.magic: "NLen", noSideEffect.}
## returns the number of children of `n`. ## returns the number of children of `n`.
@ -159,7 +165,19 @@ proc intVal*(n: PNimrodNode): BiggestInt {.magic: "NIntVal", noSideEffect.}
proc floatVal*(n: PNimrodNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.} proc floatVal*(n: PNimrodNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.}
proc symbol*(n: PNimrodNode): NimSym {.magic: "NSymbol", noSideEffect.} proc symbol*(n: PNimrodNode): NimSym {.magic: "NSymbol", noSideEffect.}
proc ident*(n: PNimrodNode): NimIdent {.magic: "NIdent", noSideEffect.} proc ident*(n: PNimrodNode): NimIdent {.magic: "NIdent", noSideEffect.}
proc typ*(n: PNimrodNode): typedesc {.magic: "NGetType", noSideEffect.}
proc getType*(n: PNimrodNode): PNimrodNode {.magic: "NGetType", noSideEffect.}
## with 'getType' you can access the node's `type`:idx:. A Nim type is
## mapped to a Nim AST too, so it's slightly confusing but it means the same
## API can be used to traverse types. Recursive types are flattened for you
## so there is no danger of infinite recursions during traversal. To
## resolve recursive types, you have to call 'getType' again. To see what
## kind of type it is, call `typeKind` on getType's result.
proc typeKind*(n: PNimrodNode): NimTypeKind {.magic: "NGetType", noSideEffect.}
## Returns the type kind of the node 'n' that should represent a type, that
## means the node should have been obtained via `getType`.
proc strVal*(n: PNimrodNode): string {.magic: "NStrVal", noSideEffect.} proc strVal*(n: PNimrodNode): string {.magic: "NStrVal", noSideEffect.}
proc `intVal=`*(n: PNimrodNode, val: BiggestInt) {.magic: "NSetIntVal", noSideEffect.} proc `intVal=`*(n: PNimrodNode, val: BiggestInt) {.magic: "NSetIntVal", noSideEffect.}
@ -216,7 +234,7 @@ proc newIdentNode*(i: string): PNimrodNode {.compileTime.} =
result.ident = !i result.ident = !i
type type
TBindSymRule* = enum ## specifies how ``bindSym`` behaves BindSymRule* = enum ## specifies how ``bindSym`` behaves
brClosed, ## only the symbols in current scope are bound brClosed, ## only the symbols in current scope are bound
brOpen, ## open wrt overloaded symbols, but may be a single brOpen, ## open wrt overloaded symbols, but may be a single
## symbol if not ambiguous (the rules match that of ## symbol if not ambiguous (the rules match that of
@ -225,7 +243,9 @@ type
## if not ambiguous (this cannot be achieved with ## if not ambiguous (this cannot be achieved with
## any other means in the language currently) ## any other means in the language currently)
proc bindSym*(ident: string, rule: TBindSymRule = brClosed): PNimrodNode {. {.deprecated: [TBindSymRule: BindSymRule].}
proc bindSym*(ident: string, rule: BindSymRule = brClosed): PNimrodNode {.
magic: "NBindSym", noSideEffect.} magic: "NBindSym", noSideEffect.}
## creates a node that binds `ident` to a symbol node. The bound symbol ## creates a node that binds `ident` to a symbol node. The bound symbol
## may be an overloaded symbol. ## may be an overloaded symbol.
@ -236,7 +256,7 @@ proc bindSym*(ident: string, rule: TBindSymRule = brClosed): PNimrodNode {.
## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is ## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
## returned even if the symbol is not ambiguous. ## returned even if the symbol is not ambiguous.
proc genSym*(kind: TNimrodSymKind = nskLet; ident = ""): PNimrodNode {. proc genSym*(kind: NimSymKind = nskLet; ident = ""): PNimrodNode {.
magic: "NGenSym", noSideEffect.} magic: "NGenSym", noSideEffect.}
## generates a fresh symbol that is guaranteed to be unique. The symbol ## generates a fresh symbol that is guaranteed to be unique. The symbol
## needs to occur in a declaration context. ## needs to occur in a declaration context.
@ -245,7 +265,7 @@ proc callsite*(): PNimrodNode {.magic: "NCallSite", benign.}
## returns the AST of the invocation expression that invoked this macro. ## returns the AST of the invocation expression that invoked this macro.
proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} = proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
## converts the AST `n` to the concrete Nimrod code and wraps that ## converts the AST `n` to the concrete Nim code and wraps that
## in a string literal node ## in a string literal node
return newStrLitNode(repr(n)) return newStrLitNode(repr(n))

View file

@ -74,7 +74,6 @@ version 0.9.X
============= =============
- macros as type pragmas - macros as type pragmas
- implement type API for macros
- lazy overloading resolution: - lazy overloading resolution:
* special case ``tyStmt`` * special case ``tyStmt``
- document NimMain and check whether it works for threading - document NimMain and check whether it works for threading