diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 23f6f04992..095d5065d9 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -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() { diff --git a/crates/gpui/grammars/context-predicate/.gitignore b/crates/gpui/grammars/context-predicate/.gitignore deleted file mode 100644 index 563ba71918..0000000000 --- a/crates/gpui/grammars/context-predicate/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/node_modules -/build \ No newline at end of file diff --git a/crates/gpui/grammars/context-predicate/Cargo.toml b/crates/gpui/grammars/context-predicate/Cargo.toml deleted file mode 100644 index a3cb197996..0000000000 --- a/crates/gpui/grammars/context-predicate/Cargo.toml +++ /dev/null @@ -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" diff --git a/crates/gpui/grammars/context-predicate/binding.gyp b/crates/gpui/grammars/context-predicate/binding.gyp deleted file mode 100644 index 16f3d1af27..0000000000 --- a/crates/gpui/grammars/context-predicate/binding.gyp +++ /dev/null @@ -1,18 +0,0 @@ -{ - "targets": [ - { - "target_name": "tree_sitter_context_predicate_binding", - "include_dirs": [ - " - -using namespace v8; - -extern "C" TSLanguage *tree_sitter_context_predicate(); - -namespace { - -NAN_METHOD(New) {} - -void Init(Local exports, Local module) { - Local tpl = Nan::New(New); - tpl->SetClassName(Nan::New("Language").ToLocalChecked()); - tpl->InstanceTemplate()->SetInternalFieldCount(1); - - Local constructor = Nan::GetFunction(tpl).ToLocalChecked(); - Local 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 diff --git a/crates/gpui/grammars/context-predicate/bindings/node/index.js b/crates/gpui/grammars/context-predicate/bindings/node/index.js deleted file mode 100644 index 3bad018a56..0000000000 --- a/crates/gpui/grammars/context-predicate/bindings/node/index.js +++ /dev/null @@ -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 (_) {} diff --git a/crates/gpui/grammars/context-predicate/bindings/rust/build.rs b/crates/gpui/grammars/context-predicate/bindings/rust/build.rs deleted file mode 100644 index c6061f0995..0000000000 --- a/crates/gpui/grammars/context-predicate/bindings/rust/build.rs +++ /dev/null @@ -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()); - */ -} diff --git a/crates/gpui/grammars/context-predicate/bindings/rust/lib.rs b/crates/gpui/grammars/context-predicate/bindings/rust/lib.rs deleted file mode 100644 index 41962c960d..0000000000 --- a/crates/gpui/grammars/context-predicate/bindings/rust/lib.rs +++ /dev/null @@ -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"); - } -} diff --git a/crates/gpui/grammars/context-predicate/corpus/expressions.txt b/crates/gpui/grammars/context-predicate/corpus/expressions.txt deleted file mode 100644 index b53bb105a8..0000000000 --- a/crates/gpui/grammars/context-predicate/corpus/expressions.txt +++ /dev/null @@ -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)))))) diff --git a/crates/gpui/grammars/context-predicate/grammar.js b/crates/gpui/grammars/context-predicate/grammar.js deleted file mode 100644 index 9a64935917..0000000000 --- a/crates/gpui/grammars/context-predicate/grammar.js +++ /dev/null @@ -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), ")"), - } -}); diff --git a/crates/gpui/grammars/context-predicate/package-lock.json b/crates/gpui/grammars/context-predicate/package-lock.json deleted file mode 100644 index 1da584a856..0000000000 --- a/crates/gpui/grammars/context-predicate/package-lock.json +++ /dev/null @@ -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 - } - } -} diff --git a/crates/gpui/grammars/context-predicate/package.json b/crates/gpui/grammars/context-predicate/package.json deleted file mode 100644 index 298e34a6b5..0000000000 --- a/crates/gpui/grammars/context-predicate/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "tree-sitter-context-predicate", - "main": "bindings/node", - "devDependencies": { - "tree-sitter-cli": "^0.19.5" - }, - "dependencies": { - "nan": "^2.14.0" - } -} diff --git a/crates/gpui/grammars/context-predicate/src/grammar.json b/crates/gpui/grammars/context-predicate/src/grammar.json deleted file mode 100644 index 9ccb50fe79..0000000000 --- a/crates/gpui/grammars/context-predicate/src/grammar.json +++ /dev/null @@ -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": [] -} - diff --git a/crates/gpui/grammars/context-predicate/src/node-types.json b/crates/gpui/grammars/context-predicate/src/node-types.json deleted file mode 100644 index 2e8868b1f1..0000000000 --- a/crates/gpui/grammars/context-predicate/src/node-types.json +++ /dev/null @@ -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 - } -] \ No newline at end of file diff --git a/crates/gpui/grammars/context-predicate/src/parser.c b/crates/gpui/grammars/context-predicate/src/parser.c deleted file mode 100644 index 57a7364a43..0000000000 --- a/crates/gpui/grammars/context-predicate/src/parser.c +++ /dev/null @@ -1,584 +0,0 @@ -#include - -#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 diff --git a/crates/gpui/grammars/context-predicate/src/tree_sitter/parser.h b/crates/gpui/grammars/context-predicate/src/tree_sitter/parser.h deleted file mode 100644 index cbbc7b4ee3..0000000000 --- a/crates/gpui/grammars/context-predicate/src/tree_sitter/parser.h +++ /dev/null @@ -1,223 +0,0 @@ -#ifndef TREE_SITTER_PARSER_H_ -#define TREE_SITTER_PARSER_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -#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_ diff --git a/crates/gpui/src/keymap_matcher/keymap_context.rs b/crates/gpui/src/keymap_matcher/keymap_context.rs index ad7ce929fe..b1dbd0e92d 100644 --- a/crates/gpui/src/keymap_matcher/keymap_context.rs +++ b/crates/gpui/src/keymap_matcher/keymap_context.rs @@ -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 { - 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 { - 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 { + 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; + + 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 { + Ok(Self::Or(Box::new(self), Box::new(other))) + } + + fn new_and(self, other: Self) -> Result { + Ok(Self::And(Box::new(self), Box::new(other))) + } + + fn new_eq(self, other: Self) -> Result { + 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 { + 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())), + ) + ); + } }