현재
PyKi라는,
1002가 개인적으로 만들어서 사용중인 위키에서의 parser 클래스 중 일부 코드이다.
{{{~cpp
from Plex import *
import cStringIO, StringIO
class Parser:
def __init__(self, anInterWikiMap={}, aScriptName='', aMacros={}):
self.interWikiMap = anInterWikiMap
self.scriptName = aScriptName
self.macros = aMacros
def makeToStream(self, aText):
return cStringIO.StringIO(aText)
def parse(self, aText):
stream = self.makeToStream(aText)
return WikiParser(stream, self.interWikiMap,self.scriptName, self.macros).linkedLine()
class WikiParser(Scanner):
def repl_bold(self, aText):
self.bold = not self.bold
return ("<B>","</B>")[not self.bold]
def repl_italic(self, aText):
self.italic = not self.italic
return ("<I>","</I>")[not self.italic]
def repl_boldAndItalic(self, aText):
self.italic = not self.italic
return ("<I><B>","</B></I>")[not self.italic]
def repl_ruler(self, aText):
return "<HR>\n"
def repl_enter(self, aText):
return "<BR>\n"
def getLinkStr(self, anUrl):
return "<a href='%(url)s'>%(url)s</a>" % {'url':anUrl}
def getImageStr(self, anUrl):
return "<img src='%(url)s'>" % {'url':anUrl}
def isImage(self, aFileUrl):
imgExtensions = ("jpg", "png", "gif", "jpeg")
extension = aFileUrl[aFileUrl.rfind(".")+1:]
return extension in imgExtensions
def repl_url(self, aText):
if self.isImage(aText):
return self.getImageStr(aText)
return self.getLinkStr(aText)
def repl_interwiki(self, aText):
wikiMapName,pageName = self.text.split(":")
if self.interWikiMap.has_key(wikiMapName):
url = self.interWikiMap[wikiMapName]
imageTag = "<IMG SRC=../intertag.gif BORDER=0>"
formatText = "<A HREF=%(url)s>%(imageTag)s</A><A HREF=%(url)s%(pageName)s>%(pageName)s</A>"
else:
url = self.scriptName+"/InterMap"
imageTag = "<IMG SRC=../intertag.gif BORDER=1 ALT='Bad:%s'>"%(wikiMapName)
formatText="<A HREF=%(url)s>%(imageTag)s</A><A HREF=%(url)s>%(pageName)s</A>"
return formatText % {'url':url, 'pageName':pageName, 'imageTag':imageTag}
def repl_pagelink(self, aText):
if aText == "NonExistPage":
return "<a class=nonexist href='%(scriptName)s/%(pageName)s'>%(pageName)s</a>" % {'scriptName':self.scriptName, 'pageName':aText}
return "<a href='%(scriptName)s/%(pageName)s'>%(pageName)s</a>" \
% {'scriptName':self.scriptName, 'pageName':aText}
def repl_pagelinkUsingUnderbar(self, aText):
pageName = aText[:-1]
return "<a href='%(scriptName)s/%(pageName)s'>%(pageName)s</a>" % {'scriptName':self.scriptName, 'pageName':pageName}
def repl_macro(self, aText):
class NoneMacro:
def execute(self):
return ''
macroName = aText[2:-2]
macro=self.macros.get(macroName) or NoneMacro()
return macro.execute()
def repl_normalString(self, aText):
return aText
# for auto link
urlHeader = Str("http://")
stringUntilSpace = Rep(AnyBut(" "))
url = urlHeader + stringUntilSpace
upperCase = Range('AZ')
lowerCase = Range('az')
upperCaseSequence = Rep1(upperCase)
lowerCaseSequence = Rep1(lowerCase)
alphabetSequence = Rep1(upperCaseSequence | lowerCaseSequence)
interwikiDelim = Str(":")
stringUntilSpaceOrCommaOrRest = Rep1(AnyBut(" .,"))
interwiki = alphabetSequence + interwikiDelim + stringUntilSpaceOrCommaOrRest
pagelinkUsingCamelWord = upperCase + Rep(lowerCase) + Rep1(upperCaseSequence + lowerCaseSequence)
underbarForLink = Str('_')
pagelinkUsingUnderbar = Rep1(AnyBut('{\n_ ')) + underbarForLink
macro = Str('[[') + Rep1(AnyBut(']]')) + Str(']]')
space = Str(" ")
# for tag
bold = Str("'''")
italic = Str("''")
boldanditalic = bold + italic
htmllefttag = Str("<")
htmlrighttag = Str(">")
htmlandtag = Str("&")
enterCode = Str("\n")
ruler = Str("----") + enterCode
rawTextStart=Str("{{{~cpp ")
rawTextEnd=Str("} } }")
def repl_rawTextStart(self, aText):
self.begin("rawtext")
return "<PRE>"
def repl_rawTextEnd(self, aText):
self.begin("")
return "</PRE>"
lexicon = Lexicon([
(rawTextStart, repl_rawTextStart),
State('rawtext', [
(rawTextEnd, repl_rawTextEnd),
(AnyChar, repl_normalString),
]),
(italic, repl_italic),
(bold, repl_bold),
(boldanditalic, repl_boldAndItalic),
(htmllefttag, "<"),
(htmlrighttag, ">"),
(htmlandtag, "&"),
(url, repl_url),
(interwiki, repl_interwiki),
(pagelinkUsingCamelWord, repl_pagelink),
(ruler, repl_ruler),
(enterCode, repl_enter),
(pagelinkUsingUnderbar, repl_pagelinkUsingUnderbar),
(macro, repl_macro),
(AnyChar | space, repl_normalString),])
def __init__(self, aStream, anInterWikiMap={}, aScriptName='pyki.cgi', aMacroList={}):
Scanner.__init__(self, self.lexicon, aStream)
self.interWikiMap=anInterWikiMap
self.scriptName = aScriptName
self.macros = aMacroList
self.bold = False
self.italic = False
def linkedLine(self):
writer = StringIO.StringIO("")
while True:
token = self.read()
if token[0] is None:
break
writer.write(token[0])
return writer.getvalue()
}}}
=== 개발중 이슈 ===
처음에는 Wiki 에서 Tag 에 대해 Tagger 클래스를 만들고, link 를 걸어주는 부분에 대해 AutoLinker 를, Macro 에는 MacroApplyer 를 각각 만들어주었다. 그러다가 문제가 생겼는데, 태그중에 그 영향력이 겹치는 부분이 생겨나게 된 것이다. 즉, 예를 든다면 Macro 의 경우 CamelWord 로 이름지어지기도 하는데, 이는 AutoLinker 의 apply 를 거치면서 archor 태그가 붙어버리는 것이다.
해결방법 : 두가지인데, 하나는 AutoLinker 에서 Macro 관련 태그시 무시하고 지나가는 방법이고 하나는 AutoLinker 와 MacroApplyer를 통합하는 방법이다.
전자의 경우 각각의 Class Responsibility 들을 유지한다는 장점이 있지만, AutoLinker 에서 원래 생각치 않았던 한가지 일을 더 해야 한다는 점이 있겠다.
후자의 경우 클래스가 커진다는 단점이 있지만, 의도한 lexical 들만 표현된다는 점과 1 pass 로 파싱이 같이 이루어질 수 있다는 장점이 있다.
결국은 후자를 선택하였다. 근데, 그러면서 이번엔 Tagger 와 AutoLinker 양쪽에 영향력을 미칠 거리가 생겼는데, 바로 텍스트를 그대로 보여주는 태그부분이다.
그러나......~ 후자로 수정하는데 40분도 안걸리다.; 작업으로 본다면 Parser 두개의 lexicon 을 합치는 작업임에도 불구하고, 그 안정성도 보장받으면서 parser 에서 line 단위 자르기 부분까지 수정하였다. 매 번 수정할때마다 테스트를 돌리면서 확인했기 때문에 그 결과가 보장이 되었다. Text Processing 에서 이러한 부분에 대한 TDD의 파워는 정말 크다란 생각이 든다.
----
[Plex]