vizml work
authorBen Pfaff <blp@cs.stanford.edu>
Sat, 11 Jan 2020 07:13:11 +0000 (07:13 +0000)
committerBen Pfaff <blp@cs.stanford.edu>
Sat, 11 Jan 2020 17:51:30 +0000 (17:51 +0000)
src/output/spv/automake.mk
src/output/spv/spv.c
src/output/spv/spv.h
src/output/spv/vizml.grammar [new file with mode: 0644]
utilities/pspp-output.c

index 198e8df7e542a1b53e0a7acb6f57ca50c5640e30..6a2b3e1ad2247ce87c2c5b06f69cc9fa88970128 100644 (file)
@@ -102,3 +102,20 @@ nodist_src_output_liboutput_la_SOURCES += $(structure_xml_out)
 BUILT_SOURCES += $(structure_xml_out)
 CLEANFILES += $(structure_xml_out)
 EXTRA_DIST += $(structure_xml_in)
+
+vizml_in = \
+       src/output/spv/xml-parser-generator \
+       src/output/spv/vizml.grammar
+vizml_out = \
+       src/output/spv/vizml-parser.c \
+       src/output/spv/vizml-parser.h
+src/output/spv/vizml-parser.c: $(vizml_in)
+       $(AM_V_GEN)$(PYTHON) $^ code vizml '"output/spv/vizml-parser.h"' > $@.tmp
+       $(AM_V_at)mv $@.tmp $@
+src/output/spv/vizml-parser.h: $(vizml_in)
+       $(AM_V_GEN)$(PYTHON) $^ header vizml > $@.tmp && mv $@.tmp $@
+nodist_src_output_liboutput_la_SOURCES += $(vizml_out)
+BUILT_SOURCES += $(vizml_out)
+CLEANFILES += $(vizml_out)
+EXTRA_DIST += $(vizmlb_in)
+
index f186e638e70c9f8944a31a826081bc36f3f5e296..ecb008c9911fd5027f6a7f363889c0a850ac19a6 100644 (file)
@@ -40,6 +40,7 @@
 #include "output/spv/spv-legacy-decoder.h"
 #include "output/spv/spv-light-decoder.h"
 #include "output/spv/structure-xml-parser.h"
+#include "output/spv/vizml-parser.h"
 
 #include "gl/c-ctype.h"
 #include "gl/intprops.h"
@@ -181,6 +182,12 @@ spv_item_is_table (const struct spv_item *item)
   return item->type == SPV_ITEM_TABLE;
 }
 
+bool
+spv_item_is_graph (const struct spv_item *item)
+{
+  return item->type == SPV_ITEM_GRAPH;
+}
+
 bool
 spv_item_is_text (const struct spv_item *item)
 {
@@ -572,6 +579,8 @@ spv_item_load (const struct spv_item *item)
 {
   if (spv_item_is_table (item))
     spv_item_get_table (item);
+  else if (spv_item_is_graph (item))
+    spv_item_get_graph (item);
 }
 
 bool
@@ -652,8 +661,11 @@ char * WARN_UNUSED_RESULT
 spv_item_get_raw_legacy_data (const struct spv_item *item,
                               void **data, size_t *size)
 {
-  if (!spv_item_is_legacy_table (item))
-    return xstrdup ("not a legacy table object");
+  if (!spv_item_is_legacy_table (item) && !spv_item_is_graph (item))
+    return xstrdup ("not a graph or legacy table object");
+
+  if (!item->bin_member)
+    return xstrdup ("graph or legacy table lacks legacy data");
 
   return zip_member_read_all (item->spv->zip, item->bin_member, data, size);
 }
@@ -873,6 +885,74 @@ spv_item_get_table (const struct spv_item *item_)
   return item->table;
 }
 
+static char * WARN_UNUSED_RESULT
+spv_open_graph (struct spv_item *item)
+{
+  assert (spv_item_is_graph (item));
+
+  struct spv_data data;
+  char *error = spv_item_get_legacy_data (item, &data);
+  if (error)
+    {
+      struct string s = DS_EMPTY_INITIALIZER;
+      spv_item_format_path (item, &s);
+      ds_put_format (&s, " (%s): %s", item->bin_member, error);
+
+      free (error);
+      return ds_steal_cstr (&s);
+    }
+
+  xmlDoc *doc;
+  error = spv_read_xml_member (item->spv, item->xml_member, false,
+                               "visualization", &doc);
+  if (error)
+    {
+      spv_data_uninit (&data);
+      return error;
+    }
+
+  struct spvxml_context ctx = SPVXML_CONTEXT_INIT (ctx);
+  struct vizml_visualization *v;
+  vizml_parse_visualization (&ctx, xmlDocGetRootElement (doc), &v);
+  error = spvxml_context_finish (&ctx, &v->node_);
+
+  if (error)
+    {
+      struct string s = DS_EMPTY_INITIALIZER;
+      spv_item_format_path (item, &s);
+      ds_put_format (&s, " (%s): %s", item->xml_member, error);
+
+      free (error);
+      error = ds_steal_cstr (&s);
+    }
+
+  spv_data_uninit (&data);
+  vizml_free_visualization (v);
+  if (doc)
+    xmlFreeDoc (doc);
+
+  return error;
+}
+
+void
+spv_item_get_graph (const struct spv_item *item_)
+{
+  struct spv_item *item = CONST_CAST (struct spv_item *, item_);
+
+  assert (spv_item_is_graph (item));
+  if (!item->graph)
+    {
+      item->graph = true;
+      char *error = spv_open_graph (item);
+      if (error)
+        {
+          item->error = true;
+          msg (ME, "%s", error);
+          free (error);
+        }
+    }
+}
+
 /* Constructs a new spv_item from XML and stores it in *ITEMP.  Returns NULL if
    successful, otherwise an error message for the caller to use and free (with
    free()).
@@ -904,7 +984,7 @@ spv_decode_container (const struct spvsx_container *c,
       item->subtype = xstrdup_if_nonempty (table->sub_type);
       if (ts->path)
         {
-          item->xml_member = ts->path ? xstrdup (ts->path->text) : NULL;
+          item->xml_member = xstrdup_if_nonempty (ts->path->text);
           char *error = decode_spvsx_legacy_properties (
             table->table_properties, &item->legacy_properties);
           if (error)
@@ -916,10 +996,12 @@ spv_decode_container (const struct spvsx_container *c,
     }
   else if (spvsx_is_graph (content))
     {
-      struct spvsx_graph *graph = spvsx_cast_graph (content);
       item->type = SPV_ITEM_GRAPH;
+
+      struct spvsx_graph *graph = spvsx_cast_graph (content);
+      item->bin_member = xstrdup_if_nonempty (graph->data_path->text);
       item->command_id = xstrdup_if_nonempty (graph->command_name);
-      /* XXX */
+      item->xml_member = xstrdup_if_nonempty (graph->path->text);
     }
   else if (spvsx_is_model (content))
     {
index 0bf2ff70141545f4161342143f4838dbf25486f7..f04cb95fe518e46e304405e9344cff1f0f77dcef 100644 (file)
@@ -131,9 +131,14 @@ struct spv_item
     /* SPV_ITEM_TABLE only. */
     struct pivot_table *table;    /* NULL if not yet loaded. */
     struct spv_legacy_properties *legacy_properties;
+    char *subtype;
+
+    /* SPV_ITEM_GRAPH only. */
+    bool graph;
+
+    /* SPV_ITEM_TABLE and SPV_ITEM_GRAPH only. */
     char *bin_member;
     char *xml_member;
-    char *subtype;
 
     /* SPV_ITEM_TEXT only.  */
     struct pivot_value *text;
@@ -159,6 +164,9 @@ struct spv_item *spv_item_get_child (const struct spv_item *, size_t idx);
 bool spv_item_is_table (const struct spv_item *);
 struct pivot_table *spv_item_get_table (const struct spv_item *);
 
+bool spv_item_is_graph (const struct spv_item *);
+void spv_item_get_graph (const struct spv_item *);
+
 bool spv_item_is_text (const struct spv_item *);
 const struct pivot_value *spv_item_get_text (const struct spv_item *);
 
diff --git a/src/output/spv/vizml.grammar b/src/output/spv/vizml.grammar
new file mode 100644 (file)
index 0000000..31b9d95
--- /dev/null
@@ -0,0 +1,592 @@
+# PSPP - a program for statistical analysis.
+# Copyright (C) 2017, 2018, 2019 Free Software Foundation, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+visualization
+   :creator?
+   :date
+   :description
+   :lang
+   :name
+   :style[style_ref]=ref style
+   :type
+   :version
+   :schemaLocation?
+   :clip?
+=> location*
+   visualization_extension?
+   userSource
+   (sourceVariable | derivedVariable | expressionVariable)+
+   categoricalDomain?
+   intervalDomain*
+   graph
+   labelFrame[lf1]*
+   container?
+   labelFrame[lf2]*
+   style+
+   layerController?
+   styleCycle*
+   style[s2]*
+
+extension[visualization_extension]
+   :numRows=int?
+   :showGridline=bool?
+   :minWidthSet=(true)?
+   :maxWidthSet=(true)?
+   :legend1?
+   :legend2?
+   :styleCycleRepository?
+   :title1?
+   :title2?
+   :subtitle?
+   :footnote1?
+   :footnote2?
+   :footnote3?
+   :textbox1?
+   :textbox2?
+   :statisticsSummary?
+   :statisticsSummary1?
+   :dataSetHandle_0?
+   :dataSetName_0?
+   :dataSetHandle_1?
+   :dataSetName_1?
+   :dataSetHandle_2?
+   :dataSetName_2?
+   :percentDomain=(percentDomain)?
+   :regionStatistic_2270?
+   :regionStatistic_2737?
+   :smoothStatistic_2717?
+   :originLineY?
+   :boxplotLinkWidth=bool?
+=> EMPTY
+
+userSource :missing=(listwise | pairwise)? => EMPTY   # Related to omit_empty?
+
+categoricalDomain => variableReference categories? simpleSort?
+
+intervalDomain
+   :date=bool?
+=> ETC
+
+categories
+   :remove=bool?
+=> ETC
+
+simpleSort
+   :method[sort_method]=(custom | natural)
+=> categoryOrder
+
+sourceVariable
+   :id
+   :categorical=bool
+   :source
+   :domain=ref categoricalDomain?
+   :sourceName
+   :dependsOn=ref sourceVariable?
+   :label?
+   :labelVariable=ref sourceVariable?
+   :creator?
+   :key=bool?
+   :weight=bool?
+   :shortLabel?
+   :description?
+=> variable_extension* (format | stringFormat)?
+
+derivedVariable
+   :id
+   :categorical=(true)
+   :value
+   :dependsOn=ref sourceVariable?
+   :creator?
+   :key=bool?
+   :label?
+   :source?
+   :weight=bool?
+=> variable_extension* (format | stringFormat)? valueMapEntry*
+
+extension[variable_extension]
+   :from?
+   :helpId?
+   :statistic?
+   :valuesAreGroupedMidpoints=bool?
+   :max?
+   :min?
+=> EMPTY
+
+expressionVariable
+   :categorical=bool?
+   :creator?
+   :expression?
+   :shortLabel?
+   :label?
+=> ETC
+
+valueMapEntry :from :to => EMPTY
+
+categoryOrder => TEXT
+
+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
+   :part=(height | width | top | bottom | left | right)
+   :method=(sizeToContent | attach | fixed | same)
+   :min?
+   :max?
+   :target=ref (labelFrame | graph | container)?
+   :value?
+   :specifiedByUser=bool?
+=> EMPTY
+
+coordinates
+=> (dimension | transposeTransform | reflectionTransform | polarTransform)*
+
+dimension
+   :domain?
+   :lowerMargin?
+   :upperMargin?
+   :niceLowerMapping=bool?
+   :niceUpperMapping=bool?
+   :unionDomain=bool?
+   :clusterGap?
+   :clusterVariable?
+=> ETC
+
+transposeTransform => ETC
+
+reflectionTransform
+   :alternating=bool?
+   :dimension?
+=> ETC
+
+polarTransform
+   :counterClockwise=bool?
+   :cycles=int?
+   :startAngle=real?
+=> ETC
+
+faceting
+   :method[faceting_method]=(nest | cross | dot)?
+=> layer[layers1]* cross layer[layers2]*
+
+cross => (unity | nest | cross | variableReference)+
+
+nest => variableReference[vars]+
+
+unity => EMPTY
+
+variableReference :ref=ref (sourceVariable | derivedVariable) => EMPTY
+
+layer
+   :variable=ref (sourceVariable | derivedVariable)
+   :value
+   :visible=bool?
+   :method[layer_method]=(nest)?
+   :titleVisible=bool?
+=> EMPTY
+
+facetLayout
+   :topDown=bool?
+   :method[facetLayout_method]=(structured)?
+   :cellAspect=int?
+=> tableLayout? setCellProperties[scp1]*
+   facetLevel* setCellProperties[scp2]*
+
+tableLayout
+   :verticalTitlesInCorner=bool
+   :style=ref style?
+   :fitCells=(ticks both)?
+=> EMPTY
+
+facetLevel
+   :level=int
+   :gap?
+=> axis?
+
+point
+   :dot=bool?
+   :name?
+   :showCollidingLabels=bool?
+   :labelCollisionHandling=(none | normal)?
+   :style=ref style?
+   :positionModifier=(none | stack)?
+   :zOrder=int?
+=> ETC
+
+line
+   :breakRepresentation?
+   :labelCollisionHandling=(none | normal)?
+   :showCollidingLabels=bool?
+   :name?
+   :style=ref style?
+   :dot=bool?
+   :positionModifier=(none | stack)?
+   :showDiscontinuity=bool?
+   :zOrder=int?
+   :coordinates?
+=> ETC
+
+lineGuide
+   :style=ref style?
+   :x=int?
+   :y=int?
+=> ETC
+
+functionGuide
+   :name
+   :segments=int?
+   :style=ref style?
+   :value?
+=> ETC
+
+schema
+   :dot=bool?
+   :extremeStyle?
+   :fenceStyle=ref style?
+   :hingeStyle=ref style?
+   :medianStyle=ref style?
+   :name?
+   :outlierStyle=ref style?
+   :showCollidingLabels=bool?
+   :labelCollisionHandling=(none | normal)?
+   :style=ref style?
+   :positionModifier=(none | stack)?
+   :zOrder=int?
+=> ETC
+
+axis
+   :style=ref style
+   :opposite=bool?
+   :repeat=(never)?
+=> label? majorTicks
+
+label
+   :style=ref style
+   :textFrameStyle=ref style?
+   :purpose=(title | subTitle | subSubTitle | layer | footnote)?
+=> text+ | descriptionGroup
+
+descriptionGroup
+   :target=ref (faceting | interval)
+   :separator?
+=> (description | text)+
+
+description
+   :name?
+=> format*
+
+majorTicks
+   :labelAngle=int
+   :length=dimension
+   :style=ref style
+   :tickFrameStyle=ref style
+   :labelFrequency=int?
+   :stagger=bool?
+   :markStyle=ref style?
+   :base=int?
+=> gridline?
+
+gridline
+   :style=ref style
+   :zOrder=int
+=> EMPTY
+
+setCellProperties
+   :applyToConverse=bool?
+=> (setStyle | setFrameStyle | setFormat | setMetaData)* union[union_]?
+
+setStyle
+   :target=ref (labeling | graph | interval | majorTicks)
+   :style=ref style
+=> EMPTY
+
+setMetaData
+   :target=ref graph
+   :key
+   :value
+=> EMPTY
+
+setFormat
+   :target=ref (majorTicks | labeling)
+   :reset=bool?
+=> format | numberFormat | stringFormat+ | dateTimeFormat | elapsedTimeFormat
+
+setFrameStyle
+   :style=ref style
+   :target=ref majorTicks
+=> EMPTY
+
+format
+   :baseFormat[f_base_format]=(date | time | dateTime | elapsedTime)?
+   :errorCharacter?
+   :separatorChars?
+   :mdyOrder=(dayMonthYear | monthDayYear | yearMonthDay)?
+   :showYear=bool?
+   :showQuarter=bool?
+   :quarterPrefix?
+   :quarterSuffix?
+   :yearAbbreviation=bool?
+   :showMonth=bool?
+   :monthFormat=(long | short | number | paddedNumber)?
+   :dayPadding=bool?
+   :dayOfMonthPadding=bool?
+   :showWeek=bool?
+   :weekPadding=bool?
+   :weekSuffix?
+   :showDayOfWeek=bool?
+   :dayOfWeekAbbreviation=bool?
+   :hourPadding=bool?
+   :minutePadding=bool?
+   :secondPadding=bool?
+   :showDay=bool?
+   :showHour=bool?
+   :showMinute=bool?
+   :showSecond=bool?
+   :showMillis=bool?
+   :dayType=(month | year)?
+   :hourFormat=(AMPM | AS_24 | AS_12)?
+   :minimumIntegerDigits=int?
+   :maximumFractionDigits=int?
+   :minimumFractionDigits?
+   :useGrouping=bool?
+   :scientific=(onlyForSmall | whenNeeded | true | false)?
+   :small=real?
+   :prefix?
+   :suffix?
+   :tryStringsAsNumbers=bool?
+   :negativesOutside=bool?
+   :hiddenseparator=bool?
+=> relabel* affix*
+
+numberFormat
+   :minimumIntegerDigits?
+   :maximumFractionDigits=int?
+   :minimumFractionDigits=int?
+   :useGrouping=bool?
+   :scientific=(onlyForSmall | whenNeeded | true | false)?
+   :small=real?
+   :prefix?
+   :suffix?
+=> affix*
+
+stringFormat => relabel* affix*
+
+dateTimeFormat
+   :baseFormat[dt_base_format]=(date | time | dateTime)
+   :separatorChars?
+   :mdyOrder=(dayMonthYear | monthDayYear | yearMonthDay)?
+   :showYear=bool?
+   :yearAbbreviation=bool?
+   :showQuarter=bool?
+   :quarterPrefix?
+   :quarterSuffix?
+   :showMonth=bool?
+   :monthFormat=(long | short | number | paddedNumber)?
+   :showWeek=bool?
+   :weekPadding=bool?
+   :weekSuffix?
+   :showDayOfWeek=bool?
+   :dayOfWeekAbbreviation=bool?
+   :dayPadding=bool?
+   :dayOfMonthPadding=bool?
+   :hourPadding=bool?
+   :minutePadding=bool?
+   :secondPadding=bool?
+   :showDay=bool?
+   :showHour=bool?
+   :showMinute=bool?
+   :showSecond=bool?
+   :showMillis=bool?
+   :dayType=(month | year)?
+   :hourFormat=(AMPM | AS_24 | AS_12)?
+=> affix*
+
+elapsedTimeFormat
+   :baseFormat[dt_base_format]=(date | time | dateTime)
+   :dayPadding=bool?
+   :hourPadding=bool?
+   :minutePadding=bool?
+   :secondPadding=bool?
+   :showYear=bool?
+   :showDay=bool?
+   :showHour=bool?
+   :showMinute=bool?
+   :showSecond=bool?
+   :showMillis=bool?
+=> affix*
+
+affix
+   :definesReference=int
+   :position=(subscript | superscript)
+   :suffix=bool
+   :value
+=> EMPTY
+
+relabel
+   :from
+   :to
+=> EMPTY
+
+union => intersect+
+
+intersect => where+ | intersectWhere | alternating | EMPTY
+
+where
+   :variable=ref (sourceVariable | derivedVariable)
+   :include
+=> EMPTY
+
+intersectWhere
+   :variable=ref (sourceVariable | derivedVariable)
+   :variable2=ref (sourceVariable | derivedVariable)
+=> EMPTY
+
+alternating => EMPTY
+
+text
+   :usesReference=int?
+   :definesReference=int?
+   :position=(subscript | superscript)?
+   :style=ref style
+=> TEXT
+
+interval
+   :style=ref style
+   :dot=bool?
+   :labelCollisionHandling=(none | normal)?
+   :showCollidingLabels=bool?
+   :name?
+   :positionModifier=(none | stack)?
+   :zOrder=int?
+=> binStatistic? summaryStatistic? styleBy? color? x? y? labeling? footnotes?
+
+labeling
+   :style=ref style?
+   :variable=ref (sourceVariable | derivedVariable)
+=> (formatting | format | footnotes)*
+
+formatting :variable=ref (sourceVariable | derivedVariable) => formatMapping*
+
+formatMapping :from=int => format?
+
+color
+   :affect=(both)?
+   :cycle=ref styleCycle?
+   :variable?
+   :clamp=bool?
+=> ETC
+
+x
+   :variable?
+=> ETC
+
+y
+   :variable?
+=> ETC
+
+styleBy
+   :styleCycle?
+   :variable?
+   :dimension=int?
+=> ETC
+
+binStatistic
+   :gridType=(square)?
+   :binWidth=real?
+   :binCount=int?
+=> ETC
+
+summaryStatistic
+   :method[summaryStatistic_method]=(count | range)?
+   :symbol[summaryStatistic_symbol]=(circle | square)?
+   :summaryBase=(coordinate)?
+   :convertIntervalToSingleValue=bool?
+=> ETC
+
+footnotes
+   :superscript=bool?
+   :variable=ref (sourceVariable | derivedVariable)
+=> footnoteMapping*
+
+footnoteMapping :definesReference=int :from=int :to => EMPTY
+
+style
+   :color=color?
+   :color2=color?
+   :labelAngle=real?
+   :border-bottom=(solid | thick | thin | double | none)?
+   :border-top=(solid | thick | thin | double | none)?
+   :border-left=(solid | thick | thin | double | none)?
+   :border-right=(solid | thick | thin | double | none)?
+   :border-bottom-color?
+   :border-top-color?
+   :border-left-color?
+   :border-right-color?
+   :font-family?
+   :font-size?
+   :font-weight=(regular | bold)?
+   :font-style=(regular | italic)?
+   :font-underline=(none | underline)?
+   :margin-bottom=dimension?
+   :margin-left=dimension?
+   :margin-right=dimension?
+   :margin-top=dimension?
+   :textAlignment=(left | right | center | decimal | mixed)?
+   :labelLocationHorizontal=(positive | negative | center)?
+   :labelLocationVertical=(positive | negative | center)?
+   :decimal-offset=dimension?
+   :size?
+   :width?
+   :visible=bool?
+   :pattern=int?
+   :stroke-linecap=(butt | round)?
+   :stroke-width=dimension?
+   :stroke-dasharray?
+   :text-fit=bool?
+   :depth?
+   :symbol=(circle | ibeam | flower)?
+   :margin=dimension?
+   :padding=dimension?
+=> style*
+
+layerController
+   :source=(tableData)
+   :target=ref label?
+=> EMPTY
+
+container
+   :style=ref style
+   :clip=bool?
+=> container_extension? location+ labelFrame* legend?
+
+extension[container_extension] :combinedFootnotes=(true) => EMPTY
+
+labelFrame :style=ref style => location+ label? paragraph?
+
+legend
+   :style=ref style?
+   :tickFrameStyle=ref style?
+   :tickTextStyle=ref style?
+=> ETC
+
+paragraph :hangingIndent=dimension? => EMPTY
+
+styleCycle
+=> ETC
index c57dda8e6dbd43e0ba75fbeb89c721b9467cdb09..7e22a3707621a43931c24a73f3a2f41aa3a0e48a 100644 (file)
@@ -108,6 +108,7 @@ dump_item (const struct spv_item *item)
       break;
 
     case SPV_ITEM_GRAPH:
+      spv_item_get_graph (item);
       break;
 
     case SPV_ITEM_MODEL: