Detailed changes
@@ -6,7 +6,6 @@ use std::{
fn main() {
generate_dispatch_bindings();
- compile_context_predicate_parser();
compile_metal_shaders();
generate_shader_bindings();
}
@@ -30,17 +29,6 @@ fn generate_dispatch_bindings() {
.expect("couldn't write dispatch bindings");
}
-fn compile_context_predicate_parser() {
- let dir = PathBuf::from("./grammars/context-predicate/src");
- let parser_c = dir.join("parser.c");
-
- println!("cargo:rerun-if-changed={}", &parser_c.to_str().unwrap());
- cc::Build::new()
- .include(&dir)
- .file(parser_c)
- .compile("tree_sitter_context_predicate");
-}
-
const SHADER_HEADER_PATH: &str = "./src/platform/mac/shaders/shaders.h";
fn compile_metal_shaders() {
@@ -1,2 +0,0 @@
-/node_modules
-/build
@@ -1,20 +0,0 @@
-[package]
-name = "tree-sitter-context-predicate"
-description = "context-predicate grammar for the tree-sitter parsing library"
-version = "0.0.1"
-keywords = ["incremental", "parsing", "context-predicate"]
-categories = ["parsing", "text-editors"]
-repository = "https://github.com/tree-sitter/tree-sitter-javascript"
-edition = "2021"
-license = "MIT"
-build = "bindings/rust/build.rs"
-include = ["bindings/rust/*", "grammar.js", "queries/*", "src/*"]
-
-[lib]
-path = "bindings/rust/lib.rs"
-
-[dependencies]
-tree-sitter = "0.20"
-
-[build-dependencies]
-cc = "1.0"
@@ -1,18 +0,0 @@
-{
- "targets": [
- {
- "target_name": "tree_sitter_context_predicate_binding",
- "include_dirs": [
- "<!(node -e \"require('nan')\")",
- "src"
- ],
- "sources": [
- "src/parser.c",
- "bindings/node/binding.cc"
- ],
- "cflags_c": [
- "-std=c99",
- ]
- }
- ]
-}
@@ -1,30 +0,0 @@
-#include "nan.h"
-#include "tree_sitter/parser.h"
-#include <node.h>
-
-using namespace v8;
-
-extern "C" TSLanguage *tree_sitter_context_predicate();
-
-namespace {
-
-NAN_METHOD(New) {}
-
-void Init(Local<Object> exports, Local<Object> module) {
- Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
- tpl->SetClassName(Nan::New("Language").ToLocalChecked());
- tpl->InstanceTemplate()->SetInternalFieldCount(1);
-
- Local<Function> constructor = Nan::GetFunction(tpl).ToLocalChecked();
- Local<Object> instance =
- constructor->NewInstance(Nan::GetCurrentContext()).ToLocalChecked();
- Nan::SetInternalFieldPointer(instance, 0, tree_sitter_context_predicate());
-
- Nan::Set(instance, Nan::New("name").ToLocalChecked(),
- Nan::New("context_predicate").ToLocalChecked());
- Nan::Set(module, Nan::New("exports").ToLocalChecked(), instance);
-}
-
-NODE_MODULE(tree_sitter_context_predicate_binding, Init)
-
-} // namespace
@@ -1,19 +0,0 @@
-try {
- module.exports = require("../../build/Release/tree_sitter_context_predicate_binding");
-} catch (error1) {
- if (error1.code !== 'MODULE_NOT_FOUND') {
- throw error1;
- }
- try {
- module.exports = require("../../build/Debug/tree_sitter_context_predicate_binding");
- } catch (error2) {
- if (error2.code !== 'MODULE_NOT_FOUND') {
- throw error2;
- }
- throw error1
- }
-}
-
-try {
- module.exports.nodeTypeInfo = require("../../src/node-types.json");
-} catch (_) {}
@@ -1,40 +0,0 @@
-fn main() {
- let src_dir = std::path::Path::new("src");
-
- let mut c_config = cc::Build::new();
- c_config.include(&src_dir);
- c_config
- .flag_if_supported("-Wno-unused-parameter")
- .flag_if_supported("-Wno-unused-but-set-variable")
- .flag_if_supported("-Wno-trigraphs");
- let parser_path = src_dir.join("parser.c");
- c_config.file(&parser_path);
-
- // If your language uses an external scanner written in C,
- // then include this block of code:
-
- /*
- let scanner_path = src_dir.join("scanner.c");
- c_config.file(&scanner_path);
- println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
- */
-
- c_config.compile("parser");
- println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap());
-
- // If your language uses an external scanner written in C++,
- // then include this block of code:
-
- /*
- let mut cpp_config = cc::Build::new();
- cpp_config.cpp(true);
- cpp_config.include(&src_dir);
- cpp_config
- .flag_if_supported("-Wno-unused-parameter")
- .flag_if_supported("-Wno-unused-but-set-variable");
- let scanner_path = src_dir.join("scanner.cc");
- cpp_config.file(&scanner_path);
- cpp_config.compile("scanner");
- println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
- */
-}
@@ -1,52 +0,0 @@
-//! This crate provides context_predicate language support for the [tree-sitter][] parsing library.
-//!
-//! Typically, you will use the [language][language func] function to add this language to a
-//! tree-sitter [Parser][], and then use the parser to parse some code:
-//!
-//! ```
-//! let code = "";
-//! let mut parser = tree_sitter::Parser::new();
-//! parser.set_language(tree_sitter_context_predicate::language()).expect("Error loading context_predicate grammar");
-//! let tree = parser.parse(code, None).unwrap();
-//! ```
-//!
-//! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
-//! [language func]: fn.language.html
-//! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html
-//! [tree-sitter]: https://tree-sitter.github.io/
-
-use tree_sitter::Language;
-
-extern "C" {
- fn tree_sitter_context_predicate() -> Language;
-}
-
-/// Get the tree-sitter [Language][] for this grammar.
-///
-/// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
-pub fn language() -> Language {
- unsafe { tree_sitter_context_predicate() }
-}
-
-/// The content of the [`node-types.json`][] file for this grammar.
-///
-/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types
-pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json");
-
-// Uncomment these to include any queries that this grammar contains
-
-// pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm");
-// pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm");
-// pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm");
-// pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm");
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn test_can_load_grammar() {
- let mut parser = tree_sitter::Parser::new();
- parser
- .set_language(super::language())
- .expect("Error loading context_predicate language");
- }
-}
@@ -1,49 +0,0 @@
-==================
-Identifiers
-==================
-
-abc12
-
----
-
-(source (identifier))
-
-==================
-Negation
-==================
-
-!abc
-
----
-
-(source (not (identifier)))
-
-==================
-And/Or
-==================
-
-a || b && c && d
-
----
-
-(source
- (or
- (identifier)
- (and
- (and (identifier) (identifier))
- (identifier))))
-
-==================
-Expressions
-==================
-
-a && (b == c || d != e)
-
----
-
-(source
- (and
- (identifier)
- (parenthesized (or
- (equal (identifier) (identifier))
- (not_equal (identifier) (identifier))))))
@@ -1,31 +0,0 @@
-module.exports = grammar({
- name: 'context_predicate',
-
- rules: {
- source: $ => $._expression,
-
- _expression: $ => choice(
- $.identifier,
- $.not,
- $.and,
- $.or,
- $.equal,
- $.not_equal,
- $.parenthesized,
- ),
-
- identifier: $ => /[A-Za-z0-9_-]+/,
-
- not: $ => prec(3, seq("!", field("expression", $._expression))),
-
- and: $ => prec.left(2, seq(field("left", $._expression), "&&", field("right", $._expression))),
-
- or: $ => prec.left(1, seq(field("left", $._expression), "||", field("right", $._expression))),
-
- equal: $ => seq(field("left", $.identifier), "==", field("right", $.identifier)),
-
- not_equal: $ => seq(field("left", $.identifier), "!=", field("right", $.identifier)),
-
- parenthesized: $ => seq("(", field("expression", $._expression), ")"),
- }
-});
@@ -1,44 +0,0 @@
-{
- "name": "tree-sitter-context-predicate",
- "lockfileVersion": 2,
- "requires": true,
- "packages": {
- "": {
- "name": "tree-sitter-context-predicate",
- "dependencies": {
- "nan": "^2.14.0"
- },
- "devDependencies": {
- "tree-sitter-cli": "^0.19.5"
- }
- },
- "node_modules/nan": {
- "version": "2.14.2",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
- "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
- },
- "node_modules/tree-sitter-cli": {
- "version": "0.19.5",
- "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.19.5.tgz",
- "integrity": "sha512-kRzKrUAwpDN9AjA3b0tPBwT1hd8N2oQvvvHup2OEsX6mdsSMLmAvR+NSqK9fe05JrRbVvG8mbteNUQsxlMQohQ==",
- "dev": true,
- "hasInstallScript": true,
- "bin": {
- "tree-sitter": "cli.js"
- }
- }
- },
- "dependencies": {
- "nan": {
- "version": "2.14.2",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
- "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
- },
- "tree-sitter-cli": {
- "version": "0.19.5",
- "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.19.5.tgz",
- "integrity": "sha512-kRzKrUAwpDN9AjA3b0tPBwT1hd8N2oQvvvHup2OEsX6mdsSMLmAvR+NSqK9fe05JrRbVvG8mbteNUQsxlMQohQ==",
- "dev": true
- }
- }
-}
@@ -1,10 +0,0 @@
-{
- "name": "tree-sitter-context-predicate",
- "main": "bindings/node",
- "devDependencies": {
- "tree-sitter-cli": "^0.19.5"
- },
- "dependencies": {
- "nan": "^2.14.0"
- }
-}
@@ -1,208 +0,0 @@
-{
- "name": "context_predicate",
- "rules": {
- "source": {
- "type": "SYMBOL",
- "name": "_expression"
- },
- "_expression": {
- "type": "CHOICE",
- "members": [
- {
- "type": "SYMBOL",
- "name": "identifier"
- },
- {
- "type": "SYMBOL",
- "name": "not"
- },
- {
- "type": "SYMBOL",
- "name": "and"
- },
- {
- "type": "SYMBOL",
- "name": "or"
- },
- {
- "type": "SYMBOL",
- "name": "equal"
- },
- {
- "type": "SYMBOL",
- "name": "not_equal"
- },
- {
- "type": "SYMBOL",
- "name": "parenthesized"
- }
- ]
- },
- "identifier": {
- "type": "PATTERN",
- "value": "[A-Za-z0-9_-]+"
- },
- "not": {
- "type": "PREC",
- "value": 3,
- "content": {
- "type": "SEQ",
- "members": [
- {
- "type": "STRING",
- "value": "!"
- },
- {
- "type": "FIELD",
- "name": "expression",
- "content": {
- "type": "SYMBOL",
- "name": "_expression"
- }
- }
- ]
- }
- },
- "and": {
- "type": "PREC_LEFT",
- "value": 2,
- "content": {
- "type": "SEQ",
- "members": [
- {
- "type": "FIELD",
- "name": "left",
- "content": {
- "type": "SYMBOL",
- "name": "_expression"
- }
- },
- {
- "type": "STRING",
- "value": "&&"
- },
- {
- "type": "FIELD",
- "name": "right",
- "content": {
- "type": "SYMBOL",
- "name": "_expression"
- }
- }
- ]
- }
- },
- "or": {
- "type": "PREC_LEFT",
- "value": 1,
- "content": {
- "type": "SEQ",
- "members": [
- {
- "type": "FIELD",
- "name": "left",
- "content": {
- "type": "SYMBOL",
- "name": "_expression"
- }
- },
- {
- "type": "STRING",
- "value": "||"
- },
- {
- "type": "FIELD",
- "name": "right",
- "content": {
- "type": "SYMBOL",
- "name": "_expression"
- }
- }
- ]
- }
- },
- "equal": {
- "type": "SEQ",
- "members": [
- {
- "type": "FIELD",
- "name": "left",
- "content": {
- "type": "SYMBOL",
- "name": "identifier"
- }
- },
- {
- "type": "STRING",
- "value": "=="
- },
- {
- "type": "FIELD",
- "name": "right",
- "content": {
- "type": "SYMBOL",
- "name": "identifier"
- }
- }
- ]
- },
- "not_equal": {
- "type": "SEQ",
- "members": [
- {
- "type": "FIELD",
- "name": "left",
- "content": {
- "type": "SYMBOL",
- "name": "identifier"
- }
- },
- {
- "type": "STRING",
- "value": "!="
- },
- {
- "type": "FIELD",
- "name": "right",
- "content": {
- "type": "SYMBOL",
- "name": "identifier"
- }
- }
- ]
- },
- "parenthesized": {
- "type": "SEQ",
- "members": [
- {
- "type": "STRING",
- "value": "("
- },
- {
- "type": "FIELD",
- "name": "expression",
- "content": {
- "type": "SYMBOL",
- "name": "_expression"
- }
- },
- {
- "type": "STRING",
- "value": ")"
- }
- ]
- }
- },
- "extras": [
- {
- "type": "PATTERN",
- "value": "\\s"
- }
- ],
- "conflicts": [],
- "precedences": [],
- "externals": [],
- "inline": [],
- "supertypes": []
-}
-
@@ -1,353 +0,0 @@
-[
- {
- "type": "and",
- "named": true,
- "fields": {
- "left": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "and",
- "named": true
- },
- {
- "type": "equal",
- "named": true
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "not",
- "named": true
- },
- {
- "type": "not_equal",
- "named": true
- },
- {
- "type": "or",
- "named": true
- },
- {
- "type": "parenthesized",
- "named": true
- }
- ]
- },
- "right": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "and",
- "named": true
- },
- {
- "type": "equal",
- "named": true
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "not",
- "named": true
- },
- {
- "type": "not_equal",
- "named": true
- },
- {
- "type": "or",
- "named": true
- },
- {
- "type": "parenthesized",
- "named": true
- }
- ]
- }
- }
- },
- {
- "type": "equal",
- "named": true,
- "fields": {
- "left": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "identifier",
- "named": true
- }
- ]
- },
- "right": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "identifier",
- "named": true
- }
- ]
- }
- }
- },
- {
- "type": "not",
- "named": true,
- "fields": {
- "expression": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "and",
- "named": true
- },
- {
- "type": "equal",
- "named": true
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "not",
- "named": true
- },
- {
- "type": "not_equal",
- "named": true
- },
- {
- "type": "or",
- "named": true
- },
- {
- "type": "parenthesized",
- "named": true
- }
- ]
- }
- }
- },
- {
- "type": "not_equal",
- "named": true,
- "fields": {
- "left": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "identifier",
- "named": true
- }
- ]
- },
- "right": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "identifier",
- "named": true
- }
- ]
- }
- }
- },
- {
- "type": "or",
- "named": true,
- "fields": {
- "left": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "and",
- "named": true
- },
- {
- "type": "equal",
- "named": true
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "not",
- "named": true
- },
- {
- "type": "not_equal",
- "named": true
- },
- {
- "type": "or",
- "named": true
- },
- {
- "type": "parenthesized",
- "named": true
- }
- ]
- },
- "right": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "and",
- "named": true
- },
- {
- "type": "equal",
- "named": true
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "not",
- "named": true
- },
- {
- "type": "not_equal",
- "named": true
- },
- {
- "type": "or",
- "named": true
- },
- {
- "type": "parenthesized",
- "named": true
- }
- ]
- }
- }
- },
- {
- "type": "parenthesized",
- "named": true,
- "fields": {
- "expression": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "and",
- "named": true
- },
- {
- "type": "equal",
- "named": true
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "not",
- "named": true
- },
- {
- "type": "not_equal",
- "named": true
- },
- {
- "type": "or",
- "named": true
- },
- {
- "type": "parenthesized",
- "named": true
- }
- ]
- }
- }
- },
- {
- "type": "source",
- "named": true,
- "fields": {},
- "children": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "and",
- "named": true
- },
- {
- "type": "equal",
- "named": true
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "not",
- "named": true
- },
- {
- "type": "not_equal",
- "named": true
- },
- {
- "type": "or",
- "named": true
- },
- {
- "type": "parenthesized",
- "named": true
- }
- ]
- }
- },
- {
- "type": "!",
- "named": false
- },
- {
- "type": "!=",
- "named": false
- },
- {
- "type": "&&",
- "named": false
- },
- {
- "type": "(",
- "named": false
- },
- {
- "type": ")",
- "named": false
- },
- {
- "type": "==",
- "named": false
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "||",
- "named": false
- }
-]
@@ -1,584 +0,0 @@
-#include <tree_sitter/parser.h>
-
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
-#endif
-
-#define LANGUAGE_VERSION 13
-#define STATE_COUNT 18
-#define LARGE_STATE_COUNT 6
-#define SYMBOL_COUNT 17
-#define ALIAS_COUNT 0
-#define TOKEN_COUNT 9
-#define EXTERNAL_TOKEN_COUNT 0
-#define FIELD_COUNT 3
-#define MAX_ALIAS_SEQUENCE_LENGTH 3
-#define PRODUCTION_ID_COUNT 3
-
-enum {
- sym_identifier = 1,
- anon_sym_BANG = 2,
- anon_sym_AMP_AMP = 3,
- anon_sym_PIPE_PIPE = 4,
- anon_sym_EQ_EQ = 5,
- anon_sym_BANG_EQ = 6,
- anon_sym_LPAREN = 7,
- anon_sym_RPAREN = 8,
- sym_source = 9,
- sym__expression = 10,
- sym_not = 11,
- sym_and = 12,
- sym_or = 13,
- sym_equal = 14,
- sym_not_equal = 15,
- sym_parenthesized = 16,
-};
-
-static const char *const ts_symbol_names[] = {
- [ts_builtin_sym_end] = "end",
- [sym_identifier] = "identifier",
- [anon_sym_BANG] = "!",
- [anon_sym_AMP_AMP] = "&&",
- [anon_sym_PIPE_PIPE] = "||",
- [anon_sym_EQ_EQ] = "==",
- [anon_sym_BANG_EQ] = "!=",
- [anon_sym_LPAREN] = "(",
- [anon_sym_RPAREN] = ")",
- [sym_source] = "source",
- [sym__expression] = "_expression",
- [sym_not] = "not",
- [sym_and] = "and",
- [sym_or] = "or",
- [sym_equal] = "equal",
- [sym_not_equal] = "not_equal",
- [sym_parenthesized] = "parenthesized",
-};
-
-static const TSSymbol ts_symbol_map[] = {
- [ts_builtin_sym_end] = ts_builtin_sym_end,
- [sym_identifier] = sym_identifier,
- [anon_sym_BANG] = anon_sym_BANG,
- [anon_sym_AMP_AMP] = anon_sym_AMP_AMP,
- [anon_sym_PIPE_PIPE] = anon_sym_PIPE_PIPE,
- [anon_sym_EQ_EQ] = anon_sym_EQ_EQ,
- [anon_sym_BANG_EQ] = anon_sym_BANG_EQ,
- [anon_sym_LPAREN] = anon_sym_LPAREN,
- [anon_sym_RPAREN] = anon_sym_RPAREN,
- [sym_source] = sym_source,
- [sym__expression] = sym__expression,
- [sym_not] = sym_not,
- [sym_and] = sym_and,
- [sym_or] = sym_or,
- [sym_equal] = sym_equal,
- [sym_not_equal] = sym_not_equal,
- [sym_parenthesized] = sym_parenthesized,
-};
-
-static const TSSymbolMetadata ts_symbol_metadata[] = {
- [ts_builtin_sym_end] =
- {
- .visible = false,
- .named = true,
- },
- [sym_identifier] =
- {
- .visible = true,
- .named = true,
- },
- [anon_sym_BANG] =
- {
- .visible = true,
- .named = false,
- },
- [anon_sym_AMP_AMP] =
- {
- .visible = true,
- .named = false,
- },
- [anon_sym_PIPE_PIPE] =
- {
- .visible = true,
- .named = false,
- },
- [anon_sym_EQ_EQ] =
- {
- .visible = true,
- .named = false,
- },
- [anon_sym_BANG_EQ] =
- {
- .visible = true,
- .named = false,
- },
- [anon_sym_LPAREN] =
- {
- .visible = true,
- .named = false,
- },
- [anon_sym_RPAREN] =
- {
- .visible = true,
- .named = false,
- },
- [sym_source] =
- {
- .visible = true,
- .named = true,
- },
- [sym__expression] =
- {
- .visible = false,
- .named = true,
- },
- [sym_not] =
- {
- .visible = true,
- .named = true,
- },
- [sym_and] =
- {
- .visible = true,
- .named = true,
- },
- [sym_or] =
- {
- .visible = true,
- .named = true,
- },
- [sym_equal] =
- {
- .visible = true,
- .named = true,
- },
- [sym_not_equal] =
- {
- .visible = true,
- .named = true,
- },
- [sym_parenthesized] =
- {
- .visible = true,
- .named = true,
- },
-};
-
-enum {
- field_expression = 1,
- field_left = 2,
- field_right = 3,
-};
-
-static const char *const ts_field_names[] = {
- [0] = NULL,
- [field_expression] = "expression",
- [field_left] = "left",
- [field_right] = "right",
-};
-
-static const TSFieldMapSlice ts_field_map_slices[PRODUCTION_ID_COUNT] = {
- [1] = {.index = 0, .length = 1},
- [2] = {.index = 1, .length = 2},
-};
-
-static const TSFieldMapEntry ts_field_map_entries[] = {
- [0] = {field_expression, 1},
- [1] = {field_left, 0},
- {field_right, 2},
-};
-
-static const TSSymbol ts_alias_sequences[PRODUCTION_ID_COUNT]
- [MAX_ALIAS_SEQUENCE_LENGTH] = {
- [0] = {0},
-};
-
-static const uint16_t ts_non_terminal_alias_map[] = {
- 0,
-};
-
-static bool ts_lex(TSLexer *lexer, TSStateId state) {
- START_LEXER();
- eof = lexer->eof(lexer);
- switch (state) {
- case 0:
- if (eof)
- ADVANCE(7);
- if (lookahead == '!')
- ADVANCE(10);
- if (lookahead == '&')
- ADVANCE(2);
- if (lookahead == '(')
- ADVANCE(15);
- if (lookahead == ')')
- ADVANCE(16);
- if (lookahead == '=')
- ADVANCE(4);
- if (lookahead == '|')
- ADVANCE(5);
- if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' ||
- lookahead == ' ')
- SKIP(0)
- if (lookahead == '-' || ('0' <= lookahead && lookahead <= '9') ||
- ('A' <= lookahead && lookahead <= 'Z') || lookahead == '_' ||
- ('a' <= lookahead && lookahead <= 'z'))
- ADVANCE(8);
- END_STATE();
- case 1:
- if (lookahead == '!')
- ADVANCE(9);
- if (lookahead == '(')
- ADVANCE(15);
- if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' ||
- lookahead == ' ')
- SKIP(1)
- if (lookahead == '-' || ('0' <= lookahead && lookahead <= '9') ||
- ('A' <= lookahead && lookahead <= 'Z') || lookahead == '_' ||
- ('a' <= lookahead && lookahead <= 'z'))
- ADVANCE(8);
- END_STATE();
- case 2:
- if (lookahead == '&')
- ADVANCE(11);
- END_STATE();
- case 3:
- if (lookahead == '=')
- ADVANCE(14);
- END_STATE();
- case 4:
- if (lookahead == '=')
- ADVANCE(13);
- END_STATE();
- case 5:
- if (lookahead == '|')
- ADVANCE(12);
- END_STATE();
- case 6:
- if (eof)
- ADVANCE(7);
- if (lookahead == '!')
- ADVANCE(3);
- if (lookahead == '&')
- ADVANCE(2);
- if (lookahead == ')')
- ADVANCE(16);
- if (lookahead == '=')
- ADVANCE(4);
- if (lookahead == '|')
- ADVANCE(5);
- if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' ||
- lookahead == ' ')
- SKIP(6)
- END_STATE();
- case 7:
- ACCEPT_TOKEN(ts_builtin_sym_end);
- END_STATE();
- case 8:
- ACCEPT_TOKEN(sym_identifier);
- if (lookahead == '-' || ('0' <= lookahead && lookahead <= '9') ||
- ('A' <= lookahead && lookahead <= 'Z') || lookahead == '_' ||
- ('a' <= lookahead && lookahead <= 'z'))
- ADVANCE(8);
- END_STATE();
- case 9:
- ACCEPT_TOKEN(anon_sym_BANG);
- END_STATE();
- case 10:
- ACCEPT_TOKEN(anon_sym_BANG);
- if (lookahead == '=')
- ADVANCE(14);
- END_STATE();
- case 11:
- ACCEPT_TOKEN(anon_sym_AMP_AMP);
- END_STATE();
- case 12:
- ACCEPT_TOKEN(anon_sym_PIPE_PIPE);
- END_STATE();
- case 13:
- ACCEPT_TOKEN(anon_sym_EQ_EQ);
- END_STATE();
- case 14:
- ACCEPT_TOKEN(anon_sym_BANG_EQ);
- END_STATE();
- case 15:
- ACCEPT_TOKEN(anon_sym_LPAREN);
- END_STATE();
- case 16:
- ACCEPT_TOKEN(anon_sym_RPAREN);
- END_STATE();
- default:
- return false;
- }
-}
-
-static const TSLexMode ts_lex_modes[STATE_COUNT] = {
- [0] = {.lex_state = 0}, [1] = {.lex_state = 1}, [2] = {.lex_state = 1},
- [3] = {.lex_state = 1}, [4] = {.lex_state = 1}, [5] = {.lex_state = 1},
- [6] = {.lex_state = 6}, [7] = {.lex_state = 0}, [8] = {.lex_state = 0},
- [9] = {.lex_state = 0}, [10] = {.lex_state = 0}, [11] = {.lex_state = 0},
- [12] = {.lex_state = 0}, [13] = {.lex_state = 0}, [14] = {.lex_state = 0},
- [15] = {.lex_state = 0}, [16] = {.lex_state = 0}, [17] = {.lex_state = 0},
-};
-
-static const uint16_t ts_parse_table[LARGE_STATE_COUNT][SYMBOL_COUNT] = {
- [0] =
- {
- [ts_builtin_sym_end] = ACTIONS(1),
- [sym_identifier] = ACTIONS(1),
- [anon_sym_BANG] = ACTIONS(1),
- [anon_sym_AMP_AMP] = ACTIONS(1),
- [anon_sym_PIPE_PIPE] = ACTIONS(1),
- [anon_sym_EQ_EQ] = ACTIONS(1),
- [anon_sym_BANG_EQ] = ACTIONS(1),
- [anon_sym_LPAREN] = ACTIONS(1),
- [anon_sym_RPAREN] = ACTIONS(1),
- },
- [1] =
- {
- [sym_source] = STATE(15),
- [sym__expression] = STATE(13),
- [sym_not] = STATE(13),
- [sym_and] = STATE(13),
- [sym_or] = STATE(13),
- [sym_equal] = STATE(13),
- [sym_not_equal] = STATE(13),
- [sym_parenthesized] = STATE(13),
- [sym_identifier] = ACTIONS(3),
- [anon_sym_BANG] = ACTIONS(5),
- [anon_sym_LPAREN] = ACTIONS(7),
- },
- [2] =
- {
- [sym__expression] = STATE(7),
- [sym_not] = STATE(7),
- [sym_and] = STATE(7),
- [sym_or] = STATE(7),
- [sym_equal] = STATE(7),
- [sym_not_equal] = STATE(7),
- [sym_parenthesized] = STATE(7),
- [sym_identifier] = ACTIONS(3),
- [anon_sym_BANG] = ACTIONS(5),
- [anon_sym_LPAREN] = ACTIONS(7),
- },
- [3] =
- {
- [sym__expression] = STATE(14),
- [sym_not] = STATE(14),
- [sym_and] = STATE(14),
- [sym_or] = STATE(14),
- [sym_equal] = STATE(14),
- [sym_not_equal] = STATE(14),
- [sym_parenthesized] = STATE(14),
- [sym_identifier] = ACTIONS(3),
- [anon_sym_BANG] = ACTIONS(5),
- [anon_sym_LPAREN] = ACTIONS(7),
- },
- [4] =
- {
- [sym__expression] = STATE(11),
- [sym_not] = STATE(11),
- [sym_and] = STATE(11),
- [sym_or] = STATE(11),
- [sym_equal] = STATE(11),
- [sym_not_equal] = STATE(11),
- [sym_parenthesized] = STATE(11),
- [sym_identifier] = ACTIONS(3),
- [anon_sym_BANG] = ACTIONS(5),
- [anon_sym_LPAREN] = ACTIONS(7),
- },
- [5] =
- {
- [sym__expression] = STATE(12),
- [sym_not] = STATE(12),
- [sym_and] = STATE(12),
- [sym_or] = STATE(12),
- [sym_equal] = STATE(12),
- [sym_not_equal] = STATE(12),
- [sym_parenthesized] = STATE(12),
- [sym_identifier] = ACTIONS(3),
- [anon_sym_BANG] = ACTIONS(5),
- [anon_sym_LPAREN] = ACTIONS(7),
- },
-};
-
-static const uint16_t ts_small_parse_table[] = {
- [0] = 3,
- ACTIONS(11),
- 1,
- anon_sym_EQ_EQ,
- ACTIONS(13),
- 1,
- anon_sym_BANG_EQ,
- ACTIONS(9),
- 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [13] = 1,
- ACTIONS(15),
- 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [20] = 1,
- ACTIONS(17),
- 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [27] = 1,
- ACTIONS(19),
- 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [34] = 1,
- ACTIONS(21),
- 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [41] = 1,
- ACTIONS(23),
- 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [48] = 2,
- ACTIONS(27),
- 1,
- anon_sym_AMP_AMP,
- ACTIONS(25),
- 3,
- ts_builtin_sym_end,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [57] = 3,
- ACTIONS(27),
- 1,
- anon_sym_AMP_AMP,
- ACTIONS(29),
- 1,
- ts_builtin_sym_end,
- ACTIONS(31),
- 1,
- anon_sym_PIPE_PIPE,
- [67] = 3,
- ACTIONS(27),
- 1,
- anon_sym_AMP_AMP,
- ACTIONS(31),
- 1,
- anon_sym_PIPE_PIPE,
- ACTIONS(33),
- 1,
- anon_sym_RPAREN,
- [77] = 1,
- ACTIONS(35),
- 1,
- ts_builtin_sym_end,
- [81] = 1,
- ACTIONS(37),
- 1,
- sym_identifier,
- [85] = 1,
- ACTIONS(39),
- 1,
- sym_identifier,
-};
-
-static const uint32_t ts_small_parse_table_map[] = {
- [SMALL_STATE(6)] = 0, [SMALL_STATE(7)] = 13, [SMALL_STATE(8)] = 20,
- [SMALL_STATE(9)] = 27, [SMALL_STATE(10)] = 34, [SMALL_STATE(11)] = 41,
- [SMALL_STATE(12)] = 48, [SMALL_STATE(13)] = 57, [SMALL_STATE(14)] = 67,
- [SMALL_STATE(15)] = 77, [SMALL_STATE(16)] = 81, [SMALL_STATE(17)] = 85,
-};
-
-static const TSParseActionEntry ts_parse_actions[] = {
- [0] = {.entry = {.count = 0, .reusable = false}},
- [1] = {.entry = {.count = 1, .reusable = false}},
- RECOVER(),
- [3] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(6),
- [5] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(2),
- [7] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(3),
- [9] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym__expression, 1),
- [11] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(16),
- [13] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(17),
- [15] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym_not, 2, .production_id = 1),
- [17] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym_equal, 3, .production_id = 2),
- [19] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym_not_equal, 3, .production_id = 2),
- [21] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym_parenthesized, 3, .production_id = 1),
- [23] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym_and, 3, .production_id = 2),
- [25] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym_or, 3, .production_id = 2),
- [27] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(4),
- [29] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym_source, 1),
- [31] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(5),
- [33] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(10),
- [35] = {.entry = {.count = 1, .reusable = true}},
- ACCEPT_INPUT(),
- [37] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(8),
- [39] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(9),
-};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-#ifdef _WIN32
-#define extern __declspec(dllexport)
-#endif
-
-extern const TSLanguage *tree_sitter_context_predicate(void) {
- static const TSLanguage language = {
- .version = LANGUAGE_VERSION,
- .symbol_count = SYMBOL_COUNT,
- .alias_count = ALIAS_COUNT,
- .token_count = TOKEN_COUNT,
- .external_token_count = EXTERNAL_TOKEN_COUNT,
- .state_count = STATE_COUNT,
- .large_state_count = LARGE_STATE_COUNT,
- .production_id_count = PRODUCTION_ID_COUNT,
- .field_count = FIELD_COUNT,
- .max_alias_sequence_length = MAX_ALIAS_SEQUENCE_LENGTH,
- .parse_table = &ts_parse_table[0][0],
- .small_parse_table = ts_small_parse_table,
- .small_parse_table_map = ts_small_parse_table_map,
- .parse_actions = ts_parse_actions,
- .symbol_names = ts_symbol_names,
- .field_names = ts_field_names,
- .field_map_slices = ts_field_map_slices,
- .field_map_entries = ts_field_map_entries,
- .symbol_metadata = ts_symbol_metadata,
- .public_symbol_map = ts_symbol_map,
- .alias_map = ts_non_terminal_alias_map,
- .alias_sequences = &ts_alias_sequences[0][0],
- .lex_modes = ts_lex_modes,
- .lex_fn = ts_lex,
- };
- return &language;
-}
-#ifdef __cplusplus
-}
-#endif
@@ -1,223 +0,0 @@
-#ifndef TREE_SITTER_PARSER_H_
-#define TREE_SITTER_PARSER_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-#define ts_builtin_sym_error ((TSSymbol)-1)
-#define ts_builtin_sym_end 0
-#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024
-
-typedef uint16_t TSStateId;
-
-#ifndef TREE_SITTER_API_H_
-typedef uint16_t TSSymbol;
-typedef uint16_t TSFieldId;
-typedef struct TSLanguage TSLanguage;
-#endif
-
-typedef struct {
- TSFieldId field_id;
- uint8_t child_index;
- bool inherited;
-} TSFieldMapEntry;
-
-typedef struct {
- uint16_t index;
- uint16_t length;
-} TSFieldMapSlice;
-
-typedef struct {
- bool visible;
- bool named;
- bool supertype;
-} TSSymbolMetadata;
-
-typedef struct TSLexer TSLexer;
-
-struct TSLexer {
- int32_t lookahead;
- TSSymbol result_symbol;
- void (*advance)(TSLexer *, bool);
- void (*mark_end)(TSLexer *);
- uint32_t (*get_column)(TSLexer *);
- bool (*is_at_included_range_start)(const TSLexer *);
- bool (*eof)(const TSLexer *);
-};
-
-typedef enum {
- TSParseActionTypeShift,
- TSParseActionTypeReduce,
- TSParseActionTypeAccept,
- TSParseActionTypeRecover,
-} TSParseActionType;
-
-typedef union {
- struct {
- uint8_t type;
- TSStateId state;
- bool extra;
- bool repetition;
- } shift;
- struct {
- uint8_t type;
- uint8_t child_count;
- TSSymbol symbol;
- int16_t dynamic_precedence;
- uint16_t production_id;
- } reduce;
- uint8_t type;
-} TSParseAction;
-
-typedef struct {
- uint16_t lex_state;
- uint16_t external_lex_state;
-} TSLexMode;
-
-typedef union {
- TSParseAction action;
- struct {
- uint8_t count;
- bool reusable;
- } entry;
-} TSParseActionEntry;
-
-struct TSLanguage {
- uint32_t version;
- uint32_t symbol_count;
- uint32_t alias_count;
- uint32_t token_count;
- uint32_t external_token_count;
- uint32_t state_count;
- uint32_t large_state_count;
- uint32_t production_id_count;
- uint32_t field_count;
- uint16_t max_alias_sequence_length;
- const uint16_t *parse_table;
- const uint16_t *small_parse_table;
- const uint32_t *small_parse_table_map;
- const TSParseActionEntry *parse_actions;
- const char * const *symbol_names;
- const char * const *field_names;
- const TSFieldMapSlice *field_map_slices;
- const TSFieldMapEntry *field_map_entries;
- const TSSymbolMetadata *symbol_metadata;
- const TSSymbol *public_symbol_map;
- const uint16_t *alias_map;
- const TSSymbol *alias_sequences;
- const TSLexMode *lex_modes;
- bool (*lex_fn)(TSLexer *, TSStateId);
- bool (*keyword_lex_fn)(TSLexer *, TSStateId);
- TSSymbol keyword_capture_token;
- struct {
- const bool *states;
- const TSSymbol *symbol_map;
- void *(*create)(void);
- void (*destroy)(void *);
- bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist);
- unsigned (*serialize)(void *, char *);
- void (*deserialize)(void *, const char *, unsigned);
- } external_scanner;
-};
-
-/*
- * Lexer Macros
- */
-
-#define START_LEXER() \
- bool result = false; \
- bool skip = false; \
- bool eof = false; \
- int32_t lookahead; \
- goto start; \
- next_state: \
- lexer->advance(lexer, skip); \
- start: \
- skip = false; \
- lookahead = lexer->lookahead;
-
-#define ADVANCE(state_value) \
- { \
- state = state_value; \
- goto next_state; \
- }
-
-#define SKIP(state_value) \
- { \
- skip = true; \
- state = state_value; \
- goto next_state; \
- }
-
-#define ACCEPT_TOKEN(symbol_value) \
- result = true; \
- lexer->result_symbol = symbol_value; \
- lexer->mark_end(lexer);
-
-#define END_STATE() return result;
-
-/*
- * Parse Table Macros
- */
-
-#define SMALL_STATE(id) id - LARGE_STATE_COUNT
-
-#define STATE(id) id
-
-#define ACTIONS(id) id
-
-#define SHIFT(state_value) \
- {{ \
- .shift = { \
- .type = TSParseActionTypeShift, \
- .state = state_value \
- } \
- }}
-
-#define SHIFT_REPEAT(state_value) \
- {{ \
- .shift = { \
- .type = TSParseActionTypeShift, \
- .state = state_value, \
- .repetition = true \
- } \
- }}
-
-#define SHIFT_EXTRA() \
- {{ \
- .shift = { \
- .type = TSParseActionTypeShift, \
- .extra = true \
- } \
- }}
-
-#define REDUCE(symbol_val, child_count_val, ...) \
- {{ \
- .reduce = { \
- .type = TSParseActionTypeReduce, \
- .symbol = symbol_val, \
- .child_count = child_count_val, \
- __VA_ARGS__ \
- }, \
- }}
-
-#define RECOVER() \
- {{ \
- .type = TSParseActionTypeRecover \
- }}
-
-#define ACCEPT_INPUT() \
- {{ \
- .type = TSParseActionTypeAccept \
- }}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // TREE_SITTER_PARSER_H_
@@ -1,11 +1,5 @@
-use anyhow::anyhow;
-
+use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet};
-use tree_sitter::{Language, Node, Parser};
-
-extern "C" {
- fn tree_sitter_context_predicate() -> Language;
-}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct KeymapContext {
@@ -35,70 +29,13 @@ pub enum KeymapContextPredicate {
}
impl KeymapContextPredicate {
- pub fn parse(source: &str) -> anyhow::Result<Self> {
- let mut parser = Parser::new();
- let language = unsafe { tree_sitter_context_predicate() };
- parser.set_language(language).unwrap();
- let source = source.as_bytes();
- let tree = parser.parse(source, None).unwrap();
- Self::from_node(tree.root_node(), source)
- }
-
- fn from_node(node: Node, source: &[u8]) -> anyhow::Result<Self> {
- let parse_error = "error parsing context predicate";
- let kind = node.kind();
-
- match kind {
- "source" => Self::from_node(node.child(0).ok_or_else(|| anyhow!(parse_error))?, source),
- "identifier" => Ok(Self::Identifier(node.utf8_text(source)?.into())),
- "not" => {
- let child = Self::from_node(
- node.child_by_field_name("expression")
- .ok_or_else(|| anyhow!(parse_error))?,
- source,
- )?;
- Ok(Self::Not(Box::new(child)))
- }
- "and" | "or" => {
- let left = Box::new(Self::from_node(
- node.child_by_field_name("left")
- .ok_or_else(|| anyhow!(parse_error))?,
- source,
- )?);
- let right = Box::new(Self::from_node(
- node.child_by_field_name("right")
- .ok_or_else(|| anyhow!(parse_error))?,
- source,
- )?);
- if kind == "and" {
- Ok(Self::And(left, right))
- } else {
- Ok(Self::Or(left, right))
- }
- }
- "equal" | "not_equal" => {
- let left = node
- .child_by_field_name("left")
- .ok_or_else(|| anyhow!(parse_error))?
- .utf8_text(source)?
- .into();
- let right = node
- .child_by_field_name("right")
- .ok_or_else(|| anyhow!(parse_error))?
- .utf8_text(source)?
- .into();
- if kind == "equal" {
- Ok(Self::Equal(left, right))
- } else {
- Ok(Self::NotEqual(left, right))
- }
- }
- "parenthesized" => Self::from_node(
- node.child_by_field_name("expression")
- .ok_or_else(|| anyhow!(parse_error))?,
- source,
- ),
- _ => Err(anyhow!(parse_error)),
+ pub fn parse(source: &str) -> Result<Self> {
+ let source = Self::skip_whitespace(source);
+ let (predicate, rest) = Self::parse_expr(source, 0)?;
+ if let Some(next) = rest.chars().next() {
+ Err(anyhow!("unexpected character {next:?}"))
+ } else {
+ Ok(predicate)
}
}
@@ -120,4 +57,236 @@ impl KeymapContextPredicate {
Self::Or(left, right) => left.eval(context) || right.eval(context),
}
}
+
+ fn parse_expr(
+ mut source: &str,
+ min_precedence: u32,
+ ) -> anyhow::Result<(KeymapContextPredicate, &str)> {
+ type Op =
+ fn(KeymapContextPredicate, KeymapContextPredicate) -> Result<KeymapContextPredicate>;
+
+ let (mut predicate, rest) = Self::parse_primary(source)?;
+ source = rest;
+
+ 'parse: loop {
+ for (operator, precedence, constructor) in [
+ ("&&", PRECEDENCE_AND, KeymapContextPredicate::new_and as Op),
+ ("||", PRECEDENCE_OR, KeymapContextPredicate::new_or as Op),
+ ("==", PRECEDENCE_EQ, KeymapContextPredicate::new_eq as Op),
+ ("!=", PRECEDENCE_EQ, KeymapContextPredicate::new_neq as Op),
+ ] {
+ if source.starts_with(operator) && precedence >= min_precedence {
+ source = Self::skip_whitespace(&source[operator.len()..]);
+ let (right, rest) = Self::parse_expr(source, precedence + 1)?;
+ predicate = constructor(predicate, right)?;
+ source = rest;
+ continue 'parse;
+ }
+ }
+ break;
+ }
+
+ Ok((predicate, source))
+ }
+
+ fn parse_primary(mut source: &str) -> anyhow::Result<(KeymapContextPredicate, &str)> {
+ let next = source
+ .chars()
+ .next()
+ .ok_or_else(|| anyhow!("unexpected eof"))?;
+ match next {
+ '(' => {
+ source = Self::skip_whitespace(&source[1..]);
+ let (predicate, rest) = Self::parse_expr(source, 0)?;
+ if rest.starts_with(')') {
+ source = Self::skip_whitespace(&rest[1..]);
+ Ok((predicate, source))
+ } else {
+ Err(anyhow!("expected a ')'"))
+ }
+ }
+ '!' => {
+ let source = Self::skip_whitespace(&source[1..]);
+ let (predicate, source) = Self::parse_expr(&source, PRECEDENCE_NOT)?;
+ Ok((KeymapContextPredicate::Not(Box::new(predicate)), source))
+ }
+ _ if next.is_alphanumeric() || next == '_' => {
+ let len = source
+ .find(|c: char| !(c.is_alphanumeric() || c == '_'))
+ .unwrap_or(source.len());
+ let (identifier, rest) = source.split_at(len);
+ source = Self::skip_whitespace(rest);
+ Ok((
+ KeymapContextPredicate::Identifier(identifier.into()),
+ source,
+ ))
+ }
+ _ => Err(anyhow!("unexpected character {next:?}")),
+ }
+ }
+
+ fn skip_whitespace(source: &str) -> &str {
+ let len = source
+ .find(|c: char| !c.is_whitespace())
+ .unwrap_or(source.len());
+ &source[len..]
+ }
+
+ fn new_or(self, other: Self) -> Result<Self> {
+ Ok(Self::Or(Box::new(self), Box::new(other)))
+ }
+
+ fn new_and(self, other: Self) -> Result<Self> {
+ Ok(Self::And(Box::new(self), Box::new(other)))
+ }
+
+ fn new_eq(self, other: Self) -> Result<Self> {
+ if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) {
+ Ok(Self::Equal(left, right))
+ } else {
+ Err(anyhow!("operands must be identifiers"))
+ }
+ }
+
+ fn new_neq(self, other: Self) -> Result<Self> {
+ if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) {
+ Ok(Self::NotEqual(left, right))
+ } else {
+ Err(anyhow!("operands must be identifiers"))
+ }
+ }
+}
+
+const PRECEDENCE_OR: u32 = 1;
+const PRECEDENCE_AND: u32 = 2;
+const PRECEDENCE_EQ: u32 = 3;
+const PRECEDENCE_NOT: u32 = 4;
+
+#[cfg(test)]
+mod tests {
+ use super::KeymapContextPredicate::{self, *};
+
+ #[test]
+ fn test_parse_identifiers() {
+ // Identifiers
+ assert_eq!(
+ KeymapContextPredicate::parse("abc12").unwrap(),
+ Identifier("abc12".into())
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse("_1a").unwrap(),
+ Identifier("_1a".into())
+ );
+ }
+
+ #[test]
+ fn test_parse_negations() {
+ assert_eq!(
+ KeymapContextPredicate::parse("!abc").unwrap(),
+ Not(Box::new(Identifier("abc".into())))
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse(" ! ! abc").unwrap(),
+ Not(Box::new(Not(Box::new(Identifier("abc".into())))))
+ );
+ }
+
+ #[test]
+ fn test_parse_equality_operators() {
+ assert_eq!(
+ KeymapContextPredicate::parse("a == b").unwrap(),
+ Equal("a".into(), "b".into())
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse("c!=d").unwrap(),
+ NotEqual("c".into(), "d".into())
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse("c == !d")
+ .unwrap_err()
+ .to_string(),
+ "operands must be identifiers"
+ );
+ }
+
+ #[test]
+ fn test_parse_boolean_operators() {
+ assert_eq!(
+ KeymapContextPredicate::parse("a || b").unwrap(),
+ Or(
+ Box::new(Identifier("a".into())),
+ Box::new(Identifier("b".into()))
+ )
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse("a || !b && c").unwrap(),
+ Or(
+ Box::new(Identifier("a".into())),
+ Box::new(And(
+ Box::new(Not(Box::new(Identifier("b".into())))),
+ Box::new(Identifier("c".into()))
+ ))
+ )
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse("a && b || c&&d").unwrap(),
+ Or(
+ Box::new(And(
+ Box::new(Identifier("a".into())),
+ Box::new(Identifier("b".into()))
+ )),
+ Box::new(And(
+ Box::new(Identifier("c".into())),
+ Box::new(Identifier("d".into()))
+ ))
+ )
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse("a == b && c || d == e && f").unwrap(),
+ Or(
+ Box::new(And(
+ Box::new(Equal("a".into(), "b".into())),
+ Box::new(Identifier("c".into()))
+ )),
+ Box::new(And(
+ Box::new(Equal("d".into(), "e".into())),
+ Box::new(Identifier("f".into()))
+ ))
+ )
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse("a && b && c && d").unwrap(),
+ And(
+ Box::new(And(
+ Box::new(And(
+ Box::new(Identifier("a".into())),
+ Box::new(Identifier("b".into()))
+ )),
+ Box::new(Identifier("c".into())),
+ )),
+ Box::new(Identifier("d".into()))
+ ),
+ );
+ }
+
+ #[test]
+ fn test_parse_parenthesized_expressions() {
+ assert_eq!(
+ KeymapContextPredicate::parse("a && (b == c || d != e)").unwrap(),
+ And(
+ Box::new(Identifier("a".into())),
+ Box::new(Or(
+ Box::new(Equal("b".into(), "c".into())),
+ Box::new(NotEqual("d".into(), "e".into())),
+ )),
+ ),
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse(" ( a || b ) ").unwrap(),
+ Or(
+ Box::new(Identifier("a".into())),
+ Box::new(Identifier("b".into())),
+ )
+ );
+ }
}