diff --git a/nodeutil/testdata/conflict-one.yang b/nodeutil/testdata/conflict-one.yang new file mode 100644 index 00000000..66205eea --- /dev/null +++ b/nodeutil/testdata/conflict-one.yang @@ -0,0 +1,7 @@ +module conflict-one { + namespace "one"; + + leaf x { + type string; + } +} \ No newline at end of file diff --git a/nodeutil/testdata/conflict-two.yang b/nodeutil/testdata/conflict-two.yang new file mode 100644 index 00000000..71e8689b --- /dev/null +++ b/nodeutil/testdata/conflict-two.yang @@ -0,0 +1,7 @@ +module conflict-two { + namespace "two"; + + leaf x { + type string; + } +} \ No newline at end of file diff --git a/nodeutil/testdata/conflict.yang b/nodeutil/testdata/conflict.yang new file mode 100644 index 00000000..c958beb6 --- /dev/null +++ b/nodeutil/testdata/conflict.yang @@ -0,0 +1,15 @@ +module conflict { + namespace "zero"; + + import conflict-one { + prefix one; + } + + import conflict-two { + prefix two; + } + + leaf x { + type string; + } +} \ No newline at end of file diff --git a/nodeutil/xml_rdr.go b/nodeutil/xml_rdr.go new file mode 100644 index 00000000..4e3d75e7 --- /dev/null +++ b/nodeutil/xml_rdr.go @@ -0,0 +1,197 @@ +package nodeutil + +import ( + "context" + "encoding/xml" + "fmt" + "io" + + "github.com/freeconf/yang/meta" + "github.com/freeconf/yang/node" + "github.com/freeconf/yang/val" +) + +// ReadXMLDoc to read xml doc where root node is assumed to be the correct +// element. +// +// +// ... +// +// module my-module { +// container my-child {... +// +// NewBrowser(myModule, ReadXMLDoc(myXmlDoc)) +func ReadXMLDoc(buf io.Reader) (*XmlNode, error) { + dec := xml.NewDecoder(buf) + var n XmlNode + err := dec.Decode(&n) + if err != nil { + return nil, err + } + return &n, nil +} + +// ReadXMLBlock to read xml doc where root node is assumed to be the correct +// element. +// +// +// ... +// +// module my-module { +// container my-child {... +// +// b := NewBrowser(myModule, myApp) +// sel, _ := b.Root().Find("my-child") +// sel.UpsertFrom(ReadXMLBlock(myXmlDoc)) +func ReadXMLBlock(buf io.Reader) (*XmlNode, error) { + n, err := ReadXMLDoc(buf) + if err != nil { + return nil, err + } + return &XmlNode{Nodes: []*XmlNode{n}}, nil +} + +type XmlNode struct { + XMLName xml.Name + Attrs []xml.Attr `xml:"-"` + Content []byte `xml:",innerxml"` + Nodes []*XmlNode `xml:",any"` +} + +func (x *XmlNode) Child(r node.ChildRequest) (node.Node, error) { + ndx := x.find(0, r.Meta) + if ndx < 0 { + return nil, nil + } + if meta.IsList(r.Meta) { + var found []*XmlNode + // 7.8.5. XML Encoding Rules + // The XML elements representing list entries MAY be interleaved with elements + // for siblings of the list + for ndx >= 0 { + found = append(found, x.Nodes[ndx]) + ndx = x.find(ndx+1, r.Meta) + } + return &XmlNode{XMLName: x.XMLName, Nodes: found}, nil + } + return x.Nodes[ndx], nil +} + +func (x *XmlNode) Next(r node.ListRequest) (node.Node, []val.Value, error) { + if r.Key != nil { + for _, n := range x.Nodes { + for i, k := range r.Key { + v, found := n.field(r.Meta.KeyMeta()[i]) + if !found { + break + } + if k.String() != v { + break + } + isLastKey := i == (len(r.Key) - 1) + if isLastKey { + return n, r.Key, nil + } + } + } + } else if r.Row < len(x.Nodes) { + // assumes x.Nodes are all of the same element tag as would be the case if + // this was created by Child() method + target := x.Nodes[r.Row] + var key []val.Value + if len(r.Meta.KeyMeta()) > 0 { + for _, kmeta := range r.Meta.KeyMeta() { + sval, valid := target.field(kmeta) + if !valid { + return nil, nil, fmt.Errorf("key '%s' missing from %s", kmeta.Ident(), r.Path) + } + v, err := node.NewValue(kmeta.Type(), sval) + if err != nil { + return nil, nil, fmt.Errorf("error reading key '%s' from %s. %w", kmeta.Ident(), r.Path, err) + } + key = append(key, v) + } + } + + return x.Nodes[r.Row], key, nil + } + return nil, nil, nil +} + +func (x *XmlNode) field(m meta.Leafable) (string, bool) { + if ndx := x.find(0, m); ndx >= 0 { + return string(x.Nodes[ndx].Content), true + } + return "", false +} + +func (x *XmlNode) Field(r node.FieldRequest, hnd *node.ValueHandle) error { + var err error + ndx := x.find(0, r.Meta) + if ndx < 0 { + return nil + } + if _, isList := r.Meta.(*meta.LeafList); isList { + var found []string + // 7.8.5. XML Encoding Rules + // The XML elements representing list entries MAY be interleaved with elements + // for siblings of the list + for ndx >= 0 { + found = append(found, string(x.Nodes[ndx].Content)) + ndx = x.find(ndx+1, r.Meta) + } + hnd.Val, err = node.NewValue(r.Meta.Type(), found) + } else { + hnd.Val, err = node.NewValue(r.Meta.Type(), string(x.Nodes[ndx].Content)) + } + return err +} + +func (x *XmlNode) find(start int, m meta.Definition) int { + ns := meta.OriginalModule(m).Namespace() + for i := start; i < len(x.Nodes); i++ { + if x.Nodes[i].XMLName.Local == m.Ident() && ns == x.Nodes[i].XMLName.Space { + return i + } + } + return -1 +} + +func (x *XmlNode) Choose(sel *node.Selection, choice *meta.Choice) (*meta.ChoiceCase, error) { + for _, c := range choice.Cases() { + for _, m := range c.DataDefinitions() { + if x.find(0, m) >= 0 { + return c, nil + } + } + } + return nil, nil +} + +// Stubs non-reader funcs + +func (n *XmlNode) Peek(sel *node.Selection, consumer interface{}) interface{} { + return n +} + +func (n *XmlNode) BeginEdit(r node.NodeRequest) error { + return nil +} + +func (n *XmlNode) EndEdit(r node.NodeRequest) error { + return nil +} + +func (n *XmlNode) Action(r node.ActionRequest) (node.Node, error) { + return nil, nil +} + +func (n *XmlNode) Notify(r node.NotifyRequest) (node.NotifyCloser, error) { + return nil, nil +} + +func (n *XmlNode) Context(sel *node.Selection) context.Context { + return sel.Context +} + +func (n *XmlNode) Release(sel *node.Selection) {} diff --git a/nodeutil/xml_rdr_test.go b/nodeutil/xml_rdr_test.go new file mode 100644 index 00000000..44e557aa --- /dev/null +++ b/nodeutil/xml_rdr_test.go @@ -0,0 +1,230 @@ +package nodeutil + +import ( + "bytes" + "strings" + "testing" + + "github.com/freeconf/yang/fc" + "github.com/freeconf/yang/node" + "github.com/freeconf/yang/parser" + "github.com/freeconf/yang/source" + "github.com/freeconf/yang/val" +) + +func TestXmlWalk(t *testing.T) { + moduleStr := ` + module xml-test { + prefix "t"; + namespace "t"; + revision 0; + list hobbies { + key "name"; + leaf name { + type string; + } + container favorite { + leaf common-name { + type string; + } + leaf location { + type string; + } + } + } + } + + ` + module, err := parser.LoadModuleFromString(nil, moduleStr) + fc.RequireEqual(t, nil, err) + xml := ` + + + birding + + towhee + double-mint + out back + + + + hockey + + bruins + Boston + + + + ` + tests := []string{ + "hobbies", + "hobbies=birding", + "hobbies=birding/favorite", + } + for _, test := range tests { + sel := node.NewBrowser(module, readXml(xml)).Root() + found, err := sel.Find(test) + fc.RequireEqual(t, nil, err, test) + fc.RequireEqual(t, true, found != nil, test) + fc.AssertEqual(t, "xml-test/"+test, found.Path.String()) + } +} + +func TestXMLNumberParse(t *testing.T) { + moduleStr := ` +module xml-test { + prefix "t"; + namespace "t"; + revision 0; + container data { + leaf id { + type int32; + } + leaf idstr { + type int32; + } + leaf idstrwrong { + type int32; + } + leaf-list readings{ + type decimal64; + } + } +} + ` + module, err := parser.LoadModuleFromString(nil, moduleStr) + fc.RequireEqual(t, nil, err) + + xml := ` + + + 4 + 4 + 3.555454 + 45.04545 + 324545.04 + + ` + + //test get id + root := node.NewBrowser(module, readXml(xml)).Root() + + data := sel(root.Find("data/id")) + found, err := data.Get() + fc.RequireEqual(t, nil, err, "failed to transmit json") + fc.RequireEqual(t, true, found != nil, "data/id - Target not found, state nil") + fc.AssertEqual(t, 4, found.Value().(int)) + + //test get idstr + data = sel(root.Find("data/idstr")) + found, err = data.Get() + fc.RequireEqual(t, nil, err, "failed to transmit json") + fc.RequireEqual(t, true, found != nil, "data/idstr - Target not found, state nil") + fc.AssertEqual(t, 4, found.Value().(int)) + + data = sel(root.Find("data/readings")) + found, err = data.Get() + fc.RequireEqual(t, nil, err, "failed to transmit json") + fc.RequireEqual(t, true, found != nil, "data/readings - Target not found, state nil") + expected := []float64{3.555454, 45.04545, 324545.04} + readings := found.Value().([]float64) + fc.AssertEqual(t, expected, readings) +} + +func readXml(data string) *XmlNode { + n, err := ReadXMLDoc(strings.NewReader(data)) + if err != nil { + panic(err) + } + return n +} + +func TestXmlEmpty(t *testing.T) { + moduleStr := ` +module xml-test { + leaf x { + type empty; + } +} + ` + m, err := parser.LoadModuleFromString(nil, moduleStr) + fc.AssertEqual(t, nil, err) + actual := make(map[string]interface{}) + b := node.NewBrowser(m, &Node{Object: actual}) + in := `` + fc.AssertEqual(t, nil, b.Root().InsertFrom(readXml(in))) + fc.AssertEqual(t, val.NotEmpty, actual["x"]) +} + +func TestReadQualifiedXmlIdentRef(t *testing.T) { + ypath := source.Dir("./testdata") + m := parser.RequireModule(ypath, "module-test") + in := ` + + module-types:derived-type + local-type + ` + actual := make(map[string]interface{}) + b := node.NewBrowser(m, ReflectChild(actual)) + fc.AssertEqual(t, nil, b.Root().InsertFrom(readXml(in))) + fc.AssertEqual(t, "derived-type", actual["type"].(val.IdentRef).Label) + fc.AssertEqual(t, "local-type", actual["type2"].(val.IdentRef).Label) +} + +func TestXmlChoice(t *testing.T) { + ypath := source.Dir("./testdata") + m := parser.RequireModule(ypath, "choice") + in := `here` + actual := make(map[string]interface{}) + b := node.NewBrowser(m, ReflectChild(actual)) + fc.AssertEqual(t, nil, b.Root().InsertFrom(readXml(in))) + fc.AssertEqual(t, "here", actual["z"]) +} + +func TestXmlRdrListByRow(t *testing.T) { + moduleStr := ` +module xml-test { + leaf x { + type string; + } + list y { + leaf z { + type string; + } + } +} + ` + m, err := parser.LoadModuleFromString(nil, moduleStr) + fc.RequireEqual(t, nil, err) + in := ` + + Exs + row 0 + row 1 + + ` + b := node.NewBrowser(m, readXml(in)) + actual, err := WriteJSON(b.Root()) + fc.RequireEqual(t, nil, err) + fc.AssertEqual(t, `{"x":"Exs","y":[{"z":"row 0"},{"z":"row 1"}]}`, actual) +} + +func TestXmlConflict(t *testing.T) { + ypath := source.Dir("./testdata") + m := parser.RequireModule(ypath, "conflict") + in := ` + + zero + one + two + ` + b := node.NewBrowser(m, readXml(in)) + var actual bytes.Buffer + w := NewJSONWtr(&actual) + w.QualifyNamespace = true + err := b.Root().UpdateInto(w.Node()) + fc.RequireEqual(t, nil, err) + // this is wrong, should have all three, but there is bug in + // underlying edit.go that doesn't request all three "x" fields + fc.AssertEqual(t, `{"conflict:x":"zero"}`, actual.String()) +} diff --git a/nodeutil/xml_wtr.go b/nodeutil/xml_wtr.go new file mode 100644 index 00000000..bc53df09 --- /dev/null +++ b/nodeutil/xml_wtr.go @@ -0,0 +1,253 @@ +package nodeutil + +import ( + "bufio" + "encoding/xml" + "fmt" + "io" + "strconv" + + "github.com/freeconf/yang/node" + "github.com/freeconf/yang/val" + + "bytes" + + "github.com/freeconf/yang/meta" +) + +const QUOTE1 = '"' +const XML1 = '<' +const XML_CLOSE = '/' +const XML2 = '>' + +type XMLWtr struct { + + // stream to write contents. contents will be flushed only at end of operation + Out io.Writer + + // otherwise enumerations are written as their labels. it may be + // useful to know that json reader can accept labels or values + EnumAsIds bool + + _out *bufio.Writer +} + +func WriteXML(s *node.Selection) (string, error) { + buff := new(bytes.Buffer) + wtr := &XMLWtr{Out: buff} + err := s.InsertInto(wtr.Node()) + return buff.String(), err +} + +func (wtr XMLWtr) XML(s *node.Selection) (string, error) { + buff := new(bytes.Buffer) + wtr.Out = buff + err := s.InsertInto(wtr.Node()) + return buff.String(), err +} + +func NewXMLWtr(out io.Writer) *XMLWtr { + return &XMLWtr{Out: out} +} + +func (wtr *XMLWtr) Node() node.Node { + wtr._out = bufio.NewWriter(wtr.Out) + + return &Extend{ + Base: wtr.container(0), + OnEndEdit: func(p node.Node, r node.NodeRequest) error { + ident := wtr.ident(r.Selection.Path) + if !meta.IsLeaf(r.Selection.Meta()) { + if err := wtr.endContainer(ident); err != nil { + return err + } + } + if err := wtr._out.Flush(); err != nil { + return err + } + return nil + }, + } +} + +func (wtr *XMLWtr) container(lvl int) node.Node { + first := true + s := &Basic{} + s.OnChild = func(r node.ChildRequest) (child node.Node, err error) { + if !r.New { + return nil, nil + } + if !meta.IsList(r.Meta) { + if err = wtr.beginContainer(wtr.ident(r.Path)); err != nil { + return nil, err + } + } + + return wtr.container(lvl + 1), nil + } + s.OnBeginEdit = func(r node.NodeRequest) error { + if !meta.IsLeaf(r.Selection.Meta()) && !r.Selection.InsideList && !meta.IsList(r.Selection.Meta()) { + if lvl == 0 && first { + ns := wtr.getXmlns(r.Selection.Path) + ident := wtr.ident(r.Selection.Path) + " xmlns=" + "\"" + ns + "\"" + if err := wtr.beginContainer(ident); err != nil { + return err + } + } + first = false + } + return nil + } + s.OnEndEdit = func(r node.NodeRequest) error { + if r.Selection.InsideList { + if err := wtr.endContainer(wtr.ident(r.Selection.Path)); err != nil { + return err + } + } else if !meta.IsList(r.Selection.Meta()) { + if err := wtr.endContainer(wtr.ident(r.Selection.Path)); err != nil { + return err + } + } + + return nil + } + s.OnField = func(r node.FieldRequest, hnd *node.ValueHandle) (err error) { + ns := "" + + if l, listable := hnd.Val.(val.Listable); listable { + for i := 0; i < l.Len(); i++ { + wtr.writeLeafElement(ns, r.Path, l.Item(i)) + } + } else { + if lvl == 0 && first { + ns = wtr.getXmlns(r.Path) + } + wtr.writeLeafElement(ns, r.Path, hnd.Val) + } + + return nil + } + s.OnNext = func(r node.ListRequest) (next node.Node, key []val.Value, err error) { + if !r.New { + return + } + + ident := wtr.ident(r.Selection.Path) + + if err = wtr.beginContainer(ident); err != nil { + return + } + return wtr.container(lvl + 1), r.Key, nil + } + return s +} + +func (wtr *XMLWtr) ident(p *node.Path) string { + s := p.Meta.(meta.Identifiable).Ident() + return s +} + +func (wtr *XMLWtr) getXmlns(p *node.Path) string { + ns := "" + if meta.OriginalModule(p.Meta).Namespace() == "" { + ns = meta.OriginalModule(p.Meta).Ident() + } else { + ns = meta.OriginalModule(p.Meta).Namespace() + } + return ns +} + +func (wtr *XMLWtr) beginContainer(ident string) (err error) { + if err = wtr.writeOpenIdent(ident); err != nil { + return err + } + return +} + +func (wtr *XMLWtr) writeOpenIdent(ident string) (err error) { + if _, err = wtr._out.WriteRune(XML1); err != nil { + return err + } + if _, err = wtr._out.WriteString(ident); err != nil { + return err + } + if _, err = wtr._out.WriteRune(XML2); err != nil { + return err + } + return nil +} + +func (wtr *XMLWtr) writeCloseIdent(ident string) (err error) { + if _, err = wtr._out.WriteRune(XML1); err != nil { + return err + } + if _, err = wtr._out.WriteRune(XML_CLOSE); err != nil { + return err + } + if _, err = wtr._out.WriteString(ident); err != nil { + return err + } + if _, err = wtr._out.WriteRune(XML2); err != nil { + return err + } + return nil +} + +func (wtr *XMLWtr) endContainer(ident string) (err error) { + if err = wtr.writeCloseIdent(ident); err != nil { + return + } + return +} + +func (wtr *XMLWtr) writeLeafElement(attibute string, p *node.Path, v val.Value) error { + var err error + stringValue, err := wtr.getStringValue(p, v) + ident := p.Meta.(meta.Identifiable).Ident() + test := xml.StartElement{Name: xml.Name{Local: ident, Space: attibute}} + xml.NewEncoder(wtr._out).EncodeElement(stringValue, test) + + return err +} + +func (wtr *XMLWtr) getStringValue(p *node.Path, v val.Value) (string, error) { + stringValue := "" + var err error + switch v.Format() { + case val.FmtIdentityRef: + stringValue = v.String() + leafMod := meta.OriginalModule(p.Meta) + bases := p.Meta.(meta.HasType).Type().Base() + idty := meta.FindIdentity(bases, stringValue) + if idty == nil { + err = fmt.Errorf("could not find ident '%s'", stringValue) + } + idtyMod := meta.RootModule(idty) + if idtyMod != leafMod { + stringValue = fmt.Sprint(idtyMod.Ident(), ":", stringValue) + } + case val.FmtString, val.FmtBinary, val.FmtAny: + stringValue = v.String() + case val.FmtEnum: + if wtr.EnumAsIds { + stringValue = strconv.Itoa(v.(val.Enum).Id) + + } else { + stringValue = v.(val.Enum).Label + } + case val.FmtDecimal64: + f := v.Value().(float64) + stringValue = strconv.FormatFloat(f, 'f', -1, 64) + default: + stringValue = v.String() + } + return stringValue, err +} + +func (wtr *XMLWtr) writeString(s string) error { + clean := bytes.NewBuffer(make([]byte, len(s)+2)) + clean.Reset() + writeString(clean, s, true) + _, ioErr := wtr._out.Write(clean.Bytes()) + return ioErr +} diff --git a/nodeutil/xml_wtr_test.go b/nodeutil/xml_wtr_test.go new file mode 100644 index 00000000..81fe4c74 --- /dev/null +++ b/nodeutil/xml_wtr_test.go @@ -0,0 +1,215 @@ +package nodeutil + +import ( + "bufio" + "bytes" + "fmt" + "testing" + + "github.com/freeconf/yang/fc" + "github.com/freeconf/yang/node" + "github.com/freeconf/yang/parser" + "github.com/freeconf/yang/source" + "github.com/freeconf/yang/val" +) + +func TestXmlWriterLeafs(t *testing.T) { + fc.DebugLog(true) + tests := []struct { + Yang string + Val val.Value + expected string + enumAsId bool + }{ + { + Yang: `leaf x { type union { type int32; type string;}}`, + Val: val.String("a"), + expected: `a`, + }, + { + Yang: `leaf x { type union { type int32; type string;}}`, + Val: val.Int32(99), + expected: `99`, + }, + { + Yang: `leaf x { type enumeration { enum zero; enum one; }}`, + Val: val.Enum{Id: 0, Label: "zero"}, + expected: `zero`, + }, + { + Yang: `leaf x { type enumeration { enum five {value 5;} enum six; }}`, + Val: val.Enum{Id: 6, Label: "six"}, + expected: `6`, + enumAsId: true, + }, + } + for _, test := range tests { + m, err := parser.LoadModuleFromString(nil, fmt.Sprintf(`module m { namespace ""; %s }`, test.Yang)) + if err != nil { + t.Fatal(err) + } + var actual bytes.Buffer + buf := bufio.NewWriter(&actual) + w := &XMLWtr{ + _out: buf, + EnumAsIds: test.enumAsId, + } + w.writeLeafElement("m", &node.Path{Parent: &node.Path{Meta: m}, Meta: m.DataDefinitions()[0]}, test.Val) + buf.Flush() + fc.AssertEqual(t, test.expected, actual.String()) + } +} + +func TestXmlWriterListInList(t *testing.T) { + moduleStr := ` +module m { + prefix "t"; + namespace "t"; + revision 0000-00-00 { + description "x"; + } + typedef td { + type string; + } + container c1 { + list l1 { + list l2 { + key "a"; + leaf a { + type td; + } + leaf b { + type string; + } + } + } + } +} + ` + m, _ := parser.LoadModuleFromString(nil, moduleStr) + root := map[string]interface{}{ + "c1": map[string]interface{}{ + "l1": []map[string]interface{}{ + { + "l2": []map[string]interface{}{ + { + "a": "hi", + "b": "bye", + }, + }, + }, + }, + }, + } + b := &Node{Object: root} + c1 := sel(node.NewBrowser(m, b).Root().Find("c1")) + actual, err := WriteXML(c1) + if err != nil { + t.Fatal(err) + } + expected := `hibye` + if actual != expected { + t.Errorf("\nExpected:%s\n Actual:%s", expected, actual) + } +} + +func TestXmlAnyData(t *testing.T) { + moduleStr := ` +module m { + prefix "t"; + namespace "t"; + revision 0000-00-00 { + description "x"; + } + container c1 { + list l1 { + list l2 { + key "a"; + leaf a { + type any; + } + leaf b { + type any; + } + } + } + } +} + ` + m, _ := parser.LoadModuleFromString(nil, moduleStr) + root := map[string]interface{}{ + "c1": map[string]interface{}{ + "l1": []map[string]interface{}{ + { + "l2": []map[string]interface{}{ + { + "a": "hi", + "b": 99, + }, + }, + }, + }, + }, + } + b := &Node{Object: root} + c1 := sel(node.NewBrowser(m, b).Root().Find("c1")) + actual, err := WriteXML(c1) + if err != nil { + t.Fatal(err) + } + expected := `hi99` + if actual != expected { + t.Errorf("\nExpected:%s\n Actual:%s", expected, actual) + } +} +func TestQualifiedXmlIdentityRef(t *testing.T) { + ypath := source.Dir("./testdata") + m := parser.RequireModule(ypath, "module-test") + d := map[string]interface{}{ + "type": "derived-type", + } + b := node.NewBrowser(m, &Node{Object: d}) + wtr := &XMLWtr{} + actual, err := wtr.XML(sel(b.Root().Find("type"))) + if err != nil { + t.Fatal(err) + } + fc.AssertEqual(t, `module-types:derived-type`, actual) +} + +func TestXmlLeafList(t *testing.T) { + moduleStr := ` +module m { + prefix "t"; + namespace "t"; + revision 0000-00-00 { + description "x"; + } + container c { + leaf-list l { + type string; + } + } +} + ` + m, _ := parser.LoadModuleFromString(nil, moduleStr) + root := map[string]interface{}{ + "c": map[string]interface{}{ + "l": []interface{}{ + "hi", + "bye", + }, + }, + } + + b := &Node{Object: root} + c := sel(node.NewBrowser(m, b).Root().Find("c")) + actual, err := WriteXML(c) + if err != nil { + t.Fatal(err) + } + expected := `hibye` + if actual != expected { + t.Errorf("\nExpected:%s\n Actual:%s", expected, actual) + } +} diff --git a/val/conv.go b/val/conv.go index 788039be..ef000141 100644 --- a/val/conv.go +++ b/val/conv.go @@ -169,7 +169,7 @@ func Conv(f Format, val interface{}) (Value, error) { } else { return String(x), err } - case FmtStringList: + case FmtStringList, FmtBinaryList: if x, err := toStringList(val); err != nil { return nil, err } else { @@ -848,6 +848,8 @@ func toBinary(val interface{}) (string, error) { case []byte: r := b64.StdEncoding.EncodeToString(x) return r, nil + case string: + return x, nil } return "", fmt.Errorf("cannot coerse '%T' to binary value", val) @@ -893,6 +895,10 @@ func toStringList(val interface{}) ([]string, error) { } } return l, err + case string: + l := make([]string, 1) + l[0] = x + return l, nil } // option 2: fallback on reflection diff --git a/xpath/parser.go b/xpath/parser.go index 349f2411..1ccbba1d 100644 --- a/xpath/parser.go +++ b/xpath/parser.go @@ -48,6 +48,7 @@ var yyToknames = [...]string{ "token_operator", "kywd_slash", } + var yyStatenames = [...]string{} const yyEofCode = 1 @@ -55,7 +56,7 @@ const yyErrCode = 2 const yyInitialStackSize = 16 //line yacctab:1 -var yyExca = [...]int{ +var yyExca = [...]int8{ -1, 1, 1, -1, -2, 0, @@ -65,49 +66,49 @@ const yyPrivate = 57344 const yyLast = 15 -var yyAct = [...]int{ - +var yyAct = [...]int8{ 4, 7, 10, 8, 7, 5, 13, 12, 2, 11, 8, 6, 3, 1, 9, } -var yyPact = [...]int{ +var yyPact = [...]int16{ -3, -1000, 0, -1000, -1000, 0, -6, 2, -1000, 0, -1000, 1, -1000, -1000, } -var yyPgo = [...]int{ +var yyPgo = [...]int8{ 0, 13, 8, 12, 0, 11, } -var yyR1 = [...]int{ +var yyR1 = [...]int8{ 0, 1, 1, 3, 2, 2, 4, 4, 5, 5, 5, } -var yyR2 = [...]int{ +var yyR2 = [...]int8{ 0, 1, 1, 2, 1, 2, 2, 1, 1, 3, 3, } -var yyChk = [...]int{ +var yyChk = [...]int16{ -1000, -1, -2, -3, -4, 8, -5, 4, -4, -2, 8, 7, 6, 5, } -var yyDef = [...]int{ +var yyDef = [...]int8{ 0, -2, 1, 2, 4, 0, 7, 8, 5, 3, 6, 0, 9, 10, } -var yyTok1 = [...]int{ +var yyTok1 = [...]int8{ 1, } -var yyTok2 = [...]int{ +var yyTok2 = [...]int8{ 2, 3, 4, 5, 6, 7, 8, } -var yyTok3 = [...]int{ + +var yyTok3 = [...]int8{ 0, } @@ -189,9 +190,9 @@ func yyErrorMessage(state, lookAhead int) string { expected := make([]int, 0, 4) // Look for shiftable tokens. - base := yyPact[state] + base := int(yyPact[state]) for tok := TOKSTART; tok-1 < len(yyToknames); tok++ { - if n := base + tok; n >= 0 && n < yyLast && yyChk[yyAct[n]] == tok { + if n := base + tok; n >= 0 && n < yyLast && int(yyChk[int(yyAct[n])]) == tok { if len(expected) == cap(expected) { return res } @@ -201,13 +202,13 @@ func yyErrorMessage(state, lookAhead int) string { if yyDef[state] == -2 { i := 0 - for yyExca[i] != -1 || yyExca[i+1] != state { + for yyExca[i] != -1 || int(yyExca[i+1]) != state { i += 2 } // Look for tokens that we accept or reduce. for i += 2; yyExca[i] >= 0; i += 2 { - tok := yyExca[i] + tok := int(yyExca[i]) if tok < TOKSTART || yyExca[i+1] == 0 { continue } @@ -238,30 +239,30 @@ func yylex1(lex yyLexer, lval *yySymType) (char, token int) { token = 0 char = lex.Lex(lval) if char <= 0 { - token = yyTok1[0] + token = int(yyTok1[0]) goto out } if char < len(yyTok1) { - token = yyTok1[char] + token = int(yyTok1[char]) goto out } if char >= yyPrivate { if char < yyPrivate+len(yyTok2) { - token = yyTok2[char-yyPrivate] + token = int(yyTok2[char-yyPrivate]) goto out } } for i := 0; i < len(yyTok3); i += 2 { - token = yyTok3[i+0] + token = int(yyTok3[i+0]) if token == char { - token = yyTok3[i+1] + token = int(yyTok3[i+1]) goto out } } out: if token == 0 { - token = yyTok2[1] /* unknown char */ + token = int(yyTok2[1]) /* unknown char */ } if yyDebug >= 3 { __yyfmt__.Printf("lex %s(%d)\n", yyTokname(token), uint(char)) @@ -316,7 +317,7 @@ yystack: yyS[yyp].yys = yystate yynewstate: - yyn = yyPact[yystate] + yyn = int(yyPact[yystate]) if yyn <= yyFlag { goto yydefault /* simple state */ } @@ -327,8 +328,8 @@ yynewstate: if yyn < 0 || yyn >= yyLast { goto yydefault } - yyn = yyAct[yyn] - if yyChk[yyn] == yytoken { /* valid shift */ + yyn = int(yyAct[yyn]) + if int(yyChk[yyn]) == yytoken { /* valid shift */ yyrcvr.char = -1 yytoken = -1 yyVAL = yyrcvr.lval @@ -341,7 +342,7 @@ yynewstate: yydefault: /* default state action */ - yyn = yyDef[yystate] + yyn = int(yyDef[yystate]) if yyn == -2 { if yyrcvr.char < 0 { yyrcvr.char, yytoken = yylex1(yylex, &yyrcvr.lval) @@ -350,18 +351,18 @@ yydefault: /* look through exception table */ xi := 0 for { - if yyExca[xi+0] == -1 && yyExca[xi+1] == yystate { + if yyExca[xi+0] == -1 && int(yyExca[xi+1]) == yystate { break } xi += 2 } for xi += 2; ; xi += 2 { - yyn = yyExca[xi+0] + yyn = int(yyExca[xi+0]) if yyn < 0 || yyn == yytoken { break } } - yyn = yyExca[xi+1] + yyn = int(yyExca[xi+1]) if yyn < 0 { goto ret0 } @@ -383,10 +384,10 @@ yydefault: /* find a state where "error" is a legal shift action */ for yyp >= 0 { - yyn = yyPact[yyS[yyp].yys] + yyErrCode + yyn = int(yyPact[yyS[yyp].yys]) + yyErrCode if yyn >= 0 && yyn < yyLast { - yystate = yyAct[yyn] /* simulate a shift of "error" */ - if yyChk[yystate] == yyErrCode { + yystate = int(yyAct[yyn]) /* simulate a shift of "error" */ + if int(yyChk[yystate]) == yyErrCode { goto yystack } } @@ -422,7 +423,7 @@ yydefault: yypt := yyp _ = yypt // guard against "declared and not used" - yyp -= yyR2[yyn] + yyp -= int(yyR2[yyn]) // yyp is now the index of $0. Perform the default action. Iff the // reduced production is ε, $1 is possibly out of range. if yyp+1 >= len(yyS) { @@ -433,16 +434,16 @@ yydefault: yyVAL = yyS[yyp+1] /* consult goto table to find next state */ - yyn = yyR1[yyn] - yyg := yyPgo[yyn] + yyn = int(yyR1[yyn]) + yyg := int(yyPgo[yyn]) yyj := yyg + yyS[yyp].yys + 1 if yyj >= yyLast { - yystate = yyAct[yyg] + yystate = int(yyAct[yyg]) } else { - yystate = yyAct[yyj] - if yyChk[yystate] != -yyn { - yystate = yyAct[yyg] + yystate = int(yyAct[yyj]) + if int(yyChk[yystate]) != -yyn { + yystate = int(yyAct[yyg]) } } // dummy call; replaced with literal code diff --git a/xpath/y.output b/xpath/y.output index 5649f953..ef478f86 100644 --- a/xpath/y.output +++ b/xpath/y.output @@ -112,14 +112,14 @@ state 13 8 terminals, 6 nonterminals -11 grammar rules, 14/8000 states +11 grammar rules, 14/16000 states 0 shift/reduce, 0 reduce/reduce conflicts reported 55 working sets used -memory: parser 10/120000 +memory: parser 10/240000 0 extra closures 9 shift entries, 1 exceptions 8 goto entries 4 entries saved by goto default -Optimizer space used: output 15/120000 +Optimizer space used: output 15/240000 15 table entries, 0 zero maximum spread: 8, maximum offset: 9