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

Minor documntation tweaks.

parent e0139695
......@@ -92,7 +92,7 @@ tag.
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
above the last two figures:
above the last two figures in the table above:
```
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