@end example
@noindent
-tree-sitter only matches arrays where the first element equals to
-the last element. To attach a predicate to a pattern, we need to
-group them together. A predicate always starts with a @samp{#}.
-Currently there are two predicates, @code{#equal} and @code{#match}.
+tree-sitter only matches arrays where the first element equals to the
+last element. To attach a predicate to a pattern, we need to group
+them together. A predicate always starts with a @samp{#}. Currently
+there are three predicates, @code{#equal}, @code{#match}, and
+@code{#pred}.
@deffn Predicate equal arg1 arg2
Matches if @var{arg1} equals to @var{arg2}. Arguments can be either
matches regular expression @var{regexp}. Matching is case-sensitive.
@end deffn
+@deffn Predicate pred fn &rest nodes
+Matches if function @var{fn} returns non-@code{nil} when passed each
+node in @var{nodes} as arguments.
+@end deffn
+
Note that a predicate can only refer to capture names that appear in
the same pattern. Indeed, it makes little sense to refer to capture
names in other patterns.
return build_pure_c_string ("#equal");
if (EQ (pattern, QCmatch))
return build_pure_c_string ("#match");
+ if (EQ (pattern, QCpred))
+ return build_pure_c_string ("#pred");
Lisp_Object opening_delimeter
= build_pure_c_string (VECTORP (pattern) ? "[" : "(");
Lisp_Object closing_delimiter
return Fnreverse (result);
}
-/* Translate a capture NAME (symbol) to the text of the captured node.
+/* Translate a capture NAME (symbol) to a node.
Signals treesit-query-error if such node is not captured. */
static Lisp_Object
-treesit_predicate_capture_name_to_text (Lisp_Object name,
+treesit_predicate_capture_name_to_node (Lisp_Object name,
struct capture_range captures)
{
Lisp_Object node = Qnil;
name, build_pure_c_string ("A predicate can only refer"
" to captured nodes in the "
"same pattern"));
+ return node;
+}
+
+/* Translate a capture NAME (symbol) to the text of the captured node.
+ Signals treesit-query-error if such node is not captured. */
+static Lisp_Object
+treesit_predicate_capture_name_to_text (Lisp_Object name,
+ struct capture_range captures)
+{
+ Lisp_Object node = treesit_predicate_capture_name_to_node (name, captures);
struct buffer *old_buffer = current_buffer;
set_buffer_internal (XBUFFER (XTS_PARSER (XTS_NODE (node)->parser)->buffer));
return false;
}
-/* About predicates: I decide to hard-code predicates in C instead of
- implementing an extensible system where predicates are translated
- to Lisp functions, and new predicates can be added by extending a
- list of functions, because I really couldn't imagine any useful
- predicates besides equal and match. If we later found out that
- such system is indeed useful and necessary, it can be easily
- added. */
+/* Handles predicate (#pred FN ARG...). Return true if FN returns
+ non-nil; return false otherwise. The arity of FN must match the
+ number of ARGs */
+static bool
+treesit_predicate_pred (Lisp_Object args, struct capture_range captures)
+{
+ if (XFIXNUM (Flength (args)) < 2)
+ xsignal2 (Qtreesit_query_error,
+ build_pure_c_string ("Predicate `pred' requires "
+ "at least two arguments, "
+ "but was only given"),
+ Flength (args));
+
+ Lisp_Object fn = Fintern (XCAR (args), Qnil);
+ Lisp_Object nodes = Qnil;
+ Lisp_Object tail = XCDR (args);
+ FOR_EACH_TAIL (tail)
+ nodes = Fcons (treesit_predicate_capture_name_to_node (XCAR (tail),
+ captures),
+ nodes);
+ nodes = Fnreverse (nodes);
+
+ return !NILP (CALLN (Fapply, fn, nodes));
+}
/* If all predicates in PREDICATES passes, return true; otherwise
return false. */
Lisp_Object fn = XCAR (predicate);
Lisp_Object args = XCDR (predicate);
if (!NILP (Fstring_equal (fn, build_pure_c_string ("equal"))))
- pass = treesit_predicate_equal (args, captures);
+ pass &= treesit_predicate_equal (args, captures);
else if (!NILP (Fstring_equal (fn, build_pure_c_string ("match"))))
- pass = treesit_predicate_match (args, captures);
+ pass &= treesit_predicate_match (args, captures);
+ else if (!NILP (Fstring_equal (fn, build_pure_c_string ("pred"))))
+ pass &= treesit_predicate_pred (args, captures);
else
xsignal3 (Qtreesit_query_error,
build_pure_c_string ("Invalid predicate"),
fn, build_pure_c_string ("Currently Emacs only supports"
- " equal and match predicate"));
+ " equal, match, and pred"
+ " predicate"));
}
/* If all predicates passed, add captures to result list. */
return pass;
DEFSYM (QCanchor, ":anchor");
DEFSYM (QCequal, ":equal");
DEFSYM (QCmatch, ":match");
+ DEFSYM (QCpred, ":pred");
DEFSYM (Qnot_found, "not-found");
DEFSYM (Qsymbol_error, "symbol-error");