123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- #Copyright ReportLab Europe Ltd. 2000-2017
- #see license.txt for license details
- #history https://hg.reportlab.com/hg-public/reportlab/log/tip/src/reportlab/lib/yaml.py
- # parses "Yet Another Markup Language" into a list of tuples.
- # Each tuple says what the data is e.g.
- # ('Paragraph', 'Heading1', 'Why Reportlab Rules')
- # and the pattern depends on type.
- """
- .h1 Welcome to YAML!
- YAML is "Yet Another Markup Language" - a markup language
- which is easier to type in than XML, yet gives us a
- reasonable selection of formats.
- The general rule is that if a line begins with a '.',
- it requires special processing. Otherwise lines
- are concatenated to paragraphs, and blank lines
- separate paragraphs.
- If the line ".foo bar bletch" is encountered,
- it immediately ends and writes out any current
- paragraph.
- It then looks for a parser method called 'foo';
- if found, it is called with arguments (bar, bletch).
- If this is not found, it assumes that 'foo' is a
- paragraph style, and the text for the first line
- of the paragraph is 'bar bletch'. It would be
- up to the formatter to decide whether on not 'foo'
- was a valid paragraph.
- Special commands understood at present are:
- dot image filename
- - adds the image to the document
- dot beginPre Code
- - begins a Preformatted object in style 'Code'
- dot endPre
- - ends a preformatted object.
- """
- __version__='3.3.0'
- import sys
- #modes:
- PLAIN = 1
- PREFORMATTED = 2
- BULLETCHAR = '\267' # assumes font Symbol, but works on all platforms
- class BaseParser:
- """"Simplest possible parser with only the most basic options.
- This defines the line-handling abilities and basic mechanism.
- The class YAMLParser includes capabilities for a fairly rich
- story."""
- def __init__(self):
- self.reset()
- def reset(self):
- self._lineNo = 0
- self._style = 'Normal' # the default
- self._results = []
- self._buf = []
- self._mode = PLAIN
- def parseFile(self, filename):
- #returns list of objects
- data = open(filename, 'r').readlines()
- for line in data:
- #strip trailing newlines
- self.readLine(line[:-1])
- self.endPara()
- return self._results
- def parseText(self, textBlock):
- "Parses the a possible multi-line text block"
- lines = textBlock.split('\n')
- for line in lines:
- self.readLine(line)
- self.endPara()
- return self._results
- def readLine(self, line):
- #this is the inner loop
- self._lineNo = self._lineNo + 1
- stripped = line.lstrip()
- if len(stripped) == 0:
- if self._mode == PLAIN:
- self.endPara()
- else: #preformatted, append it
- self._buf.append(line)
- elif line[0]=='.':
- # we have a command of some kind
- self.endPara()
- words = stripped[1:].split()
- cmd, args = words[0], words[1:]
- #is it a parser method?
- if hasattr(self.__class__, cmd):
- #this was very bad; any type error in the method was hidden
- #we have to hack the traceback
- try:
- getattr(self,cmd)(*args)
- except TypeError as err:
- sys.stderr.write("Parser method: %s(*%s) %s at line %d\n" % (cmd, args, err, self._lineNo))
- raise
- else:
- # assume it is a paragraph style -
- # becomes the formatter's problem
- self.endPara() #end the last one
- words = stripped.split(' ', 1)
- assert len(words)==2, "Style %s but no data at line %d" % (words[0], self._lineNo)
- (styletag, data) = words
- self._style = styletag[1:]
- self._buf.append(data)
- else:
- #we have data, add to para
- self._buf.append(line)
- def endPara(self):
- #ends the current paragraph, or preformatted block
- text = ' '.join(self._buf)
- if text:
- if self._mode == PREFORMATTED:
- #item 3 is list of lines
- self._results.append(('PREFORMATTED', self._style,
- '\n'.join(self._buf)))
- else:
- self._results.append(('PARAGRAPH', self._style, text))
- self._buf = []
- self._style = 'Normal'
- def beginPre(self, stylename):
- self._mode = PREFORMATTED
- self._style = stylename
- def endPre(self):
- self.endPara()
- self._mode = PLAIN
- def image(self, filename):
- self.endPara()
- self._results.append(('IMAGE', filename))
- class Parser(BaseParser):
- """This adds a basic set of "story" components compatible with HTML & PDF.
- Images, spaces"""
- def vSpace(self, points):
- """Inserts a vertical spacer"""
- self._results.append(('VSpace', points))
- def pageBreak(self):
- """Inserts a frame break"""
- self._results.append(('PageBreak','blah')) # must be a tuple
- def custom(self, moduleName, funcName):
- """Goes and gets the Python object and adds it to the story"""
- self.endPara()
- self._results.append(('Custom',moduleName, funcName))
- def nextPageTemplate(self, templateName):
- self._results.append(('NextPageTemplate',templateName))
- def parseFile(filename):
- p = Parser()
- return p.parseFile(filename)
- def parseText(textBlock):
- p = Parser()
- return p.parseText(textBlock)
- if __name__=='__main__': #NORUNTESTS
- if len(sys.argv) != 2:
- results = parseText(__doc__)
- else:
- results = parseFile(sys.argv[1])
- import pprint
- pprint.pprint(results)
|