From 342b5c31fbadb2b61cbd2acf95e7dbc7ced649a2 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 26 Jan 2020 05:57:02 +0000 Subject: [PATCH] work on vizml --- src/output/spv/spvxml-helpers.c | 31 ++- src/output/spv/vizml.grammar | 358 ++++++++++++++++++++++------ src/output/spv/xml-parser-generator | 18 +- 3 files changed, 318 insertions(+), 89 deletions(-) diff --git a/src/output/spv/spvxml-helpers.c b/src/output/spv/spvxml-helpers.c index eb99ccc3a5..fb1420351d 100644 --- a/src/output/spv/spvxml-helpers.c +++ b/src/output/spv/spvxml-helpers.c @@ -27,6 +27,7 @@ #include "libpspp/hash-functions.h" #include "libpspp/str.h" +#include "gl/c-ctype.h" #include "gl/xvasprintf.h" char * WARN_UNUSED_RESULT @@ -315,14 +316,31 @@ xmlGetPropNodeValueInternal(const xmlAttr *prop) return(NULL); } +/* Returns true if S consists entirely of digits (and at least one digit) */ +static bool +all_digits (const char *s) +{ + for (size_t i = 0; ; i++) + if (!c_isdigit (s[i])) + return i > 0 && (s[i] == '\0' || (s[i] == '_' && s[i + 1] == '\0')); +} + static struct spvxml_attribute * find_attribute (struct spvxml_node_context *nctx, const char *name) { /* XXX This is linear search but we could use binary search. */ for (struct spvxml_attribute *a = nctx->attrs; a < &nctx->attrs[nctx->n_attrs]; a++) - if (!strcmp (a->name, name)) - return a; + { + size_t a_len = strlen (a->name); + if (!strcmp (a->name, name) + || (a->name[a_len - 1] == '#' + && !strncmp (name, a->name, a_len - 1) + && all_digits (name + a_len - 1)) + || (a->name[a_len - 1] == '%' + && !strncmp (name, a->name, a_len - 1))) + return a; + } return NULL; } @@ -371,8 +389,13 @@ spvxml_parse_attributes (struct spvxml_node_context *nctx) } if (a->value) { - spvxml_attr_error (nctx, "Duplicate attribute \"%s\".", a->name); - return; + if (a->name[strlen (a->name) - 1] != '#' + && a->name[strlen (a->name) - 1] != '%') + { + spvxml_attr_error (nctx, "Duplicate attribute \"%s\".", a->name); + return; + } + free (a->value); } a->value = CHAR_CAST (char *, xmlGetPropNodeValueInternal (node)); } diff --git a/src/output/spv/vizml.grammar b/src/output/spv/vizml.grammar index 08fb88ea74..375a095538 100644 --- a/src/output/spv/vizml.grammar +++ b/src/output/spv/vizml.grammar @@ -25,15 +25,14 @@ visualization :version :schemaLocation? :clip? -=> location* - visualization_extension? - userSource - (sourceVariable | derivedVariable | expressionVariable)+ - categoricalDomain? - intervalDomain* +=> (visualization_extension | location)* + userSource+ + (sourceVariable | derivedVariable | expressionVariable + | intervalDomain | categoricalDomain)+ graph labelFrame[lf1]* - container? + container* + legend? labelFrame[lf2]* style+ layerController? @@ -48,45 +47,56 @@ extension[visualization_extension] :legend1? :legend2? :styleCycleRepository? - :title1? - :title2? + :title#? :subtitle? - :footnote1? - :footnote2? - :footnote3? - :textbox1? - :textbox2? - :statisticsSummary? - :statisticsSummary1? - :dataSetHandle_0? - :dataSetName_0? - :dataSetHandle_1? - :dataSetName_1? - :dataSetHandle_2? - :dataSetName_2? + :footnote#? + :textbox#? + :statisticsSummary[statisticsSummary_]? + :statisticsSummary#? + :dataSetHandle_#? + :dataSetName_#? :percentDomain=(percentDomain)? - :regionStatistic_2270? - :regionStatistic_2737? - :smoothStatistic_2717? + :regionStatistic_#? + :smoothStatistic_%? + :originLineX? :originLineY? :boxplotLinkWidth=bool? + :line_%? + :preserveLocations=bool? + :creator? + :template? + :accelerator=bool? + :certified=bool? => EMPTY userSource :missing=(listwise | pairwise)? => EMPTY // Related to omit_empty? -categoricalDomain => variableReference categories? simpleSort? +categoricalDomain + :date=bool? +=> (categories | variableReference | domainReference)+ simpleSort? + +domainReference + :ref=ref categoricalDomain +=> EMPTY intervalDomain :date=bool? -=> ETC +=> (variableReference | range)* + +range + :exact=bool? + :max=real + :min=real +=> EMPTY categories :remove=bool? => ETC simpleSort - :method[sort_method]=(custom | natural) -=> categoryOrder + :method[sort_method]=(custom | natural | percent) + :descending=bool? +=> categoryOrder? sourceVariable :id @@ -102,7 +112,9 @@ sourceVariable :weight=bool? :shortLabel? :description? -=> variable_extension* (format | stringFormat)? + :inputDateFormatPattern? + :measurementLevel=(ratio)? +=> variable_extension* (format | stringFormat | numberFormat)? derivedVariable :id @@ -131,6 +143,7 @@ expressionVariable :expression? :shortLabel? :label? + :source? => ETC valueMapEntry :from :to => EMPTY @@ -141,22 +154,24 @@ graph :cellStyle=ref style :style=ref style :axesOutside=bool? -=> location+ coordinates faceting? point[p1]? facetLayout? - line[line1]? point[p2]? schema? interval* lineGuide? line[line2]? - functionGuide* +=> (location | coordinates | faceting | point | facetLayout + | line | schema | interval | lineGuide | functionGuide | edge | path + | planeGuide | frameGuide)* location :part=(height | width | top | bottom | left | right) :method=(sizeToContent | attach | fixed | same) :min? :max? - :target=ref (labelFrame | graph | container)? + :target=ref (labelFrame | graph | container | legend)? :value? :specifiedByUser=bool? => EMPTY coordinates -=> (dimension | transposeTransform | reflectionTransform | polarTransform)* +=> (dimension | transposeTransform | reflectionTransform | polarTransform + | sameRatioTransform | insetTransform | rectangularTransform + | obliqueTransform)* dimension :domain? @@ -167,7 +182,14 @@ dimension :unionDomain=bool? :clusterGap? :clusterVariable? -=> ETC +=> scale? axis* + +scale + :invert=bool + :method[scale_method] // linear | log.safe + :origin=real + :base=int? +=> EMPTY transposeTransform => ETC @@ -182,20 +204,49 @@ polarTransform :startAngle=real? => ETC +sameRatioTransform + :center=bool +=> EMPTY + +insetTransform + :x-min=real + :x-max=real + :y-min=real + :y-max=real +=> EMPTY + +rectangularTransform + :distance=real + :phi=real + :theta=real + :zeta=real +=> EMPTY + +obliqueTransform + :distance=real + :foreShortening=real + :theta=real + :xscale=real +=> EMPTY + faceting :method[faceting_method]=(nest | cross | dot)? -=> layer[layers1]* cross layer[layers2]* +=> layer[layers1]* cross layer[layers2]* | dot cross => (unity | nest | cross | variableReference)+ +dot => unity variableReference + nest => variableReference[vars]+ unity => EMPTY -variableReference :ref=ref (sourceVariable | derivedVariable) => EMPTY +variableReference + :ref=ref (sourceVariable | derivedVariable | expressionVariable) +=> EMPTY layer - :variable=ref (sourceVariable | derivedVariable) + :variable=ref (sourceVariable | derivedVariable | expressionVariable) :value :visible=bool? :method[layer_method]=(nest)? @@ -218,6 +269,7 @@ tableLayout facetLevel :level=int :gap? + :style=ref style? => axis? point @@ -226,8 +278,15 @@ point :showCollidingLabels=bool? :labelCollisionHandling=(none | normal)? :style=ref style? - :positionModifier=(none | stack)? + :positionModifier=(none | stack | pile)? :zOrder=int? + :clip=(none)? +=> styleBy? color? labeling* split? x y z? +// labeling*3 x y z + +split + :split=bool + :variable=ref (sourceVariable | derivedVariable | expressionVariable) => ETC line @@ -237,11 +296,29 @@ line :name? :style=ref style? :dot=bool? - :positionModifier=(none | stack)? + :positionModifier=(none | stack | pile)? :showDiscontinuity=bool? :zOrder=int? :coordinates? -=> ETC +=> summaryStatistic? smoothStatistic? styleBy? regionStatistic? distributionStatistic? color? x y? + +smoothStatistic + :method[smoothStatistic_method]=(step-left | linear) + :bounded=bool? + :degree=int? + :kernel=(uniform)? +=> EMPTY + +regionStatistic + :alpha=real + :degree=int + :predictIndividuals=bool + :regionType=(linear) +=> EMPTY + +distributionStatistic + :distribution=(normal) +=> EMPTY lineGuide :style=ref style? @@ -254,8 +331,48 @@ functionGuide :segments=int? :style=ref style? :value? + :zOrder=int? => ETC +edge + :dot=bool + :positionModifier=(none | stack | pile) + :style=ref style +=> attach? position_element? linkStatistic? x? y? z? + +linkStatistic + :y=(base) +=> EMPTY + +path + :breakRepresentation=(wings) + :dot=bool + :labelCollisionHandling=(none | normal) + :name + :showCollidingLabels=bool + :style=ref style +=> styleBy x y + +planeGuide + :normal=(x | y | z) + :style=ref style +=> EMPTY + +frameGuide + :complete=bool? + :style=ref style + :zOrder=int? +=> EMPTY + +attach + :target=(point) + :targetKey=ref (sourceVariable | derivedVariable | expressionVariable) +=> EMPTY + +position[position_element] + :variable=ref expressionVariable +=> EMPTY + schema :dot=bool? :extremeStyle? @@ -267,7 +384,7 @@ schema :showCollidingLabels=bool? :labelCollisionHandling=(none | normal)? :style=ref style? - :positionModifier=(none | stack)? + :positionModifier=(none | stack | pile)? :zOrder=int? => ETC @@ -275,22 +392,26 @@ axis :style=ref style :opposite=bool? :repeat=(never)? -=> label? majorTicks + :combineAxisLabels=bool? + :maxSize=dimension? + :zOrder=int? + :transform=(linear)? +=> label? majorTicks minorTicks? axisTransformParameter* label :style=ref style :textFrameStyle=ref style? - :purpose=(title | subTitle | subSubTitle | layer | footnote)? + :purpose=(title | subTitle | subSubTitle | layer | footnote | auto)? => text+ | descriptionGroup descriptionGroup - :target=ref (faceting | interval) + :target=ref (faceting | interval | sourceVariable | line) :separator? => (description | text)+ description :name? -=> format* +=> (format | numberFormat)* majorTicks :labelAngle=int @@ -300,14 +421,27 @@ majorTicks :labelFrequency=int? :stagger=bool? :markStyle=ref style? - :base=int? -=> gridline? + :base=real? + :delta=int? + :position[majorTicks_position]=(outside)? +=> gridline? format? + +minorTicks + :markStyle=ref style + :number=int + :position[minorTicks_position]=(outside)? +=> gridline? format? gridline :style=ref style :zOrder=int => EMPTY +axisTransformParameter + :parameterName=(base-ratio | base-match | derived-ratio | derived-match) + :parameterValue=real +=> EMPTY + setCellProperties :applyToConverse=bool? => (setStyle | setFrameStyle | setFormat | setMetaData)* union[union_]? @@ -362,7 +496,7 @@ format :showMillis=bool? :dayType=(month | year)? :hourFormat=(AMPM | AS_24 | AS_12)? - :minimumIntegerDigits=int? + :minimumIntegerDigits? :maximumFractionDigits=int? :minimumFractionDigits? :useGrouping=bool? @@ -373,6 +507,7 @@ format :tryStringsAsNumbers=bool? :negativesOutside=bool? :hiddenseparator=bool? + :scalingFactor=real? => relabel* affix* numberFormat @@ -384,6 +519,7 @@ numberFormat :small=real? :prefix? :suffix? + :hiddenseparator=bool? => affix* stringFormat => relabel* affix* @@ -449,13 +585,13 @@ union => intersect+ intersect => where+ | intersectWhere | alternating | EMPTY where - :variable=ref (sourceVariable | derivedVariable) + :variable=ref (sourceVariable | derivedVariable | expressionVariable) :include => EMPTY intersectWhere - :variable=ref (sourceVariable | derivedVariable) - :variable2=ref (sourceVariable | derivedVariable) + :variable=ref (sourceVariable | derivedVariable | expressionVariable) + :variable2=ref (sourceVariable | derivedVariable | expressionVariable) => EMPTY alternating => EMPTY @@ -473,41 +609,75 @@ interval :labelCollisionHandling=(none | normal)? :showCollidingLabels=bool? :name? - :positionModifier=(none | stack)? + :positionModifier=(none | stack | pile)? :zOrder=int? -=> binStatistic? summaryStatistic? styleBy? color? x? y? labeling? footnotes? + :zLocation=real? +=> binStatistic? summaryStatistic* (styleBy | labeling | color | offset)* x? y? footnotes? +// styleBy labeling x +// labeling styleBy x y +// summaryStatistic color labeling x +// styleBy labeling offset x +// summaryStatistic*2 color labeling labeling :style=ref style? - :variable=ref (sourceVariable | derivedVariable) + :variable=ref (sourceVariable | derivedVariable | expressionVariable) :connectingLines=bool? :textFrameStyle=ref style? -=> (formatting | format | footnotes)* + :clamp=bool? +=> (formatting | format | summaryStatistic | numberFormat | transparency | footnotes | labelLocation)* + +offset + :domain=ref categoricalDomain + :high + :low + :variable=ref (sourceVariable | derivedVariable | expressionVariable) +=> EMPTY -formatting :variable=ref (sourceVariable | derivedVariable) => formatMapping* +transparency + :affect=(both | main) + :domain=ref intervalDomain + :low=real + :high=real + :variable=ref (sourceVariable | derivedVariable | expressionVariable) +=> EMPTY + +labelLocation + :domain=ref intervalDomain + :role=(x | y) + :variable=ref (sourceVariable | derivedVariable | expressionVariable) +=> EMPTY + +formatting :variable=ref (sourceVariable | derivedVariable | expressionVariable) => formatMapping* formatMapping :from=int => format? color - :affect=(both)? + :affect=(both | main)? :cycle=ref styleCycle? :variable? :clamp=bool? + :dimension=int? + :domain=ref categoricalDomain? => ETC x - :variable? -=> ETC + :variable=ref (sourceVariable | derivedVariable | expressionVariable) +=> EMPTY y - :variable? -=> ETC + :variable=ref (sourceVariable | derivedVariable | expressionVariable) +=> EMPTY + +z + :variable=ref (sourceVariable | derivedVariable | expressionVariable) +=> EMPTY styleBy :styleCycle? :variable? :dimension=int? -=> ETC +=> EMPTY binStatistic :gridType=(square)? @@ -516,18 +686,35 @@ binStatistic => ETC summaryStatistic - :method[summaryStatistic_method]=(count | range)? + :method[summaryStatistic_method]=(count | range | percent | sum | mean | percentCount)? :symbol[summaryStatistic_symbol]=(circle | square)? :summaryBase=(coordinate)? :convertIntervalToSingleValue=bool? + :summaryIndex=int? + :augment=(missing)? => ETC footnotes :superscript=bool? - :variable=ref (sourceVariable | derivedVariable) + :variable=ref (sourceVariable | derivedVariable | expressionVariable) => footnoteMapping* -footnoteMapping :definesReference=int :from=int :to => EMPTY +footnoteMapping + :definesReference=int + :from=int + :to +=> EMPTY + +legend + :style=ref style + :tickFrameStyle=ref style + :tickTextStyle=ref style +=> location+ label? legendTarget+ + +legendTarget + :target=ref (styleBy | color | line | point) + :domain=ref categoricalDomain? +=> EMPTY style :color=color? @@ -563,9 +750,21 @@ style :stroke-dasharray? :text-fit=bool? :depth? - :symbol=(circle | ibeam | flower)? + :symbol=(circle | ibeam | flower | square | line | acrossDown | cross + | polygon | plus | bowtie | star | arrow | none)? :margin=dimension? :padding=dimension? + :padding-left=dimension? + :padding-right=dimension? + :padding-top=dimension? + :padding-bottom=dimension? + :maxAttachedTextHeight=dimension? + :opacity=int? + :opacity2=int? + :labelInside=(preferred | false)? + :glyph-sides=int? + :glyph-angle=real? + :glyph-aspect=real? => style* layerController @@ -580,15 +779,22 @@ container extension[container_extension] :combinedFootnotes=(true) => EMPTY -labelFrame :style=ref style => location+ label? paragraph? +labelFrame + :style=ref style +=> labelFrame_extension* location+ label? paragraph? -legend - :style=ref style? - :tickFrameStyle=ref style? - :tickTextStyle=ref style? -=> ETC +extension[labelFrame_extension] + :graphId=ref graph + :position[labelFrame_position]=(title | footnote) +=> EMPTY paragraph :hangingIndent=dimension? => EMPTY -styleCycle -=> ETC +styleCycle => cycle+ styleCycle_extension* + +cycle => style+ + +extension[styleCycle_extension] + :aesthetic=(color)? + :immutables=int? +=> EMPTY diff --git a/src/output/spv/xml-parser-generator b/src/output/spv/xml-parser-generator index 546ca97d00..df34fbf8ff 100644 --- a/src/output/spv/xml-parser-generator +++ b/src/output/spv/xml-parser-generator @@ -70,7 +70,7 @@ def match_id(id_): def is_idchar(c): - return c.isalnum() or c in '-_' + return c.isalnum() or c in '-_#%' def get_token(): @@ -796,9 +796,13 @@ void print('}') def name_to_id(s): - return s[0].lower() + ''.join(['_%c' % x.lower() if x.isupper() else x - for x in s[1:]]).replace('-', '_') - + return ((s[0].lower() + + ''.join(['_%c' % x.lower() if x.isupper() else x + for x in s[1:]]).replace('-', '_')) + .replace("_#", "") + .replace("#", "") + .replace("_%", "") + .replace("%", "")) def print_recurse_members(attributes, rhs, function): if rhs['type'] == 'etc' or rhs['type'] == 'empty': @@ -931,11 +935,6 @@ void print('}') -def name_to_id(s): - return s[0].lower() + ''.join(['_%c' % x.lower() if x.isupper() else x - for x in s[1:]]).replace('-', '_') - - if __name__ == "__main__": argv0 = sys.argv[0] try: @@ -1090,3 +1089,4 @@ struct %(prefix)s%(name)s *%(prefix)scast_%(name)s (const struct spvxml_node *); #endif /* %(PREFIX)sPARSER_H */""" % {'PREFIX': prefix.upper()}) else: sys.stderr.write("%s: bad usage (use --help for help)" % argv0) + -- 2.30.2