Skip to content
This repository has been archived by the owner on Mar 8, 2020. It is now read-only.

getting an attribute doesn't work #359

Open
smacker opened this issue Feb 8, 2019 · 13 comments
Open

getting an attribute doesn't work #359

smacker opened this issue Feb 8, 2019 · 13 comments
Labels

Comments

@smacker
Copy link
Contributor

smacker commented Feb 8, 2019

According to documentation getting an attribute works like this:

document.evaluate("//div[@role='main']/@class", document, null, XPathResult.ANY_TYPE, null );

and it produces

XPathResult { resultType: 4, invalidIteratorState: false }

(you can run this on this page in console)

As in the new api everything is a node, I expect to get a node that I can convert to a string using .(nodes.String) (in go-client, not sure about other because only go client is available right now).

I use this code:

package main

import (
	"fmt"

	bblfsh "gopkg.in/bblfsh/client-go.v3"
	"gopkg.in/bblfsh/client-go.v3/tools"
)

var code = `package main
func myname() {}`

func main() {
	client, err := bblfsh.NewClient("0.0.0.0:9432")
	if err != nil {
		panic(err)
	}

	res, _, err := client.NewParseRequest().Language("go").Content(code).UAST()
	if err != nil {
		panic(err)
	}

	fmt.Println("Name node:")
	name, err := tools.Filter(res, "//uast:FunctionGroup/*/uast:Alias/Name")
	if err != nil {
		panic(err)
	}
	if !name.Next() {
		panic("shouldn't be empty")
	}
	fmt.Println(name.Node())

	fmt.Println("Get attribute:")
	name, err = tools.Filter(res, "//uast:FunctionGroup/*/uast:Alias/Name/@Name")
	if err != nil {
		panic(err)
	}
	if !name.Next() {
		panic("shouldn't be empty")
	}
	fmt.Println(name.Node())
}
$ go run does_not_work.go
Name node:
map[@type:uast:Identifier Name:myname @pos:map[start:map[@type:uast:Position col:6 line:2 offset:18] @type:uast:Positions end:map[line:2 offset:24 @type:uast:Position col:12]]]
Get attribute:
panic: shouldn't be empty
@dennwc dennwc added the bug label Feb 8, 2019
@juanjux
Copy link
Contributor

juanjux commented Feb 11, 2019

Works for me with this query:

//uast:FunctionGroup/*/uast:Alias/Name/uast:Identifier/Name

 %  ~/borrame  go run pok.go
Name node:
map[@pos:map[end:map[@type:uast:Position col:12 line:2 offset:24] start:map[col:6 line:2 offset:18 @type:uast:Position] @type:uast:Positions] @type:uast:Identifier Name:myname]
Get attribute:
myname

@smacker
Copy link
Contributor Author

smacker commented Feb 11, 2019

It's not clear for me why your query looks like that.
//uast:FunctionGroup/*/uast:Alias/Name already returns Identifier node, right? Why do I need to query it once again?

@juanjux
Copy link
Contributor

juanjux commented Feb 11, 2019

Name is the property inside the uast:Alias, now to continue "browsing" the tree to get the token you need to go trough the uast:Identifier to get its Name property because just the identifier is an object that you can't directly convert to a string.

@smacker
Copy link
Contributor Author

smacker commented Feb 11, 2019

Please help me understand what property is. I don't see such thing in specification.

The closest thing I see there is "attribute" but it has different syntax to query (starts with @).

If by property you mean just another level of nodes in the tree when I print results of //uast:FunctionGroup/*/uast:Alias/Name it should show me a list is that correct?

Sorry for maybe stupid question but I'm not that good in xpath and all my previous experience doesn't match with what I see with bblfsh at all and I'm really struggling.

As an example when I follow spec and use js on this page:

Get a node:

document.evaluate("//div[@id='issuecomment-462328405']//p[1]/code", document, null, XPathResult.ANY_TYPE, null).iterateNext()

Get a text node:

document.evaluate("//div[@id='issuecomment-462328405']//p[1]/code/text()", document, null, XPathResult.ANY_TYPE, null).iterateNext()

Get an attribute:

document.evaluate("//div[@id='issuecomment-462328405']/div/@class", document, null, XPathResult.ANY_TYPE, null).iterateNext()

There is no "property" in any of these queries.

@dennwc
Copy link
Member

dennwc commented Feb 11, 2019

@smacker Juanjo is referring to a UAST schema for Alias and Identifier, both have a Name property. But since the Name of an Alias is pointing to an object, it's projected to XML as a /Name/. Then there is a second indirection into an Identifier node that has a Name property as well.

@juanjux
Copy link
Contributor

juanjux commented Feb 11, 2019

Yes, I was speaking of properties in the JSON sense. No expert in XPath either.

@smacker
Copy link
Contributor Author

smacker commented Feb 11, 2019

@dennwc could you please explain what is Name property in XML? From what you said I understand the tree as XML like this:

<uast:FunctionGroup>
  <uast:Alias>
    <Name> -- I'll call this one Name1 below to avoid misunderstanding
      <uast:Identifier>
         <Name> --  special kind of node that can be converted to a string
      </uast:Identifier>
    </Name>
  </uast:Alias>
</uast:FunctionGroup>

I also tried to explore the tree a little bit more using this code:

	fmt.Println("Name node:")
	name, err := tools.Filter(res, "//uast:FunctionGroup/*/uast:Alias/Name")
	if err != nil {
		panic(err)
	}
	if !name.Next() {
		panic("shouldn't be empty")
	}
	nodeName := name.Node()
	fmt.Println(uast.TypeOf(nodeName), nodeName)

	fmt.Println("uast:Identifier node:")
	name, err = tools.Filter(res, "//uast:FunctionGroup/*/uast:Alias/Name/uast:Identifier")
	if err != nil {
		panic(err)
	}
	if !name.Next() {
		panic("shouldn't be empty")
	}
	identNode := name.Node()
	fmt.Println(uast.TypeOf(identNode), identNode)

	fmt.Println("same?", nodeName.SameAs(identNode))

As the result I see:

Name node:
uast:Identifier map[@pos:map[@type:uast:Positions end:map[offset:24 @type:uast:Position col:12 line:2] start:map[line:2 offset:18 @type:uast:Position col:6]] @type:uast:Identifier Name:myname]

uast:Identifier node:
uast:Identifier map[@type:uast:Identifier Name:myname @pos:map[@type:uast:Positions end:map[@type:uast:Position col:12 line:2 offset:24] start:map[@type:uast:Position col:6 line:2 offset:18]]]

same? true

So Name1 and uast:Identifier are actually the same nodes which is super weird.

@juanjux
Copy link
Contributor

juanjux commented Feb 11, 2019

It makes sense. Once you write a / you're telling XPath that you're "walking" over the nodes and then you've to specify the node types to walk so if you check the node types in the XML they follow this path:

uast:FunctionGroup/uast:Alias/Name/uast:Identifier/Name

So, to walk over to the last Name you need to go trough the uast:Identifier (or use a //*) but if you stop at the first Name and don't add a trailing slash, you get the Identifier object/node. But for example if you did a second filtering over that node to get the last Name, you would need to add the /uast.Identifier or start with //.

@smacker
Copy link
Contributor Author

smacker commented Feb 11, 2019

if you stop at the first Name and don't add a trailing slash, you get the Identifier object/node

why does it return me Identifier node, not the Name1 node?

I tried the XML from above online and it works returning Name1 node, not Identifier node:
screenshot 2019-02-11 at 18 04 31

@juanjux
Copy link
Contributor

juanjux commented Feb 11, 2019

I'm not sure of the exact mapping to XML on the SDK (@dennwc can you help us on this?) but I know it makes sense related to how the UAST is organized. If you think of it as as C structs. The Alias struct has a Name pointer whose value is a Identifier struct. This, also have a Name pointer that points to a string value.

@creachadair
Copy link
Contributor

There may be a more general question here as to whether aliases should be "collapsed" during traversal (there are good arguments on either side).

@smacker
Copy link
Contributor Author

smacker commented Feb 11, 2019

I would say it's more important at least to document how AST maps to XML.
It was very unexpected to see aliases (afaik xml doesn't have them) which leads to issues like this one.

@vmarkovtsev
Copy link

This is unexpected for me as well.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

5 participants