next steps towards term rewriting macros; simple examples work

This commit is contained in:
Araq 2012-08-30 22:55:32 +02:00
commit 1d14cb1ad8
17 changed files with 124 additions and 56 deletions

View file

@ -176,7 +176,7 @@ type
nkFromStmt, # a from * import statement
nkIncludeStmt, # an include statement
nkBindStmt, # a bind statement
nkPatternStmt, # a pattern statement ('as' statement)
nkPattern, # a pattern statement ('as' statement)
nkCommentStmt, # a comment statement
nkStmtListExpr, # a statement list followed by an expr; this is used
# to allow powerful multi-line templates
@ -687,13 +687,14 @@ const
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfAllConst}
namePos* = 0
genericParamsPos* = 1
paramsPos* = 2
pragmasPos* = 3
patternPos* = 4 # empty except for term rewriting macros
bodyPos* = 5 # position of body; use rodread.getBody() instead!
resultPos* = 6
dispatcherPos* = 7 # caution: if method has no 'result' it can be position 5!
patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2
paramsPos* = 3
pragmasPos* = 4
exceptionPos* = 5 # will be used for exception tracking
bodyPos* = 6 # position of body; use rodread.getBody() instead!
resultPos* = 7
dispatcherPos* = 8 # caution: if method has no 'result' it can be position 5!
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
nkCommand, nkCallStrLit}

View file

@ -184,6 +184,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
of "taintmode": result = contains(gGlobalOptions, optTaintMode)
of "tlsemulation": result = contains(gGlobalOptions, optTlsEmulation)
of "implicitstatic": result = contains(gOptions, optImplicitStatic)
of "patterns": result = contains(gOptions, optPatterns)
else: InvalidCmdLineOption(passCmd1, switch, info)
proc processPath(path: string): string =
@ -315,6 +316,8 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
of "taintmode": ProcessOnOffSwitchG({optTaintMode}, arg, pass, info)
of "implicitstatic":
ProcessOnOffSwitch({optImplicitStatic}, arg, pass, info)
of "patterns":
ProcessOnOffSwitch({optPatterns}, arg, pass, info)
of "opt":
expectArg(switch, arg, pass, info)
case arg.normalize

View file

@ -26,8 +26,9 @@ type # please make sure we have under 32 options
optByRef, # use pass by ref for objects
# (for interfacing with C)
optProfiler, # profiler turned on
optImplicitStatic # optimization: implicit at compile time
optImplicitStatic, # optimization: implicit at compile time
# evaluation
optPatterns # en/disable pattern matching
TOptions* = set[TOption]
TGlobalOption* = enum # **keep binary compatible**
gloptNone, optForceFullMake, optBoehmGC, optRefcGC, optDeadCodeElim,
@ -82,7 +83,8 @@ const
var
gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck,
optBoundsCheck, optOverflowCheck, optAssert, optWarns,
optHints, optStackTrace, optLineTrace}
optHints, optStackTrace, optLineTrace,
optPatterns}
gGlobalOptions*: TGlobalOptions = {optRefcGC, optThreadAnalysis}
gExitcode*: int8
searchPaths*: TLinkedList

View file

@ -700,10 +700,12 @@ proc parseDoBlock(p: var TParser): PNode =
eat(p, tkColon)
result = newNodeI(nkDo, info)
addSon(result, ast.emptyNode) # no name part
addSon(result, ast.emptyNode) # no pattern part
addSon(result, ast.emptyNode) # no generic parameters
addSon(result, params)
addSon(result, pragmas)
skipComment(p, result)
addSon(result, ast.emptyNode) # no exception list
addSon(result, parseStmt(p))
proc parseDoBlocks(p: var TParser, call: PNode) =
@ -720,14 +722,16 @@ proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
let hasSignature = p.tok.tokType in {tkParLe, tkColon}
params = parseParamList(p)
pragmas = optPragmas(p)
if (p.tok.tokType == tkEquals) and isExpr:
if p.tok.tokType == tkEquals and isExpr:
result = newNodeI(nkLambda, info)
addSon(result, ast.emptyNode) # no name part
addSon(result, ast.emptyNode) # no pattern
addSon(result, ast.emptyNode) # no generic parameters
addSon(result, params)
addSon(result, pragmas)
getTok(p)
skipComment(p, result)
addSon(result, ast.emptyNode) # no exception list
addSon(result, parseStmt(p))
else:
result = newNodeI(nkProcTy, info)
@ -961,23 +965,14 @@ proc parseYieldOrDiscard(p: var TParser, kind: TNodeKind): PNode =
optInd(p, result)
addSon(result, parseExpr(p))
proc parseBreakOrContinue(p: var TParser, kind: TNodeKind): PNode =
proc parseBreakOrContinue(p: var TParser, kind: TNodeKind): PNode =
result = newNodeP(kind, p)
getTok(p)
optInd(p, result)
case p.tok.tokType
of tkEof, tkSad, tkDed: addSon(result, ast.emptyNode)
else: addSon(result, parseSymbol(p))
proc parseAs(p: var TParser): PNode =
result = newNodeP(nkPatternStmt, p)
getTok(p) # skip `as`
if p.tok.tokType == tkColon:
eat(p, tkColon)
addSon(result, parseStmt(p))
else:
addSon(result, parseExpr(p))
proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
result = newNodeP(kind, p)
while true:
@ -1175,17 +1170,24 @@ proc parseGenericParamList(p: var TParser): PNode =
optPar(p)
eat(p, tkBracketRi)
proc parsePattern(p: var TParser): PNode =
eat(p, tkCurlyLe)
result = parseStmt(p)
eat(p, tkCurlyRi)
proc parseRoutine(p: var TParser, kind: TNodeKind): PNode =
result = newNodeP(kind, p)
getTok(p)
optInd(p, result)
addSon(result, identVis(p))
if p.tok.tokType == tkCurlyLe: addSon(result, parsePattern(p))
else: addSon(result, ast.emptyNode)
if p.tok.tokType == tkBracketLe: addSon(result, parseGenericParamList(p))
else: addSon(result, ast.emptyNode)
addSon(result, parseParamList(p))
if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p))
else: addSon(result, ast.emptyNode)
# empty pattern:
# empty exception tracking:
addSon(result, ast.emptyNode)
if p.tok.tokType == tkEquals:
getTok(p)
@ -1522,7 +1524,6 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
of tkVar: result = parseSection(p, nkVarSection, parseVariable)
of tkBind: result = parseBind(p)
of tkAs: result = parseAs(p)
else: result = simpleStmt(p)
proc parseStmt(p: var TParser): PNode =

View file

@ -92,7 +92,15 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
of nkCharLit..nkInt64Lit: result = p.intVal == n.intVal
of nkFloatLit..nkFloat64Lit: result = p.floatVal == n.floatVal
of nkStrLit..nkTripleStrLit: result = p.strVal == n.strVal
of nkEmpty, nkNilLit, nkType: result = true
of nkEmpty, nkNilLit, nkType:
result = true
# of nkStmtList:
# both are statement lists; we need to ignore comment statements and
# 'nil' statements and check whether p <: n which is however trivially
# checked as 'applyRule' is checked after every created statement
# already; We need to ensure that the matching span is passed to the
# macro and NOT simply 'n'!
# XXX
else:
if sonsLen(p) == sonsLen(n):
for i in countup(0, sonsLen(p) - 1):
@ -121,8 +129,6 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
let param = params.sons[i].sym
let x = IdNodeTableGetLazy(ctx.mapping, param)
# couldn't bind parameter:
if isNil(x):
echo "couldn't bind ", param.name.s
return nil
if isNil(x): return nil
result.add(x)
markUsed(n, s)

View file

@ -41,7 +41,7 @@ const
wFatal, wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop,
wBreakpoint, wWatchpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated,
wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
wLinearScanEnd}
wLinearScanEnd, wPatterns}
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame}
@ -302,6 +302,7 @@ proc processOption(c: PContext, n: PNode) =
excl(gOptions, optOptimizeSize)
else: LocalError(n.info, errNoneSpeedOrSizeExpected)
of wImplicitStatic: OnOff(c, n, {optImplicitStatic})
of wPatterns: OnOff(c, n, {optPatterns})
else: LocalError(n.info, errOptionExpected)
proc processPush(c: PContext, n: PNode, start: int) =
@ -643,7 +644,8 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization,
wCallConv,
wDebugger, wProfiler, wFloatChecks, wNanChecks, wInfChecks:
wDebugger, wProfiler, wFloatChecks, wNanChecks, wInfChecks,
wPatterns:
processOption(c, it) # calling conventions (boring...):
of firstCallConv..lastCallConv:
assert(sym != nil)

View file

@ -588,13 +588,13 @@ proc gwhile(g: var TSrcGen, n: PNode) =
proc gpattern(g: var TSrcGen, n: PNode) =
var c: TContext
put(g, tkAs, "as")
putWithSpace(g, tkColon, ":")
put(g, tkCurlyLe, "{")
initContext(c)
if longMode(n) or (lsub(n.sons[0]) + g.lineLen > maxLineLen):
if longMode(n) or (lsub(n.sons[0]) + g.lineLen > maxLineLen):
incl(c.flags, rfLongMode)
gcoms(g) # a good place for comments
gstmts(g, n.sons[0], c)
put(g, tkCurlyRi, "}")
proc gpragmaBlock(g: var TSrcGen, n: PNode) =
var c: TContext
@ -666,6 +666,7 @@ proc gproc(g: var TSrcGen, n: PNode) =
put(g, tkSymbol, renderDefinitionName(n.sons[namePos].sym))
else:
gsub(g, n.sons[namePos])
gsub(g, n.sons[patternPos])
gsub(g, n.sons[genericParamsPos])
gsub(g, n.sons[paramsPos])
gsub(g, n.sons[pragmasPos])
@ -1045,7 +1046,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
of nkCaseStmt, nkRecCase: gcase(g, n)
of nkMacroStmt: gmacro(g, n)
of nkTryStmt: gtry(g, n)
of nkPatternStmt: gpattern(g, n)
of nkPattern: gpattern(g, n)
of nkForStmt, nkParForStmt: gfor(g, n)
of nkBlockStmt, nkBlockExpr: gblock(g, n)
of nkStaticStmt: gstaticStmt(g, n)

View file

@ -104,7 +104,7 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
proc applyPatterns(c: PContext, n: PNode): PNode =
# fast exit:
if c.patterns.len == 0: return n
if c.patterns.len == 0 or optPatterns notin gOptions: return n
result = n
# we apply the last pattern first, so that pattern overriding is possible;
# however the resulting AST would better not trigger the old rule then
@ -113,7 +113,11 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
let x = applyRule(c, c.patterns[i], result)
if not isNil(x):
assert x.kind == nkCall
inc(evalTemplateCounter)
if evalTemplateCounter > 100:
GlobalError(n.info, errTemplateInstantiationTooNested)
result = semExpr(c, x)
dec(evalTemplateCounter)
include seminst, semcall

View file

@ -770,6 +770,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
else:
s.typ = newTypeS(tyProc, c)
rawAddSon(s.typ, nil)
if n.sons[patternPos].kind != nkEmpty:
n.sons[patternPos] = semPattern(c, n.sons[patternPos])
c.patterns.add(s)
var proto = SearchForProc(c, s, c.tab.tos-2) # -2 because we have a scope
# open for parameters
if proto == nil:
@ -1202,17 +1206,7 @@ proc SemStmt(c: PContext, n: PNode): PNode =
result = semPragmaBlock(c, n)
of nkStaticStmt:
result = semStaticStmt(c, n)
of nkPatternStmt:
let pat = semPatternStmt(c, n)
let s = getCurrOwner()
if s.kind in routineKinds and s.ast.sons[patternPos].kind == nkEmpty:
s.ast.sons[patternPos] = pat
c.patterns.add(s)
else:
LocalError(n.info, errXNotAllowedHere, "'as'")
# replace by an empty statement:
result = newNodeI(nkNilLit, n.info)
else:
else:
# in interactive mode, we embed the expression in an 'echo':
if gCmd == cmdInteractive:
result = buildEchoStmt(c, semExpr(c, n))

View file

@ -172,10 +172,11 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
else:
n.sons[namePos] = semTemplBody(c, n.sons[namePos])
openScope(c)
for i in genericParamsPos..bodyPos:
for i in patternPos..bodyPos:
n.sons[i] = semTemplBody(c, n.sons[i])
closeScope(c)
proc semPattern(c: PContext, n: PNode): PNode
proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
result = n
case n.kind
@ -396,7 +397,10 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
s.typ.n = newNodeI(nkFormalParams, n.info)
rawAddSon(s.typ, newTypeS(tyStmt, c))
addSon(s.typ.n, newNodeIT(nkType, n.info, s.typ.sons[0]))
if n.sons[patternPos].kind != nkEmpty:
n.sons[patternPos] = semPattern(c, n.sons[patternPos])
c.patterns.add(s)
var ctx: TemplCtx
ctx.toBind = initIntSet()
ctx.c = c
@ -422,7 +426,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
else:
SymTabReplace(c.tab.stack[curScope], proto, s)
proc semPatternStmt(c: PContext, n: PNode): PNode =
proc semPattern(c: PContext, n: PNode): PNode =
# not much to do here: We don't replace operators ``$``, ``*``, ``+``,
# ``|``, ``~`` as meta operators and strip the leading ``\`` of all
# operators.
@ -432,5 +436,7 @@ proc semPatternStmt(c: PContext, n: PNode): PNode =
ctx.c = c
ctx.owner = getCurrOwner()
ctx.bodyKind = bkPattern
result = semTemplBody(ctx, n.sons[0])
result = semTemplBody(ctx, n)
if result.kind in {nkStmtList, nkStmtListExpr} and result.len == 1:
result = result.sons[0]
closeScope(c.tab)

View file

@ -51,7 +51,7 @@ type
wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangechecks,
wBoundchecks, wOverflowchecks, wNilchecks,
wFloatchecks, wNanChecks, wInfChecks,
wAssertions, wWarnings,
wAssertions, wPatterns, wWarnings,
wHints, wOptimization, wSpeed, wSize, wNone,
wDeadCodeElim, wSafecode,
wPragma,
@ -131,7 +131,7 @@ const
"overflowchecks", "nilchecks",
"floatchecks", "nanchecks", "infchecks",
"assertions", "warnings", "hints",
"assertions", "patterns", "warnings", "hints",
"optimization", "speed", "size", "none",
"deadcodeelim", "safecode",
"pragma",