""" Parser classes for Cheetah's Compiler Classes: ParseError( Exception ) _LowLevelParser( Cheetah.SourceReader.SourceReader ), basically a lexer _HighLevelParser( _LowLevelParser ) Parser === _HighLevelParser (an alias) """ import os import sys import re from re import DOTALL, MULTILINE from types import StringType, ListType, TupleType, ClassType, TypeType import time from tokenize import pseudoprog import inspect import new import traceback from Cheetah.SourceReader import SourceReader from Cheetah import Filters from Cheetah import ErrorCatchers from Cheetah.Unspecified import Unspecified from Cheetah.Macros.I18n import I18n # re tools _regexCache = {} def cachedRegex(pattern): if pattern not in _regexCache: _regexCache[pattern] = re.compile(pattern) return _regexCache[pattern] def escapeRegexChars(txt, escapeRE=re.compile(r'([\$\^\*\+\.\?\{\}\[\]\(\)\|\\])')): """Return a txt with all special regular expressions chars escaped.""" return escapeRE.sub(r'\\\1' , txt) def group(*choices): return '(' + '|'.join(choices) + ')' def nongroup(*choices): return '(?:' + '|'.join(choices) + ')' def namedGroup(name, *choices): return '(P:<' + name +'>' + '|'.join(choices) + ')' def any(*choices): return apply(group, choices) + '*' def maybe(*choices): return apply(group, choices) + '?' ################################################## ## CONSTANTS & GLOBALS ## NO_CACHE = 0 STATIC_CACHE = 1 REFRESH_CACHE = 2 SET_LOCAL = 0 SET_GLOBAL = 1 SET_MODULE = 2 ################################################## ## Tokens for the parser ## #generic identchars = "abcdefghijklmnopqrstuvwxyz" \ "ABCDEFGHIJKLMNOPQRSTUVWXYZ_" namechars = identchars + "0123456789" #operators powerOp = '**' unaryArithOps = ('+', '-', '~') binaryArithOps = ('+', '-', '/', '//','%') shiftOps = ('>>','<<') bitwiseOps = ('&','|','^') assignOp = '=' augAssignOps = ('+=','-=','/=','*=', '**=','^=','%=', '>>=','<<=','&=','|=', ) assignmentOps = (assignOp,) + augAssignOps compOps = ('<','>','==','!=','<=','>=', '<>', 'is', 'in',) booleanOps = ('and','or','not') operators = (powerOp,) + unaryArithOps + binaryArithOps \ + shiftOps + bitwiseOps + assignmentOps \ + compOps + booleanOps delimeters = ('(',')','{','}','[',']', ',','.',':',';','=','`') + augAssignOps keywords = ('and', 'del', 'for', 'is', 'raise', 'assert', 'elif', 'from', 'lambda', 'return', 'break', 'else', 'global', 'not', 'try', 'class', 'except', 'if', 'or', 'while', 'continue', 'exec', 'import', 'pass', 'def', 'finally', 'in', 'print', ) single3 = "'''" double3 = '"""' tripleQuotedStringStarts = ("'''", '"""', "r'''", 'r"""', "R'''", 'R"""', "u'''", 'u"""', "U'''", 'U"""', "ur'''", 'ur"""', "Ur'''", 'Ur"""', "uR'''", 'uR"""', "UR'''", 'UR"""') tripleQuotedStringPairs = {"'''": single3, '"""': double3, "r'''": single3, 'r"""': double3, "u'''": single3, 'u"""': double3, "ur'''": single3, 'ur"""': double3, "R'''": single3, 'R"""': double3, "U'''": single3, 'U"""': double3, "uR'''": single3, 'uR"""': double3, "Ur'''": single3, 'Ur"""': double3, "UR'''": single3, 'UR"""': double3, } closurePairs= {')':'(',']':'[','}':'{'} closurePairsRev= {'(':')','[':']','{':'}'} ################################################## ## Regex chunks for the parser ## tripleQuotedStringREs = {} def makeTripleQuoteRe(start, end): start = escapeRegexChars(start) end = escapeRegexChars(end) return re.compile(r'(?:' + start + r').*?' + r'(?:' + end + r')', re.DOTALL) for start, end in tripleQuotedStringPairs.items(): tripleQuotedStringREs[start] = makeTripleQuoteRe(start, end) WS = r'[ \f\t]*' EOL = r'\r\n|\n|\r' EOLZ = EOL + r'|\Z' escCharLookBehind = nongroup(r'(?<=\A)',r'(?= len(stream): stream.setPos(len(stream) -1) self.msg = msg self.extMsg = extMsg self.lineno = lineno self.col = col def __str__(self): return self.report() def report(self): stream = self.stream if stream.filename(): f = " in file %s" % stream.filename() else: f = '' report = '' if self.lineno: lineno = self.lineno row, col, line = (lineno, (self.col or 0), self.stream.splitlines()[lineno-1]) else: row, col, line = self.stream.getRowColLine() ## get the surrounding lines lines = stream.splitlines() prevLines = [] # (rowNum, content) for i in range(1,4): if row-1-i <=0: break prevLines.append( (row-i,lines[row-1-i]) ) nextLines = [] # (rowNum, content) for i in range(1,4): if not row-1+i < len(lines): break nextLines.append( (row+i,lines[row-1+i]) ) nextLines.reverse() ## print the main message report += "\n\n%s\n" %self.msg report += "Line %i, column %i%s\n\n" % (row, col, f) report += 'Line|Cheetah Code\n' report += '----|-------------------------------------------------------------\n' while prevLines: lineInfo = prevLines.pop() report += "%(row)-4d|%(line)s\n"% {'row':lineInfo[0], 'line':lineInfo[1]} report += "%(row)-4d|%(line)s\n"% {'row':row, 'line':line} report += ' '*5 +' '*(col-1) + "^\n" while nextLines: lineInfo = nextLines.pop() report += "%(row)-4d|%(line)s\n"% {'row':lineInfo[0], 'line':lineInfo[1]} ## add the extra msg if self.extMsg: report += self.extMsg + '\n' return report class ForbiddenSyntax(ParseError): pass class ForbiddenExpression(ForbiddenSyntax): pass class ForbiddenDirective(ForbiddenSyntax): pass class CheetahVariable: def __init__(self, nameChunks, useNameMapper=True, cacheToken=None, rawSource=None): self.nameChunks = nameChunks self.useNameMapper = useNameMapper self.cacheToken = cacheToken self.rawSource = rawSource class Placeholder(CheetahVariable): pass class ArgList(object): """Used by _LowLevelParser.getArgList()""" def __init__(self): self.arguments = [] self.defaults = [] self.count = 0 def add_argument(self, name): self.arguments.append(name) self.defaults.append(None) def next(self): self.count += 1 def add_default(self, token): count = self.count if self.defaults[count] is None: self.defaults[count] = '' self.defaults[count] += token def merge(self): defaults = (isinstance(d, basestring) and d.strip() or None for d in self.defaults) return list(map(None, (a.strip() for a in self.arguments), defaults)) def __str__(self): return str(self.merge()) class _LowLevelParser(SourceReader): """This class implements the methods to match or extract ('get*') the basic elements of Cheetah's grammar. It does NOT handle any code generation or state management. """ _settingsManager = None def setSettingsManager(self, settingsManager): self._settingsManager = settingsManager def setting(self, key, default=Unspecified): if default is Unspecified: return self._settingsManager.setting(key) else: return self._settingsManager.setting(key, default=default) def setSetting(self, key, val): self._settingsManager.setSetting(key, val) def settings(self): return self._settingsManager.settings() def updateSettings(self, settings): self._settingsManager.updateSettings(settings) def _initializeSettings(self): self._settingsManager._initializeSettings() def configureParser(self): """Is called by the Compiler instance after the parser has had a settingsManager assigned with self.setSettingsManager() """ self._makeCheetahVarREs() self._makeCommentREs() self._makeDirectiveREs() self._makePspREs() self._possibleNonStrConstantChars = ( self.setting('commentStartToken')[0] + self.setting('multiLineCommentStartToken')[0] + self.setting('cheetahVarStartToken')[0] + self.setting('directiveStartToken')[0] + self.setting('PSPStartToken')[0]) self._nonStrConstMatchers = [ self.matchCommentStartToken, self.matchMultiLineCommentStartToken, self.matchVariablePlaceholderStart, self.matchExpressionPlaceholderStart, self.matchDirective, self.matchPSPStartToken, self.matchEOLSlurpToken, ] ## regex setup ## def _makeCheetahVarREs(self): """Setup the regexs for Cheetah $var parsing.""" num = r'[0-9\.]+' interval = (r'(?P' + num + r's|' + num + r'm|' + num + r'h|' + num + r'd|' + num + r'w|' + num + ')' ) cacheToken = (r'(?:' + r'(?P\*' + interval + '\*)'+ '|' + r'(?P\*)' + '|' + r'(?P)' + ')') self.cacheTokenRE = cachedRegex(cacheToken) silentPlaceholderToken = (r'(?:' + r'(?P' +escapeRegexChars('!')+')'+ '|' + r'(?P)' + ')') self.silentPlaceholderTokenRE = cachedRegex(silentPlaceholderToken) self.cheetahVarStartRE = cachedRegex( escCharLookBehind + r'(?P'+escapeRegexChars(self.setting('cheetahVarStartToken'))+')'+ r'(?P'+silentPlaceholderToken+')'+ r'(?P'+cacheToken+')'+ r'(?P|(?:(?:\{|\(|\[)[ \t\f]*))' + # allow WS after enclosure r'(?=[A-Za-z_])') validCharsLookAhead = r'(?=[A-Za-z_\*!\{\(\[])' self.cheetahVarStartToken = self.setting('cheetahVarStartToken') self.cheetahVarStartTokenRE = cachedRegex( escCharLookBehind + escapeRegexChars(self.setting('cheetahVarStartToken')) +validCharsLookAhead ) self.cheetahVarInExpressionStartTokenRE = cachedRegex( escapeRegexChars(self.setting('cheetahVarStartToken')) +r'(?=[A-Za-z_])' ) self.expressionPlaceholderStartRE = cachedRegex( escCharLookBehind + r'(?P' + escapeRegexChars(self.setting('cheetahVarStartToken')) + ')' + r'(?P' + cacheToken + ')' + #r'\[[ \t\f]*' r'(?:\{|\(|\[)[ \t\f]*' + r'(?=[^\)\}\]])' ) if self.setting('EOLSlurpToken'): self.EOLSlurpRE = cachedRegex( escapeRegexChars(self.setting('EOLSlurpToken')) + r'[ \t\f]*' + r'(?:'+EOL+')' ) else: self.EOLSlurpRE = None def _makeCommentREs(self): """Construct the regex bits that are used in comment parsing.""" startTokenEsc = escapeRegexChars(self.setting('commentStartToken')) self.commentStartTokenRE = cachedRegex(escCharLookBehind + startTokenEsc) del startTokenEsc startTokenEsc = escapeRegexChars( self.setting('multiLineCommentStartToken')) endTokenEsc = escapeRegexChars( self.setting('multiLineCommentEndToken')) self.multiLineCommentTokenStartRE = cachedRegex(escCharLookBehind + startTokenEsc) self.multiLineCommentEndTokenRE = cachedRegex(escCharLookBehind + endTokenEsc) def _makeDirectiveREs(self): """Construct the regexs that are used in directive parsing.""" startToken = self.setting('directiveStartToken') endToken = self.setting('directiveEndToken') startTokenEsc = escapeRegexChars(startToken) endTokenEsc = escapeRegexChars(endToken) validSecondCharsLookAhead = r'(?=[A-Za-z_@])' reParts = [escCharLookBehind, startTokenEsc] if self.setting('allowWhitespaceAfterDirectiveStartToken'): reParts.append('[ \t]*') reParts.append(validSecondCharsLookAhead) self.directiveStartTokenRE = cachedRegex(''.join(reParts)) self.directiveEndTokenRE = cachedRegex(escCharLookBehind + endTokenEsc) def _makePspREs(self): """Setup the regexs for PSP parsing.""" startToken = self.setting('PSPStartToken') startTokenEsc = escapeRegexChars(startToken) self.PSPStartTokenRE = cachedRegex(escCharLookBehind + startTokenEsc) endToken = self.setting('PSPEndToken') endTokenEsc = escapeRegexChars(endToken) self.PSPEndTokenRE = cachedRegex(escCharLookBehind + endTokenEsc) def isLineClearToStartToken(self, pos=None): return self.isLineClearToPos(pos) def matchTopLevelToken(self): """Returns the first match found from the following methods: self.matchCommentStartToken self.matchMultiLineCommentStartToken self.matchVariablePlaceholderStart self.matchExpressionPlaceholderStart self.matchDirective self.matchPSPStartToken self.matchEOLSlurpToken Returns None if no match. """ match = None if self.peek() in self._possibleNonStrConstantChars: for matcher in self._nonStrConstMatchers: match = matcher() if match: break return match def matchPyToken(self): match = pseudoprog.match(self.src(), self.pos()) if match and match.group() in tripleQuotedStringStarts: TQSmatch = tripleQuotedStringREs[match.group()].match(self.src(), self.pos()) if TQSmatch: return TQSmatch return match def getPyToken(self): match = self.matchPyToken() if match is None: raise ParseError(self) elif match.group() in tripleQuotedStringStarts: raise ParseError(self, msg='Malformed triple-quoted string') return self.readTo(match.end()) def matchEOLSlurpToken(self): if self.EOLSlurpRE: return self.EOLSlurpRE.match(self.src(), self.pos()) def getEOLSlurpToken(self): match = self.matchEOLSlurpToken() if not match: raise ParseError(self, msg='Invalid EOL slurp token') return self.readTo(match.end()) def matchCommentStartToken(self): return self.commentStartTokenRE.match(self.src(), self.pos()) def getCommentStartToken(self): match = self.matchCommentStartToken() if not match: raise ParseError(self, msg='Invalid single-line comment start token') return self.readTo(match.end()) def matchMultiLineCommentStartToken(self): return self.multiLineCommentTokenStartRE.match(self.src(), self.pos()) def getMultiLineCommentStartToken(self): match = self.matchMultiLineCommentStartToken() if not match: raise ParseError(self, msg='Invalid multi-line comment start token') return self.readTo(match.end()) def matchMultiLineCommentEndToken(self): return self.multiLineCommentEndTokenRE.match(self.src(), self.pos()) def getMultiLineCommentEndToken(self): match = self.matchMultiLineCommentEndToken() if not match: raise ParseError(self, msg='Invalid multi-line comment end token') return self.readTo(match.end()) def getCommaSeparatedSymbols(self): """ Loosely based on getDottedName to pull out comma separated named chunks """ srcLen = len(self) pieces = [] nameChunks = [] if not self.peek() in identchars: raise ParseError(self) while self.pos() < srcLen: c = self.peek() if c in namechars: nameChunk = self.getIdentifier() nameChunks.append(nameChunk) elif c == '.': if self.pos()+1 endOfFirstLine): self._compiler.handleWSBeforeDirective() self._compiler.addComment(comm) def eatPlaceholder(self): (expr, rawPlaceholder, lineCol, cacheTokenParts, filterArgs, isSilentPlaceholder) = self.getPlaceholder( allowCacheTokens=True, returnEverything=True) self._compiler.addPlaceholder( expr, filterArgs=filterArgs, rawPlaceholder=rawPlaceholder, cacheTokenParts=cacheTokenParts, lineCol=lineCol, silentMode=isSilentPlaceholder) return def eatPSP(self): # filtered self._filterDisabledDirectives(directiveName='psp') self.getPSPStartToken() endToken = self.setting('PSPEndToken') startPos = self.pos() while not self.atEnd(): if self.peek() == endToken[0]: if self.matchPSPEndToken(): break self.advance() pspString = self.readTo(self.pos(), start=startPos).strip() pspString = self._applyExpressionFilters(pspString, 'psp', startPos=startPos) self._compiler.addPSP(pspString) self.getPSPEndToken() ## generic directive eat methods _simpleIndentingDirectives = ''' else elif for while repeat unless try except finally'''.split() _simpleExprDirectives = ''' pass continue stop return yield break del assert raise silent echo import from'''.split() _directiveHandlerNames = {'import':'addImportStatement', 'from':'addImportStatement', } def eatDirective(self): directiveName = self.matchDirective() self._filterDisabledDirectives(directiveName) for callback in self.setting('preparseDirectiveHooks'): callback(parser=self, directiveName=directiveName) # subclasses can override the default behaviours here by providing an # eater method in self._directiveNamesAndParsers[directiveName] directiveParser = self._directiveNamesAndParsers.get(directiveName) if directiveParser: directiveParser() elif directiveName in self._simpleIndentingDirectives: handlerName = self._directiveHandlerNames.get(directiveName) if not handlerName: handlerName = 'add'+directiveName.capitalize() handler = getattr(self._compiler, handlerName) self.eatSimpleIndentingDirective(directiveName, callback=handler) elif directiveName in self._simpleExprDirectives: handlerName = self._directiveHandlerNames.get(directiveName) if not handlerName: handlerName = 'add'+directiveName.capitalize() handler = getattr(self._compiler, handlerName) if directiveName in ('silent', 'echo'): includeDirectiveNameInExpr = False else: includeDirectiveNameInExpr = True expr = self.eatSimpleExprDirective( directiveName, includeDirectiveNameInExpr=includeDirectiveNameInExpr) handler(expr) ## for callback in self.setting('postparseDirectiveHooks'): callback(parser=self, directiveName=directiveName) def _eatRestOfDirectiveTag(self, isLineClearToStartToken, endOfFirstLinePos): foundComment = False if self.matchCommentStartToken(): pos = self.pos() self.advance() if not self.matchDirective(): self.setPos(pos) foundComment = True self.eatComment() # this won't gobble the EOL else: self.setPos(pos) if not foundComment and self.matchDirectiveEndToken(): self.getDirectiveEndToken() elif isLineClearToStartToken and (not self.atEnd()) and self.peek() in '\r\n': # still gobble the EOL if a comment was found. self.readToEOL(gobble=True) if isLineClearToStartToken and (self.atEnd() or self.pos() > endOfFirstLinePos): self._compiler.handleWSBeforeDirective() def _eatToThisEndDirective(self, directiveName): finalPos = endRawPos = startPos = self.pos() directiveChar = self.setting('directiveStartToken')[0] isLineClearToStartToken = False while not self.atEnd(): if self.peek() == directiveChar: if self.matchDirective() == 'end': endRawPos = self.pos() self.getDirectiveStartToken() self.advance(len('end')) self.getWhiteSpace() if self.startswith(directiveName): if self.isLineClearToStartToken(endRawPos): isLineClearToStartToken = True endRawPos = self.findBOL(endRawPos) self.advance(len(directiveName)) # to end of directiveName self.getWhiteSpace() finalPos = self.pos() break self.advance() finalPos = endRawPos = self.pos() textEaten = self.readTo(endRawPos, start=startPos) self.setPos(finalPos) endOfFirstLinePos = self.findEOL() if self.matchDirectiveEndToken(): self.getDirectiveEndToken() elif isLineClearToStartToken and (not self.atEnd()) and self.peek() in '\r\n': self.readToEOL(gobble=True) if isLineClearToStartToken and self.pos() > endOfFirstLinePos: self._compiler.handleWSBeforeDirective() return textEaten def eatSimpleExprDirective(self, directiveName, includeDirectiveNameInExpr=True): # filtered isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLine = self.findEOL() self.getDirectiveStartToken() if not includeDirectiveNameInExpr: self.advance(len(directiveName)) startPos = self.pos() expr = self.getExpression().strip() directiveName = expr.split()[0] expr = self._applyExpressionFilters(expr, directiveName, startPos=startPos) if directiveName in self._closeableDirectives: self.pushToOpenDirectivesStack(directiveName) self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLine) return expr def eatSimpleIndentingDirective(self, directiveName, callback, includeDirectiveNameInExpr=False): # filtered isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() lineCol = self.getRowCol() self.getDirectiveStartToken() if directiveName not in 'else elif for while try except finally'.split(): self.advance(len(directiveName)) startPos = self.pos() self.getWhiteSpace() expr = self.getExpression(pyTokensToBreakAt=[':']) expr = self._applyExpressionFilters(expr, directiveName, startPos=startPos) if self.matchColonForSingleLineShortFormDirective(): self.advance() # skip over : if directiveName in 'else elif except finally'.split(): callback(expr, dedent=False, lineCol=lineCol) else: callback(expr, lineCol=lineCol) self.getWhiteSpace(max=1) self.parse(breakPoint=self.findEOL(gobble=True)) self._compiler.commitStrConst() self._compiler.dedent() else: if self.peek()==':': self.advance() self.getWhiteSpace() self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) if directiveName in self._closeableDirectives: self.pushToOpenDirectivesStack(directiveName) callback(expr, lineCol=lineCol) def eatEndDirective(self): isLineClearToStartToken = self.isLineClearToStartToken() self.getDirectiveStartToken() self.advance(3) # to end of 'end' self.getWhiteSpace() pos = self.pos() directiveName = False for key in self._endDirectiveNamesAndHandlers.keys(): if self.find(key, pos) == pos: directiveName = key break if not directiveName: raise ParseError(self, msg='Invalid end directive') endOfFirstLinePos = self.findEOL() self.getExpression() # eat in any extra comment-like crap self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) if directiveName in self._closeableDirectives: self.popFromOpenDirectivesStack(directiveName) # subclasses can override the default behaviours here by providing an # end-directive handler in self._endDirectiveNamesAndHandlers[directiveName] if self._endDirectiveNamesAndHandlers.get(directiveName): handler = self._endDirectiveNamesAndHandlers[directiveName] handler() elif directiveName in 'block capture cache call filter errorCatcher'.split(): if key == 'block': self._compiler.closeBlock() elif key == 'capture': self._compiler.endCaptureRegion() elif key == 'cache': self._compiler.endCacheRegion() elif key == 'call': self._compiler.endCallRegion() elif key == 'filter': self._compiler.closeFilterBlock() elif key == 'errorCatcher': self._compiler.turnErrorCatcherOff() elif directiveName in 'while for if try repeat unless'.split(): self._compiler.commitStrConst() self._compiler.dedent() elif directiveName=='closure': self._compiler.commitStrConst() self._compiler.dedent() # @@TR: temporary hack of useSearchList self.setSetting('useSearchList', self._useSearchList_orig) ## specific directive eat methods def eatBreakPoint(self): """Tells the parser to stop parsing at this point and completely ignore everything else. This is a debugging tool. """ self.setBreakPoint(self.pos()) def eatShbang(self): # filtered self.getDirectiveStartToken() self.advance(len('shBang')) self.getWhiteSpace() startPos = self.pos() shBang = self.readToEOL() shBang = self._applyExpressionFilters(shBang, 'shbang', startPos=startPos) self._compiler.setShBang(shBang.strip()) def eatEncoding(self): # filtered self.getDirectiveStartToken() self.advance(len('encoding')) self.getWhiteSpace() startPos = self.pos() encoding = self.readToEOL() encoding = self._applyExpressionFilters(encoding, 'encoding', startPos=startPos) self._compiler.setModuleEncoding(encoding.strip()) def eatCompiler(self): # filtered isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLine = self.findEOL() startPos = self.pos() self.getDirectiveStartToken() self.advance(len('compiler')) # to end of 'compiler' self.getWhiteSpace() startPos = self.pos() settingName = self.getIdentifier() if settingName.lower() == 'reset': self.getExpression() # gobble whitespace & junk self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLine) self._initializeSettings() self.configureParser() return self.getWhiteSpace() if self.peek() == '=': self.advance() else: raise ParseError(self) valueExpr = self.getExpression() endPos = self.pos() # @@TR: it's unlikely that anyone apply filters would have left this # directive enabled: # @@TR: fix up filtering, regardless self._applyExpressionFilters('%s=%r'%(settingName, valueExpr), 'compiler', startPos=startPos) self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLine) try: self._compiler.setCompilerSetting(settingName, valueExpr) except: sys.stderr.write('An error occurred while processing the following #compiler directive.\n') sys.stderr.write('----------------------------------------------------------------------\n') sys.stderr.write('%s\n' % self[startPos:endPos]) sys.stderr.write('----------------------------------------------------------------------\n') sys.stderr.write('Please check the syntax of these settings.\n\n') raise def eatCompilerSettings(self): # filtered isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLine = self.findEOL() self.getDirectiveStartToken() self.advance(len('compiler-settings')) # to end of 'settings' keywords = self.getTargetVarsList() self.getExpression() # gobble any garbage self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLine) if 'reset' in keywords: self._compiler._initializeSettings() self.configureParser() # @@TR: this implies a single-line #compiler-settings directive, and # thus we should parse forward for an end directive. # Subject to change in the future return startPos = self.pos() settingsStr = self._eatToThisEndDirective('compiler-settings') settingsStr = self._applyExpressionFilters(settingsStr, 'compilerSettings', startPos=startPos) try: self._compiler.setCompilerSettings(keywords=keywords, settingsStr=settingsStr) except: sys.stderr.write('An error occurred while processing the following compiler settings.\n') sys.stderr.write('----------------------------------------------------------------------\n') sys.stderr.write('%s\n' % settingsStr.strip()) sys.stderr.write('----------------------------------------------------------------------\n') sys.stderr.write('Please check the syntax of these settings.\n\n') raise def eatAttr(self): # filtered isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() startPos = self.pos() self.getDirectiveStartToken() self.advance(len('attr')) self.getWhiteSpace() startPos = self.pos() if self.matchCheetahVarStart(): self.getCheetahVarStartToken() attribName = self.getIdentifier() self.getWhiteSpace() self.getAssignmentOperator() expr = self.getExpression() expr = self._applyExpressionFilters(expr, 'attr', startPos=startPos) self._compiler.addAttribute(attribName, expr) self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) def eatDecorator(self): isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() startPos = self.pos() self.getDirectiveStartToken() #self.advance() # eat @ startPos = self.pos() decoratorExpr = self.getExpression() decoratorExpr = self._applyExpressionFilters(decoratorExpr, 'decorator', startPos=startPos) self._compiler.addDecorator(decoratorExpr) self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) self.getWhiteSpace() directiveName = self.matchDirective() if not directiveName or directiveName not in ('def', 'block', 'closure', '@'): raise ParseError( self, msg='Expected #def, #block, #closure or another @decorator') self.eatDirective() def eatDef(self): # filtered self._eatDefOrBlock('def') def eatBlock(self): # filtered startPos = self.pos() methodName, rawSignature = self._eatDefOrBlock('block') self._compiler._blockMetaData[methodName] = { 'raw':rawSignature, 'lineCol':self.getRowCol(startPos), } def eatClosure(self): # filtered self._eatDefOrBlock('closure') def _eatDefOrBlock(self, directiveName): # filtered assert directiveName in ('def','block','closure') isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() startPos = self.pos() self.getDirectiveStartToken() self.advance(len(directiveName)) self.getWhiteSpace() if self.matchCheetahVarStart(): self.getCheetahVarStartToken() methodName = self.getIdentifier() self.getWhiteSpace() if self.peek() == '(': argsList = self.getDefArgList() self.advance() # past the closing ')' if argsList and argsList[0][0] == 'self': del argsList[0] else: argsList=[] def includeBlockMarkers(): if self.setting('includeBlockMarkers'): startMarker = self.setting('blockMarkerStart') self._compiler.addStrConst(startMarker[0] + methodName + startMarker[1]) # @@TR: fix up filtering self._applyExpressionFilters(self[startPos:self.pos()], 'def', startPos=startPos) if self.matchColonForSingleLineShortFormDirective(): isNestedDef = (self.setting('allowNestedDefScopes') and [name for name in self._openDirectivesStack if name=='def']) self.getc() rawSignature = self[startPos:endOfFirstLinePos] self._eatSingleLineDef(directiveName=directiveName, methodName=methodName, argsList=argsList, startPos=startPos, endPos=endOfFirstLinePos) if directiveName == 'def' and not isNestedDef: #@@TR: must come before _eatRestOfDirectiveTag ... for some reason self._compiler.closeDef() elif directiveName == 'block': includeBlockMarkers() self._compiler.closeBlock() elif directiveName == 'closure' or isNestedDef: self._compiler.dedent() self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) else: if self.peek()==':': self.getc() self.pushToOpenDirectivesStack(directiveName) rawSignature = self[startPos:self.pos()] self._eatMultiLineDef(directiveName=directiveName, methodName=methodName, argsList=argsList, startPos=startPos, isLineClearToStartToken=isLineClearToStartToken) if directiveName == 'block': includeBlockMarkers() return methodName, rawSignature def _eatMultiLineDef(self, directiveName, methodName, argsList, startPos, isLineClearToStartToken=False): # filtered in calling method self.getExpression() # slurp up any garbage left at the end signature = self[startPos:self.pos()] endOfFirstLinePos = self.findEOL() self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) signature = ' '.join([line.strip() for line in signature.splitlines()]) parserComment = ('## CHEETAH: generated from ' + signature + ' at line %s, col %s' % self.getRowCol(startPos) + '.') isNestedDef = (self.setting('allowNestedDefScopes') and len([name for name in self._openDirectivesStack if name=='def'])>1) if directiveName=='block' or (directiveName=='def' and not isNestedDef): self._compiler.startMethodDef(methodName, argsList, parserComment) else: #closure self._useSearchList_orig = self.setting('useSearchList') self.setSetting('useSearchList', False) self._compiler.addClosure(methodName, argsList, parserComment) return methodName def _eatSingleLineDef(self, directiveName, methodName, argsList, startPos, endPos): # filtered in calling method fullSignature = self[startPos:endPos] parserComment = ('## Generated from ' + fullSignature + ' at line %s, col %s' % self.getRowCol(startPos) + '.') isNestedDef = (self.setting('allowNestedDefScopes') and [name for name in self._openDirectivesStack if name=='def']) if directiveName=='block' or (directiveName=='def' and not isNestedDef): self._compiler.startMethodDef(methodName, argsList, parserComment) else: #closure # @@TR: temporary hack of useSearchList useSearchList_orig = self.setting('useSearchList') self.setSetting('useSearchList', False) self._compiler.addClosure(methodName, argsList, parserComment) self.getWhiteSpace(max=1) self.parse(breakPoint=endPos) if directiveName=='closure' or isNestedDef: # @@TR: temporary hack of useSearchList self.setSetting('useSearchList', useSearchList_orig) def eatExtends(self): # filtered isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLine = self.findEOL() self.getDirectiveStartToken() self.advance(len('extends')) self.getWhiteSpace() startPos = self.pos() if self.setting('allowExpressionsInExtendsDirective'): baseName = self.getExpression() else: baseName = self.getCommaSeparatedSymbols() baseName = ', '.join(baseName) baseName = self._applyExpressionFilters(baseName, 'extends', startPos=startPos) self._compiler.setBaseClass(baseName) # in compiler self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLine) def eatImplements(self): # filtered isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLine = self.findEOL() self.getDirectiveStartToken() self.advance(len('implements')) self.getWhiteSpace() startPos = self.pos() methodName = self.getIdentifier() if not self.atEnd() and self.peek() == '(': argsList = self.getDefArgList() self.advance() # past the closing ')' if argsList and argsList[0][0] == 'self': del argsList[0] else: argsList=[] # @@TR: need to split up filtering of the methodname and the args #methodName = self._applyExpressionFilters(methodName, 'implements', startPos=startPos) self._applyExpressionFilters(self[startPos:self.pos()], 'implements', startPos=startPos) self._compiler.setMainMethodName(methodName) self._compiler.setMainMethodArgs(argsList) self.getExpression() # throw away and unwanted crap that got added in self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLine) def eatSuper(self): # filtered isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLine = self.findEOL() self.getDirectiveStartToken() self.advance(len('super')) self.getWhiteSpace() startPos = self.pos() if not self.atEnd() and self.peek() == '(': argsList = self.getDefArgList() self.advance() # past the closing ')' if argsList and argsList[0][0] == 'self': del argsList[0] else: argsList=[] self._applyExpressionFilters(self[startPos:self.pos()], 'super', startPos=startPos) #parserComment = ('## CHEETAH: generated from ' + signature + # ' at line %s, col %s' % self.getRowCol(startPos) # + '.') self.getExpression() # throw away and unwanted crap that got added in self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLine) self._compiler.addSuper(argsList) def eatSet(self): # filtered isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLine = self.findEOL() self.getDirectiveStartToken() self.advance(3) self.getWhiteSpace() style = SET_LOCAL if self.startswith('local'): self.getIdentifier() self.getWhiteSpace() elif self.startswith('global'): self.getIdentifier() self.getWhiteSpace() style = SET_GLOBAL elif self.startswith('module'): self.getIdentifier() self.getWhiteSpace() style = SET_MODULE startsWithDollar = self.matchCheetahVarStart() startPos = self.pos() LVALUE = self.getExpression(pyTokensToBreakAt=assignmentOps, useNameMapper=False).strip() OP = self.getAssignmentOperator() RVALUE = self.getExpression() expr = LVALUE + ' ' + OP + ' ' + RVALUE.strip() expr = self._applyExpressionFilters(expr, 'set', startPos=startPos) self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLine) class Components: pass # used for 'set global' exprComponents = Components() exprComponents.LVALUE = LVALUE exprComponents.OP = OP exprComponents.RVALUE = RVALUE self._compiler.addSet(expr, exprComponents, style) def eatSlurp(self): if self.isLineClearToStartToken(): self._compiler.handleWSBeforeDirective() self._compiler.commitStrConst() self.readToEOL(gobble=True) def eatEOLSlurpToken(self): if self.isLineClearToStartToken(): self._compiler.handleWSBeforeDirective() self._compiler.commitStrConst() self.readToEOL(gobble=True) def eatRaw(self): isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() self.getDirectiveStartToken() self.advance(len('raw')) self.getWhiteSpace() if self.matchColonForSingleLineShortFormDirective(): self.advance() # skip over : self.getWhiteSpace(max=1) rawBlock = self.readToEOL(gobble=False) else: if self.peek()==':': self.advance() self.getWhiteSpace() self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) rawBlock = self._eatToThisEndDirective('raw') self._compiler.addRawText(rawBlock) def eatInclude(self): # filtered isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() self.getDirectiveStartToken() self.advance(len('include')) self.getWhiteSpace() includeFrom = 'file' isRaw = False if self.startswith('raw'): self.advance(3) isRaw=True self.getWhiteSpace() if self.startswith('source'): self.advance(len('source')) includeFrom = 'str' self.getWhiteSpace() if not self.peek() == '=': raise ParseError(self) self.advance() startPos = self.pos() sourceExpr = self.getExpression() sourceExpr = self._applyExpressionFilters(sourceExpr, 'include', startPos=startPos) self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) self._compiler.addInclude(sourceExpr, includeFrom, isRaw) def eatDefMacro(self): # @@TR: not filtered yet isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() self.getDirectiveStartToken() self.advance(len('defmacro')) self.getWhiteSpace() if self.matchCheetahVarStart(): self.getCheetahVarStartToken() macroName = self.getIdentifier() self.getWhiteSpace() if self.peek() == '(': argsList = self.getDefArgList(useNameMapper=False) self.advance() # past the closing ')' if argsList and argsList[0][0] == 'self': del argsList[0] else: argsList=[] assert not self._directiveNamesAndParsers.has_key(macroName) argsList.insert(0, ('src',None)) argsList.append(('parser','None')) argsList.append(('macros','None')) argsList.append(('compilerSettings','None')) argsList.append(('isShortForm','None')) argsList.append(('EOLCharsInShortForm','None')) argsList.append(('startPos','None')) argsList.append(('endPos','None')) if self.matchColonForSingleLineShortFormDirective(): self.advance() # skip over : self.getWhiteSpace(max=1) macroSrc = self.readToEOL(gobble=False) self.readToEOL(gobble=True) else: if self.peek()==':': self.advance() self.getWhiteSpace() self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) macroSrc = self._eatToThisEndDirective('defmacro') #print argsList normalizedMacroSrc = ''.join( ['%def callMacro('+','.join([defv and '%s=%s'%(n,defv) or n for n,defv in argsList]) +')\n', macroSrc, '%end def']) from Cheetah.Template import Template templateAPIClass = self.setting('templateAPIClassForDefMacro', default=Template) compilerSettings = self.setting('compilerSettingsForDefMacro', default={}) searchListForMacros = self.setting('searchListForDefMacro', default=[]) searchListForMacros = list(searchListForMacros) # copy to avoid mutation bugs searchListForMacros.append({'macros':self._macros, 'parser':self, 'compilerSettings':self.settings(), }) templateAPIClass._updateSettingsWithPreprocessTokens( compilerSettings, placeholderToken='@', directiveToken='%') macroTemplateClass = templateAPIClass.compile(source=normalizedMacroSrc, compilerSettings=compilerSettings) #print normalizedMacroSrc #t = macroTemplateClass() #print t.callMacro('src') #print t.generatedClassCode() class MacroDetails: pass macroDetails = MacroDetails() macroDetails.macroSrc = macroSrc macroDetails.argsList = argsList macroDetails.template = macroTemplateClass(searchList=searchListForMacros) self._macroDetails[macroName] = macroDetails self._macros[macroName] = macroDetails.template.callMacro self._directiveNamesAndParsers[macroName] = self.eatMacroCall def eatMacroCall(self): isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() startPos = self.pos() self.getDirectiveStartToken() macroName = self.getIdentifier() macro = self._macros[macroName] if hasattr(macro, 'parse'): return macro.parse(parser=self, startPos=startPos) if hasattr(macro, 'parseArgs'): args = macro.parseArgs(parser=self, startPos=startPos) else: self.getWhiteSpace() args = self.getExpression(useNameMapper=False, pyTokensToBreakAt=[':']).strip() if self.matchColonForSingleLineShortFormDirective(): isShortForm = True self.advance() # skip over : self.getWhiteSpace(max=1) srcBlock = self.readToEOL(gobble=False) EOLCharsInShortForm = self.readToEOL(gobble=True) #self.readToEOL(gobble=False) else: isShortForm = False if self.peek()==':': self.advance() self.getWhiteSpace() self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) srcBlock = self._eatToThisEndDirective(macroName) if hasattr(macro, 'convertArgStrToDict'): kwArgs = macro.convertArgStrToDict(args, parser=self, startPos=startPos) else: def getArgs(*pargs, **kws): return pargs, kws exec 'positionalArgs, kwArgs = getArgs(%(args)s)'%locals() assert not kwArgs.has_key('src') kwArgs['src'] = srcBlock if type(macro)==new.instancemethod: co = macro.im_func.func_code elif (hasattr(macro, '__call__') and hasattr(macro.__call__, 'im_func')): co = macro.__call__.im_func.func_code else: co = macro.func_code availableKwArgs = inspect.getargs(co)[0] if 'parser' in availableKwArgs: kwArgs['parser'] = self if 'macros' in availableKwArgs: kwArgs['macros'] = self._macros if 'compilerSettings' in availableKwArgs: kwArgs['compilerSettings'] = self.settings() if 'isShortForm' in availableKwArgs: kwArgs['isShortForm'] = isShortForm if isShortForm and 'EOLCharsInShortForm' in availableKwArgs: kwArgs['EOLCharsInShortForm'] = EOLCharsInShortForm if 'startPos' in availableKwArgs: kwArgs['startPos'] = startPos if 'endPos' in availableKwArgs: kwArgs['endPos'] = self.pos() srcFromMacroOutput = macro(**kwArgs) origParseSrc = self._src origBreakPoint = self.breakPoint() origPos = self.pos() # add a comment to the output about the macro src that is being parsed # or add a comment prefix to all the comments added by the compiler self._src = srcFromMacroOutput self.setPos(0) self.setBreakPoint(len(srcFromMacroOutput)) self.parse(assertEmptyStack=False) self._src = origParseSrc self.setBreakPoint(origBreakPoint) self.setPos(origPos) #self._compiler.addRawText('end') def eatCache(self): isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() lineCol = self.getRowCol() self.getDirectiveStartToken() self.advance(len('cache')) startPos = self.pos() argList = self.getDefArgList(useNameMapper=True) argList = self._applyExpressionFilters(argList, 'cache', startPos=startPos) def startCache(): cacheInfo = self._compiler.genCacheInfoFromArgList(argList) self._compiler.startCacheRegion(cacheInfo, lineCol) if self.matchColonForSingleLineShortFormDirective(): self.advance() # skip over : self.getWhiteSpace(max=1) startCache() self.parse(breakPoint=self.findEOL(gobble=True)) self._compiler.endCacheRegion() else: if self.peek()==':': self.advance() self.getWhiteSpace() self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) self.pushToOpenDirectivesStack('cache') startCache() def eatCall(self): # @@TR: need to enable single line version of this isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() lineCol = self.getRowCol() self.getDirectiveStartToken() self.advance(len('call')) startPos = self.pos() useAutocallingOrig = self.setting('useAutocalling') self.setSetting('useAutocalling', False) self.getWhiteSpace() if self.matchCheetahVarStart(): functionName = self.getCheetahVar() else: functionName = self.getCheetahVar(plain=True, skipStartToken=True) self.setSetting('useAutocalling', useAutocallingOrig) # @@TR: fix up filtering self._applyExpressionFilters(self[startPos:self.pos()], 'call', startPos=startPos) self.getWhiteSpace() args = self.getExpression(pyTokensToBreakAt=[':']).strip() if self.matchColonForSingleLineShortFormDirective(): self.advance() # skip over : self._compiler.startCallRegion(functionName, args, lineCol) self.getWhiteSpace(max=1) self.parse(breakPoint=self.findEOL(gobble=False)) self._compiler.endCallRegion() else: if self.peek()==':': self.advance() self.getWhiteSpace() self.pushToOpenDirectivesStack("call") self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) self._compiler.startCallRegion(functionName, args, lineCol) def eatCallArg(self): isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() lineCol = self.getRowCol() self.getDirectiveStartToken() self.advance(len('arg')) startPos = self.pos() self.getWhiteSpace() argName = self.getIdentifier() self.getWhiteSpace() argName = self._applyExpressionFilters(argName, 'arg', startPos=startPos) self._compiler.setCallArg(argName, lineCol) if self.peek() == ':': self.getc() else: self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) def eatFilter(self): isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() self.getDirectiveStartToken() self.advance(len('filter')) self.getWhiteSpace() startPos = self.pos() if self.matchCheetahVarStart(): isKlass = True theFilter = self.getExpression(pyTokensToBreakAt=[':']) else: isKlass = False theFilter = self.getIdentifier() self.getWhiteSpace() theFilter = self._applyExpressionFilters(theFilter, 'filter', startPos=startPos) if self.matchColonForSingleLineShortFormDirective(): self.advance() # skip over : self.getWhiteSpace(max=1) self._compiler.setFilter(theFilter, isKlass) self.parse(breakPoint=self.findEOL(gobble=False)) self._compiler.closeFilterBlock() else: if self.peek()==':': self.advance() self.getWhiteSpace() self.pushToOpenDirectivesStack("filter") self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) self._compiler.setFilter(theFilter, isKlass) def eatTransform(self): isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() self.getDirectiveStartToken() self.advance(len('transform')) self.getWhiteSpace() startPos = self.pos() if self.matchCheetahVarStart(): isKlass = True transformer = self.getExpression(pyTokensToBreakAt=[':']) else: isKlass = False transformer = self.getIdentifier() self.getWhiteSpace() transformer = self._applyExpressionFilters(transformer, 'transform', startPos=startPos) if self.peek()==':': self.advance() self.getWhiteSpace() self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) self._compiler.setTransform(transformer, isKlass) def eatErrorCatcher(self): isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() self.getDirectiveStartToken() self.advance(len('errorCatcher')) self.getWhiteSpace() startPos = self.pos() errorCatcherName = self.getIdentifier() errorCatcherName = self._applyExpressionFilters( errorCatcherName, 'errorcatcher', startPos=startPos) self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) self._compiler.setErrorCatcher(errorCatcherName) def eatCapture(self): # @@TR: this could be refactored to use the code in eatSimpleIndentingDirective # filtered isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() lineCol = self.getRowCol() self.getDirectiveStartToken() self.advance(len('capture')) startPos = self.pos() self.getWhiteSpace() expr = self.getExpression(pyTokensToBreakAt=[':']) expr = self._applyExpressionFilters(expr, 'capture', startPos=startPos) if self.matchColonForSingleLineShortFormDirective(): self.advance() # skip over : self._compiler.startCaptureRegion(assignTo=expr, lineCol=lineCol) self.getWhiteSpace(max=1) self.parse(breakPoint=self.findEOL(gobble=False)) self._compiler.endCaptureRegion() else: if self.peek()==':': self.advance() self.getWhiteSpace() self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLinePos) self.pushToOpenDirectivesStack("capture") self._compiler.startCaptureRegion(assignTo=expr, lineCol=lineCol) def eatIf(self): # filtered isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLine = self.findEOL() lineCol = self.getRowCol() self.getDirectiveStartToken() startPos = self.pos() expressionParts = self.getExpressionParts(pyTokensToBreakAt=[':']) expr = ''.join(expressionParts).strip() expr = self._applyExpressionFilters(expr, 'if', startPos=startPos) isTernaryExpr = ('then' in expressionParts and 'else' in expressionParts) if isTernaryExpr: conditionExpr = [] trueExpr = [] falseExpr = [] currentExpr = conditionExpr for part in expressionParts: if part.strip()=='then': currentExpr = trueExpr elif part.strip()=='else': currentExpr = falseExpr else: currentExpr.append(part) conditionExpr = ''.join(conditionExpr) trueExpr = ''.join(trueExpr) falseExpr = ''.join(falseExpr) self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLine) self._compiler.addTernaryExpr(conditionExpr, trueExpr, falseExpr, lineCol=lineCol) elif self.matchColonForSingleLineShortFormDirective(): self.advance() # skip over : self._compiler.addIf(expr, lineCol=lineCol) self.getWhiteSpace(max=1) self.parse(breakPoint=self.findEOL(gobble=True)) self._compiler.commitStrConst() self._compiler.dedent() else: if self.peek()==':': self.advance() self.getWhiteSpace() self._eatRestOfDirectiveTag(isLineClearToStartToken, endOfFirstLine) self.pushToOpenDirectivesStack('if') self._compiler.addIf(expr, lineCol=lineCol) ## end directive handlers def handleEndDef(self): isNestedDef = (self.setting('allowNestedDefScopes') and [name for name in self._openDirectivesStack if name=='def']) if not isNestedDef: self._compiler.closeDef() else: # @@TR: temporary hack of useSearchList self.setSetting('useSearchList', self._useSearchList_orig) self._compiler.commitStrConst() self._compiler.dedent() ### def pushToOpenDirectivesStack(self, directiveName): assert directiveName in self._closeableDirectives self._openDirectivesStack.append(directiveName) def popFromOpenDirectivesStack(self, directiveName): if not self._openDirectivesStack: raise ParseError(self, msg="#end found, but nothing to end") if self._openDirectivesStack[-1] == directiveName: del self._openDirectivesStack[-1] else: raise ParseError(self, msg="#end %s found, expected #end %s" %( directiveName, self._openDirectivesStack[-1])) def assertEmptyOpenDirectivesStack(self): if self._openDirectivesStack: errorMsg = ( "Some #directives are missing their corresponding #end ___ tag: %s" %( ', '.join(self._openDirectivesStack))) raise ParseError(self, msg=errorMsg) ################################################## ## Make an alias to export Parser = _HighLevelParser