nvim-treesitter: query for jsdoc description within javascript file - neovim

I'm experimenting with a testing regime for my neovim plugin regexplainer. The idea is to put a JSDoc docblock containing the expected output on top of each query.
/**
* #group CaptureGroups
* **0-9** (_>= 0x_)
* capture group 1:
* **0-9**
*/
/\d*(\d)/;
My test file would then iterate through the file, getting each regex from the javascript tree, along with it's expected output from the comment. I could then run my plugin on the regexp and check the output against the comment.
I wrote this function to query the document for comments with regexp following.
function M.get_regexes_with_descriptions()
local log = require'regexplainer.utils'.debug
local query_js = vim.treesitter.query.parse_query('javascript', [[
(comment) #comment
(expression_statement
(regex
(regex_pattern) #regex))
]])
local query_jsdoc = vim.treesitter.query.parse_query('jsdoc', [[
(tag_name) #tag_name
(description) #description
]])
local parser = vim.treesitter.get_parser(0, 'javascript')
local tree, other = unpack(parser:parse())
for id, node, metadata in query_js:iter_captures(tree:root(), 0) do
local name = query_js.captures[id] -- name of the capture in the query
if node:type() == 'comment' then
for cid, cnode, cmetadata in node:iter_children() do
local cname = query_jsdoc.captures[cid] -- name of the capture in the query
log(get_info_on_capture(cid, cnode, cname, cmetadata))
end
end
end
end
I expect this to log the tag name and description nodes from the 'jsdoc' grammar, but i actually get nothing
So how do I "query down" into the embedded JSDoc part of the tree? I tried this query, but got a query: invalid node type at position 10 error when parsing the query:
(comment (description) #desc) #comment
(expression_statement
(regex
(regex_pattern) #regex))
The TSPlayground output for the file in question looks like this:
comment [0, 0] - [5, 3]
tag [1, 3] - [4, 12]
tag_name [1, 3] - [1, 9]
description [1, 10] - [4, 12]
expression_statement [6, 0] - [6, 10]
regex [6, 0] - [6, 9]
pattern: regex_pattern [6, 1] - [6, 8]
term [6, 1] - [6, 8]
character_class_escape [6, 1] - [6, 3]
zero_or_more [6, 3] - [6, 4]
anonymous_capturing_group [6, 4] - [6, 8]
pattern [6, 5] - [6, 7]
term [6, 5] - [6, 7]
character_class_escape [6, 5] - [6, 7]
EDIT: A Workaround
I developed this workaround but it's a bit unsatisfying, I'd prefer to get the comment's contents along with the regexp in a single query
function M.get_regexes_with_descriptions()
local log = require'regexplainer.utils'.debug
local parsers = require "nvim-treesitter.parsers"
local query_js = vim.treesitter.query.parse_query('javascript', [[
(expression_statement
(regex
(regex_pattern) #regex)) #expr
]])
local query_jsdoc = vim.treesitter.query.parse_query('jsdoc', [[
(tag
(tag_name) #tag_name
((description) #description
;; (#eq? #tag_name "example")
))
]])
local parser = parsers.get_parser(0)
local tree = unpack(parser:parse())
local caps = {}
for id, node in query_js:iter_captures(tree:root(), 0) do
local cap = {}
local name = query_js.captures[id] -- name of the capture in the query
log(id, name, ts_utils.get_node_text(node))
if name == 'expr' then
cap.regex = node:named_child('pattern')
cap.comment = node:prev_sibling()
table.insert(caps, cap)
end
end
local results = {}
for _, cap in ipairs(caps) do
local result = {}
result.text = ts_utils.get_node_text(cap.regex)
local comment_str = table.concat(ts_utils.get_node_text(cap.comment), '\n')
local jsdoc_parser = vim.treesitter.get_string_parser(comment_str, 'jsdoc')
local jsdoc_tree = jsdoc_parser:parse()[1]
for id, ch, metadata in query_jsdoc:iter_captures(jsdoc_tree:root()) do
if query_jsdoc.captures[id] == 'description' then
result.description = table.concat(
vim.tbl_map(function(line)
return line:gsub('^%s+%*', '')
end, ts_utils.get_node_text(ch)), '\n')
end
end
table.insert(results, result)
end
return results
end

Related

How to pick an element from matrix (list of list in python) based on decision variables (one for row, and one for column) | OR-Tools, Python

I am new to constraint programming and OR-Tools. A brief about the problem. There are 8 positions, for each position I need to decide which move of type A (move_A) and which move of type B (move_B) should be selected such that the value achieved from the combination of the 2 moves (at each position) is maximized. (This is only a part of the bigger problem though). And I want to use AddElement approach to do the sub setting.
Please see the below attempt
from ortools.sat.python import cp_model
model = cp_model.CpModel()
# value achieved from combination of different moves of type A
# (moves_A (rows)) and different moves of type B (moves_B (columns))
# for e.g. 2nd move of type A and 3rd move of type B will give value = 2
value = [
[ -1, 5, 3, 2, 2],
[ 2, 4, 2, -1, 1],
[ 4, 4, 0, -1, 2],
[ 5, 1, -1, 2, 2],
[ 0, 0, 0, 0, 0],
[ 2, 1, 1, 2, 0]
]
# 6 moves of type A
num_moves_A = len(value)
# 5 moves of type B
num_moves_B = len(value[0])
num_positions = 8
type_move_A_position = [model.NewIntVar(0, num_moves_A - 1, f"move_A[{i}]") for i in range(num_positions)]
type_move_B_position = [model.NewIntVar(0, num_moves_B - 1, f"move_B[{i}]") for i in range(num_positions)]
value_position = [model.NewIntVar(0, 10, f"value_position[{i}]") for i in range(num_positions)]
# I am getting an error when I run the below
objective_terms = []
for i in range(num_positions):
model.AddElement(type_move_B_position[i], value[type_move_A_position[i]], value_position[i])
objective_terms.append(value_position[i])
The error is as follows:
Traceback (most recent call last):
File "<ipython-input-65-3696379ce410>", line 3, in <module>
model.AddElement(type_move_B_position[i], value[type_move_A_position[i]], value_position[i])
TypeError: list indices must be integers or slices, not IntVar
In MiniZinc the below code would have worked
var int: obj = sum(i in 1..num_positions ) (value [type_move_A_position[i], type_move_B_position[i]])
I know in OR-Tools we will have to create some intermediary variables to store results first, so the above approach of minizinc will not work. But I am struggling to do so.
I can always create a 2 matrix of binary binary variables one for num_moves_A * num_positions and the other for num_moves_B * num_positions, add re;evant constraints and achieve the purpose
But I want to learn how to do the same thing via AddElement constraint
Any help on how to re-write the AddElement snippet is highly appreciated. Thanks.
AddElement is 1D only.
The way it is translated from minizinc to CP-SAT is to create an intermediate variable p == index1 * max(index2) + index2 and use it in an element constraint with a flattened matrix.
Following Laurent's suggestion (using AddElement constraint):
from ortools.sat.python import cp_model
model = cp_model.CpModel()
# value achieved from combination of different moves of type A
# (moves_A (rows)) and different moves of type B (moves_B (columns))
# for e.g. 2 move of type A and 3 move of type B will give value = 2
value = [
[-1, 5, 3, 2, 2],
[2, 4, 2, -1, 1],
[4, 4, 0, -1, 2],
[5, 1, -1, 2, 2],
[0, 0, 0, 0, 0],
[2, 1, 1, 2, 0],
]
min_value = min([min(i) for i in value])
max_value = max([max(i) for i in value])
# 6 moves of type A
num_moves_A = len(value)
# 5 moves of type B
num_moves_B = len(value[0])
# number of positions
num_positions = 5
# flattened matrix of values
value_flat = [value[i][j] for i in range(num_moves_A) for j in range(num_moves_B)]
# flattened indices
flatten_indices = [
index1 * len(value[0]) + index2
for index1 in range(len(value))
for index2 in range(len(value[0]))
]
type_move_A_position = [
model.NewIntVar(0, num_moves_A - 1, f"move_A[{i}]") for i in range(num_positions)
]
model.AddAllDifferent(type_move_A_position)
type_move_B_position = [
model.NewIntVar(0, num_moves_B - 1, f"move_B[{i}]") for i in range(num_positions)
]
model.AddAllDifferent(type_move_B_position)
# below intermediate decision variable is created which
# will store index corresponding to the selected move of type A and
# move of type B for each position
# this will act as index in the AddElement constraint
flatten_index_num = [
model.NewIntVar(0, len(flatten_indices), f"flatten_index_num[{i}]")
for i in range(num_positions)
]
# another intermediate decision variable is created which
# will store value corresponding to the selected move of type A and
# move of type B for each position
# this will act as the target in the AddElement constraint
value_position_index_num = [
model.NewIntVar(min_value, max_value, f"value_position_index_num[{i}]")
for i in range(num_positions)
]
objective_terms = []
for i in range(num_positions):
model.Add(
flatten_index_num[i]
== (type_move_A_position[i] * len(value[0])) + type_move_B_position[i]
)
model.AddElement(flatten_index_num[i], value_flat, value_position_index_num[i])
objective_terms.append(value_position_index_num[i])
model.Maximize(sum(objective_terms))
# Solve
solver = cp_model.CpSolver()
status = solver.Solve(model)
solver.ObjectiveValue()
for i in range(num_positions):
print(
str(i)
+ "--"
+ str(solver.Value(type_move_A_position[i]))
+ "--"
+ str(solver.Value(type_move_B_position[i]))
+ "--"
+ str(solver.Value(value_position_index_num[i]))
)
The below version uses AddAllowedAssignments constraint to achieve the same purpose (per Laurent's alternate approach) :
from ortools.sat.python import cp_model
model = cp_model.CpModel()
# value achieved from combination of different moves of type A
# (moves_A (rows)) and different moves of type B (moves_B (columns))
# for e.g. 2 move of type A and 3 move of type B will give value = 2
value = [
[-1, 5, 3, 2, 2],
[2, 4, 2, -1, 1],
[4, 4, 0, -1, 2],
[5, 1, -1, 2, 2],
[0, 0, 0, 0, 0],
[2, 1, 1, 2, 0],
]
min_value = min([min(i) for i in value])
max_value = max([max(i) for i in value])
# 6 moves of type A
num_moves_A = len(value)
# 5 moves of type B
num_moves_B = len(value[0])
# number of positions
num_positions = 5
type_move_A_position = [
model.NewIntVar(0, num_moves_A - 1, f"move_A[{i}]") for i in range(num_positions)
]
model.AddAllDifferent(type_move_A_position)
type_move_B_position = [
model.NewIntVar(0, num_moves_B - 1, f"move_B[{i}]") for i in range(num_positions)
]
model.AddAllDifferent(type_move_B_position)
value_position = [
model.NewIntVar(min_value, max_value, f"value_position[{i}]")
for i in range(num_positions)
]
tuples_list = []
for i in range(num_moves_A):
for j in range(num_moves_B):
tuples_list.append((i, j, value[i][j]))
for i in range(num_positions):
model.AddAllowedAssignments(
[type_move_A_position[i], type_move_B_position[i], value_position[i]],
tuples_list,
)
model.Maximize(sum(value_position))
# Solve
solver = cp_model.CpSolver()
status = solver.Solve(model)
solver.ObjectiveValue()
for i in range(num_positions):
print(
str(i)
+ "--"
+ str(solver.Value(type_move_A_position[i]))
+ "--"
+ str(solver.Value(type_move_B_position[i]))
+ "--"
+ str(solver.Value(value_position[i]))
)

How to express this"a[0:3, 0:3]" in python as c++(pybind11)?

Recently, when using pybind11, I encountered how to use python code a[0: 3,0: 3] to achieve this problem. There is currently a slice function, but I did not find the relevant user manual, resulting in the incorrect access.
ps: I have tried this way you said before, but when I printed out the matrix, I found it was not right. I don’t know the reason. Please help me. Thank you very much.
cout result
py::scoped_interpreter guard{};
py::module np = py::module::import("numpy");
py::object random = np.attr("random");
py::module sys = py::module::import("sys");
py::print(sys.attr("path"));
py::module scipy = py::module::import("scipy.ndimage");
// get scipy.optimize.curve_fit
py::function affine_transform = scipy.attr("affine_transform");
py::array_t<float> new_affine = np.attr("eye")(4);
py::array_t<float> new_af = new_affine[py::make_tuple(py::slice(0, 3, 1), py::slice(0, 3, 1))];
std::cout << numpy_to_cv_mat(new_affine) << endl;
std::cout << new_af.size() << endl;
std::cout << numpy_to_cv_mat(new_af) << endl;
cv::Mat numpy_to_cv_mat(py::array_t<float>& input) { py::buffer_info buf = input.request(); cv::Mat mat(buf.shape[0], buf.shape[1], CV_32FC1, (float*)buf.ptr); return mat; }
There are a few details required need here. First, py::slice(start, stop, step) creates a slice object via pybind11, like what would be created by slice(start, stop, step) in Python.
Second, given a py::array object a, the [] operator does work for slicing in C++ (a[py::slice(s0,s1,st)]) but there is a big caveat: a[] allows (and compiles) with multiple arguments, but only one argument is actually used for slicing, so a[slice(...), slice(...)] only applies the slice on the first dimension.
For multi-dimensional slicing, the [] operator must be passed a py::tuple of py::slice objects. For example, a[0:3,0:3] in Python would be translated to the following in C++:
// a is py::array
a[py::make_tuple(py::slice(0,3,1), py::slice(0,3,1))]
Putting this together, here's a full example which creates and slices a 2D array based on start/stop inputs:
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
namespace py = pybind11;
py::array do_slice(py::array a, py::int_ start, py::int_ stop) {
auto res = a[py::make_tuple(py::slice(start, stop, 1), py::slice(start, stop, 1))];
return res;
}
PYBIND11_MODULE(ex, m) {
m.def("do_slice", &do_slice);
}
Some usage examples after compiling:
>>> import numpy as np
>>> a = np.arange(16).reshape(4,4)
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
>>> import ex
>>> ex.do_slice(a, 0, 1)
array([[0]])
>>> ex.do_slice(a, 0, 2)
array([[0, 1],
[4, 5]])
>>> ex.do_slice(a, 0, 3)
array([[ 0, 1, 2],
[ 4, 5, 6],
[ 8, 9, 10]])

convert elements in list to nested lists in list

I have a list which is needed to be converted to nested lists in a list
my_list = [2,5,6,7,8,15,34,56,78]
I need list as
final_list = [[2,5,6],[7,8,15],[34,56,78]]
I wrote code using for loop with length command and range command, I know there is error with range function, but I couldn't figure it out.
my_list = [2,5,6,7,8,15,34,56,78]
max_split = 3
final_list = [[len(my_list) for _ in range(max_split)] for _ in range(max_split)]
print(final_list)
But the output I get is [[9,9,9],[9,9,9],[9,9,9]]
You can try following code
my_list = [2,5,6,7,8,15,34,56,78]
max_split = 3
final_list = [my_list[i:i + max_split ] for i in range(0, len(my_list), max_split )]
print(final_list)
Demo.
If you use the indexes returned by the for loops, you can use them to count through the indexes in your list like this:
my_list = [2,5,6,7,8,15,34,56,78]
max_split = 3
final_list = [[my_list[i+3*j] for i in range(max_split)] for j in range(max_split)]
print(final_list)
Output:
[[2, 5, 6], [7, 8, 15], [34, 56, 78]]

Convert string elements to int elements in an array in swift

I have a string with the following format:
var cadenaCoordenadas = """
1,1
1,3
4,1
5,1
1,5
1,6
2,5
0,0
"""
What I want is that each line is in the following format (in an array) to manipulate it (with Int data types as I will do operations with the new string):
[1,1]
I have the following code:
var arregloEntradas = cadenaCoordenadas.split(separator: "\n")
print("primer Arreglo: ", arregloEntradas)
for i in stride(from: 0, through:arregloEntradas.count - 1, by: 1){
let arregloEntradasFinal = arregloEntradas[i].split(separator: ",")
print(arregloEntradasFinal)
}
and I get the result of this:
this is the result
as you can see, the array elements are of string type, however I require them to be of Int type:
[1,1]
[1,3]
[4,1]
...
I hope you can help me, thank you in advance.
Here's one approach using some splitting and mapping:
var cadenaCoordenadas = """
1,1
1,3
4,1
5,1
1,5
1,6
2,5
0,0
"""
let arregloEntradasFinal = cadenaCoordenadas.split(separator: "\n")
.map { $0.split(separator: ",").compactMap { Int($0) } }
print(arregloEntradasFinal)
Output:
[[1, 1], [1, 3], [4, 1], [5, 1], [1, 5], [1, 6], [2, 5], [0, 0]]
var arregloEntradas = cadenaCoordenadas.split(separator: "\n")
print("primer Arreglo: ", arregloEntradas)
for i in stride(from: 0, through:arregloEntradas.count - 1, by: 1){
let arregloEntradasFinal = arregloEntradas[i].split(separator: ",").map { Int(String($0)) }
print(arregloEntradasFinal)
}
What you're getting in arregloEntradasFinal is correct since you're processing the string array. Later, when you want to use arregloEntradasFinal again, you should again split a string by a comma separator from arregloEntradasFinal and use the individual Int value. For example:
let index = 0 // You can also loop through the array
let values = arregloEntradasFinal[index].split(separator: ",")
let num1 = Int(values.first ?? 0) // If no value then returns 0
let num2 = Int(values.last ?? 0) // If no value then returns 0
Note - this is one of the way without using the map function.

Sub-titles for matrix columns in table

Here's my toy example:
t = table([1,2,3;4,5,6;7,8,9],[10,11,12;13,14,15;16,17,18]);
t.Properties.VariableNames = {'system1', 'system2'};
t.Properties.RowNames = {'obs1', 'obs2', 'obs3'};
I am wondering if it's possible to assign sub titles to the three columns of every variable, such as {'min', 'mean', 'max'}?
You can put those subtitles within the variables using a cell array like this:
t = table({'min', 'mean', 'max'; 1, 2, 3; 4, 5, 6; 7, 8, 9},...
{'min', 'mean', 'max'; 10, 11, 12; 13, 14, 15; 16, 17, 18});
t.Properties.VariableNames = {'system1', 'system2'};
t.Properties.RowNames = {'.','obs1', 'obs2', 'obs3'};
%if you don't like dot (.) as a row name, replace it with char(8203) to have nameless row
which will give:
t =
4×2 table
system1 system2
________________________ ________________________
. 'min' 'mean' 'max' 'min' 'mean' 'max'
obs1 [ 1] [ 2] [ 3] [ 10] [ 11] [ 12]
obs2 [ 4] [ 5] [ 6] [ 13] [ 14] [ 15]
obs3 [ 7] [ 8] [ 9] [ 16] [ 17] [ 18]
If you're looking for functional solution (e.g. t.system1.min) You can nest sub-tables for system1 and system2 with {'min', 'mean', 'max'} as Variable Names. Visually it won't be as useful as other solutions.
dat1 = [1,2,3;4,5,6;7,8,9];
dat2 = [10,11,12;13,14,15;16,17,18];
s1 = table(dat1(:,1),dat1(:,2),dat1(:,3));
s2 = table(dat2(:,1),dat2(:,2),dat2(:,3));
s1.Properties.VariableNames = {'min','mean','max'};
s1.Properties.RowNames = {'obs1', 'obs2', 'obs3'};
s2.Properties.VariableNames = {'min','mean','max'};
s2.Properties.RowNames = {'obs1', 'obs2', 'obs3'};
t = table(s1,s2);
t.Properties.VariableNames = {'system1', 'system2'};
t.Properties.RowNames = {'obs1', 'obs2', 'obs3'};