pax_global_header00006660000000000000000000000064127341103460014513gustar00rootroot0000000000000052 comment=2ad3b31cf4a21db70fc0cacd0e0120eaae566f98 goxpath-1.0-alpha3/000077500000000000000000000000001273411034600141735ustar00rootroot00000000000000goxpath-1.0-alpha3/.travis.yml000066400000000000000000000007531273411034600163110ustar00rootroot00000000000000language: go go: - 1.6 before_install: - go get -u github.com/ChrisTrenkamp/goxpath - go get -u github.com/ChrisTrenkamp/goxpath/cmd/goxpath - go get -u github.com/wadey/gocovmerge script: - go list -f '{{if gt (len .TestGoFiles) 0}}"go test -covermode count -coverprofile {{.Name}}.coverprofile -coverpkg ./... {{.ImportPath}}"{{end}}' ./... | xargs -I {} bash -c {} - gocovmerge `ls *.coverprofile` > coverage.txt after_success: - bash <(curl -s https://codecov.io/bash) goxpath-1.0-alpha3/LICENSE000066400000000000000000000020711273411034600152000ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 ChrisTrenkamp Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. goxpath-1.0-alpha3/README.md000066400000000000000000000012601273411034600154510ustar00rootroot00000000000000# goxpath [![GoDoc](https://godoc.org/gopkg.in/src-d/go-git.v2?status.svg)](https://godoc.org/github.com/ChrisTrenkamp/goxpath) [![Build Status](https://travis-ci.org/ChrisTrenkamp/goxpath.svg?branch=master)](https://travis-ci.org/ChrisTrenkamp/goxpath) [![codecov.io](https://codecov.io/github/ChrisTrenkamp/goxpath/coverage.svg?branch=master)](https://codecov.io/github/ChrisTrenkamp/goxpath?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/ChrisTrenkamp/goxpath)](https://goreportcard.com/report/github.com/ChrisTrenkamp/goxpath) An XPath 1.0 implementation written in Go. See the [wiki](https://github.com/ChrisTrenkamp/goxpath/wiki) for more information. goxpath-1.0-alpha3/cmd/000077500000000000000000000000001273411034600147365ustar00rootroot00000000000000goxpath-1.0-alpha3/cmd/goxpath/000077500000000000000000000000001273411034600164105ustar00rootroot00000000000000goxpath-1.0-alpha3/cmd/goxpath/goxpath.go000066400000000000000000000076171273411034600204240ustar00rootroot00000000000000package main import ( "bytes" "flag" "fmt" "io" "io/ioutil" "os" "path/filepath" "strings" "github.com/ChrisTrenkamp/goxpath" "github.com/ChrisTrenkamp/goxpath/tree" "github.com/ChrisTrenkamp/goxpath/tree/xmltree" ) type namespace map[string]string func (n *namespace) String() string { return fmt.Sprint(*n) } func (n *namespace) Set(value string) error { nsMap := strings.Split(value, "=") if len(nsMap) != 2 { nsErr = fmt.Errorf("Invalid namespace mapping: %s\n", value) } (*n)[nsMap[0]] = nsMap[1] return nil } type variables map[string]string func (v *variables) String() string { return fmt.Sprint(*v) } func (v *variables) Set(value string) error { varMap := strings.Split(value, "=") if len(varMap) != 2 { nsErr = fmt.Errorf("Invalid variable mapping: %s\n", value) } (*v)[varMap[0]] = varMap[1] return nil } var rec bool var value bool var ns = make(namespace) var vars = make(variables) var nsErr error var unstrict bool var noFileName bool var args = []string{} var stdin io.Reader = os.Stdin var stdout io.ReadWriter = os.Stdout var stderr io.ReadWriter = os.Stderr var retCode = 0 func main() { exec() os.Exit(retCode) } func exec() { flag.BoolVar(&rec, "r", false, "Recursive") flag.BoolVar(&value, "v", false, "Output the string value of the XPath result") flag.Var(&ns, "ns", "Namespace mappings. e.g. -ns myns=http://example.com") flag.Var(&vars, "var", "Variables mappings. e.g. -var myvar=myvalue") flag.BoolVar(&unstrict, "u", false, "Turns off strict XML validation") flag.BoolVar(&noFileName, "h", false, "Suppress filename prefixes.") flag.Parse() args = flag.Args() if nsErr != nil { fmt.Fprintf(stderr, nsErr.Error()) retCode = 1 return } if len(args) < 1 { fmt.Fprintf(stderr, "Specify an XPath expression with one or more files, or pipe the XML from stdin. Run 'goxpath --help' for more information.\n") retCode = 1 return } xp, err := goxpath.Parse(args[0]) if err != nil { fmt.Fprintf(stderr, "%s\n", err.Error()) retCode = 1 return } if len(args) == 1 { ret, err := runXPath(xp, stdin, ns, value) if err != nil { fmt.Fprintf(stderr, "%s\n", err.Error()) retCode = 1 } printResult(ret, "") } for i := 1; i < len(args); i++ { procPath(args[i], xp, ns, value) } } func procPath(path string, x goxpath.XPathExec, ns namespace, value bool) { fi, err := os.Stat(path) if err != nil { fmt.Fprintf(stderr, "Could not open file: %s\n", path) retCode = 1 return } if fi.IsDir() { procDir(path, x, ns, value) return } data, _ := ioutil.ReadFile(path) ret, err := runXPath(x, bytes.NewBuffer(data), ns, value) if err != nil { fmt.Fprintf(stderr, "%s: %s\n", path, err.Error()) retCode = 1 } printResult(ret, path) } func printResult(ret []string, path string) { for _, j := range ret { if (len(flag.Args()) > 2 || rec) && !noFileName { fmt.Fprintf(stdout, "%s:", path) } fmt.Fprintf(stdout, "%s\n", j) } } func procDir(path string, x goxpath.XPathExec, ns namespace, value bool) { if !rec { fmt.Fprintf(stderr, "%s: Is a directory\n", path) retCode = 1 return } list, _ := ioutil.ReadDir(path) for _, i := range list { procPath(filepath.Join(path, i.Name()), x, ns, value) } } func runXPath(x goxpath.XPathExec, r io.Reader, ns namespace, value bool) ([]string, error) { t, err := xmltree.ParseXML(r, func(o *xmltree.ParseOptions) { o.Strict = !unstrict }) if err != nil { return nil, err } res, err := x.Exec(t, func(o *goxpath.Opts) { o.NS = ns for k, v := range vars { o.Vars[k] = tree.String(v) } }) if err != nil { return nil, err } var ret []string if nodes, ok := res.(tree.NodeSet); ok && !value { ret = make([]string, len(nodes)) for i, v := range nodes { ret[i], _ = goxpath.MarshalStr(v) ret[i] = strings.Replace(ret[i], "\n", " ", -1) } } else { str := res.String() if str != "" { ret = strings.Split(str, "\n") } } return ret, nil } goxpath-1.0-alpha3/cmd/goxpath/goxpath_test.go000066400000000000000000000104501273411034600214500ustar00rootroot00000000000000package main import ( "bytes" "encoding/xml" "flag" "os" "strings" "testing" ) func setup(in string, args ...string) (*bytes.Buffer, *bytes.Buffer) { retCode = 0 os.Args = append([]string{"test"}, args...) flag.CommandLine = flag.NewFlagSet("test", flag.ExitOnError) out := &bytes.Buffer{} err := &bytes.Buffer{} stdout = out stderr = err stdin = strings.NewReader(in) exec() return out, err } func TestStdinVal(t *testing.T) { out, _ := setup(xml.Header+"test", "-v", "/root/tag") if out.String() != "test\n" { t.Error("Expecting 'test' for the result. Recieved: ", out.String()) } if retCode != 0 { t.Error("Incorrect return value") } } func TestStdinNonVal(t *testing.T) { out, _ := setup(xml.Header+"test", "/root/tag") if out.String() != "test\n" { t.Error("Expecting 'test' for the result. Recieved: ", out.String()) } if retCode != 0 { t.Error("Incorrect return value") } } func TestFile(t *testing.T) { out, _ := setup("", "-ns", "foo=http://foo.bar", "/foo:test/foo:path", "test/1.xml") if out.String() != `path`+"\n" { t.Error(`Expecting 'path' for the result. Recieved: `, out.String()) } if retCode != 0 { t.Error("Incorrect return value") } } func TestDir(t *testing.T) { out, _ := setup("", "-r", "/foo", "test/subdir") val := strings.Replace(out.String(), "test\\subdir\\", "test/subdir/", -1) if val != `test/subdir/2.xml:bar`+"\n"+`test/subdir/3.xml:bar2`+"\n" { t.Error(`Incorrect result. Recieved: `, val) } if retCode != 0 { t.Error("Incorrect return value") } } func TestDirNonRec(t *testing.T) { _, err := setup("", "/foo", "test/subdir") val := strings.Replace(err.String(), "test\\subdir\\", "test/subdir/", -1) if val != `test/subdir: Is a directory`+"\n" { t.Error(`Incorrect result. Recieved: `, val) } if retCode != 1 { t.Error("Incorrect return value") } } func TestNoXPath(t *testing.T) { _, err := setup("") if err.String() != "Specify an XPath expression with one or more files, or pipe the XML from stdin. Run 'goxpath --help' for more information.\n" { t.Error("No XPath error") } if retCode != 1 { t.Error("Incorrect return value") } } func TestInvalidXPathExpr(t *testing.T) { _, err := setup("", "/foo()", "test/1.xml") if err.String() != "Invalid node-type foo\n" { t.Error("Invalid XPath error") } if retCode != 1 { t.Error("Incorrect return value") } } func TestInvalidFilePath(t *testing.T) { _, err := setup("", "/foo", "foo.xml") if err.String() != "Could not open file: foo.xml\n" { t.Error("Invalid error") } if retCode != 1 { t.Error("Incorrect return value") } } func TestXPathExecErr(t *testing.T) { _, err := setup("", "foobar()", "test/1.xml") if err.String() != "test/1.xml: Unknown function: foobar\n" { t.Error("Invalid error", err.String()) } if retCode != 1 { t.Error("Incorrect return value") } } func TestXPathExecErrStdin(t *testing.T) { _, err := setup(xml.Header+"test", "foobar()") if err.String() != "Unknown function: foobar\n" { t.Error("Invalid error", err.String()) } if retCode != 1 { t.Error("Incorrect return value") } } func TestInvalidXML(t *testing.T) { _, err := setup("", "/root") if err.String() != "Malformed XML file\n" { t.Error("Invalid error", err.String()) } if retCode != 1 { t.Error("Incorrect return value") } } func TestVarRef(t *testing.T) { out, _ := setup(xml.Header+"test", "-var=foo=test", "/root/tag = $foo") if out.String() != "true\n" { t.Error("Expecting 'true' for the result. Recieved: ", out.String()) } if retCode != 0 { t.Error("Incorrect return value") } } func TestInvalidNSMap(t *testing.T) { _, err := setup(xml.Header+"", "-ns=foo=http://foo=bar", "/root") if err.String() != "Invalid namespace mapping: foo=http://foo=bar\n" { t.Error("Invalid error", err.String()) } if retCode != 1 { t.Error("Incorrect return value") } } func TestInvalidVarMap(t *testing.T) { _, err := setup(xml.Header+"", "-var=test=blag=foo", "/root") if err.String() != "Invalid variable mapping: test=blag=foo\n" { t.Error("Invalid error", err.String()) } if retCode != 1 { t.Error("Incorrect return value") } } goxpath-1.0-alpha3/cmd/goxpath/test/000077500000000000000000000000001273411034600173675ustar00rootroot00000000000000goxpath-1.0-alpha3/cmd/goxpath/test/1.xml000066400000000000000000000001451273411034600202510ustar00rootroot00000000000000testpathtest2 goxpath-1.0-alpha3/cmd/goxpath/test/subdir/000077500000000000000000000000001273411034600206575ustar00rootroot00000000000000goxpath-1.0-alpha3/cmd/goxpath/test/subdir/2.xml000066400000000000000000000000651273411034600215430ustar00rootroot00000000000000bar goxpath-1.0-alpha3/cmd/goxpath/test/subdir/3.xml000066400000000000000000000000661273411034600215450ustar00rootroot00000000000000bar2 goxpath-1.0-alpha3/comp_test.go000066400000000000000000000051311273411034600165170ustar00rootroot00000000000000package goxpath import ( "fmt" "testing" ) func TestAddition(t *testing.T) { p := `1 + 1` x := `` exp := "2" execVal(p, x, exp, nil, t) } func TestParenths(t *testing.T) { p := `(1 + 2) * 3` x := `` exp := "9" execVal(p, x, exp, nil, t) } func TestParenths2(t *testing.T) { p := `(1 + 2 * 3)` x := `` exp := "7" execVal(p, x, exp, nil, t) } func TestEquals(t *testing.T) { p := `/test/test2 = 3` x := `3` exp := "true" execVal(p, x, exp, nil, t) } func TestEqualsStr(t *testing.T) { p := `/test/test2 = 'foobar'` x := `foobar` exp := "true" execVal(p, x, exp, nil, t) } func TestEqualsStr2(t *testing.T) { p := `/root[@test="foo"]` x := `test` exp := "test" execVal(p, x, exp, nil, t) } func TestNumberOps(t *testing.T) { x := `2352` testFloatMap := make(map[string]float64) testFloatMap[`/t/t1 * 3`] = 2 * 3 testFloatMap[`5 div /t/t1`] = 5.0 / 2.0 testFloatMap[`/t/t2 + /t/t3`] = 3 + 5 testFloatMap[`/t/t2 - /t/t3`] = 3 - 5 testFloatMap[`/t/t3 mod /t/t1`] = 5 % 2 testFloatMap[`number('5')`] = 5 testFloatMap[`sum(/t/*)`] = 2 + 3 + 5 + 2 testFloatMap[`floor(/t/t3 div /t/t1)`] = 2 testFloatMap[`ceiling(t/t3 div /t/t1)`] = 3 testFloatMap[`round(-1.5)`] = -2 testFloatMap[`round(1.5)`] = 2 testFloatMap[`round(0)`] = 0 for k, v := range testFloatMap { execVal(k, x, fmt.Sprintf("%g", float64(v)), nil, t) } execVal(`/t/t3 div 0`, x, "Infinity", nil, t) execVal(`-1 div 0`, x, "-Infinity", nil, t) execVal(`0 div 0`, x, "NaN", nil, t) testBoolMap := make(map[string]string) testBoolMap[`/t/t1 = 2`] = "true" testBoolMap[`2 = /t/t1`] = "true" testBoolMap[`/t/t1 != 2`] = "false" testBoolMap[`4 = /t/t2`] = "false" testBoolMap[`/t/t1 != /t/t2`] = "true" testBoolMap[`2 < /t/t4`] = "false" testBoolMap[`/t/t1 <= 2`] = "true" testBoolMap[`/t/t1 > /t/t4`] = "false" testBoolMap[`2 >= /t/t4`] = "true" testBoolMap[`/t/t1 >= /t/t4`] = "true" testBoolMap[`/t/t1 != /t/t2 and /t/t1 < /t/t4`] = "false" testBoolMap[`/t/t1 != /t/t2 or /t/t1 < /t/t4`] = "true" testBoolMap[`(/t/t1 != /t/t2 or /t/t1 < /t/t4) = true()`] = "true" testBoolMap[`(/t/t1 != /t/t2 and /t/t1 < /t/t4) != true()`] = "true" for k, v := range testBoolMap { execVal(k, x, v, nil, t) } } goxpath-1.0-alpha3/coverage.sh000077500000000000000000000011441273411034600163250ustar00rootroot00000000000000#!/bin/bash cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" go get github.com/ChrisTrenkamp/goxpath/cmd/goxpath if [ $? != 0 ]; then exit 1 fi go test >/dev/null 2>&1 if [ $? != 0 ]; then go test exit 1 fi gometalinter --deadline=1m ./... go list -f '{{if gt (len .TestGoFiles) 0}}"go test -covermode count -coverprofile {{.Name}}.coverprofile -coverpkg ./... {{.ImportPath}}"{{end}} >/dev/null' ./... | xargs -I {} bash -c {} 2>/dev/null gocovmerge `ls *.coverprofile` > coverage.txt go tool cover -html=coverage.txt -o coverage.html firefox coverage.html rm coverage.html coverage.txt *.coverprofile goxpath-1.0-alpha3/err_test.go000066400000000000000000000175411273411034600163610ustar00rootroot00000000000000package goxpath import ( "bytes" "encoding/xml" "runtime/debug" "testing" "github.com/ChrisTrenkamp/goxpath/tree" "github.com/ChrisTrenkamp/goxpath/tree/xmltree" "github.com/ChrisTrenkamp/goxpath/tree/xmltree/xmlele" ) type dummyType string func (d dummyType) String() string { return string(d) } func dummyFunc(c tree.Ctx, args ...tree.Result) (tree.Result, error) { return dummyType(""), nil } var custFns = map[xml.Name]tree.Wrap{ {Local: "dummy"}: {Fn: dummyFunc}, {Space: "http://foo.com", Local: "spaceDummy"}: {Fn: dummyFunc}, } func execErr(xp, x string, errStr string, ns map[string]string, t *testing.T) { defer func() { if r := recover(); r != nil { t.Error("Panicked: from XPath expr: '" + xp) t.Error(r) t.Error(string(debug.Stack())) } }() _, err := ParseExec(xp, xmltree.MustParseXML(bytes.NewBufferString(x)), func(o *Opts) { o.NS = ns; o.Funcs = custFns }) if err.Error() != errStr { t.Error("Incorrect result:'" + err.Error() + "' from XPath expr: '" + xp + "'. Expecting: '" + errStr + "'") return } } func TestBadAxis(t *testing.T) { x := `` execErr(`/test/chil::p2`, x, "Invalid Axis specifier, chil", nil, t) } func TestIncompleteStep(t *testing.T) { x := `` execErr(`/child::+2`, x, "Step is not complete", nil, t) execErr(`/foo:`, x, "Step is not complete", nil, t) } func TestParseErr(t *testing.T) { _, err := xmltree.ParseXML(bytes.NewBufferString("")) if err.Error() != "Malformed XML file" { t.Error("Incorrect error message:", err.Error()) } _, err = xmltree.ParseXML(bytes.NewBufferString("")) if err.Error() != "EOF" { t.Error("Incorrect error message:", err.Error()) } _, err = xmltree.ParseXML(bytes.NewBufferString(""), func(s *xmltree.ParseOptions) { s.Strict = false }) if err != nil { t.Error("Error not nil:", err.Error()) } } func TestBadNodeType(t *testing.T) { x := `` execErr(`/test/foo()`, x, "Invalid node-type foo", nil, t) } func TestXPathErr(t *testing.T) { x := `` execErr(`/test/chil::p2`, x, "Invalid Axis specifier, chil", nil, t) } func TestNodeSetConvErr(t *testing.T) { x := `` for _, i := range []string{"sum", "count", "local-name", "namespace-uri", "name"} { execErr("/p1["+i+"(1)]", x, "Cannot convert object to a node-set", nil, t) } } func TestNodeSetConvUnionErr(t *testing.T) { x := `` execErr(`/p1 | 'invalid'`, x, "Cannot convert data type to node-set", nil, t) } func TestUnknownFunction(t *testing.T) { x := `` execErr(`invFunc()`, x, "Unknown function: invFunc", nil, t) } func TestUnterminatedString(t *testing.T) { x := `` execErr(`"asdf`, x, "Unexpected end of string literal.", nil, t) } func TestUnterminatedParenths(t *testing.T) { x := `` execErr(`(1 + 2 * 3`, x, "Missing end )", nil, t) } func TestUnterminatedNTQuotes(t *testing.T) { x := `` execErr(`//processing-instruction('foo)`, x, "Unexpected end of string literal.", nil, t) } func TestUnterminatedNTParenths(t *testing.T) { x := `` execErr(`//processing-instruction('foo'`, x, "Missing ) at end of NodeType declaration.", nil, t) } func TestUnterminatedFnParenths(t *testing.T) { x := `` execErr(`true(`, x, "Missing ) at end of function declaration.", nil, t) } func TestEmptyPred(t *testing.T) { x := `text` execErr(`/p1[ ]`, x, "Missing content in predicate.", nil, t) } func TestUnterminatedPred(t *testing.T) { x := `text` execErr(`/p1[. = 'text'`, x, "Missing ] at end of predicate.", nil, t) } func TestNotEnoughArgs(t *testing.T) { x := `text` execErr(`concat('test')`, x, "Invalid number of arguments", nil, t) } func TestMarshalErr(t *testing.T) { x := `` n := xmltree.MustParseXML(bytes.NewBufferString(x)) f := tree.FindNodeByPos(n, 3).(*xmlele.XMLEle) f.Name.Local = "" buf := &bytes.Buffer{} err := Marshal(n, buf) if err == nil { t.Error("No error") } } func TestParsePanic(t *testing.T) { errs := 0 defer func() { if errs != 1 { t.Error("Err not 1") } }() defer func() { if r := recover(); r != nil { errs++ } }() MustParse(`/foo()`) } func TestExecPanic(t *testing.T) { errs := 0 defer func() { if errs != 1 { t.Error("Err not 1") } }() defer func() { if r := recover(); r != nil { errs++ } }() MustParse("foo()").MustExec(xmltree.MustParseXML(bytes.NewBufferString(xml.Header + ""))) } func TestParseXMLPanic(t *testing.T) { errs := 0 defer func() { if errs != 1 { t.Error("Err not 1") } }() defer func() { if r := recover(); r != nil { errs++ } }() xmltree.MustParseXML(bytes.NewBufferString("")) } func TestDummyType(t *testing.T) { ns := map[string]string{"foo": "http://foo.com"} x := `` execErr(`dummy() = 1`, x, "Cannot convert data type to number", nil, t) execErr(`dummy() = true()`, x, "Cannot convert argument to boolean", nil, t) execErr(`dummy() and true()`, x, "Cannot convert argument to boolean", nil, t) execErr(`not(dummy()) = 1`, x, "Cannot convert object to a boolean", nil, t) execErr(`1 = not(dummy())`, x, "Cannot convert object to a boolean", nil, t) execErr(`not(dummy() = 1)`, x, "Cannot convert data type to number", nil, t) execErr(`/p1[dummy()]`, x, "Cannot convert argument to boolean", nil, t) for _, i := range []string{"boolean", "not"} { execErr(i+`(dummy())`, x, "Cannot convert object to a boolean", nil, t) } for _, i := range []string{"number", "floor", "ceiling", "round"} { execErr(i+`(dummy())`, x, "Cannot convert object to a number", nil, t) } execErr(`substring("12345", dummy(), 2)`, x, "Cannot convert object to a number", nil, t) execErr(`substring("12345", 2, dummy())`, x, "Cannot convert object to a number", nil, t) execErr(`foo:spaceDummy() = 1`, x, "Cannot convert data type to number", ns, t) } func TestGoxpathBool(t *testing.T) { opts := func(o *Opts) { o.Funcs = custFns } x := xmltree.MustParseXML(bytes.NewBufferString(``)) _, err := MustParse(`dummy() = 1`).ExecBool(x, opts) if err == nil { t.Error("Error not nil") } _, err = MustParse(`dummy()`).ExecBool(x, opts) if err == nil { t.Error("Error not nil") } b, err := MustParse(`/p1`).ExecBool(x, opts) if !b || err != nil { t.Error("Incorrect result") } } func TestGoxpathNum(t *testing.T) { opts := func(o *Opts) { o.Funcs = custFns } x := xmltree.MustParseXML(bytes.NewBufferString(`3`)) _, err := MustParse(`dummy() = 1`).ExecNum(x, opts) if err == nil { t.Error("Error not nil") } _, err = MustParse(`dummy()`).ExecNum(x, opts) if err == nil { t.Error("Error not nil") } n, err := MustParse(`/p1`).ExecNum(x, opts) if n != 3 || err != nil { t.Error("Incorrect result") } } func TestGoxpathNode(t *testing.T) { opts := func(o *Opts) { o.Funcs = custFns } x := xmltree.MustParseXML(bytes.NewBufferString(``)) _, err := MustParse(`dummy() = 1`).ExecNode(x, opts) if err == nil { t.Error("Error not nil") } _, err = MustParse(`dummy()`).ExecNode(x, opts) if err == nil { t.Error("Error not nil") } n, err := MustParse(`/p1`).ExecNode(x, opts) if len(n) != 1 || err != nil { t.Error("Incorrect result") } } goxpath-1.0-alpha3/fns_test.go000066400000000000000000000165271273411034600163620ustar00rootroot00000000000000package goxpath import ( "fmt" "testing" ) func TestStrLit(t *testing.T) { p := `'strlit'` x := `` exp := "strlit" execVal(p, x, exp, nil, t) } func TestNumLit(t *testing.T) { p := `123` x := `` exp := "123" execVal(p, x, exp, nil, t) p = `123.456` exp = "123.456" execVal(p, x, exp, nil, t) } func TestLast(t *testing.T) { p := `/p1/*[last()]` x := `` exp := []string{""} execPath(p, x, exp, nil, t) p = `/p1/p5[last()]` exp = []string{} execPath(p, x, exp, nil, t) p = `/p1[last()]` exp = []string{""} execPath(p, x, exp, nil, t) } func TestCount(t *testing.T) { p := `count(//p1)` x := `` exp := "2" execVal(p, x, exp, nil, t) } func TestCount2(t *testing.T) { x := ` y31 y32 y21 y22 y11 y12 y03 y04 ` execVal(`count(//x)`, x, "7", nil, t) execVal(`count(//x[1])`, x, "4", nil, t) execVal(`count(//x/y)`, x, "8", nil, t) execVal(`count(//x/y[1])`, x, "4", nil, t) execVal(`count(//x[1]/y[1])`, x, "2", nil, t) } func TestNames(t *testing.T) { x := `` testMap := make(map[string]map[string]string) testMap["/*"] = make(map[string]string) testMap["/*"]["local-name"] = "test" testMap["/*"]["namespace-uri"] = "http://foo.com" testMap["/*"]["name"] = "{http://foo.com}test" testMap["/none"] = make(map[string]string) testMap["/none"]["local-name"] = "" testMap["/none"]["namespace-uri"] = "" testMap["/none"]["name"] = "" testMap["/*/@*:attr"] = make(map[string]string) testMap["/*/@*:attr"]["local-name"] = "attr" testMap["/*/@*:attr"]["namespace-uri"] = "http://bar.com" testMap["/*/@*:attr"]["name"] = "{http://bar.com}attr" testMap["//processing-instruction()"] = make(map[string]string) testMap["//processing-instruction()"]["local-name"] = "pi" testMap["//processing-instruction()"]["namespace-uri"] = "" testMap["//processing-instruction()"]["name"] = "pi" testMap["//comment()"] = make(map[string]string) testMap["//comment()"]["local-name"] = "" testMap["//comment()"]["namespace-uri"] = "" testMap["//comment()"]["name"] = "" for path, i := range testMap { for nt, res := range i { p := fmt.Sprintf("%s(%s)", nt, path) exp := res execVal(p, x, exp, nil, t) } } x = `` execPath("/*[local-name() = 'test']", x, []string{``}, nil, t) execPath("/*[namespace-uri() = 'http://foo.com']", x, []string{``}, nil, t) execPath("/*[name() = '{http://foo.com}test']", x, []string{``}, nil, t) } func TestBoolean(t *testing.T) { x := `` execVal(`true()`, x, "true", nil, t) execVal(`false()`, x, "false", nil, t) p := `boolean(/p1/p2)` exp := "true" execVal(p, x, exp, nil, t) p = `boolean(/p1/p5)` exp = "false" execVal(p, x, exp, nil, t) p = `boolean('123')` exp = "true" execVal(p, x, exp, nil, t) p = `boolean(123)` exp = "true" execVal(p, x, exp, nil, t) p = `boolean('')` exp = "false" execVal(p, x, exp, nil, t) p = `boolean(0)` exp = "false" execVal(p, x, exp, nil, t) } func TestNot(t *testing.T) { x := `` execVal(`not(false())`, x, "true", nil, t) execVal(`not(true())`, x, "false", nil, t) } func TestConversions(t *testing.T) { x := `foo` execVal(`number(true())`, x, "1", nil, t) execVal(`number(false())`, x, "0", nil, t) execVal(`string(/p2)`, x, "", nil, t) execVal(`/p1[string() = 'foo']`, x, "foo", nil, t) execVal(`number('abc')`, x, "NaN", nil, t) } func TestLang(t *testing.T) { x := `

I went up a floor.

I took the lift.

I rode the elevator.

` execVal(`count(//p[lang('en')])`, x, "3", nil, t) execVal(`count(//text()[lang('en-GB')])`, x, "1", nil, t) execVal(`count(//p[lang('en-US')])`, x, "1", nil, t) execVal(`count(//p[lang('de')])`, x, "0", nil, t) execVal(`count(/p1[lang('en')])`, x, "0", nil, t) } func TestString(t *testing.T) { x := `text` execVal(`string(2 + 2)`, x, "4", nil, t) execVal(`string(/p1)`, x, "text", nil, t) } func TestConcat(t *testing.T) { x := `` execVal(`concat('abc', 'def', 'hij', '123')`, x, "abcdefhij123", nil, t) } func TestStartsWith(t *testing.T) { x := `` execVal(`starts-with('abcd', 'ab')`, x, "true", nil, t) execVal(`starts-with('abcd', 'abd')`, x, "false", nil, t) } func TestContains(t *testing.T) { x := `` execVal(`contains('abcd', 'bcd')`, x, "true", nil, t) execVal(`contains('abcd', 'bd')`, x, "false", nil, t) } func TestSubstrBefore(t *testing.T) { x := `` execVal(`substring-before("1999/04/01","/")`, x, "1999", nil, t) execVal(`substring-before("1999/04/01","2")`, x, "", nil, t) } func TestSubstrAfter(t *testing.T) { x := `` execVal(`substring-after("1999/04/01","/")`, x, "04/01", nil, t) execVal(`substring-after("1999/04/01","19")`, x, "99/04/01", nil, t) execVal(`substring-after("1999/04/01","a")`, x, "", nil, t) } func TestSubstring(t *testing.T) { x := `` execVal(`substring("12345", 2, 3)`, x, "234", nil, t) execVal(`substring("12345", 2)`, x, "2345", nil, t) execVal(`substring('abcd', -2, 5)`, x, "ab", nil, t) execVal(`substring('abcd', 0)`, x, "abcd", nil, t) execVal(`substring('abcd', 1, 4)`, x, "abcd", nil, t) execVal(`substring("12345", 1.5, 2.6)`, x, "234", nil, t) execVal(`substring("12345", 0 div 0, 3)`, x, "", nil, t) execVal(`substring("12345", 1, 0 div 0)`, x, "", nil, t) execVal(`substring("12345", -42, 1 div 0)`, x, "12345", nil, t) execVal(`substring("12345", -1 div 0, 1 div 0)`, x, "", nil, t) } func TestStrLength(t *testing.T) { x := `abc` execVal(`string-length('def')`, x, "3", nil, t) execVal(`/p1[string-length() = 3]`, x, "abc", nil, t) } func TestNormalizeSpace(t *testing.T) { x := ` a b ` execVal(`normalize-space(/p1)`, x, "a b", nil, t) execVal(`/p1[normalize-space(/p1) = 'a b']`, x, ` a b `, nil, t) execVal(`/p1[normalize-space() = 'a b']`, x, ` a b `, nil, t) } func TestTranslate(t *testing.T) { x := `` execVal(`translate("bar","abc","ABC")`, x, "BAr", nil, t) execVal(`translate("--aaa--","abc-","ABC")`, x, "AAA", nil, t) } goxpath-1.0-alpha3/goxpath.go000066400000000000000000000055501273411034600162010ustar00rootroot00000000000000package goxpath import ( "encoding/xml" "fmt" "github.com/ChrisTrenkamp/goxpath/internal/execxp" "github.com/ChrisTrenkamp/goxpath/internal/parser" "github.com/ChrisTrenkamp/goxpath/tree" ) //Opts defines namespace mappings and custom functions for XPath expressions. type Opts struct { NS map[string]string Funcs map[xml.Name]tree.Wrap Vars map[string]tree.Result } //FuncOpts is a function wrapper for Opts. type FuncOpts func(*Opts) //XPathExec is the XPath executor, compiled from an XPath string type XPathExec struct { n *parser.Node } //Parse parses the XPath expression, xp, returning an XPath executor. func Parse(xp string) (XPathExec, error) { n, err := parser.Parse(xp) return XPathExec{n: n}, err } //MustParse is like Parse, but panics instead of returning an error. func MustParse(xp string) XPathExec { ret, err := Parse(xp) if err != nil { panic(err) } return ret } //Exec executes the XPath expression, xp, against the tree, t, with the //namespace mappings, ns, and returns the result as a stringer. func (xp XPathExec) Exec(t tree.Node, opts ...FuncOpts) (tree.Result, error) { o := &Opts{ NS: make(map[string]string), Funcs: make(map[xml.Name]tree.Wrap), Vars: make(map[string]tree.Result), } for _, i := range opts { i(o) } return execxp.Exec(xp.n, t, o.NS, o.Funcs, o.Vars) } //ExecBool is like Exec, except it will attempt to convert the result to its boolean value. func (xp XPathExec) ExecBool(t tree.Node, opts ...FuncOpts) (bool, error) { res, err := xp.Exec(t, opts...) if err != nil { return false, err } b, ok := res.(tree.IsBool) if !ok { return false, fmt.Errorf("Cannot convert result to a boolean") } return bool(b.Bool()), nil } //ExecNum is like Exec, except it will attempt to convert the result to its number value. func (xp XPathExec) ExecNum(t tree.Node, opts ...FuncOpts) (float64, error) { res, err := xp.Exec(t, opts...) if err != nil { return 0, err } n, ok := res.(tree.IsNum) if !ok { return 0, fmt.Errorf("Cannot convert result to a number") } return float64(n.Num()), nil } //ExecNode is like Exec, except it will attempt to return the result as a node-set. func (xp XPathExec) ExecNode(t tree.Node, opts ...FuncOpts) (tree.NodeSet, error) { res, err := xp.Exec(t, opts...) if err != nil { return nil, err } n, ok := res.(tree.NodeSet) if !ok { return nil, fmt.Errorf("Cannot convert result to a node-set") } return n, nil } //MustExec is like Exec, but panics instead of returning an error. func (xp XPathExec) MustExec(t tree.Node, opts ...FuncOpts) tree.Result { res, err := xp.Exec(t, opts...) if err != nil { panic(err) } return res } //ParseExec parses the XPath string, xpstr, and runs Exec. func ParseExec(xpstr string, t tree.Node, opts ...FuncOpts) (tree.Result, error) { xp, err := Parse(xpstr) if err != nil { return nil, err } return xp.Exec(t, opts...) } goxpath-1.0-alpha3/internal/000077500000000000000000000000001273411034600160075ustar00rootroot00000000000000goxpath-1.0-alpha3/internal/execxp/000077500000000000000000000000001273411034600173035ustar00rootroot00000000000000goxpath-1.0-alpha3/internal/execxp/execxp.go000066400000000000000000000011461273411034600211300ustar00rootroot00000000000000package execxp import ( "encoding/xml" "github.com/ChrisTrenkamp/goxpath/internal/parser" "github.com/ChrisTrenkamp/goxpath/tree" ) //Exec executes the XPath expression, xp, against the tree, t, with the //namespace mappings, ns. func Exec(n *parser.Node, t tree.Node, ns map[string]string, fns map[xml.Name]tree.Wrap, v map[string]tree.Result) (tree.Result, error) { f := xpFilt{ t: t, ns: ns, ctx: tree.NodeSet{t}, fns: fns, variables: v, } return exec(&f, n) } func exec(f *xpFilt, n *parser.Node) (tree.Result, error) { err := xfExec(f, n) return f.ctx, err } goxpath-1.0-alpha3/internal/execxp/operators.go000066400000000000000000000100401273411034600216430ustar00rootroot00000000000000package execxp import ( "fmt" "math" "github.com/ChrisTrenkamp/goxpath/tree" ) func bothNodeOperator(left tree.NodeSet, right tree.NodeSet, f *xpFilt, op string) error { var err error for _, l := range left { for _, r := range right { lStr := l.ResValue() rStr := r.ResValue() if eqOps[op] { err = equalsOperator(tree.String(lStr), tree.String(rStr), f, op) if err == nil && f.ctx.String() == tree.True { return nil } } else { err = numberOperator(tree.String(lStr), tree.String(rStr), f, op) if err == nil && f.ctx.String() == tree.True { return nil } } } } f.ctx = tree.Bool(false) return nil } func leftNodeOperator(left tree.NodeSet, right tree.Result, f *xpFilt, op string) error { var err error for _, l := range left { lStr := l.ResValue() if eqOps[op] { err = equalsOperator(tree.String(lStr), right, f, op) if err == nil && f.ctx.String() == tree.True { return nil } } else { err = numberOperator(tree.String(lStr), right, f, op) if err == nil && f.ctx.String() == tree.True { return nil } } } f.ctx = tree.Bool(false) return nil } func rightNodeOperator(left tree.Result, right tree.NodeSet, f *xpFilt, op string) error { var err error for _, r := range right { rStr := r.ResValue() if eqOps[op] { err = equalsOperator(left, tree.String(rStr), f, op) if err == nil && f.ctx.String() == "true" { return nil } } else { err = numberOperator(left, tree.String(rStr), f, op) if err == nil && f.ctx.String() == "true" { return nil } } } f.ctx = tree.Bool(false) return nil } func equalsOperator(left, right tree.Result, f *xpFilt, op string) error { _, lOK := left.(tree.Bool) _, rOK := right.(tree.Bool) if lOK || rOK { lTest, lt := left.(tree.IsBool) rTest, rt := right.(tree.IsBool) if !lt || !rt { return fmt.Errorf("Cannot convert argument to boolean") } if op == "=" { f.ctx = tree.Bool(lTest.Bool() == rTest.Bool()) } else { f.ctx = tree.Bool(lTest.Bool() != rTest.Bool()) } return nil } _, lOK = left.(tree.Num) _, rOK = right.(tree.Num) if lOK || rOK { return numberOperator(left, right, f, op) } lStr := left.String() rStr := right.String() if op == "=" { f.ctx = tree.Bool(lStr == rStr) } else { f.ctx = tree.Bool(lStr != rStr) } return nil } func numberOperator(left, right tree.Result, f *xpFilt, op string) error { lt, lOK := left.(tree.IsNum) rt, rOK := right.(tree.IsNum) if !lOK || !rOK { return fmt.Errorf("Cannot convert data type to number") } ln, rn := lt.Num(), rt.Num() switch op { case "*": f.ctx = ln * rn case "div": if rn != 0 { f.ctx = ln / rn } else { if ln == 0 { f.ctx = tree.Num(math.NaN()) } else { if math.Signbit(float64(ln)) == math.Signbit(float64(rn)) { f.ctx = tree.Num(math.Inf(1)) } else { f.ctx = tree.Num(math.Inf(-1)) } } } case "mod": f.ctx = tree.Num(int(ln) % int(rn)) case "+": f.ctx = ln + rn case "-": f.ctx = ln - rn case "=": f.ctx = tree.Bool(ln == rn) case "!=": f.ctx = tree.Bool(ln != rn) case "<": f.ctx = tree.Bool(ln < rn) case "<=": f.ctx = tree.Bool(ln <= rn) case ">": f.ctx = tree.Bool(ln > rn) case ">=": f.ctx = tree.Bool(ln >= rn) } return nil } func andOrOperator(left, right tree.Result, f *xpFilt, op string) error { lt, lOK := left.(tree.IsBool) rt, rOK := right.(tree.IsBool) if !lOK || !rOK { return fmt.Errorf("Cannot convert argument to boolean") } l, r := lt.Bool(), rt.Bool() if op == "and" { f.ctx = l && r } else { f.ctx = l || r } return nil } func unionOperator(left, right tree.Result, f *xpFilt, op string) error { lNode, lOK := left.(tree.NodeSet) rNode, rOK := right.(tree.NodeSet) if !lOK || !rOK { return fmt.Errorf("Cannot convert data type to node-set") } uniq := make(map[int]tree.Node) for _, i := range lNode { uniq[i.Pos()] = i } for _, i := range rNode { uniq[i.Pos()] = i } res := make(tree.NodeSet, 0, len(uniq)) for _, v := range uniq { res = append(res, v) } f.ctx = res return nil } goxpath-1.0-alpha3/internal/execxp/paths.go000066400000000000000000000171261273411034600207600ustar00rootroot00000000000000package execxp import ( "encoding/xml" "fmt" "strconv" "strings" "github.com/ChrisTrenkamp/goxpath/internal/parser" "github.com/ChrisTrenkamp/goxpath/internal/parser/findutil" "github.com/ChrisTrenkamp/goxpath/internal/parser/intfns" "github.com/ChrisTrenkamp/goxpath/internal/xconst" "github.com/ChrisTrenkamp/goxpath/internal/xsort" "github.com/ChrisTrenkamp/goxpath/internal/lexer" "github.com/ChrisTrenkamp/goxpath/internal/parser/pathexpr" "github.com/ChrisTrenkamp/goxpath/tree" ) type xpFilt struct { t tree.Node ctx tree.Result expr pathexpr.PathExpr ns map[string]string ctxPos int ctxSize int proxPos map[int]int fns map[xml.Name]tree.Wrap variables map[string]tree.Result } type xpExecFn func(*xpFilt, string) var xpFns = map[lexer.XItemType]xpExecFn{ lexer.XItemAbsLocPath: xfAbsLocPath, lexer.XItemAbbrAbsLocPath: xfAbbrAbsLocPath, lexer.XItemRelLocPath: xfRelLocPath, lexer.XItemAbbrRelLocPath: xfAbbrRelLocPath, lexer.XItemAxis: xfAxis, lexer.XItemAbbrAxis: xfAbbrAxis, lexer.XItemNCName: xfNCName, lexer.XItemQName: xfQName, lexer.XItemNodeType: xfNodeType, lexer.XItemProcLit: xfProcInstLit, lexer.XItemStrLit: xfStrLit, lexer.XItemNumLit: xfNumLit, } func xfExec(f *xpFilt, n *parser.Node) (err error) { for n != nil { if fn, ok := xpFns[n.Val.Typ]; ok { fn(f, n.Val.Val) n = n.Left } else if n.Val.Typ == lexer.XItemPredicate { if err = xfPredicate(f, n.Left); err != nil { return } n = n.Right } else if n.Val.Typ == lexer.XItemFunction { if err = xfFunction(f, n); err != nil { return } n = n.Right } else if n.Val.Typ == lexer.XItemOperator { lf := xpFilt{ t: f.t, ns: f.ns, ctx: f.ctx, ctxPos: f.ctxPos, ctxSize: f.ctxSize, proxPos: f.proxPos, fns: f.fns, variables: f.variables, } left, err := exec(&lf, n.Left) if err != nil { return err } rf := xpFilt{ t: f.t, ns: f.ns, ctx: f.ctx, fns: f.fns, variables: f.variables, } right, err := exec(&rf, n.Right) if err != nil { return err } return xfOperator(left, right, f, n.Val.Val) } else if n.Val.Typ == lexer.XItemVariable { if res, ok := f.variables[n.Val.Val]; ok { f.ctx = res return nil } return fmt.Errorf("Invalid variable '%s'", n.Val.Val) } else if string(n.Val.Typ) == "" { n = n.Left //} else { // return fmt.Errorf("Cannot process " + string(n.Val.Typ)) } } return } func xfPredicate(f *xpFilt, n *parser.Node) (err error) { res := f.ctx.(tree.NodeSet) newRes := make(tree.NodeSet, 0, len(res)) for i := range res { pf := xpFilt{ t: f.t, ns: f.ns, ctxPos: i, ctxSize: f.ctxSize, ctx: tree.NodeSet{res[i]}, fns: f.fns, variables: f.variables, } predRes, err := exec(&pf, n) if err != nil { return err } ok, err := checkPredRes(predRes, f, res[i]) if err != nil { return err } if ok { newRes = append(newRes, res[i]) } } f.ctx = newRes return } func checkPredRes(ret tree.Result, f *xpFilt, node tree.Node) (bool, error) { if num, ok := ret.(tree.Num); ok { if float64(f.proxPos[node.Pos()]) == float64(num) { return true, nil } return false, nil } if b, ok := ret.(tree.IsBool); ok { return bool(b.Bool()), nil } return false, fmt.Errorf("Cannot convert argument to boolean") } func xfFunction(f *xpFilt, n *parser.Node) error { spl := strings.Split(n.Val.Val, ":") var name xml.Name if len(spl) == 1 { name.Local = spl[0] } else { name.Space = f.ns[spl[0]] name.Local = spl[1] } fn, ok := intfns.BuiltIn[name] if !ok { fn, ok = f.fns[name] } if ok { args := []tree.Result{} param := n.Left for param != nil { pf := xpFilt{ t: f.t, ctx: f.ctx, ns: f.ns, ctxPos: f.ctxPos, ctxSize: f.ctxSize, fns: f.fns, variables: f.variables, } res, err := exec(&pf, param.Left) if err != nil { return err } args = append(args, res) param = param.Right } filt, err := fn.Call(tree.Ctx{NodeSet: f.ctx.(tree.NodeSet), Size: f.ctxSize, Pos: f.ctxPos + 1}, args...) f.ctx = filt return err } return fmt.Errorf("Unknown function: %s", n.Val.Val) } var eqOps = map[string]bool{ "=": true, "!=": true, } var booleanOps = map[string]bool{ "=": true, "!=": true, "<": true, "<=": true, ">": true, ">=": true, } var numOps = map[string]bool{ "*": true, "div": true, "mod": true, "+": true, "-": true, "=": true, "!=": true, "<": true, "<=": true, ">": true, ">=": true, } var andOrOps = map[string]bool{ "and": true, "or": true, } func xfOperator(left, right tree.Result, f *xpFilt, op string) error { if booleanOps[op] { lNode, lOK := left.(tree.NodeSet) rNode, rOK := right.(tree.NodeSet) if lOK && rOK { return bothNodeOperator(lNode, rNode, f, op) } if lOK { return leftNodeOperator(lNode, right, f, op) } if rOK { return rightNodeOperator(left, rNode, f, op) } if eqOps[op] { return equalsOperator(left, right, f, op) } } if numOps[op] { return numberOperator(left, right, f, op) } if andOrOps[op] { return andOrOperator(left, right, f, op) } //if op == "|" { return unionOperator(left, right, f, op) //} //return fmt.Errorf("Unknown operator " + op) } func xfAbsLocPath(f *xpFilt, val string) { i := f.t for i.GetNodeType() != tree.NtRoot { i = i.GetParent() } f.ctx = tree.NodeSet{i} } func xfAbbrAbsLocPath(f *xpFilt, val string) { i := f.t for i.GetNodeType() != tree.NtRoot { i = i.GetParent() } f.ctx = tree.NodeSet{i} f.expr = abbrPathExpr() find(f) } func xfRelLocPath(f *xpFilt, val string) { } func xfAbbrRelLocPath(f *xpFilt, val string) { f.expr = abbrPathExpr() find(f) } func xfAxis(f *xpFilt, val string) { f.expr.Axis = val } func xfAbbrAxis(f *xpFilt, val string) { f.expr.Axis = xconst.AxisAttribute } func xfNCName(f *xpFilt, val string) { f.expr.Name.Space = val } func xfQName(f *xpFilt, val string) { f.expr.Name.Local = val find(f) } func xfNodeType(f *xpFilt, val string) { f.expr.NodeType = val find(f) } func xfProcInstLit(f *xpFilt, val string) { filt := tree.NodeSet{} for _, i := range f.ctx.(tree.NodeSet) { if i.GetToken().(xml.ProcInst).Target == val { filt = append(filt, i) } } f.ctx = filt } func xfStrLit(f *xpFilt, val string) { f.ctx = tree.String(val) } func xfNumLit(f *xpFilt, val string) { num, _ := strconv.ParseFloat(val, 64) f.ctx = tree.Num(num) } func abbrPathExpr() pathexpr.PathExpr { return pathexpr.PathExpr{ Name: xml.Name{}, Axis: xconst.AxisDescendentOrSelf, NodeType: xconst.NodeTypeNode, } } func find(f *xpFilt) { dupFilt := make(map[int]tree.Node) f.proxPos = make(map[int]int) if f.expr.Axis == "" && f.expr.NodeType == "" && f.expr.Name.Space == "" { if f.expr.Name.Local == "." { f.expr = pathexpr.PathExpr{ Name: xml.Name{}, Axis: xconst.AxisSelf, NodeType: xconst.NodeTypeNode, } } if f.expr.Name.Local == ".." { f.expr = pathexpr.PathExpr{ Name: xml.Name{}, Axis: xconst.AxisParent, NodeType: xconst.NodeTypeNode, } } } f.expr.NS = f.ns for _, i := range f.ctx.(tree.NodeSet) { for pos, j := range findutil.Find(i, f.expr) { dupFilt[j.Pos()] = j f.proxPos[j.Pos()] = pos + 1 } } res := make(tree.NodeSet, 0, len(dupFilt)) for _, i := range dupFilt { res = append(res, i) } xsort.SortNodes(res) f.expr = pathexpr.PathExpr{} f.ctxSize = len(res) f.ctx = res } goxpath-1.0-alpha3/internal/lexer/000077500000000000000000000000001273411034600171265ustar00rootroot00000000000000goxpath-1.0-alpha3/internal/lexer/lexer.go000066400000000000000000000161221273411034600205760ustar00rootroot00000000000000package lexer import ( "fmt" "strings" "unicode" "unicode/utf8" ) const ( //XItemError is an error with the parser input XItemError XItemType = "Error" //XItemAbsLocPath is an absolute path XItemAbsLocPath = "Absolute path" //XItemAbbrAbsLocPath represents an abbreviated absolute path XItemAbbrAbsLocPath = "Abbreviated absolute path" //XItemAbbrRelLocPath marks the start of a path expression XItemAbbrRelLocPath = "Abbreviated relative path" //XItemRelLocPath represents a relative location path XItemRelLocPath = "Relative path" //XItemEndPath marks the end of a path XItemEndPath = "End path instruction" //XItemAxis marks an axis specifier of a path XItemAxis = "Axis" //XItemAbbrAxis marks an abbreviated axis specifier (just @ at this point) XItemAbbrAxis = "Abbreviated attribute axis" //XItemNCName marks a namespace name in a node test XItemNCName = "Namespace" //XItemQName marks the local name in an a node test XItemQName = "Local name" //XItemNodeType marks a node type in a node test XItemNodeType = "Node type" //XItemProcLit marks a processing-instruction literal XItemProcLit = "processing-instruction" //XItemFunction marks a function call XItemFunction = "function" //XItemArgument marks a function argument XItemArgument = "function argument" //XItemEndFunction marks the end of a function XItemEndFunction = "end of function" //XItemPredicate marks a predicate in an axis XItemPredicate = "predicate" //XItemEndPredicate marks a predicate in an axis XItemEndPredicate = "end of predicate" //XItemStrLit marks a string literal XItemStrLit = "string literal" //XItemNumLit marks a numeric literal XItemNumLit = "numeric literal" //XItemOperator marks an operator XItemOperator = "operator" //XItemVariable marks a variable reference XItemVariable = "variable" ) const ( eof = -(iota + 1) ) //XItemType is the parser token types type XItemType string //XItem is the token emitted from the parser type XItem struct { Typ XItemType Val string } type stateFn func(*Lexer) stateFn //Lexer lexes out XPath expressions type Lexer struct { input string start int pos int width int items chan XItem } //Lex an XPath expresion on the io.Reader func Lex(xpath string) chan XItem { l := &Lexer{ input: xpath, items: make(chan XItem), } go l.run() return l.items } func (l *Lexer) run() { for state := startState; state != nil; { state = state(l) } if l.peek() != eof { l.errorf("Malformed XPath expression") } close(l.items) } func (l *Lexer) emit(t XItemType) { l.items <- XItem{t, l.input[l.start:l.pos]} l.start = l.pos } func (l *Lexer) emitVal(t XItemType, val string) { l.items <- XItem{t, val} l.start = l.pos } func (l *Lexer) next() (r rune) { if l.pos >= len(l.input) { l.width = 0 return eof } r, l.width = utf8.DecodeRuneInString(l.input[l.pos:]) l.pos += l.width return r } func (l *Lexer) ignore() { l.start = l.pos } func (l *Lexer) backup() { l.pos -= l.width } func (l *Lexer) peek() rune { r := l.next() l.backup() return r } func (l *Lexer) peekAt(n int) rune { if n <= 1 { return l.peek() } width := 0 var ret rune for count := 0; count < n; count++ { r, s := utf8.DecodeRuneInString(l.input[l.pos+width:]) width += s if l.pos+width > len(l.input) { return eof } ret = r } return ret } func (l *Lexer) accept(valid string) bool { if strings.ContainsRune(valid, l.next()) { return true } l.backup() return false } func (l *Lexer) acceptRun(valid string) { for strings.ContainsRune(valid, l.next()) { } l.backup() } func (l *Lexer) skip(num int) { for i := 0; i < num; i++ { l.next() } l.ignore() } func (l *Lexer) skipWS(ig bool) { for { n := l.next() if n == eof || !unicode.IsSpace(n) { break } } l.backup() if ig { l.ignore() } } func (l *Lexer) errorf(format string, args ...interface{}) stateFn { l.items <- XItem{ XItemError, fmt.Sprintf(format, args...), } return nil } func isElemChar(r rune) bool { return string(r) != ":" && string(r) != "/" && (unicode.Is(first, r) || unicode.Is(second, r) || string(r) == "*") && r != eof } func startState(l *Lexer) stateFn { l.skipWS(true) if string(l.peek()) == "/" { l.next() l.ignore() if string(l.next()) == "/" { l.ignore() return abbrAbsLocPathState } l.backup() return absLocPathState } else if string(l.peek()) == `'` || string(l.peek()) == `"` { if err := getStrLit(l, XItemStrLit); err != nil { return l.errorf(err.Error()) } if l.peek() != eof { return startState } } else if getNumLit(l) { l.skipWS(true) if l.peek() != eof { return startState } } else if string(l.peek()) == "$" { l.next() l.ignore() r := l.peek() for unicode.Is(first, r) || unicode.Is(second, r) { l.next() r = l.peek() } tok := l.input[l.start:l.pos] if len(tok) == 0 { return l.errorf("Empty variable name") } l.emit(XItemVariable) l.skipWS(true) if l.peek() != eof { return startState } } else if st := findOperatorState(l); st != nil { return st } else { if isElemChar(l.peek()) { colons := 0 for { if isElemChar(l.peek()) { l.next() } else if string(l.peek()) == ":" { l.next() colons++ } else { break } } if string(l.peek()) == "(" && colons <= 1 { tok := l.input[l.start:l.pos] err := procFunc(l, tok) if err != nil { return l.errorf(err.Error()) } return startState } l.pos = l.start return relLocPathState } else if string(l.peek()) == "@" { return relLocPathState } } return nil } func strPeek(str string, l *Lexer) bool { for i := 0; i < len(str); i++ { if string(l.peekAt(i+1)) != string(str[i]) { return false } } return true } func findOperatorState(l *Lexer) stateFn { l.skipWS(true) switch string(l.peek()) { case ">", "<", "!": l.next() if string(l.peek()) == "=" { l.next() } l.emit(XItemOperator) return startState case "|", "+", "-", "*", "=": l.next() l.emit(XItemOperator) return startState case "(": l.next() l.emit(XItemOperator) for state := startState; state != nil; { state = state(l) } l.skipWS(true) if string(l.next()) != ")" { return l.errorf("Missing end )") } l.emit(XItemOperator) return startState } if strPeek("and", l) { l.next() l.next() l.next() l.emit(XItemOperator) return startState } if strPeek("or", l) { l.next() l.next() l.emit(XItemOperator) return startState } if strPeek("mod", l) { l.next() l.next() l.next() l.emit(XItemOperator) return startState } if strPeek("div", l) { l.next() l.next() l.next() l.emit(XItemOperator) return startState } return nil } func getStrLit(l *Lexer, tok XItemType) error { q := l.next() var r rune l.ignore() for r != q { r = l.next() if r == eof { return fmt.Errorf("Unexpected end of string literal.") } } l.backup() l.emit(tok) l.next() l.ignore() return nil } func getNumLit(l *Lexer) bool { const dig = "0123456789" l.accept("-") start := l.pos l.acceptRun(dig) if l.pos == start { return false } if l.accept(".") { l.acceptRun(dig) } l.emit(XItemNumLit) return true } goxpath-1.0-alpha3/internal/lexer/paths.go000066400000000000000000000101471273411034600205770ustar00rootroot00000000000000package lexer import ( "fmt" "github.com/ChrisTrenkamp/goxpath/internal/xconst" ) func absLocPathState(l *Lexer) stateFn { l.emit(XItemAbsLocPath) return stepState } func abbrAbsLocPathState(l *Lexer) stateFn { l.emit(XItemAbbrAbsLocPath) return stepState } func relLocPathState(l *Lexer) stateFn { l.emit(XItemRelLocPath) return stepState } func abbrRelLocPathState(l *Lexer) stateFn { l.emit(XItemAbbrRelLocPath) return stepState } func stepState(l *Lexer) stateFn { l.skipWS(true) r := l.next() for isElemChar(r) { r = l.next() } l.backup() tok := l.input[l.start:l.pos] state, err := parseSeparators(l, tok) if err != nil { return l.errorf(err.Error()) } return getNextPathState(l, state) } func parseSeparators(l *Lexer, tok string) (XItemType, error) { l.skipWS(false) state := XItemType(XItemQName) r := l.peek() if string(r) == ":" && string(l.peekAt(2)) == ":" { var err error if state, err = getAxis(l, tok); err != nil { return state, fmt.Errorf(err.Error()) } } else if string(r) == ":" { state = XItemNCName l.emitVal(state, tok) l.skip(1) l.skipWS(true) } else if string(r) == "@" { state = XItemAbbrAxis l.emitVal(state, tok) l.skip(1) l.skipWS(true) } else if string(r) == "(" { var err error if state, err = getNT(l, tok); err != nil { return state, fmt.Errorf(err.Error()) } } else if len(tok) > 0 { l.emitVal(state, tok) } return state, nil } func getAxis(l *Lexer, tok string) (XItemType, error) { var state XItemType for i := range xconst.AxisNames { if tok == xconst.AxisNames[i] { state = XItemAxis } } if state != XItemAxis { return state, fmt.Errorf("Invalid Axis specifier, %s", tok) } l.emitVal(state, tok) l.skip(2) l.skipWS(true) return state, nil } func getNT(l *Lexer, tok string) (XItemType, error) { isNT := false for _, i := range xconst.NodeTypes { if tok == i { isNT = true break } } if isNT { return procNT(l, tok) } return XItemError, fmt.Errorf("Invalid node-type " + tok) } func procNT(l *Lexer, tok string) (XItemType, error) { state := XItemType(XItemNodeType) l.emitVal(state, tok) l.skip(1) l.skipWS(true) n := l.peek() if tok == xconst.NodeTypeProcInst && (string(n) == `"` || string(n) == `'`) { if err := getStrLit(l, XItemProcLit); err != nil { return state, fmt.Errorf(err.Error()) } l.skipWS(true) n = l.next() } if string(n) != ")" { return state, fmt.Errorf("Missing ) at end of NodeType declaration.") } l.skip(1) return state, nil } func procFunc(l *Lexer, tok string) error { state := XItemType(XItemFunction) l.emitVal(state, tok) l.skip(1) l.skipWS(true) if string(l.peek()) != ")" { l.emit(XItemArgument) for { for state := startState; state != nil; { state = state(l) } l.skipWS(true) if string(l.peek()) == "," { l.emit(XItemArgument) l.skip(1) } else if string(l.peek()) == ")" { l.emit(XItemEndFunction) l.skip(1) break } else if l.peek() == eof { return fmt.Errorf("Missing ) at end of function declaration.") } } } else { l.emit(XItemEndFunction) l.skip(1) } return nil } func getNextPathState(l *Lexer, state XItemType) stateFn { isMultiPart := state == XItemAxis || state == XItemAbbrAxis || state == XItemNCName l.skipWS(true) if string(l.peek()) == "[" { if err := getPred(l); err != nil { return l.errorf(err.Error()) } } if string(l.peek()) == "/" && !isMultiPart { l.skip(1) if string(l.peek()) == "/" { l.skip(1) return abbrRelLocPathState } l.skipWS(true) return relLocPathState } else if isMultiPart && isElemChar(l.peek()) { return stepState } if isMultiPart { return l.errorf("Step is not complete") } l.emit(XItemEndPath) return findOperatorState } func getPred(l *Lexer) error { l.emit(XItemPredicate) l.skip(1) l.skipWS(true) if string(l.peek()) == "]" { return fmt.Errorf("Missing content in predicate.") } for state := startState; state != nil; { state = state(l) } l.skipWS(true) if string(l.peek()) != "]" { return fmt.Errorf("Missing ] at end of predicate.") } l.skip(1) l.emit(XItemEndPredicate) l.skipWS(true) return nil } goxpath-1.0-alpha3/internal/lexer/xmlchars.go000066400000000000000000000161321273411034600213010ustar00rootroot00000000000000package lexer import "unicode" //first and second was copied from src/encoding/xml/xml.go var first = &unicode.RangeTable{ R16: []unicode.Range16{ {0x003A, 0x003A, 1}, {0x0041, 0x005A, 1}, {0x005F, 0x005F, 1}, {0x0061, 0x007A, 1}, {0x00C0, 0x00D6, 1}, {0x00D8, 0x00F6, 1}, {0x00F8, 0x00FF, 1}, {0x0100, 0x0131, 1}, {0x0134, 0x013E, 1}, {0x0141, 0x0148, 1}, {0x014A, 0x017E, 1}, {0x0180, 0x01C3, 1}, {0x01CD, 0x01F0, 1}, {0x01F4, 0x01F5, 1}, {0x01FA, 0x0217, 1}, {0x0250, 0x02A8, 1}, {0x02BB, 0x02C1, 1}, {0x0386, 0x0386, 1}, {0x0388, 0x038A, 1}, {0x038C, 0x038C, 1}, {0x038E, 0x03A1, 1}, {0x03A3, 0x03CE, 1}, {0x03D0, 0x03D6, 1}, {0x03DA, 0x03E0, 2}, {0x03E2, 0x03F3, 1}, {0x0401, 0x040C, 1}, {0x040E, 0x044F, 1}, {0x0451, 0x045C, 1}, {0x045E, 0x0481, 1}, {0x0490, 0x04C4, 1}, {0x04C7, 0x04C8, 1}, {0x04CB, 0x04CC, 1}, {0x04D0, 0x04EB, 1}, {0x04EE, 0x04F5, 1}, {0x04F8, 0x04F9, 1}, {0x0531, 0x0556, 1}, {0x0559, 0x0559, 1}, {0x0561, 0x0586, 1}, {0x05D0, 0x05EA, 1}, {0x05F0, 0x05F2, 1}, {0x0621, 0x063A, 1}, {0x0641, 0x064A, 1}, {0x0671, 0x06B7, 1}, {0x06BA, 0x06BE, 1}, {0x06C0, 0x06CE, 1}, {0x06D0, 0x06D3, 1}, {0x06D5, 0x06D5, 1}, {0x06E5, 0x06E6, 1}, {0x0905, 0x0939, 1}, {0x093D, 0x093D, 1}, {0x0958, 0x0961, 1}, {0x0985, 0x098C, 1}, {0x098F, 0x0990, 1}, {0x0993, 0x09A8, 1}, {0x09AA, 0x09B0, 1}, {0x09B2, 0x09B2, 1}, {0x09B6, 0x09B9, 1}, {0x09DC, 0x09DD, 1}, {0x09DF, 0x09E1, 1}, {0x09F0, 0x09F1, 1}, {0x0A05, 0x0A0A, 1}, {0x0A0F, 0x0A10, 1}, {0x0A13, 0x0A28, 1}, {0x0A2A, 0x0A30, 1}, {0x0A32, 0x0A33, 1}, {0x0A35, 0x0A36, 1}, {0x0A38, 0x0A39, 1}, {0x0A59, 0x0A5C, 1}, {0x0A5E, 0x0A5E, 1}, {0x0A72, 0x0A74, 1}, {0x0A85, 0x0A8B, 1}, {0x0A8D, 0x0A8D, 1}, {0x0A8F, 0x0A91, 1}, {0x0A93, 0x0AA8, 1}, {0x0AAA, 0x0AB0, 1}, {0x0AB2, 0x0AB3, 1}, {0x0AB5, 0x0AB9, 1}, {0x0ABD, 0x0AE0, 0x23}, {0x0B05, 0x0B0C, 1}, {0x0B0F, 0x0B10, 1}, {0x0B13, 0x0B28, 1}, {0x0B2A, 0x0B30, 1}, {0x0B32, 0x0B33, 1}, {0x0B36, 0x0B39, 1}, {0x0B3D, 0x0B3D, 1}, {0x0B5C, 0x0B5D, 1}, {0x0B5F, 0x0B61, 1}, {0x0B85, 0x0B8A, 1}, {0x0B8E, 0x0B90, 1}, {0x0B92, 0x0B95, 1}, {0x0B99, 0x0B9A, 1}, {0x0B9C, 0x0B9C, 1}, {0x0B9E, 0x0B9F, 1}, {0x0BA3, 0x0BA4, 1}, {0x0BA8, 0x0BAA, 1}, {0x0BAE, 0x0BB5, 1}, {0x0BB7, 0x0BB9, 1}, {0x0C05, 0x0C0C, 1}, {0x0C0E, 0x0C10, 1}, {0x0C12, 0x0C28, 1}, {0x0C2A, 0x0C33, 1}, {0x0C35, 0x0C39, 1}, {0x0C60, 0x0C61, 1}, {0x0C85, 0x0C8C, 1}, {0x0C8E, 0x0C90, 1}, {0x0C92, 0x0CA8, 1}, {0x0CAA, 0x0CB3, 1}, {0x0CB5, 0x0CB9, 1}, {0x0CDE, 0x0CDE, 1}, {0x0CE0, 0x0CE1, 1}, {0x0D05, 0x0D0C, 1}, {0x0D0E, 0x0D10, 1}, {0x0D12, 0x0D28, 1}, {0x0D2A, 0x0D39, 1}, {0x0D60, 0x0D61, 1}, {0x0E01, 0x0E2E, 1}, {0x0E30, 0x0E30, 1}, {0x0E32, 0x0E33, 1}, {0x0E40, 0x0E45, 1}, {0x0E81, 0x0E82, 1}, {0x0E84, 0x0E84, 1}, {0x0E87, 0x0E88, 1}, {0x0E8A, 0x0E8D, 3}, {0x0E94, 0x0E97, 1}, {0x0E99, 0x0E9F, 1}, {0x0EA1, 0x0EA3, 1}, {0x0EA5, 0x0EA7, 2}, {0x0EAA, 0x0EAB, 1}, {0x0EAD, 0x0EAE, 1}, {0x0EB0, 0x0EB0, 1}, {0x0EB2, 0x0EB3, 1}, {0x0EBD, 0x0EBD, 1}, {0x0EC0, 0x0EC4, 1}, {0x0F40, 0x0F47, 1}, {0x0F49, 0x0F69, 1}, {0x10A0, 0x10C5, 1}, {0x10D0, 0x10F6, 1}, {0x1100, 0x1100, 1}, {0x1102, 0x1103, 1}, {0x1105, 0x1107, 1}, {0x1109, 0x1109, 1}, {0x110B, 0x110C, 1}, {0x110E, 0x1112, 1}, {0x113C, 0x1140, 2}, {0x114C, 0x1150, 2}, {0x1154, 0x1155, 1}, {0x1159, 0x1159, 1}, {0x115F, 0x1161, 1}, {0x1163, 0x1169, 2}, {0x116D, 0x116E, 1}, {0x1172, 0x1173, 1}, {0x1175, 0x119E, 0x119E - 0x1175}, {0x11A8, 0x11AB, 0x11AB - 0x11A8}, {0x11AE, 0x11AF, 1}, {0x11B7, 0x11B8, 1}, {0x11BA, 0x11BA, 1}, {0x11BC, 0x11C2, 1}, {0x11EB, 0x11F0, 0x11F0 - 0x11EB}, {0x11F9, 0x11F9, 1}, {0x1E00, 0x1E9B, 1}, {0x1EA0, 0x1EF9, 1}, {0x1F00, 0x1F15, 1}, {0x1F18, 0x1F1D, 1}, {0x1F20, 0x1F45, 1}, {0x1F48, 0x1F4D, 1}, {0x1F50, 0x1F57, 1}, {0x1F59, 0x1F5B, 0x1F5B - 0x1F59}, {0x1F5D, 0x1F5D, 1}, {0x1F5F, 0x1F7D, 1}, {0x1F80, 0x1FB4, 1}, {0x1FB6, 0x1FBC, 1}, {0x1FBE, 0x1FBE, 1}, {0x1FC2, 0x1FC4, 1}, {0x1FC6, 0x1FCC, 1}, {0x1FD0, 0x1FD3, 1}, {0x1FD6, 0x1FDB, 1}, {0x1FE0, 0x1FEC, 1}, {0x1FF2, 0x1FF4, 1}, {0x1FF6, 0x1FFC, 1}, {0x2126, 0x2126, 1}, {0x212A, 0x212B, 1}, {0x212E, 0x212E, 1}, {0x2180, 0x2182, 1}, {0x3007, 0x3007, 1}, {0x3021, 0x3029, 1}, {0x3041, 0x3094, 1}, {0x30A1, 0x30FA, 1}, {0x3105, 0x312C, 1}, {0x4E00, 0x9FA5, 1}, {0xAC00, 0xD7A3, 1}, }, } var second = &unicode.RangeTable{ R16: []unicode.Range16{ {0x002D, 0x002E, 1}, {0x0030, 0x0039, 1}, {0x00B7, 0x00B7, 1}, {0x02D0, 0x02D1, 1}, {0x0300, 0x0345, 1}, {0x0360, 0x0361, 1}, {0x0387, 0x0387, 1}, {0x0483, 0x0486, 1}, {0x0591, 0x05A1, 1}, {0x05A3, 0x05B9, 1}, {0x05BB, 0x05BD, 1}, {0x05BF, 0x05BF, 1}, {0x05C1, 0x05C2, 1}, {0x05C4, 0x0640, 0x0640 - 0x05C4}, {0x064B, 0x0652, 1}, {0x0660, 0x0669, 1}, {0x0670, 0x0670, 1}, {0x06D6, 0x06DC, 1}, {0x06DD, 0x06DF, 1}, {0x06E0, 0x06E4, 1}, {0x06E7, 0x06E8, 1}, {0x06EA, 0x06ED, 1}, {0x06F0, 0x06F9, 1}, {0x0901, 0x0903, 1}, {0x093C, 0x093C, 1}, {0x093E, 0x094C, 1}, {0x094D, 0x094D, 1}, {0x0951, 0x0954, 1}, {0x0962, 0x0963, 1}, {0x0966, 0x096F, 1}, {0x0981, 0x0983, 1}, {0x09BC, 0x09BC, 1}, {0x09BE, 0x09BF, 1}, {0x09C0, 0x09C4, 1}, {0x09C7, 0x09C8, 1}, {0x09CB, 0x09CD, 1}, {0x09D7, 0x09D7, 1}, {0x09E2, 0x09E3, 1}, {0x09E6, 0x09EF, 1}, {0x0A02, 0x0A3C, 0x3A}, {0x0A3E, 0x0A3F, 1}, {0x0A40, 0x0A42, 1}, {0x0A47, 0x0A48, 1}, {0x0A4B, 0x0A4D, 1}, {0x0A66, 0x0A6F, 1}, {0x0A70, 0x0A71, 1}, {0x0A81, 0x0A83, 1}, {0x0ABC, 0x0ABC, 1}, {0x0ABE, 0x0AC5, 1}, {0x0AC7, 0x0AC9, 1}, {0x0ACB, 0x0ACD, 1}, {0x0AE6, 0x0AEF, 1}, {0x0B01, 0x0B03, 1}, {0x0B3C, 0x0B3C, 1}, {0x0B3E, 0x0B43, 1}, {0x0B47, 0x0B48, 1}, {0x0B4B, 0x0B4D, 1}, {0x0B56, 0x0B57, 1}, {0x0B66, 0x0B6F, 1}, {0x0B82, 0x0B83, 1}, {0x0BBE, 0x0BC2, 1}, {0x0BC6, 0x0BC8, 1}, {0x0BCA, 0x0BCD, 1}, {0x0BD7, 0x0BD7, 1}, {0x0BE7, 0x0BEF, 1}, {0x0C01, 0x0C03, 1}, {0x0C3E, 0x0C44, 1}, {0x0C46, 0x0C48, 1}, {0x0C4A, 0x0C4D, 1}, {0x0C55, 0x0C56, 1}, {0x0C66, 0x0C6F, 1}, {0x0C82, 0x0C83, 1}, {0x0CBE, 0x0CC4, 1}, {0x0CC6, 0x0CC8, 1}, {0x0CCA, 0x0CCD, 1}, {0x0CD5, 0x0CD6, 1}, {0x0CE6, 0x0CEF, 1}, {0x0D02, 0x0D03, 1}, {0x0D3E, 0x0D43, 1}, {0x0D46, 0x0D48, 1}, {0x0D4A, 0x0D4D, 1}, {0x0D57, 0x0D57, 1}, {0x0D66, 0x0D6F, 1}, {0x0E31, 0x0E31, 1}, {0x0E34, 0x0E3A, 1}, {0x0E46, 0x0E46, 1}, {0x0E47, 0x0E4E, 1}, {0x0E50, 0x0E59, 1}, {0x0EB1, 0x0EB1, 1}, {0x0EB4, 0x0EB9, 1}, {0x0EBB, 0x0EBC, 1}, {0x0EC6, 0x0EC6, 1}, {0x0EC8, 0x0ECD, 1}, {0x0ED0, 0x0ED9, 1}, {0x0F18, 0x0F19, 1}, {0x0F20, 0x0F29, 1}, {0x0F35, 0x0F39, 2}, {0x0F3E, 0x0F3F, 1}, {0x0F71, 0x0F84, 1}, {0x0F86, 0x0F8B, 1}, {0x0F90, 0x0F95, 1}, {0x0F97, 0x0F97, 1}, {0x0F99, 0x0FAD, 1}, {0x0FB1, 0x0FB7, 1}, {0x0FB9, 0x0FB9, 1}, {0x20D0, 0x20DC, 1}, {0x20E1, 0x3005, 0x3005 - 0x20E1}, {0x302A, 0x302F, 1}, {0x3031, 0x3035, 1}, {0x3099, 0x309A, 1}, {0x309D, 0x309E, 1}, {0x30FC, 0x30FE, 1}, }, } goxpath-1.0-alpha3/internal/parser/000077500000000000000000000000001273411034600173035ustar00rootroot00000000000000goxpath-1.0-alpha3/internal/parser/ast.go000066400000000000000000000042551273411034600204270ustar00rootroot00000000000000package parser import "github.com/ChrisTrenkamp/goxpath/internal/lexer" //NodeType enumerations const ( Empty lexer.XItemType = "" ) //Node builds an AST tree for operating on XPath expressions type Node struct { Val lexer.XItem Left *Node Right *Node Parent *Node next *Node } var beginPathType = map[lexer.XItemType]bool{ lexer.XItemAbsLocPath: true, lexer.XItemAbbrAbsLocPath: true, lexer.XItemAbbrRelLocPath: true, lexer.XItemRelLocPath: true, lexer.XItemFunction: true, } func (n *Node) add(i lexer.XItem) { if n.Val.Typ == Empty { n.Val = i } else if n.Left == nil { n.Left = &Node{Val: n.Val, Parent: n} n.Val = i } else if beginPathType[n.Val.Typ] { next := &Node{Val: n.Val, Left: n.Left, Parent: n} n.Left = next n.Val = i } else if n.Right == nil { n.Right = &Node{Val: i, Parent: n} } else { next := &Node{Val: n.Val, Left: n.Left, Right: n.Right, Parent: n} n.Left, n.Right = next, nil n.Val = i } n.next = n } func (n *Node) push(i lexer.XItem) { if n.Left == nil { n.Left = &Node{Val: i, Parent: n} n.next = n.Left } else if n.Right == nil { n.Right = &Node{Val: i, Parent: n} n.next = n.Right } else { next := &Node{Val: i, Left: n.Right, Parent: n} n.Right = next n.next = n.Right } } func (n *Node) pushNotEmpty(i lexer.XItem) { if n.Val.Typ == Empty { n.add(i) } else { n.push(i) } } /* func (n *Node) prettyPrint(depth, width int) { nodes := []*Node{} n.getLine(depth, &nodes) fmt.Printf("%*s", (width-depth)*2, "") toggle := true if len(nodes) > 1 { for _, i := range nodes { if i != nil { if toggle { fmt.Print("/ ") } else { fmt.Print("\\ ") } } toggle = !toggle } fmt.Println() fmt.Printf("%*s", (width-depth)*2, "") } for _, i := range nodes { if i != nil { fmt.Print(i.Val.Val, " ") } } fmt.Println() } func (n *Node) getLine(depth int, ret *[]*Node) { if depth <= 0 && n != nil { *ret = append(*ret, n) return } if n.Left != nil { n.Left.getLine(depth-1, ret) } else if depth-1 <= 0 { *ret = append(*ret, nil) } if n.Right != nil { n.Right.getLine(depth-1, ret) } else if depth-1 <= 0 { *ret = append(*ret, nil) } } */ goxpath-1.0-alpha3/internal/parser/findutil/000077500000000000000000000000001273411034600211215ustar00rootroot00000000000000goxpath-1.0-alpha3/internal/parser/findutil/findUtil.go000066400000000000000000000140531273411034600232310ustar00rootroot00000000000000package findutil import ( "encoding/xml" "github.com/ChrisTrenkamp/goxpath/internal/parser/pathexpr" "github.com/ChrisTrenkamp/goxpath/internal/xconst" "github.com/ChrisTrenkamp/goxpath/tree" ) const ( wildcard = "*" ) type findFunc func(tree.Node, *pathexpr.PathExpr, *[]tree.Node) var findMap = map[string]findFunc{ xconst.AxisAncestor: findAncestor, xconst.AxisAncestorOrSelf: findAncestorOrSelf, xconst.AxisAttribute: findAttribute, xconst.AxisChild: findChild, xconst.AxisDescendent: findDescendent, xconst.AxisDescendentOrSelf: findDescendentOrSelf, xconst.AxisFollowing: findFollowing, xconst.AxisFollowingSibling: findFollowingSibling, xconst.AxisNamespace: findNamespace, xconst.AxisParent: findParent, xconst.AxisPreceding: findPreceding, xconst.AxisPrecedingSibling: findPrecedingSibling, xconst.AxisSelf: findSelf, } //Find finds nodes based on the pathexpr.PathExpr func Find(x tree.Node, p pathexpr.PathExpr) []tree.Node { ret := []tree.Node{} if p.Axis == "" { findChild(x, &p, &ret) return ret } f := findMap[p.Axis] f(x, &p, &ret) return ret } func findAncestor(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if x.GetNodeType() == tree.NtRoot { return } addNode(x.GetParent(), p, ret) findAncestor(x.GetParent(), p, ret) } func findAncestorOrSelf(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { findSelf(x, p, ret) findAncestor(x, p, ret) } func findAttribute(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if ele, ok := x.(tree.Elem); ok { for _, i := range ele.GetAttrs() { addNode(i, p, ret) } } } func findChild(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if ele, ok := x.(tree.Elem); ok { ch := ele.GetChildren() for i := range ch { addNode(ch[i], p, ret) } } } func findDescendent(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if ele, ok := x.(tree.Elem); ok { ch := ele.GetChildren() for i := range ch { addNode(ch[i], p, ret) findDescendent(ch[i], p, ret) } } } func findDescendentOrSelf(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { findSelf(x, p, ret) findDescendent(x, p, ret) } func findFollowing(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if x.GetNodeType() == tree.NtRoot { return } par := x.GetParent() ch := par.GetChildren() i := 0 for x != ch[i] { i++ } i++ for i < len(ch) { findDescendentOrSelf(ch[i], p, ret) i++ } findFollowing(par, p, ret) } func findFollowingSibling(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if x.GetNodeType() == tree.NtRoot { return } par := x.GetParent() ch := par.GetChildren() i := 0 for x != ch[i] { i++ } i++ for i < len(ch) { findSelf(ch[i], p, ret) i++ } } func findNamespace(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if ele, ok := x.(tree.NSElem); ok { ns := tree.BuildNS(ele) for _, i := range ns { addNode(i, p, ret) } } } func findParent(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if x.GetNodeType() != tree.NtRoot { addNode(x.GetParent(), p, ret) } } func findPreceding(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if x.GetNodeType() == tree.NtRoot { return } par := x.GetParent() ch := par.GetChildren() i := len(ch) - 1 for x != ch[i] { i-- } i-- for i >= 0 { findDescendentOrSelf(ch[i], p, ret) i-- } findPreceding(par, p, ret) } func findPrecedingSibling(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { if x.GetNodeType() == tree.NtRoot { return } par := x.GetParent() ch := par.GetChildren() i := len(ch) - 1 for x != ch[i] { i-- } i-- for i >= 0 { findSelf(ch[i], p, ret) i-- } } func findSelf(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { addNode(x, p, ret) } func addNode(x tree.Node, p *pathexpr.PathExpr, ret *[]tree.Node) { add := false tok := x.GetToken() switch x.GetNodeType() { case tree.NtAttr: add = evalAttr(p, tok.(xml.Attr)) case tree.NtChd: add = evalChd(p) case tree.NtComm: add = evalComm(p) case tree.NtElem, tree.NtRoot: add = evalEle(p, tok.(xml.StartElement)) case tree.NtNs: add = evalNS(p, tok.(xml.Attr)) case tree.NtPi: add = evalPI(p) } if add { *ret = append(*ret, x) } } func evalAttr(p *pathexpr.PathExpr, a xml.Attr) bool { if p.NodeType == "" { if p.Name.Space != wildcard { if a.Name.Space != p.NS[p.Name.Space] { return false } } if p.Name.Local == wildcard && p.Axis == xconst.AxisAttribute { return true } if p.Name.Local == a.Name.Local { return true } } else { if p.NodeType == xconst.NodeTypeNode { return true } } return false } func evalChd(p *pathexpr.PathExpr) bool { if p.NodeType == xconst.NodeTypeText || p.NodeType == xconst.NodeTypeNode { return true } return false } func evalComm(p *pathexpr.PathExpr) bool { if p.NodeType == xconst.NodeTypeComment || p.NodeType == xconst.NodeTypeNode { return true } return false } func evalEle(p *pathexpr.PathExpr, ele xml.StartElement) bool { if p.NodeType == "" { return checkNameAndSpace(p, ele) } if p.NodeType == xconst.NodeTypeNode { return true } return false } func checkNameAndSpace(p *pathexpr.PathExpr, ele xml.StartElement) bool { if p.Name.Local == wildcard && p.Name.Space == "" { return true } if p.Name.Space != wildcard && ele.Name.Space != p.NS[p.Name.Space] { return false } if p.Name.Local == wildcard && p.Axis != xconst.AxisAttribute && p.Axis != xconst.AxisNamespace { return true } if p.Name.Local == ele.Name.Local { return true } return false } func evalNS(p *pathexpr.PathExpr, ns xml.Attr) bool { if p.NodeType == "" { if p.Name.Space != "" && p.Name.Space != wildcard { return false } if p.Name.Local == wildcard && p.Axis == xconst.AxisNamespace { return true } if p.Name.Local == ns.Name.Local { return true } } else { if p.NodeType == xconst.NodeTypeNode { return true } } return false } func evalPI(p *pathexpr.PathExpr) bool { if p.NodeType == xconst.NodeTypeProcInst || p.NodeType == xconst.NodeTypeNode { return true } return false } goxpath-1.0-alpha3/internal/parser/intfns/000077500000000000000000000000001273411034600206045ustar00rootroot00000000000000goxpath-1.0-alpha3/internal/parser/intfns/boolfns.go000066400000000000000000000033101273411034600225720ustar00rootroot00000000000000package intfns import ( "fmt" "github.com/ChrisTrenkamp/goxpath/tree" "golang.org/x/text/language" ) func boolean(c tree.Ctx, args ...tree.Result) (tree.Result, error) { if b, ok := args[0].(tree.IsBool); ok { return b.Bool(), nil } return nil, fmt.Errorf("Cannot convert object to a boolean") } func not(c tree.Ctx, args ...tree.Result) (tree.Result, error) { b, ok := args[0].(tree.IsBool) if !ok { return nil, fmt.Errorf("Cannot convert object to a boolean") } return !b.Bool(), nil } func _true(c tree.Ctx, args ...tree.Result) (tree.Result, error) { return tree.Bool(true), nil } func _false(c tree.Ctx, args ...tree.Result) (tree.Result, error) { return tree.Bool(false), nil } func lang(c tree.Ctx, args ...tree.Result) (tree.Result, error) { lStr := args[0].String() var n tree.Elem for _, i := range c.NodeSet { if i.GetNodeType() == tree.NtElem { n = i.(tree.Elem) } else { n = i.GetParent() } for n.GetNodeType() != tree.NtRoot { if attr, ok := tree.GetAttribute(n, "lang", tree.XMLSpace); ok { return checkLang(lStr, attr.Value), nil } n = n.GetParent() } } return tree.Bool(false), nil } func checkLang(srcStr, targStr string) tree.Bool { srcLang := language.Make(srcStr) srcRegion, srcRegionConf := srcLang.Region() targLang := language.Make(targStr) targRegion, targRegionConf := targLang.Region() if srcRegionConf == language.Exact && targRegionConf != language.Exact { return tree.Bool(false) } if srcRegion != targRegion && srcRegionConf == language.Exact && targRegionConf == language.Exact { return tree.Bool(false) } _, _, conf := language.NewMatcher([]language.Tag{srcLang}).Match(targLang) return tree.Bool(conf >= language.High) } goxpath-1.0-alpha3/internal/parser/intfns/intfns.go000066400000000000000000000034151273411034600224370ustar00rootroot00000000000000package intfns import ( "encoding/xml" "github.com/ChrisTrenkamp/goxpath/tree" ) //BuiltIn contains the list of built-in XPath functions var BuiltIn = map[xml.Name]tree.Wrap{ //String functions {Local: "string"}: {Fn: _string, NArgs: 1, LastArgOpt: tree.Optional}, {Local: "concat"}: {Fn: concat, NArgs: 3, LastArgOpt: tree.Variadic}, {Local: "starts-with"}: {Fn: startsWith, NArgs: 2}, {Local: "contains"}: {Fn: contains, NArgs: 2}, {Local: "substring-before"}: {Fn: substringBefore, NArgs: 2}, {Local: "substring-after"}: {Fn: substringAfter, NArgs: 2}, {Local: "substring"}: {Fn: substring, NArgs: 3, LastArgOpt: tree.Optional}, {Local: "string-length"}: {Fn: stringLength, NArgs: 1, LastArgOpt: tree.Optional}, {Local: "normalize-space"}: {Fn: normalizeSpace, NArgs: 1, LastArgOpt: tree.Optional}, {Local: "translate"}: {Fn: translate, NArgs: 3}, //Node set functions {Local: "last"}: {Fn: last}, {Local: "position"}: {Fn: position}, {Local: "count"}: {Fn: count, NArgs: 1}, {Local: "local-name"}: {Fn: localName, NArgs: 1, LastArgOpt: tree.Optional}, {Local: "namespace-uri"}: {Fn: namespaceURI, NArgs: 1, LastArgOpt: tree.Optional}, {Local: "name"}: {Fn: name, NArgs: 1, LastArgOpt: tree.Optional}, //boolean functions {Local: "boolean"}: {Fn: boolean, NArgs: 1}, {Local: "not"}: {Fn: not, NArgs: 1}, {Local: "true"}: {Fn: _true}, {Local: "false"}: {Fn: _false}, {Local: "lang"}: {Fn: lang, NArgs: 1}, //number functions {Local: "number"}: {Fn: number, NArgs: 1, LastArgOpt: tree.Optional}, {Local: "sum"}: {Fn: sum, NArgs: 1}, {Local: "floor"}: {Fn: floor, NArgs: 1}, {Local: "ceiling"}: {Fn: ceiling, NArgs: 1}, {Local: "round"}: {Fn: round, NArgs: 1}, } goxpath-1.0-alpha3/internal/parser/intfns/nodesetfns.go000066400000000000000000000047431273411034600233130ustar00rootroot00000000000000package intfns import ( "encoding/xml" "fmt" "github.com/ChrisTrenkamp/goxpath/tree" ) func last(c tree.Ctx, args ...tree.Result) (tree.Result, error) { return tree.Num(c.Size), nil } func position(c tree.Ctx, args ...tree.Result) (tree.Result, error) { return tree.Num(c.Pos), nil } func count(c tree.Ctx, args ...tree.Result) (tree.Result, error) { n, ok := args[0].(tree.NodeSet) if !ok { return nil, fmt.Errorf("Cannot convert object to a node-set") } return tree.Num(len(n)), nil } func localName(c tree.Ctx, args ...tree.Result) (tree.Result, error) { var n tree.NodeSet ok := true if len(args) == 1 { n, ok = args[0].(tree.NodeSet) } else { n = c.NodeSet } if !ok { return nil, fmt.Errorf("Cannot convert object to a node-set") } ret := "" if len(n) == 0 { return tree.String(ret), nil } node := n[0] tok := node.GetToken() switch node.GetNodeType() { case tree.NtElem: ret = tok.(xml.StartElement).Name.Local case tree.NtAttr: ret = tok.(xml.Attr).Name.Local case tree.NtPi: ret = tok.(xml.ProcInst).Target } return tree.String(ret), nil } func namespaceURI(c tree.Ctx, args ...tree.Result) (tree.Result, error) { var n tree.NodeSet ok := true if len(args) == 1 { n, ok = args[0].(tree.NodeSet) } else { n = c.NodeSet } if !ok { return nil, fmt.Errorf("Cannot convert object to a node-set") } ret := "" if len(n) == 0 { return tree.String(ret), nil } node := n[0] tok := node.GetToken() switch node.GetNodeType() { case tree.NtElem: ret = tok.(xml.StartElement).Name.Space case tree.NtAttr: ret = tok.(xml.Attr).Name.Space } return tree.String(ret), nil } func name(c tree.Ctx, args ...tree.Result) (tree.Result, error) { var n tree.NodeSet ok := true if len(args) == 1 { n, ok = args[0].(tree.NodeSet) } else { n = c.NodeSet } if !ok { return nil, fmt.Errorf("Cannot convert object to a node-set") } ret := "" if len(n) == 0 { return tree.String(ret), nil } node := n[0] switch node.GetNodeType() { case tree.NtElem: t := node.GetToken().(xml.StartElement) space := "" if t.Name.Space != "" { space = fmt.Sprintf("{%s}", t.Name.Space) } ret = fmt.Sprintf("%s%s", space, t.Name.Local) case tree.NtAttr: t := node.GetToken().(xml.Attr) space := "" if t.Name.Space != "" { space = fmt.Sprintf("{%s}", t.Name.Space) } ret = fmt.Sprintf("%s%s", space, t.Name.Local) case tree.NtPi: ret = fmt.Sprintf("%s", node.GetToken().(xml.ProcInst).Target) } return tree.String(ret), nil } goxpath-1.0-alpha3/internal/parser/intfns/numfns.go000066400000000000000000000026241273411034600224450ustar00rootroot00000000000000package intfns import ( "fmt" "math" "github.com/ChrisTrenkamp/goxpath/tree" ) func number(c tree.Ctx, args ...tree.Result) (tree.Result, error) { if b, ok := args[0].(tree.IsNum); ok { return b.Num(), nil } return nil, fmt.Errorf("Cannot convert object to a number") } func sum(c tree.Ctx, args ...tree.Result) (tree.Result, error) { n, ok := args[0].(tree.NodeSet) if !ok { return nil, fmt.Errorf("Cannot convert object to a node-set") } ret := 0.0 for _, i := range n { ret += float64(tree.GetNodeNum(i)) } return tree.Num(ret), nil } func floor(c tree.Ctx, args ...tree.Result) (tree.Result, error) { n, ok := args[0].(tree.IsNum) if !ok { return nil, fmt.Errorf("Cannot convert object to a number") } return tree.Num(math.Floor(float64(n.Num()))), nil } func ceiling(c tree.Ctx, args ...tree.Result) (tree.Result, error) { n, ok := args[0].(tree.IsNum) if !ok { return nil, fmt.Errorf("Cannot convert object to a number") } return tree.Num(math.Ceil(float64(n.Num()))), nil } func round(c tree.Ctx, args ...tree.Result) (tree.Result, error) { isn, ok := args[0].(tree.IsNum) if !ok { return nil, fmt.Errorf("Cannot convert object to a number") } n := isn.Num() if math.IsNaN(float64(n)) || math.IsInf(float64(n), 0) { return n, nil } if n < -0.5 { n = tree.Num(int(n - 0.5)) } else if n > 0.5 { n = tree.Num(int(n + 0.5)) } else { n = 0 } return n, nil } goxpath-1.0-alpha3/internal/parser/intfns/stringfns.go000066400000000000000000000055261273411034600231600ustar00rootroot00000000000000package intfns import ( "math" "regexp" "strings" "github.com/ChrisTrenkamp/goxpath/tree" ) func _string(c tree.Ctx, args ...tree.Result) (tree.Result, error) { if len(args) == 1 { return tree.String(args[0].String()), nil } return tree.String(c.NodeSet.String()), nil } func concat(c tree.Ctx, args ...tree.Result) (tree.Result, error) { ret := "" for _, i := range args { ret += i.String() } return tree.String(ret), nil } func startsWith(c tree.Ctx, args ...tree.Result) (tree.Result, error) { return tree.Bool(strings.Index(args[0].String(), args[1].String()) == 0), nil } func contains(c tree.Ctx, args ...tree.Result) (tree.Result, error) { return tree.Bool(strings.Contains(args[0].String(), args[1].String())), nil } func substringBefore(c tree.Ctx, args ...tree.Result) (tree.Result, error) { ind := strings.Index(args[0].String(), args[1].String()) if ind == -1 { return tree.String(""), nil } return tree.String(args[0].String()[:ind]), nil } func substringAfter(c tree.Ctx, args ...tree.Result) (tree.Result, error) { ind := strings.Index(args[0].String(), args[1].String()) if ind == -1 { return tree.String(""), nil } return tree.String(args[0].String()[ind+len(args[1].String()):]), nil } func substring(c tree.Ctx, args ...tree.Result) (tree.Result, error) { str := args[0].String() bNum, bErr := round(c, args[1]) if bErr != nil { return nil, bErr } b := bNum.(tree.Num).Num() if float64(b-1) >= float64(len(str)) || math.IsNaN(float64(b)) { return tree.String(""), nil } if len(args) == 2 { if b <= 1 { b = 1 } return tree.String(str[int(b)-1:]), nil } eNum, eErr := round(c, args[2]) if eErr != nil { return nil, eErr } e := eNum.(tree.Num).Num() if e <= 0 || math.IsNaN(float64(e)) || (math.IsInf(float64(b), 0) && math.IsInf(float64(e), 0)) { return tree.String(""), nil } if b <= 1 { e = b + e - 1 b = 1 } if float64(b+e-1) >= float64(len(str)) { e = tree.Num(len(str)) - b + 1 } return tree.String(str[int(b)-1 : int(b+e)-1]), nil } func stringLength(c tree.Ctx, args ...tree.Result) (tree.Result, error) { var str string if len(args) == 1 { str = args[0].String() } else { str = c.NodeSet.String() } return tree.Num(len(str)), nil } var spaceTrim = regexp.MustCompile(`\s+`) func normalizeSpace(c tree.Ctx, args ...tree.Result) (tree.Result, error) { var str string if len(args) == 1 { str = args[0].String() } else { str = c.NodeSet.String() } str = strings.TrimSpace(str) return tree.String(spaceTrim.ReplaceAllString(str, " ")), nil } func translate(c tree.Ctx, args ...tree.Result) (tree.Result, error) { ret := args[0].String() src := args[1].String() repl := args[2].String() for i := range src { r := "" if i < len(repl) { r = string(repl[i]) } ret = strings.Replace(ret, string(src[i]), r, -1) } return tree.String(ret), nil } goxpath-1.0-alpha3/internal/parser/parser.go000066400000000000000000000065561273411034600211420ustar00rootroot00000000000000package parser import ( "fmt" "github.com/ChrisTrenkamp/goxpath/internal/lexer" ) type stateType int const ( defState stateType = iota xpathState funcState paramState predState parenState ) type parseStack struct { stack []*Node stateTypes []stateType cur *Node } func (p *parseStack) push(t stateType) { p.stack = append(p.stack, p.cur) p.stateTypes = append(p.stateTypes, t) } func (p *parseStack) pop() { stackPos := len(p.stack) - 1 p.cur = p.stack[stackPos] p.stack = p.stack[:stackPos] p.stateTypes = p.stateTypes[:stackPos] } func (p *parseStack) curState() stateType { if len(p.stateTypes) == 0 { return defState } return p.stateTypes[len(p.stateTypes)-1] } type lexFn func(*parseStack, lexer.XItem) var parseMap = map[lexer.XItemType]lexFn{ lexer.XItemAbsLocPath: xiXPath, lexer.XItemAbbrAbsLocPath: xiXPath, lexer.XItemAbbrRelLocPath: xiXPath, lexer.XItemRelLocPath: xiXPath, lexer.XItemEndPath: xiEndPath, lexer.XItemAxis: xiXPath, lexer.XItemAbbrAxis: xiXPath, lexer.XItemNCName: xiXPath, lexer.XItemQName: xiXPath, lexer.XItemNodeType: xiXPath, lexer.XItemProcLit: xiXPath, lexer.XItemFunction: xiFunc, lexer.XItemArgument: xiFuncArg, lexer.XItemEndFunction: xiEndFunc, lexer.XItemPredicate: xiPred, lexer.XItemEndPredicate: xiEndPred, lexer.XItemStrLit: xiValue, lexer.XItemNumLit: xiValue, lexer.XItemOperator: xiOp, lexer.XItemVariable: xiValue, } var opPrecedence = map[string]int{ "|": 1, "*": 2, "div": 2, "mod": 2, "+": 3, "-": 3, "=": 4, "!=": 4, "<": 4, "<=": 4, ">": 4, ">=": 4, "and": 5, "or": 6, } //Parse creates an AST tree for XPath expressions. func Parse(xp string) (*Node, error) { var err error c := lexer.Lex(xp) n := &Node{} p := &parseStack{cur: n} for next := range c { if next.Typ != lexer.XItemError { parseMap[next.Typ](p, next) } else if err == nil { err = fmt.Errorf(next.Val) } } return n, err } func xiXPath(p *parseStack, i lexer.XItem) { if p.curState() == xpathState { p.cur.push(i) p.cur = p.cur.next return } p.cur.pushNotEmpty(i) p.push(xpathState) p.cur = p.cur.next } func xiEndPath(p *parseStack, i lexer.XItem) { p.pop() } func xiFunc(p *parseStack, i lexer.XItem) { p.cur.push(i) p.cur = p.cur.next p.push(funcState) } func xiFuncArg(p *parseStack, i lexer.XItem) { if p.curState() != funcState { p.pop() } p.cur.push(i) p.cur = p.cur.next p.push(paramState) p.cur.push(lexer.XItem{Typ: Empty, Val: ""}) p.cur = p.cur.next } func xiEndFunc(p *parseStack, i lexer.XItem) { if p.curState() == paramState { p.pop() } p.pop() } func xiPred(p *parseStack, i lexer.XItem) { p.cur.push(i) p.cur = p.cur.next p.push(predState) p.cur.push(lexer.XItem{Typ: Empty, Val: ""}) p.cur = p.cur.next } func xiEndPred(p *parseStack, i lexer.XItem) { p.pop() } func xiValue(p *parseStack, i lexer.XItem) { p.cur.add(i) } func xiOp(p *parseStack, i lexer.XItem) { if i.Val == "(" { p.cur.push(lexer.XItem{Typ: Empty, Val: ""}) p.push(parenState) p.cur = p.cur.next return } if i.Val == ")" { p.pop() return } if p.cur.Val.Typ == lexer.XItemOperator { if opPrecedence[p.cur.Val.Val] <= opPrecedence[i.Val] { p.cur.add(i) } else { p.cur.push(i) } } else { p.cur.add(i) } p.cur = p.cur.next } goxpath-1.0-alpha3/internal/parser/pathexpr/000077500000000000000000000000001273411034600211365ustar00rootroot00000000000000goxpath-1.0-alpha3/internal/parser/pathexpr/pathexpr.go000066400000000000000000000003371273411034600233230ustar00rootroot00000000000000package pathexpr import "encoding/xml" //PathExpr represents XPath step's. xmltree.XMLTree uses it to find nodes. type PathExpr struct { Name xml.Name Axis string NodeType string NS map[string]string } goxpath-1.0-alpha3/internal/xconst/000077500000000000000000000000001273411034600173255ustar00rootroot00000000000000goxpath-1.0-alpha3/internal/xconst/xconst.go000066400000000000000000000036621273411034600212010ustar00rootroot00000000000000package xconst const ( //AxisAncestor represents the "ancestor" axis AxisAncestor = "ancestor" //AxisAncestorOrSelf represents the "ancestor-or-self" axis AxisAncestorOrSelf = "ancestor-or-self" //AxisAttribute represents the "attribute" axis AxisAttribute = "attribute" //AxisChild represents the "child" axis AxisChild = "child" //AxisDescendent represents the "descendant" axis AxisDescendent = "descendant" //AxisDescendentOrSelf represents the "descendant-or-self" axis AxisDescendentOrSelf = "descendant-or-self" //AxisFollowing represents the "following" axis AxisFollowing = "following" //AxisFollowingSibling represents the "following-sibling" axis AxisFollowingSibling = "following-sibling" //AxisNamespace represents the "namespace" axis AxisNamespace = "namespace" //AxisParent represents the "parent" axis AxisParent = "parent" //AxisPreceding represents the "preceding" axis AxisPreceding = "preceding" //AxisPrecedingSibling represents the "preceding-sibling" axis AxisPrecedingSibling = "preceding-sibling" //AxisSelf represents the "self" axis AxisSelf = "self" ) //AxisNames is all the possible Axis identifiers wrapped in an array for convenience var AxisNames = []string{ AxisAncestor, AxisAncestorOrSelf, AxisAttribute, AxisChild, AxisDescendent, AxisDescendentOrSelf, AxisFollowing, AxisFollowingSibling, AxisNamespace, AxisParent, AxisPreceding, AxisPrecedingSibling, AxisSelf, } const ( //NodeTypeComment represents the "comment" node test NodeTypeComment = "comment" //NodeTypeText represents the "text" node test NodeTypeText = "text" //NodeTypeProcInst represents the "processing-instruction" node test NodeTypeProcInst = "processing-instruction" //NodeTypeNode represents the "node" node test NodeTypeNode = "node" ) //NodeTypes is all the possible node tests wrapped in an array for convenience var NodeTypes = []string{ NodeTypeComment, NodeTypeText, NodeTypeProcInst, NodeTypeNode, } goxpath-1.0-alpha3/internal/xsort/000077500000000000000000000000001273411034600171665ustar00rootroot00000000000000goxpath-1.0-alpha3/internal/xsort/xsort.go000066400000000000000000000006421273411034600206760ustar00rootroot00000000000000package xsort import ( "sort" "github.com/ChrisTrenkamp/goxpath/tree" ) type nodeSort []tree.Node func (ns nodeSort) Len() int { return len(ns) } func (ns nodeSort) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] } func (ns nodeSort) Less(i, j int) bool { return ns[i].Pos() < ns[j].Pos() } //SortNodes sorts the array by the node document order func SortNodes(res []tree.Node) { sort.Sort(nodeSort(res)) } goxpath-1.0-alpha3/marshal.go000066400000000000000000000036621273411034600161600ustar00rootroot00000000000000package goxpath import ( "bytes" "encoding/xml" "io" "github.com/ChrisTrenkamp/goxpath/tree" ) //Marshal prints the result tree, r, in XML form to w. func Marshal(n tree.Node, w io.Writer) error { return marshal(n, w) } //MarshalStr is like Marhal, but returns a string. func MarshalStr(n tree.Node) (string, error) { ret := bytes.NewBufferString("") err := marshal(n, ret) return ret.String(), err } func marshal(n tree.Node, w io.Writer) error { e := xml.NewEncoder(w) err := encTok(n, e) if err != nil { return err } return e.Flush() } func encTok(n tree.Node, e *xml.Encoder) error { switch n.GetNodeType() { case tree.NtAttr: return encAttr(n.GetToken().(xml.Attr), e) case tree.NtElem: return encEle(n.(tree.Elem), e) case tree.NtNs: return encNS(n.GetToken().(xml.Attr), e) case tree.NtRoot: for _, i := range n.(tree.Elem).GetChildren() { err := encTok(i, e) if err != nil { return err } } return nil } //case tree.NtChd, tree.NtComm, tree.NtPi: return e.EncodeToken(n.GetToken()) } func encAttr(a xml.Attr, e *xml.Encoder) error { str := a.Name.Local + `="` + a.Value + `"` if a.Name.Space != "" { str += ` xmlns="` + a.Name.Space + `"` } pi := xml.ProcInst{ Target: "attribute", Inst: ([]byte)(str), } return e.EncodeToken(pi) } func encNS(ns xml.Attr, e *xml.Encoder) error { pi := xml.ProcInst{ Target: "namespace", Inst: ([]byte)(ns.Value), } return e.EncodeToken(pi) } func encEle(n tree.Elem, e *xml.Encoder) error { ele := xml.StartElement{ Name: n.GetToken().(xml.StartElement).Name, } attrs := n.GetAttrs() ele.Attr = make([]xml.Attr, len(attrs)) for i := range attrs { ele.Attr[i] = attrs[i].GetToken().(xml.Attr) } err := e.EncodeToken(ele) if err != nil { return err } if x, ok := n.(tree.Elem); ok { for _, i := range x.GetChildren() { err := encTok(i, e) if err != nil { return err } } } return e.EncodeToken(ele.End()) } goxpath-1.0-alpha3/misc_test.go000066400000000000000000000100561273411034600165160ustar00rootroot00000000000000package goxpath import ( "bytes" "encoding/xml" "testing" "github.com/ChrisTrenkamp/goxpath/tree" "github.com/ChrisTrenkamp/goxpath/tree/xmltree" ) func TestISO_8859_1(t *testing.T) { p := `/test` x := `testpathtest2` exp := "testpathtest2" execVal(p, x, exp, nil, t) } func TestNodePos(t *testing.T) { ns := map[string]string{"test": "http://test", "test2": "http://test2", "test3": "http://test3"} x := `text` testPos := func(path string, pos int) { res := MustParse(path).MustExec(xmltree.MustParseXML(bytes.NewBufferString(x)), func(o *Opts) { o.NS = ns }).(tree.NodeSet) if len(res) != 1 { t.Errorf("Result length not 1: %s", path) return } exPos := res[0].(tree.Node).Pos() if exPos != pos { t.Errorf("Node position not correct. Recieved %d, expected %d", exPos, pos) } } testPos("/", 0) testPos("/*", 1) testPos("/*/namespace::*[1]", 2) testPos("/*/namespace::*[2]", 3) testPos("/*/attribute::*[1]", 4) testPos("//*:p2", 5) testPos("//*:p2/namespace::*[1]", 6) testPos("//*:p2/namespace::*[2]", 7) testPos("//*:p2/namespace::*[3]", 8) testPos("//*:p2/attribute::*[1]", 9) testPos("//text()", 10) } func TestNSSort(t *testing.T) { testNS := func(n tree.Node, url string) { if n.(tree.NS).Value != url { t.Errorf("Unexpected namespace %s. Expecting %s", n.(tree.NS).Value, url) } } ns := map[string]string{"test": "http://test", "test2": "http://test2", "test3": "http://test3"} x := `` res := MustParse("/*:p1/namespace::*").MustExec(xmltree.MustParseXML(bytes.NewBufferString(x)), func(o *Opts) { o.NS = ns }).(tree.NodeSet) testNS(res[0], ns["test"]) testNS(res[1], ns["test2"]) testNS(res[2], ns["test3"]) testNS(res[3], "http://www.w3.org/XML/1998/namespace") } func TestFindNodeByPos(t *testing.T) { x := `text` nt := xmltree.MustParseXML(bytes.NewBufferString(x)) if tree.FindNodeByPos(nt, 5).GetNodeType() != tree.NtElem { t.Error("Node 5 not element") } if tree.FindNodeByPos(nt, 14).GetNodeType() != tree.NtChd { t.Error("Node 14 not char data") } if tree.FindNodeByPos(nt, 4).GetNodeType() != tree.NtAttr { t.Error("Node 4 not attribute") } if tree.FindNodeByPos(nt, 3).GetNodeType() != tree.NtNs { t.Error("Node 3 not namespace") } if tree.FindNodeByPos(nt, 19) != nil { t.Error("Invalid node returned") } } func TestFindAttr(t *testing.T) { x := `` nt := xmltree.MustParseXML(bytes.NewBufferString(x)) res, _ := ParseExec("/p1", nt) node := res.(tree.NodeSet)[0].(tree.Elem) if val, ok := tree.GetAttributeVal(node, "attr1", ""); !ok || val != "foo" { t.Error("attr1 not foo") } if val, ok := tree.GetAttributeVal(node, "attr2", "http://test"); !ok || val != "bar" { t.Error("attr2 not bar") } if val, ok := tree.GetAttributeVal(node, "attr3", ""); ok || val != "" { t.Error("attr3 is set") } if val := tree.GetAttrValOrEmpty(node, "attr3", ""); val != "" { t.Error("attr3 is set") } if val := tree.GetAttrValOrEmpty(node, "attr1", ""); val != "foo" { t.Error("attr1 not foo") } } func TestVariable(t *testing.T) { x := xmltree.MustParseXML(bytes.NewBufferString(xml.Header + "foobar")) xp := MustParse(`/p1/p2`) res := xp.MustExec(x) opt := func(o *Opts) { o.Vars["prev"] = res } xp = MustParse(`$prev = 'foo'`) if res, err := xp.ExecBool(x, opt); err != nil || !res { t.Error("Incorrect result", res, err) } if _, err := xp.ExecBool(x); err == nil { t.Error("Error not nil") } if _, err := Parse(`$ = 'foo'`); err == nil { t.Error("Parse error not nil") } } goxpath-1.0-alpha3/path_test.go000066400000000000000000000415011273411034600165160ustar00rootroot00000000000000package goxpath import ( "bytes" "runtime/debug" "testing" "github.com/ChrisTrenkamp/goxpath/tree" "github.com/ChrisTrenkamp/goxpath/tree/xmltree" ) func execPath(xp, x string, exp []string, ns map[string]string, t *testing.T) { defer func() { if r := recover(); r != nil { t.Error("Panicked: from XPath expr: '" + xp) t.Error(r) t.Error(string(debug.Stack())) } }() res := MustParse(xp).MustExec(xmltree.MustParseXML(bytes.NewBufferString(x)), func(o *Opts) { o.NS = ns }).(tree.NodeSet) if len(res) != len(exp) { t.Error("Result length not valid in XPath expression '"+xp+"':", len(res), ", expecting", len(exp)) for i := range res { t.Error(MarshalStr(res[i].(tree.Node))) } return } for i := range res { r, err := MarshalStr(res[i].(tree.Node)) if err != nil { t.Error(err.Error()) return } valid := false for j := range exp { if r == exp[j] { valid = true } } if !valid { t.Error("Incorrect result in XPath expression '" + xp + "':" + r) t.Error("Expecting one of:") for j := range exp { t.Error(exp[j]) } return } } } func TestRoot(t *testing.T) { p := `/` x := `` exp := []string{""} execPath(p, x, exp, nil, t) } func TestRoot2(t *testing.T) { x := xmltree.MustParseXML(bytes.NewBufferString(``)) x = MustParse("/test/path").MustExec(x).(tree.NodeSet)[0] for _, i := range []string{"/", "//test"} { res, err := MarshalStr(MustParse(i).MustExec(x).(tree.NodeSet)[0]) if err != nil { t.Error(err) } if res != "" { t.Error("Incorrect result", res) } } } func TestAbsPath(t *testing.T) { p := ` / test / path` x := `` exp := []string{""} execPath(p, x, exp, nil, t) } func TestNonAbsPath(t *testing.T) { p := ` test ` x := `` exp := []string{""} execPath(p, x, exp, nil, t) } func TestRelPath(t *testing.T) { p := ` // path ` x := `` exp := []string{""} execPath(p, x, exp, nil, t) } func TestRelPath2(t *testing.T) { p := ` // path ` x := `test` exp := []string{"test", `test`} execPath(p, x, exp, nil, t) } func TestRelNonRootPath(t *testing.T) { p := ` / test // path ` x := `` exp := []string{""} execPath(p, x, exp, nil, t) } func TestParent(t *testing.T) { p := `/test/path/ parent:: test` x := `` exp := []string{""} execPath(p, x, exp, nil, t) } func TestAncestor(t *testing.T) { p := `/p1/p2/p3/p1/ancestor::p1` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestAncestorOrSelf(t *testing.T) { p := `/p1/p2/p3/p1/ancestor-or-self::p1` x := `` exp := []string{``, ``} execPath(p, x, exp, nil, t) } func TestDescendant(t *testing.T) { p := `/p1/descendant::p1` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestDescendantOrSelf(t *testing.T) { p := `/p1/descendant-or-self::p1` x := `` exp := []string{``, ``} execPath(p, x, exp, nil, t) } func TestAttribute(t *testing.T) { p := `/p1/attribute::test` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestAttributeSelf(t *testing.T) { p := `/p1/attribute::test/self::node()` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestAttributeAbbr(t *testing.T) { p := `/p1/ @ test` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestAttributeNoChild(t *testing.T) { p := `/p1/attribute::test/p2` x := `` exp := []string{} execPath(p, x, exp, nil, t) } func TestAttributeParent(t *testing.T) { p := `/p1/attribute::test/ ..` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestNodeTypeNode(t *testing.T) { p := `/p1/child:: node( ) ` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestNodeTypeNodeAbbr(t *testing.T) { p := `/p1/ .` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestNodeTypeParent(t *testing.T) { p := `/p1/p2/parent::node()` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestNodeTypeParentAbbr(t *testing.T) { p := `/p1/p2/..` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestFollowing(t *testing.T) { p := `//p3/following::node()` x := `` exp := []string{``, ``, ``} execPath(p, x, exp, nil, t) } func TestPreceding(t *testing.T) { p := `//p6/preceding::node()` x := `` exp := []string{``, ``, ``} execPath(p, x, exp, nil, t) } func TestPrecedingSibling(t *testing.T) { p := `//p4/preceding-sibling::node()` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestPrecedingSibling2(t *testing.T) { p := `/preceding-sibling::node()` x := `` exp := []string{} execPath(p, x, exp, nil, t) } func TestFollowingSibling(t *testing.T) { p := `//p2/following-sibling::node()` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestFollowingSibling2(t *testing.T) { p := `/following-sibling::node()` x := `` exp := []string{} execPath(p, x, exp, nil, t) } func TestComment(t *testing.T) { p := `// comment()` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestCommentInvPath(t *testing.T) { p := `//comment()/ self :: processing-instruction ( ) ` x := `` exp := []string{} execPath(p, x, exp, nil, t) } func TestCommentParent(t *testing.T) { p := `//comment()/..` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestText(t *testing.T) { p := `//text()` x := `text` exp := []string{`text`} execPath(p, x, exp, nil, t) } func TestTextParent(t *testing.T) { p := `//text()/..` x := `text` exp := []string{`text`} execPath(p, x, exp, nil, t) } func TestProcInst(t *testing.T) { p := `//processing-instruction()` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestProcInstParent(t *testing.T) { p := `//processing-instruction()/..` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestProcInstInvPath(t *testing.T) { p := `//processing-instruction()/self::comment()` x := `` exp := []string{} execPath(p, x, exp, nil, t) } func TestProcInst2(t *testing.T) { p := `//processing-instruction( 'proc2' ) ` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestNamespace(t *testing.T) { p := `/*:p1/*:p2/namespace::*` x := `` exp := []string{``, ``, ``} execPath(p, x, exp, nil, t) } func TestRootNamespace(t *testing.T) { p := `/namespace::*` x := `` exp := []string{} execPath(p, x, exp, nil, t) } func TestNamespaceXml(t *testing.T) { p := `/*:p1/ * : p2 /namespace::xml` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestNamespaceNodeType(t *testing.T) { p := `/*:p1/*:p2/namespace::foo/ self :: node ( ) ` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestNamespaceNodeWildcard(t *testing.T) { p := `/*:p1/*:p2/namespace::*:foo` x := `` exp := []string{``} execPath(p, x, exp, nil, t) p = `/*:p1/*:p2/namespace::invalid:foo` exp = []string{} execPath(p, x, exp, nil, t) } func TestNamespaceChild(t *testing.T) { p := `/*:p1/namespace::*/ * : p2` x := `` exp := []string{} execPath(p, x, exp, nil, t) } func TestNamespaceParent(t *testing.T) { p := `/*:p1/*:p2/namespace::foo/..` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestNamespace2(t *testing.T) { p := `//namespace::test` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestNamespace3(t *testing.T) { p := `/p1/p2/p3/namespace :: foo` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestNamespace4(t *testing.T) { p := `/test:p1/p2/p3/namespace::*` x := `` exp := []string{``} execPath(p, x, exp, map[string]string{"test": "http://test"}, t) } func TestNodeNamespace(t *testing.T) { p := `/ test : p1 ` x := `` exp := []string{``} execPath(p, x, exp, map[string]string{"test": "http://test"}, t) } func TestNodeNamespace2(t *testing.T) { p := `/test:p1/test2:p2` x := `` exp := []string{``} execPath(p, x, exp, map[string]string{"test": "http://test", "test2": "http://test2"}, t) } func TestNodeNamespace3(t *testing.T) { p := `/test:p1/foo:p2` x := `` exp := []string{``} execPath(p, x, exp, map[string]string{"test": "http://test", "foo": "http://foo.bar"}, t) } func TestAttrNamespace(t *testing.T) { p := `/test:p1/@foo:test` x := `` exp := []string{``} execPath(p, x, exp, map[string]string{"test": "http://test", "foo": "http://foo.bar"}, t) p = `/test:p1/@test:test` exp = []string{} execPath(p, x, exp, map[string]string{"test": "http://test", "foo": "http://foo.bar"}, t) } func TestWildcard(t *testing.T) { p := `/p1/*` x := `` exp := []string{``, ``, ``} execPath(p, x, exp, nil, t) } func TestWildcardNS(t *testing.T) { p := `//*:p1` x := `` exp := []string{``, ``} execPath(p, x, exp, map[string]string{"test": "http://test", "foo": "http://foo.bar"}, t) } func TestWildcardLocal(t *testing.T) { p := `//foo:*` x := `` exp := []string{``, ``} execPath(p, x, exp, map[string]string{"test": "http://test", "foo": "http://foo.bar"}, t) } func TestWildcardNSAttr(t *testing.T) { p := `/p1/@*:attr` x := `` exp := []string{``} execPath(p, x, exp, map[string]string{"test": "http://test", "foo": "http://foo.bar"}, t) } func TestWildcardLocalAttr(t *testing.T) { p := `/p1/@foo:*` x := `` exp := []string{``, ``} execPath(p, x, exp, map[string]string{"test": "http://test", "foo": "http://foo.bar"}, t) } func TestPredicate1(t *testing.T) { p := `/p1/p2 [ p3 ] ` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestPredicate2(t *testing.T) { p := `/p1/p2 [ 2 ] ` x := `` exp := []string{``} execPath(p, x, exp, nil, t) p = `/p1/p2 [ position() = 2 ] ` execPath(p, x, exp, nil, t) } func TestPredicate3(t *testing.T) { p := `/p1/p2 [ last() ] /p3` x := `` exp := []string{``} execPath(p, x, exp, nil, t) } func TestPredicate4(t *testing.T) { p := `/p1/p2[not(@test)]` x := `` exp := []string{``, ``} execPath(p, x, exp, nil, t) } func TestPredicate5(t *testing.T) { p := `/p1/p2[.//p1]` x := `lkj` exp := []string{`lkj`} execPath(p, x, exp, nil, t) } func TestPredicate6(t *testing.T) { p := `/p1/p2[//p1]` x := `lkj` exp := []string{``, ``, `lkj`} execPath(p, x, exp, nil, t) } func TestPredicate7(t *testing.T) { p := `/p3[//p1]` x := `lkj` exp := []string{} execPath(p, x, exp, nil, t) } func TestPredicate8(t *testing.T) { p := `/test[@attr1='a' and @attr2='b']/a` x := `aaaabbb` exp := []string{`aaaa`} execPath(p, x, exp, nil, t) } func TestUnion(t *testing.T) { p := `/test/test2 | /test/test3` x := `foobarhamneggs` exp := []string{"foobar", "hamneggs"} execPath(p, x, exp, nil, t) } goxpath-1.0-alpha3/tree/000077500000000000000000000000001273411034600151325ustar00rootroot00000000000000goxpath-1.0-alpha3/tree/interfaces.go000066400000000000000000000005361273411034600176100ustar00rootroot00000000000000package tree import "fmt" //Result is used for all data types. type Result interface { fmt.Stringer } //IsBool is used for the XPath boolean function. It turns the data type to a bool. type IsBool interface { Bool() Bool } //IsNum is used for the XPath number function. It turns the data type to a number. type IsNum interface { Num() Num } goxpath-1.0-alpha3/tree/tree.go000066400000000000000000000113351273411034600164230ustar00rootroot00000000000000package tree import ( "encoding/xml" "sort" ) //XMLSpace is the W3C XML namespace const XMLSpace = "http://www.w3.org/XML/1998/namespace" //NodePos is a helper for representing the node's document order type NodePos int //Pos returns the node's document order position func (n NodePos) Pos() int { return int(n) } //NodeType is a safer way to determine a node's type than type assertions. type NodeType int //GetNodeType returns the node's type. func (t NodeType) GetNodeType() NodeType { return t } //These are all the possible node types const ( NtAttr NodeType = iota NtChd NtComm NtElem NtNs NtRoot NtPi ) //Node is a XPath result that is a node except elements type Node interface { //ResValue prints the node's string value ResValue() string //Pos returns the node's position in the document order Pos() int //GetToken returns the xml.Token representation of the node GetToken() xml.Token //GetParent returns the parent node, which will always be an XML element GetParent() Elem //GetNodeType returns the node's type GetNodeType() NodeType } //Elem is a XPath result that is an element node type Elem interface { Node //GetChildren returns the elements children. GetChildren() []Node //GetAttrs returns the attributes of the element GetAttrs() []Node } //NSElem is a node that keeps track of namespaces. type NSElem interface { Elem GetNS() map[xml.Name]string } //NSBuilder is a helper-struct for satisfying the NSElem interface type NSBuilder struct { NS map[xml.Name]string } //GetNS returns the namespaces found on the current element. It should not be //confused with BuildNS, which actually resolves the namespace nodes. func (ns NSBuilder) GetNS() map[xml.Name]string { return ns.NS } type nsValueSort []NS func (ns nsValueSort) Len() int { return len(ns) } func (ns nsValueSort) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] } func (ns nsValueSort) Less(i, j int) bool { return ns[i].Value < ns[j].Value } //BuildNS resolves all the namespace nodes of the element and returns them func BuildNS(t Elem) (ret []NS) { vals := make(map[xml.Name]string) if nselem, ok := t.(NSElem); ok { buildNS(nselem, vals) ret = make([]NS, 0, len(vals)) i := 1 for k, v := range vals { if !(k.Local == "xmlns" && k.Space == "" && v == "") { ret = append(ret, NS{ Attr: xml.Attr{Name: k, Value: v}, Parent: t, NodeType: NtNs, }) i++ } } sort.Sort(nsValueSort(ret)) for i := range ret { ret[i].NodePos = NodePos(t.Pos() + i + 1) } } return ret } func buildNS(x NSElem, ret map[xml.Name]string) { if x.GetNodeType() == NtRoot { return } if nselem, ok := x.GetParent().(NSElem); ok { buildNS(nselem, ret) } for k, v := range x.GetNS() { ret[k] = v } } //NS is a namespace node. type NS struct { xml.Attr Parent Elem NodePos NodeType } //GetToken returns the xml.Token representation of the namespace. func (ns NS) GetToken() xml.Token { return ns.Attr } //GetParent returns the parent node of the namespace. func (ns NS) GetParent() Elem { return ns.Parent } //ResValue returns the string value of the namespace func (ns NS) ResValue() string { return ns.Attr.Value } //GetAttribute is a convenience function for getting the specified attribute from an element. //false is returned if the attribute is not found. func GetAttribute(n Elem, local, space string) (xml.Attr, bool) { attrs := n.GetAttrs() for _, i := range attrs { attr := i.GetToken().(xml.Attr) if local == attr.Name.Local && space == attr.Name.Space { return attr, true } } return xml.Attr{}, false } //GetAttributeVal is like GetAttribute, except it returns the attribute's value. func GetAttributeVal(n Elem, local, space string) (string, bool) { attr, ok := GetAttribute(n, local, space) return attr.Value, ok } //GetAttrValOrEmpty is like GetAttributeVal, except it returns an empty string if //the attribute is not found instead of false. func GetAttrValOrEmpty(n Elem, local, space string) string { val, ok := GetAttributeVal(n, local, space) if !ok { return "" } return val } //FindNodeByPos finds a node from the given position. Returns nil if the node //is not found. func FindNodeByPos(n Node, pos int) Node { if n.Pos() == pos { return n } if elem, ok := n.(Elem); ok { chldrn := elem.GetChildren() for i := 1; i < len(chldrn); i++ { if chldrn[i-1].Pos() <= pos && chldrn[i].Pos() > pos { return FindNodeByPos(chldrn[i-1], pos) } } if len(chldrn) > 0 { if chldrn[len(chldrn)-1].Pos() <= pos { return FindNodeByPos(chldrn[len(chldrn)-1], pos) } } attrs := elem.GetAttrs() for _, i := range attrs { if i.Pos() == pos { return i } } ns := BuildNS(elem) for _, i := range ns { if i.Pos() == pos { return i } } } return nil } goxpath-1.0-alpha3/tree/xfn.go000066400000000000000000000021511273411034600162530ustar00rootroot00000000000000package tree import ( "fmt" ) //Ctx represents the current context position, size, node, and the current filtered result type Ctx struct { NodeSet Pos int Size int } //Fn is a XPath function, written in Go type Fn func(c Ctx, args ...Result) (Result, error) //LastArgOpt sets whether the last argument in a function is optional, variadic, or neither type LastArgOpt int //LastArgOpt options const ( None LastArgOpt = iota Optional Variadic ) //Wrap interfaces XPath function calls with Go type Wrap struct { Fn Fn //NArgs represents the number of arguments to the XPath function. -1 represents a single optional argument NArgs int LastArgOpt LastArgOpt } //Call checks the arguments and calls Fn if they are valid func (w Wrap) Call(c Ctx, args ...Result) (Result, error) { switch w.LastArgOpt { case Optional: if len(args) == w.NArgs || len(args) == w.NArgs-1 { return w.Fn(c, args...) } case Variadic: if len(args) >= w.NArgs-1 { return w.Fn(c, args...) } default: if len(args) == w.NArgs { return w.Fn(c, args...) } } return nil, fmt.Errorf("Invalid number of arguments") } goxpath-1.0-alpha3/tree/xmlstruct/000077500000000000000000000000001273411034600171775ustar00rootroot00000000000000goxpath-1.0-alpha3/tree/xmlstruct/xmlele.go000066400000000000000000000070351273411034600210210ustar00rootroot00000000000000package xmlstruct import ( "encoding/xml" "fmt" "reflect" "strings" "github.com/ChrisTrenkamp/goxpath/tree" ) type XMLEle struct { Val reflect.Value pos int prnt tree.Elem prntTag string } func (x *XMLEle) ResValue() string { if x.Val.Kind() != reflect.Struct { return fmt.Sprintf("%v", x.Val.Interface()) } ret := "" for _, i := range x.GetChildren() { switch i.GetNodeType() { case tree.NtChd, tree.NtElem, tree.NtRoot: ret += i.ResValue() } } return ret } func (x *XMLEle) Pos() int { return x.pos } func (x *XMLEle) GetToken() xml.Token { ret := xml.StartElement{} if x.prntTag != "" { ret.Name = getTagInfo(x.prntTag).name } else { ret.Name = getXMLName(x.Val) } return ret } func (x *XMLEle) GetParent() tree.Elem { return x.prnt } func (x *XMLEle) GetNodeType() tree.NodeType { return tree.NtElem } func (x *XMLEle) GetChildren() []tree.Node { n, _ := getChildren(x, x.Val, x.pos, false) return n } func (x *XMLEle) GetAttrs() []tree.Node { n, _ := getChildren(x, x.Val, x.pos, true) return n } type tagInfo struct { name xml.Name attr bool cdata bool comment bool } func getTagInfo(tag string) (ret tagInfo) { spl := strings.Split(tag, ",") name := strings.Split(spl[0], " ") if len(spl) >= 2 { for i := 1; i < len(spl); i++ { if spl[i] == "chardata" || spl[i] == "cdata" { ret.cdata = true } else if spl[i] == "attr" { ret.attr = true } else if spl[i] == "comment" { ret.comment = true } } } if len(name) == 2 { ret.name.Space = name[1] } ret.name.Local = name[0] return } func getXMLName(val reflect.Value) xml.Name { n := val.FieldByName("XMLName") zero := reflect.Value{} if zero != n { field, _ := val.Type().FieldByName("XMLName") tagInfo := getTagInfo(field.Tag.Get("xml")) if tagInfo.name.Local != "" { return tagInfo.name } if name, ok := n.Interface().(xml.Name); ok { return name } } return xml.Name{Local: val.Type().Name()} } func getChildren(x *XMLEle, val reflect.Value, pos int, getAttrs bool) ([]tree.Node, int) { if val.Kind() == reflect.Ptr { val = val.Elem() } if val.Kind() == reflect.Interface { val = reflect.ValueOf(val.Interface()) } if val.Kind() != reflect.Struct { if getAttrs { return []tree.Node{}, x.pos + 1 } return []tree.Node{&XMLNode{ Val: x.Val, pos: x.pos + 1, prnt: x, nodeType: tree.NtChd, prntTag: "", }}, x.pos + 2 } ret := make([]tree.Node, 0, val.NumField()) for i := 0; i < val.NumField(); i++ { field := val.Field(i) name := val.Type().Field(i).Name if val.Type().Field(i).Anonymous { nodes, newPos := getChildren(x, field, pos, getAttrs) ret = append(ret, nodes...) pos = newPos continue } tag := val.Type().Field(i).Tag.Get("xml") if tag == "-" || name == "XMLName" { continue } tagInfo := getTagInfo(tag) pos++ if tagInfo.attr || tagInfo.cdata || tagInfo.comment { if !getAttrs && tagInfo.attr || getAttrs && !tagInfo.attr { continue } child := &XMLNode{ Val: field, pos: pos, prnt: x, prntTag: tag, } if tagInfo.attr { child.nodeType = tree.NtAttr } else if tagInfo.cdata { child.nodeType = tree.NtChd } else { child.nodeType = tree.NtComm } if tagInfo.name.Local == "" { child.prntTag = name } ret = append(ret, child) continue } if getAttrs { continue } child := &XMLEle{Val: field, pos: pos} if tag == "" { tag = name } child.prntTag = tag ret = append(ret, child) } return ret, pos + 1 } goxpath-1.0-alpha3/tree/xmlstruct/xmlnode.go000066400000000000000000000015241273411034600211760ustar00rootroot00000000000000package xmlstruct import ( "encoding/xml" "fmt" "reflect" "github.com/ChrisTrenkamp/goxpath/tree" ) type XMLNode struct { Val reflect.Value pos int prnt tree.Elem nodeType tree.NodeType prntTag string } func (x *XMLNode) ResValue() string { if x.Val.Kind() == reflect.Ptr { return fmt.Sprintf("%v", x.Val.Elem().Interface()) } return fmt.Sprintf("%v", x.Val.Interface()) } func (x *XMLNode) Pos() int { return x.pos } func (x *XMLNode) GetToken() xml.Token { switch x.nodeType { case tree.NtAttr: return xml.Attr{Name: getTagInfo(x.prntTag).name, Value: x.ResValue()} case tree.NtChd: return xml.CharData(x.ResValue()) } //case tree.NtComm: return xml.Comment(x.ResValue()) } func (x *XMLNode) GetParent() tree.Elem { return x.prnt } func (x *XMLNode) GetNodeType() tree.NodeType { return x.nodeType } goxpath-1.0-alpha3/tree/xmlstruct/xmlroot.go000066400000000000000000000010661273411034600212350ustar00rootroot00000000000000package xmlstruct import ( "encoding/xml" "github.com/ChrisTrenkamp/goxpath/tree" ) type XMLRoot struct { Ele *XMLEle } func (x *XMLRoot) ResValue() string { return x.Ele.ResValue() } func (x *XMLRoot) Pos() int { return 0 } func (x *XMLRoot) GetToken() xml.Token { return xml.StartElement{} } func (x *XMLRoot) GetParent() tree.Elem { return x } func (x *XMLRoot) GetNodeType() tree.NodeType { return tree.NtRoot } func (x *XMLRoot) GetChildren() []tree.Node { return []tree.Node{x.Ele} } func (x *XMLRoot) GetAttrs() []tree.Node { return nil } goxpath-1.0-alpha3/tree/xmlstruct/xmlstruct.go000066400000000000000000000010761273411034600215770ustar00rootroot00000000000000package xmlstruct import ( "fmt" "reflect" ) func ParseStruct(i interface{}) (*XMLRoot, error) { ret := &XMLRoot{} val := reflect.ValueOf(i) if val.Kind() == reflect.Ptr { val = val.Elem() } if val.Kind() != reflect.Struct { return ret, fmt.Errorf("Interface is not a struct") } if getXMLName(val).Local == "" { return nil, fmt.Errorf("Invalid XML struct") } ret.Ele = &XMLEle{Val: val, pos: 1, prnt: ret} return ret, nil } func MustParseStruct(i interface{}) *XMLRoot { ret, err := ParseStruct(i) if err != nil { panic(err) } return ret } goxpath-1.0-alpha3/tree/xmlstruct/xmlstruct_test.go000066400000000000000000000015431273411034600226350ustar00rootroot00000000000000package xmlstruct import ( "encoding/xml" "testing" "github.com/ChrisTrenkamp/goxpath" ) func TestSingleFields(t *testing.T) { str := struct { XMLName xml.Name `xml:"struct"` Elem string `xml:"elem"` Attr string `xml:"attr,attr"` Attr2 string `xml:",attr"` Comm string `xml:",comment"` CD string `xml:",chardata"` Test interface{} }{ Elem: "foo", Attr: "bar", Attr2: "baz", Comm: "steak", CD: "eggs", Test: struct { Elem2 string `xml:"elem2"` Attr3 string `xml:",attr"` }{ Elem2: "elem2", Attr3: "attr3", }, } x := MustParseStruct(&str) x1, err := xml.Marshal(str) str1 := string(x1) if err != nil { t.Error(err) } str2, err := goxpath.MarshalStr(x) if err != nil { t.Error(err) } if str1 != str2 { t.Error("Strings not equal") t.Error(str1) t.Error(str2) } } goxpath-1.0-alpha3/tree/xmltree/000077500000000000000000000000001273411034600166125ustar00rootroot00000000000000goxpath-1.0-alpha3/tree/xmltree/xmlbuilder/000077500000000000000000000000001273411034600207615ustar00rootroot00000000000000goxpath-1.0-alpha3/tree/xmltree/xmlbuilder/xmlbuilder.go000066400000000000000000000010151273411034600234540ustar00rootroot00000000000000package xmlbuilder import ( "encoding/xml" "github.com/ChrisTrenkamp/goxpath/tree" ) //BuilderOpts supplies all the information needed to create an XML node. type BuilderOpts struct { Dec *xml.Decoder Tok xml.Token NodeType tree.NodeType NS map[xml.Name]string Attrs []*xml.Attr NodePos int AttrStartPos int } //XMLBuilder is an interface for creating XML structures. type XMLBuilder interface { tree.Node CreateNode(*BuilderOpts) XMLBuilder EndElem() XMLBuilder } goxpath-1.0-alpha3/tree/xmltree/xmlele/000077500000000000000000000000001273411034600201005ustar00rootroot00000000000000goxpath-1.0-alpha3/tree/xmltree/xmlele/xmlele.go000066400000000000000000000051271273411034600217220ustar00rootroot00000000000000package xmlele import ( "encoding/xml" "github.com/ChrisTrenkamp/goxpath/tree" "github.com/ChrisTrenkamp/goxpath/tree/xmltree/xmlbuilder" "github.com/ChrisTrenkamp/goxpath/tree/xmltree/xmlnode" ) //XMLEle is an implementation of XPRes for XML elements type XMLEle struct { xml.StartElement tree.NSBuilder Attrs []tree.Node Children []tree.Node Parent tree.Elem tree.NodePos tree.NodeType } //Root is the default root node builder for xmltree.ParseXML func Root() xmlbuilder.XMLBuilder { return &XMLEle{NodeType: tree.NtRoot} } //CreateNode is an implementation of xmlbuilder.XMLBuilder. It appends the node //specified in opts and returns the child if it is an element. Otherwise, it returns x. func (x *XMLEle) CreateNode(opts *xmlbuilder.BuilderOpts) xmlbuilder.XMLBuilder { if opts.NodeType == tree.NtElem { ele := &XMLEle{ StartElement: opts.Tok.(xml.StartElement), NSBuilder: tree.NSBuilder{NS: opts.NS}, Attrs: make([]tree.Node, len(opts.Attrs)), Parent: x, NodePos: tree.NodePos(opts.NodePos), NodeType: opts.NodeType, } for i := range opts.Attrs { ele.Attrs[i] = xmlnode.XMLNode{ Token: opts.Attrs[i], NodePos: tree.NodePos(opts.AttrStartPos + i), NodeType: tree.NtAttr, Parent: ele, } } x.Children = append(x.Children, ele) return ele } node := xmlnode.XMLNode{ Token: opts.Tok, NodePos: tree.NodePos(opts.NodePos), NodeType: opts.NodeType, Parent: x, } x.Children = append(x.Children, node) return x } //EndElem is an implementation of xmlbuilder.XMLBuilder. It returns x's parent. func (x *XMLEle) EndElem() xmlbuilder.XMLBuilder { return x.Parent.(*XMLEle) } //GetToken returns the xml.Token representation of the node func (x *XMLEle) GetToken() xml.Token { return x.StartElement } //GetParent returns the parent node, or itself if it's the root func (x *XMLEle) GetParent() tree.Elem { return x.Parent } //GetChildren returns all child nodes of the element func (x *XMLEle) GetChildren() []tree.Node { ret := make([]tree.Node, len(x.Children)) for i := range x.Children { ret[i] = x.Children[i] } return ret } //GetAttrs returns all attributes of the element func (x *XMLEle) GetAttrs() []tree.Node { ret := make([]tree.Node, len(x.Attrs)) for i := range x.Attrs { ret[i] = x.Attrs[i] } return ret } //ResValue returns the string value of the element and children func (x *XMLEle) ResValue() string { ret := "" for i := range x.Children { switch x.Children[i].GetNodeType() { case tree.NtChd, tree.NtElem, tree.NtRoot: ret += x.Children[i].ResValue() } } return ret } goxpath-1.0-alpha3/tree/xmltree/xmlnode/000077500000000000000000000000001273411034600202605ustar00rootroot00000000000000goxpath-1.0-alpha3/tree/xmltree/xmlnode/xmlnode.go000066400000000000000000000016141273411034600222570ustar00rootroot00000000000000package xmlnode import ( "encoding/xml" "github.com/ChrisTrenkamp/goxpath/tree" ) //XMLNode will represent an attribute, character data, comment, or processing instruction node type XMLNode struct { xml.Token tree.NodePos tree.NodeType Parent tree.Elem } //GetToken returns the xml.Token representation of the node func (a XMLNode) GetToken() xml.Token { if a.NodeType == tree.NtAttr { ret := a.Token.(*xml.Attr) return *ret } return a.Token } //GetParent returns the parent node func (a XMLNode) GetParent() tree.Elem { return a.Parent } //ResValue returns the string value of the attribute func (a XMLNode) ResValue() string { switch a.NodeType { case tree.NtAttr: return a.Token.(*xml.Attr).Value case tree.NtChd: return string(a.Token.(xml.CharData)) case tree.NtComm: return string(a.Token.(xml.Comment)) } //case tree.NtPi: return string(a.Token.(xml.ProcInst).Inst) } goxpath-1.0-alpha3/tree/xmltree/xmltree.go000066400000000000000000000073761273411034600206360ustar00rootroot00000000000000package xmltree import ( "encoding/xml" "fmt" "io" "golang.org/x/net/html/charset" "github.com/ChrisTrenkamp/goxpath/tree" "github.com/ChrisTrenkamp/goxpath/tree/xmltree/xmlbuilder" "github.com/ChrisTrenkamp/goxpath/tree/xmltree/xmlele" ) //ParseOptions is a set of methods and function pointers that alter //the way the XML decoder works and the Node types that are created. //Options that are not set will default to what is set in internal/defoverride.go type ParseOptions struct { Strict bool XMLRoot func() xmlbuilder.XMLBuilder } //DirectiveParser is an optional interface extended from XMLBuilder that handles //XML directives. type DirectiveParser interface { xmlbuilder.XMLBuilder Directive(xml.Directive, *xml.Decoder) } //ParseSettings is a function for setting the ParseOptions you want when //parsing an XML tree. type ParseSettings func(s *ParseOptions) //MustParseXML is like ParseXML, but panics instead of returning an error. func MustParseXML(r io.Reader, op ...ParseSettings) tree.Node { ret, err := ParseXML(r, op...) if err != nil { panic(err) } return ret } //ParseXML creates an XMLTree structure from an io.Reader. func ParseXML(r io.Reader, op ...ParseSettings) (tree.Node, error) { ov := ParseOptions{ Strict: true, XMLRoot: xmlele.Root, } for _, i := range op { i(&ov) } dec := xml.NewDecoder(r) dec.CharsetReader = charset.NewReaderLabel dec.Strict = ov.Strict ordrPos := 1 xmlTree := ov.XMLRoot() t, err := dec.Token() if err != nil { return nil, err } brokenHeader := false if head, ok := t.(xml.ProcInst); !ok || head.Target != "xml" { if ov.Strict { return nil, fmt.Errorf("Malformed XML file") } brokenHeader = true } if !brokenHeader { t, err = dec.Token() } opts := xmlbuilder.BuilderOpts{ Dec: dec, } for err == nil { switch xt := t.(type) { case xml.StartElement: setEle(&opts, xmlTree, xt, &ordrPos) xmlTree = xmlTree.CreateNode(&opts) case xml.CharData: setNode(&opts, xmlTree, xt, tree.NtChd, &ordrPos) xmlTree = xmlTree.CreateNode(&opts) case xml.Comment: setNode(&opts, xmlTree, xt, tree.NtComm, &ordrPos) xmlTree = xmlTree.CreateNode(&opts) case xml.ProcInst: setNode(&opts, xmlTree, xt, tree.NtPi, &ordrPos) xmlTree = xmlTree.CreateNode(&opts) case xml.EndElement: xmlTree = xmlTree.EndElem() case xml.Directive: if dp, ok := xmlTree.(DirectiveParser); ok { dp.Directive(xt.Copy(), dec) } } t, err = dec.Token() } if err == io.EOF { err = nil } return xmlTree, err } func setEle(opts *xmlbuilder.BuilderOpts, xmlTree xmlbuilder.XMLBuilder, ele xml.StartElement, ordrPos *int) { opts.NodePos = *ordrPos opts.Tok = ele opts.Attrs = opts.Attrs[0:0:cap(opts.Attrs)] opts.NS = make(map[xml.Name]string) opts.NodeType = tree.NtElem for i := range ele.Attr { attr := ele.Attr[i].Name val := ele.Attr[i].Value if (attr.Local == "xmlns" && attr.Space == "") || attr.Space == "xmlns" { opts.NS[attr] = val } else { opts.Attrs = append(opts.Attrs, &ele.Attr[i]) } } if nstree, ok := xmlTree.(tree.NSElem); ok { ns := make(map[xml.Name]string) for _, i := range tree.BuildNS(nstree) { ns[i.Name] = i.Value } for k, v := range opts.NS { ns[k] = v } if ns[xml.Name{Local: "xmlns"}] == "" { delete(ns, xml.Name{Local: "xmlns"}) } for k, v := range ns { opts.NS[k] = v } if xmlTree.GetNodeType() == tree.NtRoot { opts.NS[xml.Name{Space: "xmlns", Local: "xml"}] = tree.XMLSpace } } opts.AttrStartPos = len(opts.NS) + len(opts.Attrs) + *ordrPos *ordrPos = opts.AttrStartPos + 1 } func setNode(opts *xmlbuilder.BuilderOpts, xmlTree xmlbuilder.XMLBuilder, tok xml.Token, nt tree.NodeType, ordrPos *int) { opts.Tok = xml.CopyToken(tok) opts.NodeType = nt opts.NodePos = *ordrPos *ordrPos++ } goxpath-1.0-alpha3/tree/xtypes.go000066400000000000000000000037211273411034600170200ustar00rootroot00000000000000package tree import ( "fmt" "math" "strconv" "strings" ) //Boolean strings const ( True = "true" False = "false" ) //Bool is a boolean XPath type type Bool bool //ResValue satisfies the Res interface for Bool func (b Bool) String() string { if b { return True } return False } //Bool satisfies the HasBool interface for Bool's func (b Bool) Bool() Bool { return b } //Num satisfies the HasNum interface for Bool's func (b Bool) Num() Num { if b { return Num(1) } return Num(0) } //Num is a number XPath type type Num float64 //ResValue satisfies the Res interface for Num func (n Num) String() string { if math.IsInf(float64(n), 0) { if math.IsInf(float64(n), 1) { return "Infinity" } return "-Infinity" } return fmt.Sprintf("%g", float64(n)) } //Bool satisfies the HasBool interface for Num's func (n Num) Bool() Bool { return n != 0 } //Num satisfies the HasNum interface for Num's func (n Num) Num() Num { return n } //String is string XPath type type String string //ResValue satisfies the Res interface for String func (s String) String() string { return string(s) } //Bool satisfies the HasBool interface for String's func (s String) Bool() Bool { return Bool(len(s) > 0) } //Num satisfies the HasNum interface for String's func (s String) Num() Num { num, err := strconv.ParseFloat(strings.TrimSpace(string(s)), 64) if err != nil { return Num(math.NaN()) } return Num(num) } //NodeSet is a node-set XPath type type NodeSet []Node //GetNodeNum converts the node to a string-value and to a number func GetNodeNum(n Node) Num { return String(n.ResValue()).Num() } //String satisfies the Res interface for NodeSet func (n NodeSet) String() string { if len(n) == 0 { return "" } return n[0].ResValue() } //Bool satisfies the HasBool interface for node-set's func (n NodeSet) Bool() Bool { return Bool(len(n) > 0) } //Num satisfies the HasNum interface for NodeSet's func (n NodeSet) Num() Num { return String(n.String()).Num() } goxpath-1.0-alpha3/value_test.go000066400000000000000000000030071273411034600166750ustar00rootroot00000000000000package goxpath import ( "bytes" "runtime/debug" "testing" "github.com/ChrisTrenkamp/goxpath/tree/xmltree" ) func execVal(xp, x string, exp string, ns map[string]string, t *testing.T) { defer func() { if r := recover(); r != nil { t.Error("Panicked: from XPath expr: '" + xp) t.Error(r) t.Error(string(debug.Stack())) } }() res := MustParse(xp).MustExec(xmltree.MustParseXML(bytes.NewBufferString(x)), func(o *Opts) { o.NS = ns }) if res.String() != exp { t.Error("Incorrect result:'" + res.String() + "' from XPath expr: '" + xp + "'. Expecting: '" + exp + "'") return } } func TestNodeVal(t *testing.T) { p := `/test` x := `testpathtest2` exp := "testpathtest2" execVal(p, x, exp, nil, t) } func TestAttrVal(t *testing.T) { p := `/p1/@test` x := `` exp := "foo" execVal(p, x, exp, nil, t) } func TestCommentVal(t *testing.T) { p := `//comment()` x := `` exp := ` comment ` execVal(p, x, exp, nil, t) } func TestProcInstVal(t *testing.T) { p := `//processing-instruction()` x := `` exp := `test` execVal(p, x, exp, nil, t) } func TestNodeNamespaceVal(t *testing.T) { p := `/test:p1/namespace::test` x := `` exp := `http://test` execVal(p, x, exp, nil, t) }