Walk through all tree nodes? - treesitter

I'm using py-tree-sitter and this code to walk the entire node tree in sequence (code is from this github issue: https://github.com/tree-sitter/py-tree-sitter/issues/33#issuecomment-864557166):
def traverse_tree(tree: Tree):
cursor = tree.walk()
reached_root = False
while reached_root == False:
yield cursor.node
if cursor.goto_first_child():
continue
if cursor.goto_next_sibling():
continue
retracing = True
while retracing:
if not cursor.goto_parent():
retracing = False
reached_root = True
if cursor.goto_next_sibling():
retracing = False
for node in traverse_tree:
print(node)
I'm using this code for syntax highlighting and this method does not give empty tokens like whitespace and newline which are not part of another AST token.
Is this the correct way to get all the nodes in the tree?
Thanks

Another solution is to use recursion. For example, since tree-sitter does not support query syntax for missing nodes yet, one possible solution to still get them:
missing_nodes = []
def traverse_tree(node: Node):
for n in node.children:
if n.is_missing: missing_nodes.append(n)
traverse_tree(n)
traverse_tree(tree.root_node)

Related

Return keyword inside the inline function in Scala

I've heard about to not use Return keyword in Scala, because it might change the flow of the program like;
// this will return only 2 because of return keyword
List(1, 2, 3).map(value => return value * 2)
Here is my case; I've recursive case class, and using DFS to do some calculation on it. So, maximum depth could be 5. Here is the model;
case class Line(
lines: Option[Seq[Line]],
balls: Option[Seq[Ball]],
op: Option[String]
)
I'm using DFS approach to search this recursive model. But at some point, if a special value exist in the data, I want to stop iterating over the data left and return the result directly instead. Here is an example;
Line(
lines = Some(Seq(
Line(None, Some(Seq(Ball(1), Ball(3))), Some("s")),
Line(None, Some(Seq(Ball(5), Ball(2))), Some("d")),
Line(None, Some(Seq(Ball(9))), None)
)),
balls = None,
None
)
In this data, I want to return as like "NOT_OKAY" if I run into the Ball(5), which means I do not need to any operation on Ball(2) and Ball(9) anymore. Otherwise, I will apply a calculation to the each Ball(x) with the given operator.
I'm using this sort of DFS method;
def calculate(line: Line) = {
// string here is the actual result that I want, Boolean just keeps if there is a data that I don't want
def dfs(line: Line): (String, Boolean) = {
line.balls.map{_.map { ball =>
val result = someCalculationOnBall(ball)
// return keyword here because i don't want to iterate values left in the balls
if (result == "NOTREQUIRED") return ("NOT_OKAY", true)
("OKAY", false)
}}.getOrElse(
line.lines.map{_.map{ subLine =>
val groupResult = dfs(subLine)
// here is I'm using return because I want to return the result directly instead of iterating the values left in the lines
if (groupResult._2) return ("NOT_OKAY", true)
("OKAY", false)
}}
)
}
.... rest of the thing
}
In this case, I'm using return keyword in the inline functions, and change the behaviour of the inner map functions completely. I've just read somethings about not using return keyword in Scala, but couldn't make sure this will create a problem or not. Because in my case, I don't want to do any calculation if I run into a value that I don't want to see. Also I couldn't find the functional way to get rid of return keyword.
Is there any side effect like stack exception etc. to use return keyword here? I'm always open to the alternative ways. Thank you so much!

Have assertion error return respective list position instead of the object?

test("Comparison") {
val list: List[String] = List("Thing", "Entity", "Variable")
val expected: List[String] = List("Thingg", "Entityy", "Variablee")
var expectedPosition = 0
for (item <- list) {
assert(list(expectedPosition) == expected(expectedPosition))
expectedPosition += 1
}
}
In Scala, in order to make my low-level code tests more readable, I thought it would be a good idea to just use one assertion and have it iterate through a loop and increase an accumulator at the end.
This is to test more than one input at a time when the multiple inputs would more or less have similar attributes. When the assertion fails, it comes back as "Thing[]" did not equal "Thing[g]". Instead of it reporting the item in the list that failed, is there a way to get it to directly state the list position without concatenating the list position before the assertion or using a conditional statement that returns the list position? Like I would rather keep it all contained within the assert() error report.
val expected: LazyList[String] = ...
expected.zip(list)
.zipWithIndex
.foreach{case ((exp,itm),idx) =>
assert(exp == itm, s"off at index $idx")
}
//java.lang.AssertionError: assertion failed: off at index 0
expected is lazy so that it is traversed only once even though it gets zipped twice.

Filtering a collection of IO's: List[IO[Page]] scala

I am refactoring a scala http4s application to remove some pesky side effects causing my app to block. I'm replacing .unsafeRunSync with cats.effect.IO. The problem is as follows:
I have 2 lists: alreadyAccessible: IO[List[Page]] and pages: List[Page]
I need to filter out the pages that are not contained in alreadyAccessible.
Then map over the resulting list to "grant Access" in the database to these pages. (e.g. call another method that hits the database and returns an IO[Page].
val addable: List[Page] = pages.filter(p => !alreadyAccessible.contains(p))
val added: List[Page] = addable.map((p: Page) => {
pageModel.grantAccess(roleLst.head.id, p.id) match {
case Right(p) => p
}
})
This is close to what I want; However, it does not work because filter requires a function that returns a Boolean but alreadyAccessible is of type IO[List[Page]] which precludes you from removing anything from the IO monad. I understand you can't remove data from the IO so maybe transform it:
val added: List[IO[Page]] = for(page <- pages) {
val granted = alreadyAccessible.flatMap((aa: List[Page]) => {
if (!aa.contains(page))
pageModel.grantAccess(roleLst.head.id, page.id) match { case Right(p) => p }
else null
})
} yield granted
this unfortunately does not work with the following error:
Error:(62, 7) ';' expected but 'yield' found.
} yield granted
I think because I am somehow mistreating the for comprehension syntax, I just don't understand why I cannot do what I'm doing.
I know there must be a straight forward solution to such a problem, so any input or advice is greatly appreciates. Thank you for your time in reading this!
granted is going to be an IO[List[Page]]. There's no particular point in having IO inside anything else unless you truly are going to treat the actions like values and reorder them/filter them etc.
val granted: IO[List[Page]] = for {
How do you compute it? Well, the first step is to execute alreadyAccessible to get the actual list. In fact, alreadyAccessible is misnamed. It is not the list of accessible pages; it is an action that gets the list of accessible pages. I would recommend you rename it getAlreadyAccessible.
alreadyAccessible <- getAlreadyAccessible
Then you filter pages with it
val required = pages.filterNot(alreadyAccessible.contains)
Now, I cannot decipher what you're doing to these pages. I'm just going to assume you have some kind of function grantAccess: Page => IO[Page]. If you map this function over required, you will get a List[IO[Page]], which is not desirable. Instead, we should traverse with grantAccess, which will produce a IO[List[Page]] that executes each IO[Page] and then assembles all the results into a List[Page].
granted <- required.traverse(grantAccess)
And we're done
} yield granted

How to use Optional in Netlogo Extensions

I want to create a netlogo primitive that may receive a boolean or may not. Therefore I want make possible to the user that he uses the primitive of these two ways:
1:
ask Walkers [
qlearningextension:learning
]
2:
ask Walkers [
qlearningextension:learning true
]
I tried to do that with OptionalType, but I could not make it. Is it possible to do what I want? If so, how can I do it?
So OptionalType unfortunately only works with CommandBlockType. For a good example of how that works, check out the sample-scala extension (maybe that's where you saw a reference to it in the first pace). OptionalType will not work with BooleanType.
There is a secondary option, that's a little hacky. You can use RepeatableType along with setting defaultOption and minimumOption in your syntax (so NetLogo knows that 0 arguments is okay/expected). Scala code example:
object RepeatableTypeTest extends api.Command {
override def getSyntax =
commandSyntax(
right = List(BooleanType | RepeatableType),
defaultOption = Some(0),
minimumOption = Some(0)
)
def perform(args: Array[api.Argument], context: api.Context) {
println(args.length)
if (args.length > 0) {
val ref = args(0).getBoolean
println(ref)
} else {
println("No argument given!")
}
}
}
Then you just have to wrap calls with the boolean argument in parenthesis, so NetLogo knows you're not starting a new command (it expects the defaultOption without the parens):
to test
sample-scala:rep-bool
(sample-scala:rep-bool true)
(sample-scala:rep-bool false)
(sample-scala:rep-bool false true false true false)
end
The problem with this, as you can see in the example, is if your users want to they can provide extra useless booleans: (sample-scala:rep-bool false true false false true false false). If your code ignores them they won't have an effect, but they could be confusing or weird to extension users.

Pytest - skip (xfail) mixed with parametrize

is there a way to use the #incremental plugin like described att Pytest: how to skip the rest of tests in the class if one has failed? mixed with #pytest.mark.parametrize like below:
#pytest.mark.incremental
Class TestClass:
#pytest.mark.parametrize("input", data)
def test_preprocess_check(self,input):
# prerequisite for test
#pytest.mark.parametrize("input",data)
def test_process_check(self,input):
# test only if test_preprocess_check succeed
The problem i encountered is, at the first fail of test_preprocess_check with a given input of my data set, the following test_preprocess_check and test_process_check are labeled "xfail".
The behaviour i expect will be, at each new "input" of my parametrized data set, the test will act in an incremental fashion.
ex: data = [0,1,2]
if only test_preprocess_check(0) failed:
i got the following report:
1 failed, 5 xfailed
but i expect the report:
1 failed, 1 xfailed, 4 passed
Thanks
After some experiments i found a way to generalize the #incremental to works with parametrize annotation. Simply rewrite the _previousfailed argument to make it unique for each input. The argument _genid was excactly the need.
I added a #pytest.mark.incrementalparam to achieve this.
Code become:
def pytest_runtest_setup(item):
previousfailed_attr = getattr(item, "_genid",None)
if previousfailed_attr is not None:
previousfailed = getattr(item.parent, previousfailed_attr, None)
if previousfailed is not None:
pytest.xfail("previous test failed (%s)" %previousfailed.name)
previousfailed = getattr(item.parent, "_previousfailed", None)
if previousfailed is not None:
pytest.xfail("previous test failed (%s)" %previousfailed.name)
def pytest_runtest_makereport(item, call):
if "incrementalparam" in item.keywords:
if call.excinfo is not None:
previousfailed_attr = item._genid
setattr(item.parent,previousfailed_attr, item)
if "incremental" in item.keywords:
if call.excinfo is not None:
parent = item.parent
parent._previousfailed = item
It's interesting to mention that's it can't be used without parametrize cause parametrize annotation creates automatically _genid variable.
Hope this can helps others than me.