Commit 1e9b2df9 authored by Paul Warren's avatar Paul Warren
Browse files

Minor documntation tweaks.

parent e0139695
...@@ -92,7 +92,7 @@ tag. ...@@ -92,7 +92,7 @@ tag.
Styling can be applied by using CSS selectors to target the intersection of row Styling can be applied by using CSS selectors to target the intersection of row
and column classes. For example, the following CSS would put a horizontal line and column classes. For example, the following CSS would put a horizontal line
above the last two figures: above the last two figures in the table above:
``` ```
tr.total td.figure { tr.total td.figure {
......
import pyparsing as pp
import sys
import re
from .parsedtemplate import ParsedTemplate, ContentItem, NonNumericFactItem, NumericFactItem, MonetaryFactItem, ContinuationItem, NamespaceDeclarations, Row, Cell, Header, AnchorItem, idify
from .qname import QName
NCNAME_TYPE = "NCName"
QNAME_TYPE = "QName"
NUMERIC_TYPE = "Numeric"
builtInAspects = {
"period": NCNAME_TYPE,
"period-type": NCNAME_TYPE,
'monetary-units': NCNAME_TYPE,
'table-fact-type': NCNAME_TYPE,
'numeric-transform': NCNAME_TYPE,
'sign': NCNAME_TYPE,
'transform': NCNAME_TYPE,
'concept': QNAME_TYPE,
'decimals': NUMERIC_TYPE,
}
class Parser:
_pt = None
_containerStack = None
_defaults = None
_rowAspects = []
_lastClosedTag = None
def __init__(self):
pass
def parseNamespaceDecl(s, loc, toks):
Parser._pt.addNamespace(toks[1], toks[2])
def parseSchemaRef(s, loc, toks):
Parser._pt.addSchemaRef(toks[1])
def parseHeaderTag(s, loc, toks):
Parser._containerStack[-1].addItem(Header(Parser._pt))
def parseQName(s, loc, toks):
if toks.getName() == 'qname' or toks.getName() == 'concept':
uri = Parser._pt.resolvePrefix(toks.get('prefix', ""))
if uri is None:
raise pp.ParseFatalException(s, loc, msg="Error: undefined prefix '%s'" % toks.get('prefix',''))
return QName(uri, toks['localname'])
elif toks.getName() == 'ncname':
return QName("", toks[0])
else:
print("Unexpected tag type: %s" % toks.getName())
def parseAspects(s, loc, toks):
aspects = dict()
for k in toks:
if k.getName() == 'concept':
aspects[QName("", "concept")] = Parser.parseQName(s, loc, k)
elif k[0].ncname:
# Built-in aspect
aspectType = builtInAspects.get(k[0].ncname, None)
if aspectType is None:
raise pp.ParseFatalException(s, loc, msg="Unknown aspect '%s'" % k[0].ncname)
elif aspectType == NCNAME_TYPE:
if not k[1].ncname:
raise pp.ParseFatalException(s, loc, msg="Aspect '%s' takes an NCName value" % k[0].ncname)
else:
aspects[QName("", k[0].ncname)] = k[1].ncname
elif aspectType == QNAME_TYPE:
if not k[1].qname:
raise pp.ParseFatalException(s, loc, msg="Aspect '%s' takes a QName value" % k[0].ncname)
else:
aspects[QName("", k[0].ncname)] = Parser.parseQName(s, loc, k[1])
elif aspectType == NUMERIC_TYPE:
print(repr(k[1]))
if k[1].numeric:
print(k[0].ncname + "It's a number")
aspects[QName("", k[0].ncname)] = k[1]
#print(repr(k[1]))
else:
v = None
if type(k[1]) is str:
v = k[1]
else:
v = Parser.parseQName(s, loc, k[1])
aspects[Parser.parseQName(s, loc, k[0])] = v
print("Foo")
return aspects
def parseTag(s, loc, toks):
pass
def parseNonTag(s, loc, toks):
Parser._containerStack[-1].addItem(ContentItem(Parser._pt, "".join(toks)))
def applyDefaults(aspects, monetary = False):
for d, v in Parser._defaults.items():
if d not in aspects:
aspects[d] = v
return aspects
def mergeAspects(aspects, aspects2):
newAspects = aspects.copy()
for d, v in aspects2.items():
if d not in newAspects:
newAspects[d] = v
return newAspects
# ("num"|"monetary"|"string") <aspect-set>
def parseFactTag(s, loc, toks):
aspects = Parser.applyDefaults(Parser.parseAspects(s, loc, toks[1]))
if "monetary" == toks[0]:
fact = MonetaryFactItem(Parser._pt, aspects)
elif "string"== toks[0]:
fact = NonNumericFactItem(Parser._pt, aspects)
else:
fact = NumericFactItem(Parser._pt, aspects)
Parser._containerStack[-1].addItem(fact)
Parser._containerStack.append(fact)
def parseContinueTag(s, loc, toks):
fact = ContinuationItem(Parser._pt)
if not Parser._lastClosedTag:
raise pp.ParseFatalException(s, loc, msg="Continuation specified before any facts have been closed")
Parser._lastClosedTag.addContinuation(fact)
Parser._containerStack[-1].addItem(fact)
Parser._containerStack.append(fact)
def parseAnchorTag(s, loc, toks):
anchor = AnchorItem(Parser._pt)
Parser._containerStack[-1].addItem(anchor)
Parser._containerStack.append(anchor)
# row <styles> <aspect-set> [ value ]
def parseRowTag(s, loc, toks):
styles = toks[1]
rowAspects = None
if toks[2].getName() == 'aspectSet':
rowAspects = Parser.parseAspects(s, loc, toks[2])
row = Row(Parser._pt, styles)
Parser._containerStack[-1].addItem(row)
i = 0;
rowData = toks[3:]
if len(rowData) < len(list(filter(lambda c: type(c) is not str or c != 'skip', Parser._columnAspects))):
print("(line %d) %s" % (pp.lineno(loc,s), pp.line(loc,s)))
print("%d data columns defined but only %d values provided - skipping row." % (len(Parser._columnAspects), len(rowData)))
return
for j, cd in enumerate(Parser._columnAspects):
if j < len(Parser._columnStyles):
columnStyle = Parser._columnStyles[j]
else:
columnStyle = ""
if type(cd) is not str or cd != 'skip':
v = rowData[i]
fact = None
if (type(cd) is str and (cd == 'static' or cd == 'link')) or rowAspects is None:
if cd == 'link':
fact = ContentItem(Parser._pt, "<a href=\"#%s\">%s</a>" % (idify(v),v))
else:
fact = ContentItem(Parser._pt, v)
row.addItem(Cell(Parser._pt, columnStyle, fact))
else:
aspects = Parser.mergeAspects(rowAspects, cd)
aspects = Parser.applyDefaults(aspects)
factType = aspects.get(QName("", "table-fact-type"), "monetary")
fact = None
if factType == 'monetary':
fact = MonetaryFactItem(Parser._pt, aspects)
elif factType == 'numeric':
fact = NumericFactItem(Parser._pt, aspects)
elif factType == 'string':
fact = NonNumericFactItem(Parser._pt, aspects)
else:
raise pp.ParseFatalException(s, loc, msg="Unknown table-fact-type: " % factType)
row.addItem(Cell(Parser._pt, columnStyle, fact))
fact.addItem(ContentItem(Parser._pt, v))
i += 1
else:
fact = ContentItem(Parser._pt, "")
row.addItem(Cell(Parser._pt, columnStyle, fact))
def parseColumnAspects(s, loc, toks):
aspects = []
for v in toks[1:]:
if v.getName() == 'keyword':
# "static", "skip" or 'link'
aspects.append(v[0])
else:
aspects.append(Parser.parseAspects(s, loc, v))
Parser._columnAspects = aspects
def parseColumnStyles(s, loc, toks):
Parser._columnStyles = toks[1:]
def parsePeriodDecl(s, loc, toks):
Parser._pt.addPeriod(toks[1], toks[2], toks[3])
def parseDefaultDecl(s, loc, toks):
k,v = list(Parser.parseAspects(s, loc, [toks[1]]).items())[0]
Parser._defaults[k] = v
def unknownTagError(s, loc, toks):
raise pp.ParseFatalException(s, loc, msg="Error: unknown tag content: '%s'" % toks[0])
def parseEndTag(s, loc, toks):
# XXX Need to ignore Row tags here
Parser._lastClosedTag = Parser._containerStack.pop()
def parseNamespacesTag(s, loc, toks):
Parser._containerStack[-1].addItem(NamespaceDeclarations(Parser._pt))
def buildParser():
ncname = pp.Combine(pp.Word(pp.alphas + '_', exact = 1) + pp.Optional(pp.Word(pp.alphanums + "_" + "-"))).setResultsName("ncname")
qname = pp.Group(ncname.copy().setResultsName("prefix")
+ pp.Suppress(":").leaveWhitespace()
+ ncname.copy().leaveWhitespace().setResultsName("localname")).setResultsName("qname")
aspectValue = pp.Group((qname | pp.Group(ncname)) + pp.Suppress("=") + (qname | pp.Group(ncname) | pp.QuotedString('"') | pp.Group(pp.Regex(r'-?\d+(?:\.\d+)?')).setResultsName("numeric")))
aspectSet = pp.Group(pp.Optional(qname.copy().setResultsName("concept")) + pp.Suppress("[").leaveWhitespace() + pp.ZeroOrMore( aspectValue ) + pp.Suppress("]"))("aspectSet")
#aspectSet.setParseAction(Parser.parseAspectSet)
namespaceDecl = pp.Keyword("namespace") + ncname + pp.Word(pp.printables)
namespaceDecl.setParseAction(Parser.parseNamespaceDecl)
namespaceDecl.setResultsName("namespaceDecl")
schemaRef = pp.Keyword("schema-ref") + pp.Word(pp.printables)
schemaRef.setParseAction(Parser.parseSchemaRef)
nonFractionTag = pp.Literal("nf") + pp.Word(pp.alphas)
date = pp.Word(pp.printables).setParseAction(pp.pyparsing_common.convertToDate())
periodDecl = pp.Literal("period") + ncname + date + date
periodDecl.setParseAction(Parser.parsePeriodDecl)
defaultDecl = pp.Keyword("default") + aspectValue
defaultDecl.setParseAction(Parser.parseDefaultDecl)
factTag = (pp.Literal("num") | pp.Literal("string") | pp.Literal("monetary")) + aspectSet
factTag.setParseAction(Parser.parseFactTag)
continueTag = pp.Keyword("continue")
continueTag.setParseAction(Parser.parseContinueTag)
unknownTag = pp.SkipTo("}}")
unknownTag.setParseAction(Parser.unknownTagError)
columnAspectsTag = pp.Literal("column-aspects") + pp.ZeroOrMore(aspectSet | pp.Group(pp.Keyword("static") | pp.Keyword("link") | pp.Keyword("skip"))("keyword") )
columnAspectsTag.setParseAction(Parser.parseColumnAspects)
columnStylesTag = pp.Literal("column-styles") + pp.ZeroOrMore(pp.QuotedString('"'))
columnStylesTag.setParseAction(Parser.parseColumnStyles)
rowTag = pp.Literal("row") - pp.QuotedString('"') + (aspectSet | pp.Group(pp.Keyword("static"))("keyword")) + pp.ZeroOrMore(pp.QuotedString('"'))
rowTag.setParseAction(Parser.parseRowTag)
namespacesTag = pp.Literal("namespaces")
namespacesTag.setParseAction(Parser.parseNamespacesTag)
headerTag = pp.Literal("header")
headerTag.setParseAction(Parser.parseHeaderTag)
anchorTag = pp.Keyword("a")
anchorTag.setParseAction(Parser.parseAnchorTag)
endTag = pp.Group(pp.Suppress("{{") + pp.Literal("end") + pp.Suppress("}}")).setParseAction(Parser.parseEndTag)
nonTag = pp.OneOrMore(pp.White() | pp.Regex(r'((?!{{).)+') )
selfClosingTag = pp.NotAny(endTag) + pp.Suppress("{{") + (
nonFractionTag |
periodDecl |
defaultDecl |
schemaRef |
namespacesTag |
namespaceDecl |
columnAspectsTag |
columnStylesTag |
rowTag |
headerTag |
unknownTag
) + pp.Suppress("}}")
startTag = pp.Suppress("{{") + (factTag | anchorTag | continueTag) + pp.Suppress("}}")
enclosingTag = pp.Forward()
tag = enclosingTag | selfClosingTag
enclosingTag << (startTag + pp.ZeroOrMore(nonTag | tag) + endTag)
tag.setParseAction(Parser.parseTag)
tag.setName("tag")
nonTag.setParseAction(Parser.parseNonTag)
document = pp.ZeroOrMore( pp.Group(tag | nonTag )) + pp.StringEnd()
document.setName("iXBRL Template")
return document
def parse(self, s):
Parser._pt = ParsedTemplate()
Parser._containerStack = [ Parser._pt ]
Parser._defaults = dict()
parser = Parser.buildParser()
try:
result = parser.parseString(s, parseAll = True)
except pp.ParseFatalException as err:
sys.stderr.write(repr(err) + "\n")
sys.exit(1)
return Parser._pt.serialise()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment