diff --git a/.gitignore b/.gitignore index f0c9c10..6f49c65 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,12 @@ node_modules yarn.lock Cargo.lock *.wasm +codegen/codegen # Rust codegen js/nodes.js js/messages.js +js/index.d.ts bindings/src/nodes.rs bindings/src/messages.rs diff --git a/Makefile b/Makefile index 0b9f5b1..ad409e8 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,13 @@ include codegen/build.mk -include build/build.mk include tests/build.mk include gh-pages/build.mk include benchmark/build.mk +build/nodejs: $(CODEGEN_FILES) + wasm-pack build --release --target nodejs --no-typescript --out-dir ../build/nodejs bindings + cp js/* build/nodejs + +CLEAN += build/nodejs + clean: rm -rf $(CLEAN) - touch codegen/build.rs diff --git a/bindings/Cargo.toml b/bindings/Cargo.toml index ee7d574..70490e8 100644 --- a/bindings/Cargo.toml +++ b/bindings/Cargo.toml @@ -12,7 +12,7 @@ default = ["console_error_panic_hook"] [dependencies] js-sys = "0.3.55" -lib-ruby-parser = {version = "3.0.12"} +lib-ruby-parser = {version = "4.0"} wasm-bindgen = "0.2.63" # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/bindings/src/lib.rs b/bindings/src/lib.rs index d00e93d..0d8cbd3 100644 --- a/bindings/src/lib.rs +++ b/bindings/src/lib.rs @@ -46,7 +46,7 @@ where } } -#[wasm_bindgen] +#[wasm_bindgen(raw_module = "./types")] extern "C" { #[wasm_bindgen(js_name = Loc)] pub type JsLoc; @@ -111,29 +111,20 @@ impl IntoJs for u8 { } } -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(js_name = Bytes)] - pub type JsBytes; - - #[wasm_bindgen(constructor, js_class = Bytes)] - fn new(raw: Vec) -> JsBytes; -} - impl IntoJs for RustBytes { - type Output = JsBytes; - fn into_js(self) -> JsBytes { - JsBytes::new(self.raw) + type Output = Vec; + fn into_js(self) -> Vec { + self.raw } } -#[wasm_bindgen] +#[wasm_bindgen(raw_module = "./types")] extern "C" { #[wasm_bindgen(js_name = Token)] pub type JsToken; #[wasm_bindgen(constructor, js_class = Token)] - fn new(token_type: i32, token_value: JsBytes, loc: JsLoc) -> JsToken; + fn new(token_type: i32, token_value: Vec, loc: JsLoc) -> JsToken; } impl IntoJs for RustToken { @@ -147,7 +138,7 @@ impl IntoJs for RustToken { } } -#[wasm_bindgen] +#[wasm_bindgen(raw_module = "./types")] extern "C" { #[wasm_bindgen(js_name = Diagnostic)] pub type JsDiagnostic; @@ -179,7 +170,7 @@ impl IntoJs for RustErrorLevel { } } -#[wasm_bindgen] +#[wasm_bindgen(raw_module = "./types")] extern "C" { #[wasm_bindgen(js_name = Comment)] pub type JsComment; @@ -207,7 +198,7 @@ impl IntoJs for RustCommentType { } } -#[wasm_bindgen] +#[wasm_bindgen(raw_module = "./types")] extern "C" { #[wasm_bindgen(js_name = MagicComment)] pub type JsMagicComment; @@ -240,7 +231,7 @@ impl IntoJs for RustMagicCommentKind { } } -#[wasm_bindgen] +#[wasm_bindgen(raw_module = "./types")] extern "C" { #[wasm_bindgen(js_name = DecodedInput)] pub type JsDecodedInput; @@ -263,7 +254,7 @@ impl IntoJs for RustDecodedInput { } } -#[wasm_bindgen] +#[wasm_bindgen(raw_module = "./types")] extern "C" { #[wasm_bindgen(js_name = SourceLine)] pub type JsSourceLine; @@ -279,7 +270,7 @@ impl IntoJs for RustSourceLine { } } -#[wasm_bindgen] +#[wasm_bindgen(raw_module = "./types")] extern "C" { #[wasm_bindgen(js_name = ParserResult)] pub type JsParserResult; diff --git a/build/build.mk b/build/build.mk deleted file mode 100644 index b762184..0000000 --- a/build/build.mk +++ /dev/null @@ -1,46 +0,0 @@ -JS_FILES += js/types.js -JS_FILES += js/messages.js -JS_FILES += js/nodes.js - -RS_FILES += bindings/src/lib.rs -RS_FILES += bindings/src/messages.rs -RS_FILES += bindings/src/nodes.rs - -ifndef TARGET -$(error TARGET variable is required) -endif - -ifeq ($(TARGET),nodejs) -ENV = nodejs -else -ifeq ($(TARGET),no-modules) -ENV = web -else -$(error TARGET can only be `nodejs` or `no-modules`, but `$(TARGET)` was passed) -endif -endif - -$(info TARGET = $(TARGET)) - -RUN_WASM_PACK = cd bindings && \ - wasm-pack build --release --target $(TARGET) && \ - cd .. && \ - cp bindings/pkg/lib_ruby_parser_wasm.js build/$(ENV)-lib-ruby-parser-wrapper.js && \ - cp bindings/pkg/lib_ruby_parser_wasm_bg.wasm build/$(ENV)-lib-ruby-parser.wasm - -# WASM wrapper generated by wasm-pack -build/$(ENV)-lib-ruby-parser-wrapper.js: $(RS_FILES) - $(RUN_WASM_PACK) - -build/$(ENV)-lib-ruby-parser.wasm: $(RS_FILES) - $(RUN_WASM_PACK) - -build/$(ENV)-lib-ruby-parser.js: build/scripts/merge.js build/$(ENV)-lib-ruby-parser-wrapper.js $(JS_FILES) - ENV=$(ENV) node build/scripts/merge.js - -CLEAN += build/web-lib-ruby-parser-wrapper.js -CLEAN += build/web-lib-ruby-parser.wasm -CLEAN += build/web-lib-ruby-parser.js -CLEAN += build/nodejs-lib-ruby-parser-wrapper.js -CLEAN += build/nodejs-lib-ruby-parser.wasm -CLEAN += build/nodejs-lib-ruby-parser.js diff --git a/build/scripts/merge.js b/build/scripts/merge.js deleted file mode 100644 index cd9ffaf..0000000 --- a/build/scripts/merge.js +++ /dev/null @@ -1,63 +0,0 @@ -const fs = require('fs'); -const ENV = process.env.ENV; - -const wrapper_src = fs.readFileSync(`./build/${ENV}-lib-ruby-parser-wrapper.js`).toString().replace('lib_ruby_parser_wasm_bg', `${ENV}-lib-ruby-parser`); -const types_src = fs.readFileSync('./js/types.js').toString(); -const nodes_src = fs.readFileSync('./js/nodes.js').toString(); -const messages_src = fs.readFileSync('./js/messages.js').toString(); - -let POST_INIT; -if (ENV == 'web') { - POST_INIT = ` -root.parse = wasm_bindgen.parse; -let onLoadCallbacks = []; -let loaded = false; -root.onLoad = function(cb) { - if (loaded) { - cb() - } else { - onLoadCallbacks.push(cb); - } -} -const lib_ruby_parser_wasm_url = document.currentScript.src.replace(/\.js$/, '.wasm'); -wasm_bindgen(lib_ruby_parser_wasm_url).then(() => { - loaded = true; - onLoadCallbacks.forEach(cb => cb()); - onLoadCallbacks = []; -}); -`; -} else { - POST_INIT = ``; -} - -const HEADER = ` -(function () { - let root; - - if (typeof (module) !== 'undefined') { - root = module.exports; - } else if (typeof (window) !== 'undefined') { - window.LibRubyParser = {}; - root = window.LibRubyParser; - } -`; - -const FOOTER = ` -})(); -`; - -const output = ` -${HEADER} - -${nodes_src} -${messages_src} -${types_src} - -${wrapper_src} - -${POST_INIT} - -${FOOTER} -` - -fs.writeFileSync(`./build/${ENV}-lib-ruby-parser.js`, output); diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml deleted file mode 100644 index cdfb555..0000000 --- a/codegen/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -edition = "2018" -name = "codegen" -version = "0.1.0" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[build-dependencies] -lib-ruby-parser-nodes = {version = "0.39.0"} diff --git a/codegen/build.mk b/codegen/build.mk index 668bf33..994cc67 100644 --- a/codegen/build.mk +++ b/codegen/build.mk @@ -1,20 +1,34 @@ -CODEGEN_DEPS = $(wildcard codegen/codegen/*.rs) -CODEGEN_DEPS += codegen/Cargo.toml -CODEGEN_DEPS += codegen/build.rs -DO_CODEGEN = cd codegen && cargo build +define download_file +curl "$(1)" --fail-with-body -LSso "$(2).tmp" && mv "$(2).tmp" "$(2)" +endef -js/nodes.js: $(CODEGEN_DEPS) - $(DO_CODEGEN) +CODEGEN_EXE = codegen/codegen$(EXE) +$(CODEGEN_EXE): + $(call download_file,https://github.com/lib-ruby-parser/lib-ruby-parser/releases/download/codegen-v1.0.1/codegen-x86_64-apple-darwin,$@) + chmod +x $(CODEGEN_EXE) +CLEAN += $(CODEGEN_EXE) + +js/index.d.ts: codegen/types.d.ts.liquid $(CODEGEN_EXE) + $(CODEGEN_EXE) --template $< --write-to $@ +CLEAN += js/index.d.ts +CODEGEN_FILES += js/index.d.ts + +js/nodes.js: codegen/nodes.js.liquid $(CODEGEN_EXE) + $(CODEGEN_EXE) --template $< --write-to $@ CLEAN += js/nodes.js +CODEGEN_FILES += js/nodes.js -js/messages.js: $(CODEGEN_DEPS) - $(DO_CODEGEN) +js/messages.js: codegen/messages.js.liquid $(CODEGEN_EXE) + $(CODEGEN_EXE) --template $< --write-to $@ CLEAN += js/messages.js +CODEGEN_FILES += js/messages.js -bindings/src/nodes.rs: $(CODEGEN_DEPS) - $(DO_CODEGEN) +bindings/src/nodes.rs: codegen/nodes.rs.liquid $(CODEGEN_EXE) + $(CODEGEN_EXE) --template $< --write-to $@ CLEAN += bindings/src/nodes.rs +CODEGEN_FILES += bindings/src/nodes.rs -bindings/src/messages.rs: $(CODEGEN_DEPS) - $(DO_CODEGEN) +bindings/src/messages.rs: codegen/messages.rs.liquid $(CODEGEN_EXE) + $(CODEGEN_EXE) --template $< --write-to $@ CLEAN += bindings/src/messages.rs +CODEGEN_FILES += bindings/src/messages.rs diff --git a/codegen/build.rs b/codegen/build.rs deleted file mode 100644 index 1c7db0b..0000000 --- a/codegen/build.rs +++ /dev/null @@ -1,7 +0,0 @@ -extern crate lib_ruby_parser_nodes; - -mod codegen; - -fn main() { - codegen::codegen(); -} diff --git a/codegen/codegen/fns.rs b/codegen/codegen/fns.rs deleted file mode 100644 index f4d6561..0000000 --- a/codegen/codegen/fns.rs +++ /dev/null @@ -1,198 +0,0 @@ -use lib_ruby_parser_nodes::{ - template::TemplateFns, template::F, Message, MessageField, Node, NodeField, -}; - -pub(crate) mod nodes { - use super::*; - - pub(crate) fn camelcase_name(node: &Node) -> String { - node.camelcase_name.to_owned() - } - - pub(crate) fn comment(node: &Node) -> String { - node.render_comment("///", 0) - } - - pub(crate) fn str_type(node: &Node) -> String { - node.wqp_name.to_string() - } - - pub(crate) fn upper_name(node: &Node) -> String { - node.upper_name() - } - - pub(crate) fn lower_name(node: &Node) -> String { - node.lower_name() - } - - pub(crate) fn is_last(node: &Node) -> bool { - lib_ruby_parser_nodes::template::ALL_DATA - .nodes - .last() - .unwrap() - == &node - } -} - -pub(crate) mod node_fields { - use super::*; - - pub(crate) fn name(node_field: &NodeField) -> String { - match &node_field.snakecase_name[..] { - "const" => "const_", - "as" => "as_", - "else" => "else_", - other => other, - } - .to_string() - } - - pub(crate) fn comment(node_field: &NodeField) -> String { - node_field.render_comment("///", 4) - } - - pub(crate) fn js_name(node_field: &NodeField) -> String { - match &node_field.snakecase_name[..] { - "const" => "const_", - "default" => "default_", - "var" => "var_", - "else" => "else_", - other => other, - } - .to_string() - } - - pub(crate) fn js_type(node_field: &NodeField) -> String { - use lib_ruby_parser_nodes::NodeFieldType::*; - - match node_field.field_type { - Node => "JsNode", - Nodes => "Vec", - MaybeNode { .. } => "Option", - Loc => "JsLoc", - MaybeLoc => "Option", - Str { .. } => "String", - MaybeStr { .. } => "Option", - StringValue => "JsBytes", - U8 => "u8", - } - .to_string() - } - - pub(crate) fn is_last(node_field: &NodeField) -> bool { - node_field.node.fields.last().unwrap() == &node_field - } -} - -pub(crate) mod messages { - use super::*; - - pub(crate) fn camelcase_name(message: &Message) -> String { - message.camelcase_name.to_owned() - } - - pub(crate) fn upper_name(message: &Message) -> String { - message.upper_name() - } - - pub(crate) fn lower_name(message: &Message) -> String { - message.lower_name() - } - - pub(crate) fn has_no_fields(message: &Message) -> bool { - message.fields.is_empty() - } - - pub(crate) fn comment(message: &Message) -> String { - message.render_comment("///", 0) - } - - pub(crate) fn is_last(message: &Message) -> bool { - lib_ruby_parser_nodes::template::ALL_DATA - .messages - .last() - .unwrap() - == &message - } -} - -pub(crate) mod message_fields { - use super::*; - - pub(crate) fn name(message_field: &MessageField) -> String { - message_field.snakecase_name.to_owned() - } - - pub(crate) fn js_name(message_field: &MessageField) -> String { - message_field.snakecase_name.to_owned() - } - - pub(crate) fn js_type(message_field: &MessageField) -> String { - use lib_ruby_parser_nodes::MessageFieldType::*; - - match message_field.field_type { - Str => "String", - Byte => "u8", - } - .to_string() - } - - pub(crate) fn comment(message_field: &MessageField) -> String { - message_field.render_comment("///", 4) - } - - pub(crate) fn is_last(message_field: &MessageField) -> bool { - message_field.message.fields.last().unwrap() == &message_field - } -} - -pub(crate) fn build() -> TemplateFns { - let mut fns = TemplateFns::new(); - - fns.register::("node-camelcase-name", nodes::camelcase_name); - fns.register::("node-comment", nodes::comment); - fns.register::("node-str-type", nodes::str_type); - fns.register::("node-upper-name", nodes::upper_name); - fns.register::("node-lower-name", nodes::lower_name); - fns.register::("node-is-last", nodes::is_last); - - fns.register::("node-field-name", node_fields::name); - fns.register::("node-field-comment", node_fields::comment); - fns.register::("node-field-js-name", node_fields::js_name); - fns.register::("node-field-js-type", node_fields::js_type); - fns.register::("node-field-is-last", node_fields::is_last); - - fns.register::("message-camelcase-name", messages::camelcase_name); - fns.register::("message-upper-name", messages::upper_name); - fns.register::("message-lower-name", messages::lower_name); - fns.register::("message-comment", messages::comment); - fns.register::("message-is-last", messages::is_last); - fns.register::("message-has-no-fields", messages::has_no_fields); - - fns.register::("message-field-name", message_fields::name); - fns.register::("mesage-field-comment", message_fields::comment); - fns.register::("message-field-js-name", message_fields::js_name); - fns.register::("message-field-js-type", message_fields::js_type); - fns.register::("message-field-is-last", message_fields::is_last); - - fns -} - -macro_rules! default_fns { - () => {{ - fn generated_by(_: &lib_ruby_parser_nodes::template::GlobalContext) -> String { - file!().to_string() - } - fn generated_by_for_node(_: &lib_ruby_parser_nodes::Node) -> String { - file!().to_string() - } - let mut fns = $crate::codegen::fns::build(); - fns.register::("generated-by", generated_by); - fns.register::( - "generated-by", - generated_by_for_node, - ); - fns - }}; -} -pub(crate) use default_fns; diff --git a/codegen/codegen/messages_js.rs b/codegen/codegen/messages_js.rs deleted file mode 100644 index 265379b..0000000 --- a/codegen/codegen/messages_js.rs +++ /dev/null @@ -1,38 +0,0 @@ -use lib_ruby_parser_nodes::template::*; - -const TEMPLATE: &str = "// This file is autogenerated by {{ helper generated-by }} - -class DiagnsoticMessage {} -const messages = {}; - -(function() { - -{{ each message }} -class {{ helper message-camelcase-name }} extends DiagnsoticMessage { - constructor( -{{ each message-field }} - {{ helper message-field-js-name }}, -{{ end }} - ) { - super(); -{{ each message-field }} - this.{{ helper message-field-js-name }} = {{ helper message-field-js-name }}; -{{ end }} - } -} -messages.{{ helper message-camelcase-name }} = {{ helper message-camelcase-name }}; -{{ end }} - -})(); - -root.DiagnsoticMessage = DiagnsoticMessage; -root.messages = messages; -"; - -pub(crate) fn codegen() { - let template = TemplateRoot::new(TEMPLATE).unwrap(); - let fns = crate::codegen::fns::default_fns!(); - - let contents = template.render(ALL_DATA, &fns); - std::fs::write("../js/messages.js", contents).unwrap(); -} diff --git a/codegen/codegen/messages_rs.rs b/codegen/codegen/messages_rs.rs deleted file mode 100644 index e3ac7bf..0000000 --- a/codegen/codegen/messages_rs.rs +++ /dev/null @@ -1,60 +0,0 @@ -use lib_ruby_parser_nodes::template::*; - -const TEMPLATE: &str = "// This file is autogenerated by {{ helper generated-by }} - -use wasm_bindgen::prelude::*; -use crate::IntoJs; - -{{ each message }} -#[wasm_bindgen] -extern \"C\" { - #[wasm_bindgen(js_name = {{ helper message-camelcase-name }})] - pub type Js{{ helper message-camelcase-name }}; - - #[wasm_bindgen(constructor, js_namespace = messages, js_class = {{ helper message-camelcase-name }})] - fn new( -{{ each message-field }} - {{ helper message-field-name }}: {{ helper message-field-js-type }}, -{{ end }} - ) -> Js{{ helper message-camelcase-name }}; -} -{{ end }} - -#[wasm_bindgen] -extern \"C\" { - #[wasm_bindgen(js_name = DiagnosticMessage)] - pub type JsDiagnosticMessage; - - #[wasm_bindgen(constructor, js_class = DiagnosticMessage)] - fn new(v: JsValue) -> JsDiagnosticMessage; -} -use lib_ruby_parser::DiagnosticMessage as RustDiagnosticMessage; -impl IntoJs for RustDiagnosticMessage { - type Output = JsDiagnosticMessage; - fn into_js(self) -> JsDiagnosticMessage { - match self { -{{ each message }} - Self::{{ helper message-camelcase-name }} { {{ each message-field }}{{ helper message-field-name }}, {{ end }} } => { - JsDiagnosticMessage::from( - JsValue::from( - Js{{ helper message-camelcase-name }}::new( -{{ each message-field }} - {{ helper message-field-name }}.into_js(), -{{ end }} - ) - ) - ) - }, -{{ end }} - } - } -} -"; - -pub(crate) fn codegen() { - let template = TemplateRoot::new(TEMPLATE).unwrap(); - let fns = crate::codegen::fns::default_fns!(); - - let contents = template.render(ALL_DATA, &fns); - std::fs::write("../bindings/src/messages.rs", contents).unwrap(); -} diff --git a/codegen/codegen/mod.rs b/codegen/codegen/mod.rs deleted file mode 100644 index b20b29b..0000000 --- a/codegen/codegen/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -mod messages_rs; -mod nodes_rs; - -mod messages_js; -mod nodes_js; - -mod fns; - -pub(crate) fn codegen() { - nodes_rs::codegen(); - messages_rs::codegen(); - - nodes_js::codegen(); - messages_js::codegen(); -} diff --git a/codegen/codegen/nodes_js.rs b/codegen/codegen/nodes_js.rs deleted file mode 100644 index 623eaef..0000000 --- a/codegen/codegen/nodes_js.rs +++ /dev/null @@ -1,38 +0,0 @@ -use lib_ruby_parser_nodes::template::*; - -const TEMPLATE: &str = "// This file is autogenerated by {{ helper generated-by }} - -class Node {} -const nodes = {}; - -(function() { - -{{ each node }} -class {{ helper node-camelcase-name }} extends Node { - constructor( -{{ each node-field }} - {{ helper node-field-js-name }}, -{{ end }} - ) { - super(); -{{ each node-field }} - this.{{ helper node-field-js-name }} = {{ helper node-field-js-name }}; -{{ end }} - } -} -nodes.{{ helper node-camelcase-name }} = {{ helper node-camelcase-name }}; -{{ end }} - -})(); - -root.Node = Node; -root.nodes = nodes; -"; - -pub(crate) fn codegen() { - let template = TemplateRoot::new(TEMPLATE).unwrap(); - let fns = crate::codegen::fns::default_fns!(); - - let contents = template.render(ALL_DATA, &fns); - std::fs::write("../js/nodes.js", contents).unwrap(); -} diff --git a/codegen/codegen/nodes_rs.rs b/codegen/codegen/nodes_rs.rs deleted file mode 100644 index ab2ffa6..0000000 --- a/codegen/codegen/nodes_rs.rs +++ /dev/null @@ -1,64 +0,0 @@ -use lib_ruby_parser_nodes::template::*; - -const TEMPLATE: &str = "// This file is autogenerated by {{ helper generated-by }} - -use wasm_bindgen::prelude::*; -use crate::{IntoJs, JsLoc, JsBytes}; - -{{ each node }} -#[wasm_bindgen] -extern \"C\" { - #[wasm_bindgen(js_name = {{ helper node-camelcase-name }})] - pub type Js{{ helper node-camelcase-name }}; - - #[wasm_bindgen(constructor, js_namespace = nodes, js_class = {{ helper node-camelcase-name }})] - fn new( -{{ each node-field }} - {{ helper node-field-name }}: {{ helper node-field-js-type }}, -{{ end }} - ) -> Js{{ helper node-camelcase-name }}; -} -use lib_ruby_parser::nodes::{{ helper node-camelcase-name }} as Rust{{ helper node-camelcase-name }}; -impl IntoJs for Rust{{ helper node-camelcase-name }} { - type Output = Js{{ helper node-camelcase-name }}; - fn into_js(self) -> Js{{ helper node-camelcase-name }} { - Js{{ helper node-camelcase-name }}::new( -{{ each node-field }} - self.{{ helper node-field-name }}.into_js(), -{{ end }} - ) - } -} -{{ end }} - -#[wasm_bindgen] -extern \"C\" { - #[wasm_bindgen(js_name = Node)] - pub type JsNode; - - #[wasm_bindgen(constructor, js_class = Node)] - fn new(v: JsValue) -> JsNode; -} -use lib_ruby_parser::Node as RustNode; -impl IntoJs for RustNode { - type Output = JsNode; - fn into_js(self) -> JsNode { - match self { -{{ each node }} - Self::{{ helper node-camelcase-name }}(inner) => { - let js_inner: Js{{ helper node-camelcase-name }} = inner.into_js(); - JsNode::from(JsValue::from(js_inner)) - }, -{{ end }} - } - } -} -"; - -pub(crate) fn codegen() { - let template = TemplateRoot::new(TEMPLATE).unwrap(); - let fns = crate::codegen::fns::default_fns!(); - - let contents = template.render(ALL_DATA, &fns); - std::fs::write("../bindings/src/nodes.rs", contents).unwrap(); -} diff --git a/codegen/messages.js.liquid b/codegen/messages.js.liquid new file mode 100644 index 0000000..23ea41b --- /dev/null +++ b/codegen/messages.js.liquid @@ -0,0 +1,27 @@ +// This file is autogenerated by {{ template }} + +class Message {} + +{%- for message in messages %} +class {{ message.camelcase_name }} extends Message { + constructor( + {%- for field in message.fields %} + {{ field.snakecase_name | escape_js_keyword }}, + {%- endfor %} + ) { + super(); + {%- for field in message.fields %} + this.{{ field.snakecase_name | escape_js_keyword }} = {{ field.snakecase_name | escape_js_keyword }}; + {%- endfor %} + } +} +{%- endfor %} + +module.exports = { + Message, + messages: { + {%- for message in messages %} + {{ message.camelcase_name }}, + {%- endfor %} + } +} diff --git a/codegen/messages.rs.liquid b/codegen/messages.rs.liquid new file mode 100644 index 0000000..f6275dd --- /dev/null +++ b/codegen/messages.rs.liquid @@ -0,0 +1,56 @@ +// This field is autogenerated by {{ template }} + +use wasm_bindgen::prelude::*; +use crate::IntoJs; + +{%- for message in messages %} +#[wasm_bindgen(raw_module = "./messages")] +extern "C" { + #[wasm_bindgen(js_name = {{ message.camelcase_name }})] + pub type Js{{ message.camelcase_name }}; + + #[wasm_bindgen(constructor, js_namespace = messages, js_class = {{ message.camelcase_name }})] + fn new( +{% for field in message.fields %} + {{ field.comment | render_comment: "///", 8 }} + {{ field.snakecase_name }}: + {%- case field.field_type -%} + {%- when "Str" -%} + String, + {%- when "Byte" -%} + u8, + {%- endcase -%} +{% endfor %} + ) -> Js{{ message.camelcase_name }}; +} +{%- endfor %} + +#[wasm_bindgen(raw_module = "./messages")] +extern "C" { + #[wasm_bindgen(js_name = DiagnosticMessage)] + pub type JsDiagnosticMessage; + + #[wasm_bindgen(constructor, js_class = DiagnosticMessage)] + fn new(v: JsValue) -> JsDiagnosticMessage; +} +use lib_ruby_parser::DiagnosticMessage as RustDiagnosticMessage; +impl IntoJs for RustDiagnosticMessage { + type Output = JsDiagnosticMessage; + fn into_js(self) -> JsDiagnosticMessage { + match self { +{%- for message in messages %} + Self::{{ message.camelcase_name }} { {%- for field in message.fields %}{{ field.snakecase_name }}, {%- endfor %} } => { + JsDiagnosticMessage::from( + JsValue::from( + Js{{ message.camelcase_name }}::new( +{%- for field in message.fields %} + {{ field.snakecase_name }}.into_js(), +{%- endfor %} + ) + ) + ) + }, +{%- endfor %} + } + } +} diff --git a/codegen/nodes.js.liquid b/codegen/nodes.js.liquid new file mode 100644 index 0000000..e9b8f33 --- /dev/null +++ b/codegen/nodes.js.liquid @@ -0,0 +1,27 @@ +// This file is autogenerated by {{ template }} + +class Node {} + +{%- for node in nodes %} +class {{ node.camelcase_name }} extends Node { + constructor( + {%- for field in node.fields %} + {{ field.snakecase_name | escape_js_keyword }}, + {%- endfor %} + ) { + super(); + {%- for field in node.fields %} + this.{{ field.snakecase_name | escape_js_keyword }} = {{ field.snakecase_name | escape_js_keyword }}; + {%- endfor %} + } +} +{%- endfor %} + +module.exports = { + Node, + nodes: { + {%- for node in nodes %} + {{ node.camelcase_name }}, + {%- endfor %} + } +} diff --git a/codegen/nodes.rs.liquid b/codegen/nodes.rs.liquid new file mode 100644 index 0000000..2a95735 --- /dev/null +++ b/codegen/nodes.rs.liquid @@ -0,0 +1,78 @@ +// This file is autogenerated by {{ template }} + +use wasm_bindgen::prelude::*; +use crate::{IntoJs, JsLoc}; + +{%- for node in nodes %} +#[wasm_bindgen(raw_module = "./nodes")] +extern "C" { + #[wasm_bindgen(js_name = {{ node.camelcase_name }})] + pub type Js{{ node.camelcase_name }}; + + #[wasm_bindgen(constructor, js_namespace = nodes, js_class = {{ node.camelcase_name }})] + fn new( +{%- for field in node.fields %} + {{ field.snakecase_name | escape_rust_keyword }}: + {{- " " -}} + {%- case field.field_type -%} + {%- when "Node" -%} + JsNode + {%- when "Nodes" -%} + Vec + {%- when "MaybeNode" or "RegexpOptions" -%} + Option + {%- when "Loc" -%} + JsLoc + {%- when "MaybeLoc" -%} + Option + {%- when "Str" or "RawStr" -%} + String + {%- when "MaybeStr" or "Chars" -%} + Option + {%- when "StringValue" -%} + Vec + {%- when "U8" -%} + u8 + {%- else -%} + compile_error!("Unsupported field type {{ field.field_type }}") + {%- endcase -%}, +{%- endfor %} + ) -> Js{{ node.camelcase_name }}; +} + +use lib_ruby_parser::nodes::{{ node.camelcase_name }} as Rust{{ node.camelcase_name }}; +impl IntoJs for Rust{{ node.camelcase_name }} { + type Output = Js{{ node.camelcase_name }}; + fn into_js(self) -> Js{{ node.camelcase_name }} { + Js{{ node.camelcase_name }}::new( +{%- for field in node.fields %} + self.{{ field.snakecase_name | escape_rust_keyword }}.into_js(), +{%- endfor %} + ) + } +} +{%- endfor %} + +#[wasm_bindgen(raw_module = "./nodes")] +extern "C" { + #[wasm_bindgen(js_name = Node)] + pub type JsNode; + + #[wasm_bindgen(constructor, js_class = Node)] + fn new(v: JsValue) -> JsNode; +} + +use lib_ruby_parser::Node as RustNode; +impl IntoJs for RustNode { + type Output = JsNode; + fn into_js(self) -> JsNode { + match self { +{%- for node in nodes %} + Self::{{ node.camelcase_name }}(inner) => { + let js_inner: Js{{ node.camelcase_name }} = inner.into_js(); + JsNode::from(JsValue::from(js_inner)) + }, +{%- endfor %} + } + } +} diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs deleted file mode 100644 index 8b13789..0000000 --- a/codegen/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/codegen/tokens.js.liquid b/codegen/tokens.js.liquid new file mode 100644 index 0000000..b99ad0f --- /dev/null +++ b/codegen/tokens.js.liquid @@ -0,0 +1,14 @@ +// This file is autogenerated by {{ template }} + +module.exports = { + name_to_id: { + {%- for token in tokens %} + {{ token.name }}: {{ token.value }}, + {%- endfor %} + }, + id_to_name: { + {%- for token in tokens %} + {{ token.value }}: "{{ token.name }}", + {%- endfor %} + } +} diff --git a/codegen/types.d.ts.liquid b/codegen/types.d.ts.liquid new file mode 100644 index 0000000..116f8cb --- /dev/null +++ b/codegen/types.d.ts.liquid @@ -0,0 +1,232 @@ +// This file is autogenerated by {{ template }} + +export function parse( + input: string, + buffer_name: string, + decoder: (encoding: string, input: Uint8Array) => Uint8Array +): ParserResult; + +export class ParserResult { + ast: Node | null; + tokens: Token[]; + diagnostics: Diagnostic[]; + comments: Comment[]; + magic_comments: MagicComment[]; + input: DecodedInput; + + constructor( + ast: Node | null, + tokens: Token[], + diagnostics: Diagnostic[], + comments: Comment[], + magic_comments: MagicComment[], + input: DecodedInput, + ); +} + +export class Token { + token_type: number; + token_value: Uint8Array; + loc: Loc; + lex_state_before: number; + lex_state_after: number; + + constructor( + token_type: number, + token_value: Uint8Array, + loc: Loc, + lex_state_before: number, + lex_state_after: number, + ); + + name(): string; +} + + +export class Loc { + begin: number; + end: number; + + constructor( + begin: number, + end: number, + ); +} + +export class Comment { + loc: Loc; + kind: CommentKind; + + constructor( + loc: Loc, + kind: CommentKind, + ); +} + +export type CommentKind = + | "inline" + | "document" + | "unknown"; + +export class MagicComment { + kind: MagicCommentKind; + key_l: Loc; + value_l: Loc; + + constructor( + kind: MagicCommentKind, + key_l: Loc, + value_l: Loc, + ); +} + +export type MagicCommentKind = + | "encoding" + | "frozen_string_literal" + | "warn_indent" + | "shareable_constant_value"; + +export class DecodedInput { + name: string; + lines: SourceLine[]; + bytes: Uint8Array; + + constructor( + name: string, + lines: SourceLine[], + bytes: Uint8Array, + ); +} + +export class SourceLine { + start: number; + end: number; + ends_with_eof: boolean; + + constructor( + start: number, + end: number, + ends_with_eof: boolean, + ); +} + +export class Diagnostic { + level: ErrorLevel; + message: DiagnosticMessage; + loc: Loc; + + constructor( + level: ErrorLevel, + message: DiagnosticMessage, + loc: Loc, + ); +} + +export type ErrorLevel = + | "warning" + | "error"; + +export class Node {} + +export namespace nodes { + {%- for node in nodes %} + {{ node.comment | render_comment: "//", 4 }} + export class {{ node.camelcase_name }} { + {%- for field in node.fields %} + {%- case field.field_type -%} + {%- when "Node" -%} + {%- assign field_type = "Node" -%} + {%- when "Nodes" -%} + {%- assign field_type = "Node[]" -%} + {%- when "MaybeNode" or "RegexpOptions" -%} + {%- assign field_type = "Node | null" -%} + {%- when "Loc" -%} + {%- assign field_type = "Loc" -%} + {%- when "MaybeLoc" -%} + {%- assign field_type = "Loc | null" -%} + {%- when "Str" or "RawStr" -%} + {%- assign field_type = "string" -%} + {%- when "MaybeStr" or "Chars" -%} + {%- assign field_type = "string | null" -%} + {%- when "StringValue" -%} + {%- assign field_type = "Uint8Array" -%} + {%- when "U8" -%} + {%- assign field_type = "number" -%} + {%- else -%} + {%- assign field_type = "COMPILE ERROR" -%} + {%- endcase %} + {{ field.comment | render_comment: "//", 8 }} + {{ field.snakecase_name | escape_js_keyword }}: {{ field_type }}; + {%- endfor %} + + constructor( + {%- for field in node.fields %} + {%- case field.field_type -%} + {%- when "Node" -%} + {%- assign field_type = "Node" -%} + {%- when "Nodes" -%} + {%- assign field_type = "Node[]" -%} + {%- when "MaybeNode" or "RegexpOptions" -%} + {%- assign field_type = "Node | null" -%} + {%- when "Loc" -%} + {%- assign field_type = "Loc" -%} + {%- when "MaybeLoc" -%} + {%- assign field_type = "Loc | null" -%} + {%- when "Str" or "RawStr" -%} + {%- assign field_type = "string" -%} + {%- when "MaybeStr" or "Chars" -%} + {%- assign field_type = "string | null" -%} + {%- when "StringValue" -%} + {%- assign field_type = "Uint8Array" -%} + {%- when "U8" -%} + {%- assign field_type = "number" -%} + {%- else -%} + {%- assign field_type = "COMPILE ERROR" -%} + {%- endcase %} + {{ field.snakecase_name | escape_js_keyword }}: {{ field_type }}, + {%- endfor %} + ); + } + {%- endfor %} +} + +export class DiagnosticMessage {} + +export namespace messages { + {%- for message in messages %} + {{ message.comment | render_comment: "//", 4 }} + export class {{ message.camelcase_name }} { + {%- for field in message.fields %} + {%- case field.field_type -%} + {%- when "Str" -%} + {%- assign field_type = "string" -%} + {%- when "Byte" -%} + {%- assign field_type = "number" -%} + {%- else -%} + {%- assign field_type = "COMPILE ERROR" -%} + {%- endcase %} + {{ field.comment | render_comment: "//", 8 }} + {{ field.snakecase_name | escape_js_keyword }}: {{ field_type }}; + {%- endfor %} + + constructor( + {%- for field in message.fields %} + {%- case field.field_type -%} + {%- when "Str" -%} + {%- assign field_type = "string" -%} + {%- when "Byte" -%} + {%- assign field_type = "number" -%} + {%- else -%} + {%- assign field_type = "COMPILE ERROR" -%} + {%- endcase %} + {{ field.snakecase_name | escape_js_keyword }}: {{ field_type }}, + {%- endfor %} + ); + } + {%- endfor %} +} + +export namespace tokens { + export const id_to_name: { [K in number]: string }; + export const name_to_id: { [K in string]: number }; +} diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..b9d6515 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,5321 @@ +// This file is autogenerated by codegen/types.d.ts.liquid + +export function parse( + input: string, + buffer_name: string, + decoder: (encoding: string, input: Uint8Array) => Uint8Array +): ParserResult; + +export class ParserResult { + ast: Node | null; + tokens: Token[]; + diagnostics: Diagnostic[]; + comments: Comment[]; + magic_comments: MagicComment[]; + input: DecodedInput; + + constructor( + ast: Node | null, + tokens: Token[], + diagnostics: Diagnostic[], + comments: Comment[], + magic_comments: MagicComment[], + input: DecodedInput, + ); +} + +export class Token { + token_type: number; + token_value: Uint8Array; + loc: Loc; + lex_state_before: number; + lex_state_after: number; + + constructor( + token_type: number, + token_value: Uint8Array, + loc: Loc, + lex_state_before: number, + lex_state_after: number, + ); + + name(): string; +} + + +export class Loc { + begin: number; + end: number; + + constructor( + begin: number, + end: number, + ); +} + +export class Comment { + loc: Loc; + kind: CommentKind; + + constructor( + loc: Loc, + kind: CommentKind, + ); +} + +export type CommentKind = + | "inline" + | "document" + | "unknown"; + +export class MagicComment { + kind: MagicCommentKind; + key_l: Loc; + value_l: Loc; + + constructor( + kind: MagicCommentKind, + key_l: Loc, + value_l: Loc, + ); +} + +export type MagicCommentKind = + | "encoding" + | "frozen_string_literal" + | "warn_indent" + | "shareable_constant_value"; + +export class DecodedInput { + name: string; + lines: SourceLine[]; + bytes: Uint8Array; + + constructor( + name: string, + lines: SourceLine[], + bytes: Uint8Array, + ); +} + +export class SourceLine { + start: number; + end: number; + ends_with_eof: boolean; + + constructor( + start: number, + end: number, + ends_with_eof: boolean, + ); +} + +export class Diagnostic { + level: ErrorLevel; + message: DiagnosticMessage; + loc: Loc; + + constructor( + level: ErrorLevel, + message: DiagnosticMessage, + loc: Loc, + ); +} + +export type ErrorLevel = + | "warning" + | "error"; + +export class Node {} + +export namespace nodes { + // Represents `alias to from` statement. + export class Alias { + // Target of the `alias`. + // + // `Sym("foo")` node for `alias :foo :bar` + to: Node; + // Source of the `alias`. + // + // `Sym("bar")` node for `alias :foo :bar` + from: Node; + // Location of the `alias` keyword + // + // ```text + // alias foo bar + // ~~~~~ + // ``` + keyword_l: Loc; + // Location of the full expression + // + // ```text + // alias foo bar + // ~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + to: Node, + from: Node, + keyword_l: Loc, + expression_l: Loc, + ); + } + // Represents `foo && bar` (or `foo and bar`) statement. + export class And { + // Left hand statament of the `&&` operation. + // + // `Lvar("foo")` node for `foo && bar` + lhs: Node; + // Right hand statement of the `&&` operation. + // + // `Lvar("bar")` node for `foo && bar` + rhs: Node; + // Location of the `&&` (or `and`) operator + // + // ```text + // a && b + // ~~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // a && b + // ~~~~~~ + // ``` + expression_l: Loc; + + constructor( + lhs: Node, + rhs: Node, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents `a &&= 1` statement. + export class AndAsgn { + // Receiver of the `&&=` operation. + // + // `Lvasgn("a")` node for `a &&= 1` + recv: Node; + // Right hand statement of assignment + // + // `Int("1")` node for `a &&= 1` + value: Node; + // Location of the `&&=` operator + // + // ```text + // a &&= 1 + // ~~~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // a &&= 1 + // ~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + recv: Node, + value: Node, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents a positional required block/method argument. + // + // `a` in `def m(a); end` or `proc { |a| }` + export class Arg { + // Name of the argument + name: string; + // Location of the full expression + // + // ```text + // def m(argument); end + // ~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + expression_l: Loc, + ); + } + // Represents an arguments list + // + // `Args(vec![Arg("a"), Optarg("b", Int("1"))])` in `def m(a, b = 1); end` + export class Args { + // List of arguments + args: Node[]; + // Location of the full expression + // + // ```text + // def m(a, b = 1, c:, &blk); end + // ~~~~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + // Location of the open parenthesis + // + // ```text + // def m(a, b = 1, c:, &blk); end + // ~ + // ``` + // + // `None` for code like `def m; end` or `def m arg; end` + begin_l: Loc | null; + // Location of the closing parenthesis + // + // ```text + // def m(a, b = 1, c:, &blk); end + // ~ + // ``` + // + // `None` for code like `def m; end` or `def m arg; end` + end_l: Loc | null; + + constructor( + args: Node[], + expression_l: Loc, + begin_l: Loc | null, + end_l: Loc | null, + ); + } + // Represents an array literal + export class Array { + // A list of elements + elements: Node[]; + // Location of the open bracket + // + // ```text + // [1, 2, 3] + // ~ + // ``` + begin_l: Loc | null; + // Location of the closing bracket + // + // ```text + // [1, 2, 3] + // ~ + // ``` + end_l: Loc | null; + // Location of the full expression + // + // ```text + // [1, 2, 3] + // ~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + elements: Node[], + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents an array pattern used in pattern matching + export class ArrayPattern { + // A list of elements + elements: Node[]; + // Location of the open bracket + // + // ```text + // [1, ^a, 3 => foo] + // ~ + // ``` + // + // `None` for pattern like `1, 2` without brackets + begin_l: Loc | null; + // Location of the closing bracket + // + // ```text + // [1, ^a, 3 => foo] + // ~ + // ``` + // + // `None` for pattern like `1, 2` without brackets + end_l: Loc | null; + // Location of the full expression + // + // ```text + // [1, ^a, 3 => foo] + // ~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + elements: Node[], + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents an array pattern *with trailing comma* used in pattern matching + // + // It's slightly different from `ArrayPattern`, trailing comma at the end works as `, *` + export class ArrayPatternWithTail { + // A list of elements + elements: Node[]; + // Location of the open bracket + // + // ```text + // [1, ^a, 3 => foo,] + // ~ + // ``` + // + // `None` for pattern like `1, 2,` without brackets + begin_l: Loc | null; + // Location of the closing bracket + // + // ```text + // [1, ^a, 3 => foo,] + // ~ + // ``` + // + // `None` for pattern like `1, 2,` without brackets + end_l: Loc | null; + // Location of the full expression + // + // ```text + // [1, ^a, 3 => foo,] + // ~~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + elements: Node[], + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents special global variables: + // 1. `` $` `` + // 2. `$&` + // 3. `$'` + // 4. `$+` + export class BackRef { + // Name of the variable (`"$+"` for `$+`) + name: string; + // Location of the full expression + // + // ```text + // $+ + // ~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + expression_l: Loc, + ); + } + // Represents compound statement (i.e. a multi-statement) + // + // Basically all blocks of code are wrapped into `Begin` node (e.g. method/block body, rescue/ensure handler etc) + export class Begin { + // A list of statements + statements: Node[]; + // Begin of the block + // + // ```text + // (1; 2) + // ~ + // ``` + // + // `None` if the block of code is "implicit", like + // + // ```text + // if true; 1; 2; end + // ``` + begin_l: Loc | null; + // End of the block + // + // ```text + // (1; 2) + // ~ + // ``` + // + // `None` if the block of code is "implicit", like + // + // ```text + // if true; 1; 2; end + // ``` + end_l: Loc | null; + // Location of the full expression + // + // ```text + // (1; 2) + // ~~~~~~ + // ``` + expression_l: Loc; + + constructor( + statements: Node[], + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a Ruby block that is passed to a method (`proc { |foo| bar }`) + export class Block { + // Method call that takes a block + // + // `Send("foo")` in `foo {}` + call: Node; + // A list of argument that block takes + // + // `vec![ Arg("a"), Optarg("b", Int("1")) ]` for `proc { |a, b = 1| }` + // + // `None` if the block takes no arguments + args: Node | null; + // Block body, `None` if block has no body. + body: Node | null; + // Location of the open brace + // + // ```text + // proc { } + // ~ + // ``` + begin_l: Loc; + // Location of the closing brace + // + // ```text + // proc { } + // ~ + // ``` + end_l: Loc; + // Location of the full expression + // + // ```text + // proc { } + // ~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + call: Node, + args: Node | null, + body: Node | null, + begin_l: Loc, + end_l: Loc, + expression_l: Loc, + ); + } + // Represents a `&blk` argument in the method definition (but not in the method call, see `BlockPass`) + export class Blockarg { + // Name of the argument, `String("foo")` for `def m(&foo)` + name: string | null; + // Location of the `&` operator + // + // ```text + // def m(&foo); end + // ~ + // ``` + operator_l: Loc; + // Location of the name + // + // ```text + // def m(&foo); end + // ~~~ + // ``` + name_l: Loc | null; + // Location of the full expression + // + // ```text + // def m(&foo); end + // ~~~~ + // ``` + expression_l: Loc; + + constructor( + name: string | null, + operator_l: Loc, + name_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a `&blk` argument of the method call (but not of the method definition, see `BlockArg`) + export class BlockPass { + // Value that is converted to a block + // + // `Int("1")` in `foo(&1)` (yes, it's possible) + value: Node | null; + // Location of the `&` operator + // + // ```text + // foo(&blk) + // ~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // foo(&bar) + // ~~~~ + // ``` + expression_l: Loc; + + constructor( + value: Node | null, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents a `break` keyword (with optional argument) + export class Break { + // A list of arguments + args: Node[]; + // Location of the `break` keyword + // + // ```text + // break :foo + // ~~~~~ + // ``` + keyword_l: Loc; + // Location of the full expression + // + // ```text + // break(:foo) + // ~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + args: Node[], + keyword_l: Loc, + expression_l: Loc, + ); + } + // Represents a `case` statement (for pattern matching see `CaseMatch` node) + export class Case { + // Expression given to `case`, `Int("1")` for `case 1; end` + // `None` for code like + // + // ```text + // case + // when pattern + // end + // ``` + expr: Node | null; + // A list of `When` nodes (each has `patterns` and `body`) + when_bodies: Node[]; + // Body of the `else` branch, `None` if there's no `else` branch + else_body: Node | null; + // Location of the `case` keyword + // + // ```text + // case 1; end + // ~~~~ + // ``` + keyword_l: Loc; + // Location of the `else` keyword + // + // ```text + // case 1; else; end + // ~~~~ + // ``` + // + // `None` if there's no `else` branch + else_l: Loc | null; + // Location of the `end` keyword + // + // ```text + // case 1; end + // ~~~ + // ``` + end_l: Loc; + // Location of the full expression + // + // ```text + // case 1; end + // ~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + expr: Node | null, + when_bodies: Node[], + else_body: Node | null, + keyword_l: Loc, + else_l: Loc | null, + end_l: Loc, + expression_l: Loc, + ); + } + // Represents a `case` statement used for pattern matching (for regular `case` see `Case` node) + export class CaseMatch { + // Expression given to `case`, `Int("1")` for `case 1; in 1; end` + // `None` for code like + // + // ```text + // case + // in pattern + // end + // ``` + expr: Node; + // A list of `InPattern` nodes (each has `pattern`, `guard` and `body`) + in_bodies: Node[]; + // Body of the `else` branch, `None` if there's no `else` branch + else_body: Node | null; + // Location of the `case` keyword + // + // ```text + // case 1; in 2; end + // ~~~~ + // ``` + keyword_l: Loc; + // Location of the `else` keyword + // + // ```text + // case 1; in 2; else; end + // ~~~~ + // ``` + // + // `None` if there's no `else` branch + else_l: Loc | null; + // Location of the `end` keyword + // + // ```text + // case 1; in 2; end + // ~~~ + // ``` + end_l: Loc; + // Location of the full expression + // + // ```text + // case 1; in 2; end + // ~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + expr: Node, + in_bodies: Node[], + else_body: Node | null, + keyword_l: Loc, + else_l: Loc | null, + end_l: Loc, + expression_l: Loc, + ); + } + // Represents a constant assignment (i.e. `A = 1`) + export class Casgn { + // Scope where the constant is defined: + // 1. `Some(Const("A"))` for `A::B = 1` + // 2. `None` if it's defined in the current scope (i.e. `A = 1`) + // 3. `Some(Cbase)` if it's defined in the global scope (i.e. `::A = 1`) + scope: Node | null; + // Name of the constant, `String("A")` for `A = 1` + name: string; + // Value that is assigned to a constant, `Int("1")` for `A = 1`. + // + // **Note**: `None` if constant assignment is a part of the multi-assignment. + // In such case `value` belongs to `Masgn` node of the multi-assignment. + value: Node | null; + // Location of the `::` operator + // + // ```text + // A::B = 1 + // ~~ + // + // ::A = 1 + // ~~ + // ``` + // + // `None` if the constant is defined in the current scope + double_colon_l: Loc | null; + // Location of the constant name + // + // ```text + // A::CONST = 1 + // ~~~~~ + // ``` + name_l: Loc; + // Location of the `=` operator + // + // ```text + // A = 1 + // ~ + // ``` + // + // `None` if constant assignment is a part of the multi-assignment. + // In such case `=` belongs to a `Masgn` node + operator_l: Loc | null; + // Location of the full expression + // + // ```text + // A = 1 + // ~~~~~ + // ``` + expression_l: Loc; + + constructor( + scope: Node | null, + name: string, + value: Node | null, + double_colon_l: Loc | null, + name_l: Loc, + operator_l: Loc | null, + expression_l: Loc, + ); + } + // Represents leading `::` part of the constant access/assignment that is used to get/set on a global namespace. + export class Cbase { + // Location of the full expression + // + // ```text + // ::A + // ~~ + // ``` + expression_l: Loc; + + constructor( + expression_l: Loc, + ); + } + // Represents a class definition (using a `class` keyword, `Class.new` is just a method call) + export class Class { + // Name of the class, `String("Foo")` for `class Foo; end` + name: Node; + // Superclass. Can be an expression in cases like `class A < (obj.foo + 1); end` + // + // `None` if no explicit superclass given (i.e. `class Foo; end`) + superclass: Node | null; + // Body of the method, `None` if there's no body. + body: Node | null; + // Location of the `class` keyword. + // + // ```text + // class Foo; end + // ~~~~~ + // ``` + keyword_l: Loc; + // Location of the `<` operator + // + // ```text + // class A < B; end + // ~ + // ``` + // + // `None` if there's no explicit superclass given. + operator_l: Loc | null; + // Location of the `end` keyword. + // + // ```text + // class Foo; end + // ~~~ + // ``` + end_l: Loc; + // Location of the full expression + // + // ```text + // class Foo; end + // ~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + name: Node, + superclass: Node | null, + body: Node | null, + keyword_l: Loc, + operator_l: Loc | null, + end_l: Loc, + expression_l: Loc, + ); + } + // Represents a `Complex` literal (that returns an `Complex` number) + export class Complex { + // Value of the complex literal, returned as a `String`, `String("1i")` for `1i` + value: string; + // Location of the `-` (but not `+`) operator. `+` is a part of the literal: + // 1. `+1i` is `String("+1i")` with `operator = None` + // 2. `-1i` is `String("1i")` with `operator = String("-")` + // + // ```text + // -1i + // ~ + // ``` + operator_l: Loc | null; + // Location of the full expression + // + // ```text + // -1i + // ~~~ + // ``` + expression_l: Loc; + + constructor( + value: string, + operator_l: Loc | null, + expression_l: Loc, + ); + } + // Represents constant access (i.e. `Foo::Bar`) + export class Const { + // Scope where the constant is taken from: + // 1. `Some(Const("A"))` for `A::B` + // 2. `None` if it's taken from the current scope (i.e. `A`) + // 3. `Some(Cbase)` if it's taken from the global scope (i.e. `::A`) + scope: Node | null; + // Name of the constant, `String("Foo")` for `Foo` + name: string; + // Location of the `::` operator. `None` if constant is taken from the current scope. + // + // ```text + // A::B + // ~~ + // ``` + double_colon_l: Loc | null; + // Location of the constant name + // + // ```text + // Foo::Bar + // ~~~ + // ``` + name_l: Loc; + // Location of the full expression + // + // ```text + // Foo::Bar + // ~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + scope: Node | null, + name: string, + double_colon_l: Loc | null, + name_l: Loc, + expression_l: Loc, + ); + } + // Const pattern used in pattern matching (e.g. `in A(1, 2)`) + export class ConstPattern { + // Constant that is used, `Const("Foo")` for `in For(42)` + const_: Node; + // Inner part of the constant pattern + // + // `ArrayPattern(vec![ Int("1") ])` for `Foo(1)` + pattern: Node; + // Location of the open parenthesis + // + // ```text + // case 1; in Foo(42); end + // ~ + // ``` + begin_l: Loc; + // Location of the closing parenthesis + // + // ```text + // case 1; in Foo(42); end + // ~ + // ``` + end_l: Loc; + // Location of the full expression + // + // ```text + // case 1; in Foo(42); end + // ~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + const_: Node, + pattern: Node, + begin_l: Loc, + end_l: Loc, + expression_l: Loc, + ); + } + // Represents conditional method call using `&.` operator + export class CSend { + // Receiver of the method call, `Int("1")` for `1&.foo` + recv: Node; + // Name of the method, `String("foo")` for `1&.foo` + method_name: string; + // List of arguments + // + // ```text + // foo&.bar(42) + // # and also setters like + // foo&.bar = 42 + // ``` + args: Node[]; + // Location of the `&.` operator + // + // ```text + // foo&.bar + // ~~ + // ``` + dot_l: Loc; + // Location of the method name + // + // ```text + // foo&.bar(42) + // ~~~ + // ``` + // + // `None` in a very special case when method call is implicit (i.e. `foo&.()`) + selector_l: Loc | null; + // Location of the open parenthesis + // + // ```text + // foo&.bar(42) + // ~ + // ``` + // + // `None` if there are no parentheses + begin_l: Loc | null; + // Location of the closing parenthesis + // + // ```text + // foo&.bar(42) + // ~ + // ``` + // + // `None` if there are no parentheses + end_l: Loc | null; + // Location of the operator if `CSend` is a part of assignment like + // + // ```text + // foo&.bar = 1 + // ~ + // ``` + // + // `None` for a regular call. + operator_l: Loc | null; + // Location of the full expression + // + // ```text + // foo&.bar(42) + // ~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + recv: Node, + method_name: string, + args: Node[], + dot_l: Loc, + selector_l: Loc | null, + begin_l: Loc | null, + end_l: Loc | null, + operator_l: Loc | null, + expression_l: Loc, + ); + } + // Represents access to class variable (i.e. `@@var`) + export class Cvar { + // Name of the class variable, `String("@@foo")` for `@@foo` + name: string; + // Location of the full expression + // + // ```text + // @@foo + // ~~~~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + expression_l: Loc, + ); + } + // Represents class variable assignment (i.e. `@@var = 42`) + export class Cvasgn { + // Name of the class variable, `String("@@foo")` for `@@foo = 1` + name: string; + // Value that is assigned to class variable, `Int("1")` for `@@foo = 1` + value: Node | null; + // Location of the class variable name + // + // ```text + // @@foo = 1 + // ~~~~~ + // ``` + name_l: Loc; + // Location of the `=` operator + // + // ```text + // @@foo = 1 + // ~ + // ``` + operator_l: Loc | null; + // Location of the full expression + // + // ```text + // @@foo = 1 + // ~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + value: Node | null, + name_l: Loc, + operator_l: Loc | null, + expression_l: Loc, + ); + } + // Represents method definition using `def` keyword (not on a singleton, see `Defs` node). + export class Def { + // Name of the method, `String("foo")` for `def foo; end` + name: string; + // Arguments of a method, `None` if there's no arguments. + // + // All information about parentheses around arguments is stored in this node. + args: Node | null; + // Body of a method, `None` if there's no body. + body: Node | null; + // Location of the `def` keyword. + // + // ```text + // def foo; end + // ~~~ + // ``` + keyword_l: Loc; + // Location of the method name. + // + // ```text + // def foo; end + // ~~~ + // ``` + name_l: Loc; + // Location of the `end` keyword. + // + // ```text + // def foo; end + // ~~~ + // ``` + // + // `None` for endless method definition + end_l: Loc | null; + // Location of the `=` operator for endless method definition + // + // ```text + // def m() = 1 + // ~ + // ``` + // + // `None` for regular method definition + assignment_l: Loc | null; + // Location of the full expression + // + // ```text + // def m(a); foo; end + // ~~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + args: Node | null, + body: Node | null, + keyword_l: Loc, + name_l: Loc, + end_l: Loc | null, + assignment_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a `defined?(foo)` expression + export class Defined { + // Value given to `defined?` + value: Node; + // Location of the `defined?` keyword + // + // ```text + // defined?(foo) + // ~~~~~~~~ + // ``` + keyword_l: Loc; + // Location of the open parenthesis + // + // ```text + // defined?(foo) + // ~ + // ``` + // + // `None` if there are no parentheses + begin_l: Loc | null; + // Location of the closing parenthesis + // + // ```text + // defined?(foo) + // ~ + // ``` + // + // `None` if there are no parentheses + end_l: Loc | null; + // Location of the full expression + // + // ```text + // defined?(foo) + // ~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + value: Node, + keyword_l: Loc, + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a singleton method definition (i.e. `def self.foo; end`) + export class Defs { + // Definee of a method definition, `Lvar("x")` for `def x.foo; end` + definee: Node; + // Name of the method, `String("foo")` for `def x.foo; end` + name: string; + // Arguments of a method, `None` if there's no arguments. + // + // All information about parentheses around arguments is stored in this node. + args: Node | null; + // Body of the method, `None` if there's no body. + body: Node | null; + // Location of the `def` keyword + // + // ```text + // def self.foo; end + // ~~~ + // ``` + keyword_l: Loc; + // Location of the `.` + // + // ```text + // def self.foo; end + // ~ + // ``` + operator_l: Loc; + // Location of the method name + // + // ```text + // def self.foo; end + // ~~~ + // ``` + name_l: Loc; + // Location of the `=` operator for endless method definition + // + // ```text + // def self.foo() = 42 + // ~ + // ``` + // + // `None` for regular method definition + assignment_l: Loc | null; + // Location of the `end` keyword + // + // ```text + // def self.foo; end + // ~~~ + // ``` + // + // `None` for endless method definition + end_l: Loc | null; + // Location of the full expression + // + // ```text + // def self.foo; end + // ~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + definee: Node, + name: string, + args: Node | null, + body: Node | null, + keyword_l: Loc, + operator_l: Loc, + name_l: Loc, + assignment_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a string with interpolation (i.e. `"#{foo}"`) + export class Dstr { + // A list of string parts (static literals and interpolated expressions) + parts: Node[]; + // Location of the string begin + // + // ```text + // "#{foo}" + // ~ + // + // %Q{#{foo}} + // ~~~ + // ``` + begin_l: Loc | null; + // Location of the string end + // + // ```text + // "#{foo}" + // ~ + // + // %Q{#{foo}} + // ~ + // ``` + end_l: Loc | null; + // Location of the full expression + // + // ```text + // "#{foo}" + // ~~~~~~~~ + // + // %Q{#{foo}} + // ~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + parts: Node[], + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a symbol with interpolation (i.e. `:"#{foo}"`) + export class Dsym { + // A list of symbol parts (static literals and interpolated expressions) + parts: Node[]; + // Location of the symbol begin + // + // ```text + // :"#{foo}" + // ~~ + // ``` + // + // `None` if `Dsym` is a part of the interpolated symbol array: + // + // ```text + // %I[#{bar}] + // ``` + begin_l: Loc | null; + // Location of the symbol begin + // + // ```text + // :"#{foo}" + // ~ + // ``` + // + // `None` if `Dsym` is a part of the interpolated symbol array: + // + // ```text + // %I[#{bar}] + // ``` + end_l: Loc | null; + // Location of the full expression + // + // ```text + // :"#{foo}" + // ~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + parts: Node[], + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents exclusive flip-flop (i.e. in `if foo...bar; end`) + export class EFlipFlop { + // Left part of the flip-flop. `None` if based on a range without begin (`...bar`) + left: Node | null; + // Right part of the flip-flop. `None` if based on a range without end (`foo...`) + right: Node | null; + // Location of the `...` operator + // + // ```text + // if foo...bar; end + // ~~~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // if foo...bar; end + // ~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + left: Node | null, + right: Node | null, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents a special empty else that is a part of the pattern matching. + // + // Usually empty else (e.g. part of the `if` statement) doesn't mean anything, + // however in pattern matching it prevents raising a `NoPatternError`. + // + // Throwing away this `else` may affect your code. + export class EmptyElse { + // Location of the `else` keyword + // + // ```text + // case foo; in 1; else; end + // ~~~~ + // ``` + expression_l: Loc; + + constructor( + expression_l: Loc, + ); + } + // Represents a special `__ENCODING__` keyword + export class Encoding { + // Location of the `__ENCODING__` keyword + // + // ```text + // __ENCODING__ + // ~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + expression_l: Loc, + ); + } + // Represents a block of code with `ensure` (i.e. `begin; ensure; end`) + export class Ensure { + // Block of code that is wrapped into `ensure` + // **Note**: that's the body of the `ensure` block + // + // `Int("1")` for `begin; 1; ensure; 2; end` + body: Node | null; + // Body of the `ensure` block + // + // `Int("2")` for `begin; 1; ensure; 2; end` + ensure: Node | null; + // Location of the `ensure` keyword + // + // ```text + // begin; ensure; end + // ~~~~~~ + // ``` + keyword_l: Loc; + // Location of the full expression + // + // ```text + // begin; 1; rescue; 2; else; 3; ensure; 4; end + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // ``` + // + // **Note**: begin/end belong to `KwBegin` node. + expression_l: Loc; + + constructor( + body: Node | null, + ensure: Node | null, + keyword_l: Loc, + expression_l: Loc, + ); + } + // Represents range literal with excluded `end` (i.e. `1...3`) + export class Erange { + // Begin of the range, `None` if range has no begin (i.e `...42`) + left: Node | null; + // End of the range, `None` if range has no end (i.e `42...`) + right: Node | null; + // Location of the `...` operator + // + // ```text + // 1...3 + // ~~~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // 1...3 + // ~~~~~ + // ``` + expression_l: Loc; + + constructor( + left: Node | null, + right: Node | null, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents a `false` literal + export class False { + // Location of the `false` literal + // + // ```text + // false + // ~~~~~ + // ``` + expression_l: Loc; + + constructor( + expression_l: Loc, + ); + } + // Represents a special `__FILE__` literal + export class File { + // Location of the `__FILE__` literal + // + // ```text + // __FILE__ + // ~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + expression_l: Loc, + ); + } + // Represents a find pattern using in pattern matching (i.e. `in [*x, 1 => a, *y]`) + // + // It's different from `ArrayPattern`/`ConstPattern` because it supports multiple wildcard pattern + export class FindPattern { + // Inner part of the find pattern + elements: Node[]; + // Location of the begin + // + // ```text + // case foo; in [*x, 1 => a, *y]; end + // ~ + // ``` + // + // `None` if there are no brackets/parentheses + begin_l: Loc | null; + // Location of the end + // + // ```text + // case foo; in [*x, 1 => a, *y]; end + // ~ + // ``` + // + // `None` if there are no brackets/parentheses + end_l: Loc | null; + // Location of the full expression + // + // ```text + // case foo; in [*x, 1 => a, *y]; end + // ~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + elements: Node[], + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a float literal (i.e. `42.5`) + export class Float { + // String value of the literal, `String("42.5")` for `42.5` + value: string; + // Location of unary `-` (but not `+`) + // + // ```text + // -42.5 + // ~ + // ``` + operator_l: Loc | null; + // Location of the full expression + // + // ```text + // -42.5 + // ~~~~~ + // ``` + expression_l: Loc; + + constructor( + value: string, + operator_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a `for` loop + export class For { + // Variable that is used in loop, `Lvasgn("a")` in `for a in b; end` + iterator: Node; + // Collection that is for iteration. `Lvar("b")` in `for a in b; end` + iteratee: Node; + // Body of the loop. `None` if there's no body + body: Node | null; + // Location of the `for` keyword + // + // ```text + // for a in b; end + // ~~~ + // ``` + keyword_l: Loc; + // Location of the `in` keyword + // + // ```text + // for a in b; end + // ~~ + // ``` + operator_l: Loc; + // Location of the `do` keyword + // + // ```text + // for a in b do; end + // ~~ + // ``` + // + // **Note**: this `do` is optional, and so `begin_l` can be `None`. + begin_l: Loc; + // Location of the `end` keyword + // + // ```text + // for a in b; end + // ~~~ + // ``` + end_l: Loc; + // Location of the full expression + // + // ```text + // for a in b; end + // ~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + iterator: Node, + iteratee: Node, + body: Node | null, + keyword_l: Loc, + operator_l: Loc, + begin_l: Loc, + end_l: Loc, + expression_l: Loc, + ); + } + // Represents a special `...` argument that forwards positional/keyword/block arguments. + export class ForwardArg { + // Location of the `...` + // + // ```text + // def m(...); end + // ~~~ + // ``` + expression_l: Loc; + + constructor( + expression_l: Loc, + ); + } + // Represents a `...` operator that contains forwarded argument (see `ForwardArg`) + export class ForwardedArgs { + // Location of the `...` + // + // ```text + // def m(...); foo(...); end + // ~~~ + // ``` + expression_l: Loc; + + constructor( + expression_l: Loc, + ); + } + // Represents access to global variable (i.e. `$foo`) + export class Gvar { + // Name of the global variable, `String("$foo")` for `$foo` + name: string; + // Location of the full expression + // + // ```text + // $foo + // ~~~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + expression_l: Loc, + ); + } + // Represents global variable assignment (i.e. `$foo = 42`) + export class Gvasgn { + // Name of the global variable, `String("$foo")` for `$foo` + name: string; + // Value that is assigned to global variable, `Int("42")` for `$foo = 42` + // + // `None` if global variable assignment is a part of the multi-assignment. + // In such case `value` is a part of the `Masgn` node. + value: Node | null; + // Location of the global variable name + // + // ```text + // $foo = 42 + // ~~~~ + // ``` + name_l: Loc; + // Location of the `=` operator + // + // ```text + // $foo = 42 + // ~ + // ``` + // + // `None` if global variable assignment is a part of the multi-assignment. + // In such case `=` operator belongs to the `Masgn` node. + operator_l: Loc | null; + // Location of the full expression + // + // ```text + // $foo = 42 + // ~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + value: Node | null, + name_l: Loc, + operator_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a hash literal (i.e. `{ foo: 42 }`) + export class Hash { + // A list of key-value pairs + pairs: Node[]; + // Location of the open parenthesis + // + // ```text + // { a: 1 } + // ~ + // ``` + // + // `None` if hash literal is implicit, e.g. `foo(key: "value")` + begin_l: Loc | null; + // Location of the closing parenthesis + // + // ```text + // { a: 1 } + // ~ + // ``` + // + // `None` if hash literal is implicit, e.g. `foo(key: "value")` + end_l: Loc | null; + // Location of the full expression + // + // ```text + // { a: 1 } + // ~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + pairs: Node[], + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a hash pattern used in pattern matching (i.e. `in { a: 1 }`) + export class HashPattern { + // A list of inner patterns + elements: Node[]; + // Location of the open parenthesis + // + // ```text + // case foo; in { a: 1 }; end + // ~ + // ``` + // + // `None` if there are no parentheses + begin_l: Loc | null; + // Location of the open parenthesis + // + // ```text + // case foo; in { a: 1 }; end + // ~ + // ``` + // + // `None` if there are no parentheses + end_l: Loc | null; + // Location of the full expression + // + // ```text + // case foo; in { a: 1 }; end + // ~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + elements: Node[], + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a here-document literal (both with and without interpolation) + // + // It's similar to `Dstr` in terms of abstract syntax tree, but has different source maps. + export class Heredoc { + // A list of string parts (static literals and interpolated expressions) + parts: Node[]; + // Location of the here-document body + // + // ```text + // <<-HERE\n a\n #{42}\nHERE + // ~~~~~~~~~~~~~~~ + // ``` + heredoc_body_l: Loc; + // Location of the here-document end + // + // ```text + // <<-HERE\n a\n #{42}\nHERE + // ~~~~ + // ``` + heredoc_end_l: Loc; + // Location of the here-document identifier + // + // ```text + // <<-HERE\n a\n #{42}\nHERE + // ~~~~~~~ + // ``` + // + // **Note**: This is the only node (with `XHeredoc`) that has `expression_l` smaller that all other sub-locations merged. + // The reason for that is that it's possible to add more code after here-document ID: + // + // ```text + // <<-HERE + "rest" + // content + // HERE + // ``` + expression_l: Loc; + + constructor( + parts: Node[], + heredoc_body_l: Loc, + heredoc_end_l: Loc, + expression_l: Loc, + ); + } + // Represents an `if` statement (i.e. `if foo; bar; else; baz; end`) + export class If { + // Condition given to the `if` statement, `Lvar("a")` for `if a; b; else; c; end` + cond: Node; + // True-branch of the `if` statement, `Lvar("b")` for `if a; b; else; c; end` + if_true: Node | null; + // False-branch of the `if` statement, `Lvar("c")` for `if a; b; else; c; end` + if_false: Node | null; + // Location of the `if` keyword + // + // ```text + // if foo; end + // ~~ + // ``` + keyword_l: Loc; + // Location of the `then` keyword + // + // ```text + // if foo then; end + // ~~~~ + // ``` + // + // `None` if `then` keyword is omitted + begin_l: Loc; + // Location of the `else` keyword + // + // ```text + // if foo; else; end + // ~~~~ + // ``` + // + // `None` if there's no `else` branch + else_l: Loc | null; + // Location of the `end` keyword + // + // ```text + // if foo; end + // ~~~ + // ``` + end_l: Loc | null; + // Location of the full expression + // + // ```text + // if a then; b; else; c end + // ~~~~~~~~~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + cond: Node, + if_true: Node | null, + if_false: Node | null, + keyword_l: Loc, + begin_l: Loc, + else_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents an `if` guard used in pattern matching (i.e. `case foo; in pattern if guard; end`) + export class IfGuard { + // Condition of the guard, `Lvar("foo")` in `in pattern if guard` + cond: Node; + // Location of the `if` keyword + // + // ```text + // case foo; in pattern if cond; end + // ~~ + // ``` + keyword_l: Loc; + // Location of the full expression + // + // ```text + // case foo; in pattern if cond; end + // ~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + cond: Node, + keyword_l: Loc, + expression_l: Loc, + ); + } + // Represents inclusive flip-flop (i.e. in `if foo..bar; end`) + export class IFlipFlop { + // Left part of the flip-flop. `None` if based on a range without begin (`..bar`) + left: Node | null; + // Right part of the flip-flop. `None` if based on a range without end (`foo..`) + right: Node | null; + // Location of the `..` operator + // + // ```text + // if foo..bar; end + // ~~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // if foo..bar; end + // ~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + left: Node | null, + right: Node | null, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents an `if`/`unless` modifier (i.e. `stmt if cond`) + export class IfMod { + // Condition of the modifier + cond: Node; + // True-branch of the modifier. + // + // Always set for `if` modifier. + // Always `None` for `unless` modifier. + if_true: Node | null; + // False-branch of the modifier. + // + // Always set for `unless` modifier. + // Always `None` for `if` modifier. + if_false: Node | null; + // Location of the `if`/`unless` keyword + // + // ```text + // stmt if cond + // ~~ + // + // stmt unless cond + // ~~~~~~ + // ``` + keyword_l: Loc; + // Location of the full expression + // + // ```text + // stmt if cond + // ~~~~~~~~~~~~ + // + // stmt unless cond + // ~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + cond: Node, + if_true: Node | null, + if_false: Node | null, + keyword_l: Loc, + expression_l: Loc, + ); + } + // Represents ternary `if` statement (i.e. `cond ? if_true : if_false`) + export class IfTernary { + // Condition of the `if` statement + cond: Node; + // True-branch + if_true: Node; + // True-branch + if_false: Node; + // Location of the `?` operator + // + // ```text + // cond ? if_true : if_false + // ~ + // ``` + question_l: Loc; + // Location of the `:` operator + // + // ```text + // cond ? if_true : if_false + // ~ + // ``` + colon_l: Loc; + // Location of the full expression + // + // ```text + // cond ? if_true : if_false + // ~~~~~~~~~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + cond: Node, + if_true: Node, + if_false: Node, + question_l: Loc, + colon_l: Loc, + expression_l: Loc, + ); + } + // Represents indexing operation (i.e. `foo[1,2,3]`) + export class Index { + // Receiver of indexing + recv: Node; + // A list of indexes + indexes: Node[]; + // Location of open bracket + // + // ```text + // foo[1, 2, 3] + // ~ + // ``` + begin_l: Loc; + // Location of closing bracket + // + // ```text + // foo[1, 2, 3] + // ~ + // ``` + end_l: Loc; + // Location of the full expression + // + // ```text + // foo[1, 2, 3] + // ~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + recv: Node, + indexes: Node[], + begin_l: Loc, + end_l: Loc, + expression_l: Loc, + ); + } + // Represents assignment using indexing operation (i.e. `foo[1, 2, 3] = bar`) + export class IndexAsgn { + // Receiver of the indexing + recv: Node; + // A list of indexes + indexes: Node[]; + // Value that is assigned + // + // `None` if assignment is a part of the multi-assignment. + // In such case `value` belongs to `Masgn` node. + value: Node | null; + // Location of open bracket + // + // ```text + // foo[1, 2, 3] = bar + // ~ + // ``` + begin_l: Loc; + // Location of closing bracket + // + // ```text + // foo[1, 2, 3] = bar + // ~ + // ``` + end_l: Loc; + // Location of the `=` operator + // + // ```text + // foo[1, 2, 3] = bar + // ~ + // ``` + // + // `None` if assignment is a part of the multi-assignment. + // In such case operator `=` belongs to `Masgn` node. + operator_l: Loc | null; + // Location of the full expression + // + // ```text + // foo[1, 2, 3] = bar + // ~~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + recv: Node, + indexes: Node[], + value: Node | null, + begin_l: Loc, + end_l: Loc, + operator_l: Loc | null, + expression_l: Loc, + ); + } + // Represents an `in pattern` branch of the pattern matching + export class InPattern { + // Value that is used for matching + pattern: Node; + // Guard that is used for matching + // + // Optional, so can be `None` + guard: Node | null; + // Body of the branch that is invoked if value matches pattern + body: Node | null; + // Location of the `in` keyword + // + // ```text + // case value; in pattern; end + // ~~ + // ``` + keyword_l: Loc; + // Location of the `then` keyword + // + // ```text + // case value; in pattern then; end + // ~~~~ + // ``` + begin_l: Loc; + // Location of the full expression + // + // ```text + // case value; in pattern then; 42; end + // ~~~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + pattern: Node, + guard: Node | null, + body: Node | null, + keyword_l: Loc, + begin_l: Loc, + expression_l: Loc, + ); + } + // Represents an integer literal (i.e. `42`) + export class Int { + // String value of the literal, `String("42")` for `42` + value: string; + // Location of unary `-` (but not `+`) + // + // ```text + // -42 + // ~ + // ``` + operator_l: Loc | null; + // Location of the full expression + // + // ```text + // -42 + // ~~~ + // ``` + expression_l: Loc; + + constructor( + value: string, + operator_l: Loc | null, + expression_l: Loc, + ); + } + // Represents inclusive range (i.e. `2..4`) + export class Irange { + // Begin of the range, `None` if range has no `begin` (i.e. `..4`) + left: Node | null; + // End of the range, `None` if range has no `end` (i.e. `2..`) + right: Node | null; + // Location of the `..` operator + // + // ```text + // 2..4 + // ~~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // 2..4 + // ~~~~ + // ``` + expression_l: Loc; + + constructor( + left: Node | null, + right: Node | null, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents access to instance variable (i.e. `@foo`) + export class Ivar { + // Name of the instance variable, `String("@foo")` in `@foo` + name: string; + // Location of the full expression + // + // ```text + // @foo + // ~~~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + expression_l: Loc, + ); + } + // Represents instance variable assignment (i.e `@foo = 42`) + export class Ivasgn { + // Name of the instance variable, `String("@foo")` in `@foo = 42` + name: string; + // Value that is assigned to instance variable. + // + // `None` if instance variable assignment is a part of the multi-assignment. + // In such case `value` is a part of the `Masgn` node. + value: Node | null; + // Location of the instance variable name. + // + // ```text + // @foo = 1 + // ~~~~ + // ``` + name_l: Loc; + // Location of the `=` operator. + // + // ```text + // @foo = 1 + // ~ + // ``` + // + // `None` if instance variable assignment is a part of the multi-assignment. + // In such case `value` is a part of the `Masgn` node. + operator_l: Loc | null; + // Location of the full expression + // + // ```text + // @foo = 42 + // ~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + value: Node | null, + name_l: Loc, + operator_l: Loc | null, + expression_l: Loc, + ); + } + // Represents required keyword argument (i.e. `foo` in `def m(foo:); end`) + export class Kwarg { + // Name of the keyword argument + name: string; + // Location of the name + // + // ```text + // def foo(bar:); end + // ~~~ + // ``` + name_l: Loc; + // Location of the full expression + // + // ```text + // def foo(bar:); end + // ~~~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + name_l: Loc, + expression_l: Loc, + ); + } + // Represents kwargs that are given to a method call, super or yield (i.e. `foo(bar: 1)`) + export class Kwargs { + // A list of key-value pairs + pairs: Node[]; + // Location of the full expression + // + // ```text + // foo(bar: 1) + // ~~~~~~ + // ``` + expression_l: Loc; + + constructor( + pairs: Node[], + expression_l: Loc, + ); + } + // Represents an explicit `begin; end` block. + // + // The reason why it's different is that + // ```text + // begin; foo; end while cond + // ``` + // is a post-while loop (same with post-until loop) + export class KwBegin { + // A list of statements + statements: Node[]; + // Location of the `begin` keyword + // + // ```text + // begin; foo; end + // ~~~~~ + // ``` + begin_l: Loc | null; + // Location of the `end` keyword + // + // ```text + // begin; foo; end + // ~~~ + // ``` + end_l: Loc | null; + // Location of the full expression + // + // ```text + // begin; foo; bar + // ~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + statements: Node[], + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents an special argument that rejects all keyword arguments (i.e. `def m(**nil); end`) + export class Kwnilarg { + // Location of the `nil` + // + // ```text + // def m(**nil); end + // ~~~ + // ``` + name_l: Loc; + // Location of the `nil` + // + // ```text + // def m(**nil); end + // ~~~~~ + // ``` + expression_l: Loc; + + constructor( + name_l: Loc, + expression_l: Loc, + ); + } + // Represents an optional keyword argument (i.e. `foo` in `def m(foo: 42); end`) + export class Kwoptarg { + // Name of the optional keyword argument + name: string; + // Default value of the optional keyword argument + default_: Node; + // Location of the argument name + // + // ```text + // def m(foo: 1); end + // ~~~ + // ``` + name_l: Loc; + // Location of the argument name + // + // ```text + // def m(foo: 1); end + // ~~~~~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + default_: Node, + name_l: Loc, + expression_l: Loc, + ); + } + // Represents a keyword rest argument (i.e. `foo` in `def m(**foo); end`) + export class Kwrestarg { + // Name of the keyword rest argument, `String("foo")` in `def m(**foo); end`. + // + // `None` if argument has no name (`def m(**); end`) + name: string | null; + // Location of the `**` operator + // + // ```text + // def m(**foo); end + // ~~ + // ``` + operator_l: Loc; + // Location of the argument name + // + // ```text + // def m(**foo); end + // ~~~ + // ``` + // + // `None` if argument has no name (`def m(**); end`) + name_l: Loc | null; + // Location of the full expression + // + // ```text + // def m(**foo); end + // ~~~~~ + // ``` + expression_l: Loc; + + constructor( + name: string | null, + operator_l: Loc, + name_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a keyword arguments splat (i.e. `**bar` in a call like `foo(**bar)`) + export class Kwsplat { + // Value that is converted into a `Hash` using `**` + value: Node; + // Location of the `**` operator + // + // ```text + // foo(**bar) + // ~~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // foo(**bar) + // ~~~~~ + // ``` + expression_l: Loc; + + constructor( + value: Node, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents a lambda call using `->` (i.e. `-> {}`) + // + // Note that `Lambda` is a part of the `Block`, not other way around. + export class Lambda { + // Location of the `->` + // + // ```text + // -> {} + // ~~ + // ``` + expression_l: Loc; + + constructor( + expression_l: Loc, + ); + } + // Represents a special `__LINE__` literal + export class Line { + // Location of the `__LINE__` literal + // + // ```text + // __LINE__ + // ~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + expression_l: Loc, + ); + } + // Represents access to a local variable (i.e. `foo`) + // + // Parser knows that it's a local variable because: + // 1. there was an assignment to this variable **before** accessing it + // 2. it's an argument of the current method / block + // 3. it's been implicitly declared by `MatchWithLvasgn` node + // + // Otherwise it's a method call (see `Send`) + export class Lvar { + // Name of the local variable + name: string; + // Location of the local variable + // + // ```text + // foo + // ~~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + expression_l: Loc, + ); + } + // Represents local variable assignment (i.e. `foo = 42`) + export class Lvasgn { + // Name of the local variable + name: string; + // Value that is assigned to a local variable + value: Node | null; + // Location of the local variable name + // + // ```text + // foo = 42 + // ~~~ + // ``` + name_l: Loc; + // Location of the `=` operator + // + // ```text + // foo = 42 + // ~ + // ``` + // + // `None` if local variable assignment is a part of the multi-assignment. + // In such case `value` is a part of the `Masgn` node. + operator_l: Loc | null; + // Location of the full expression + // + // ```text + // foo = 42 + // ~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + value: Node | null, + name_l: Loc, + operator_l: Loc | null, + expression_l: Loc, + ); + } + // Represents mass-assignment (i.e. `foo, bar = 1, 2`) + export class Masgn { + // Left hand statement of the assignment + lhs: Node; + // Left hand statement of the assignment + rhs: Node; + // Location of the `=` operator + // + // ```text + // foo, bar = 1, 2 + // ~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // foo, bar = 1, 2 + // ~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + lhs: Node, + rhs: Node, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents pattern matching using one of the given patterns (i.e. `foo in 1 | 2`) + export class MatchAlt { + // Left pattern + lhs: Node; + // Right pattern + rhs: Node; + // Location of the `|` operator + // + // ```text + // foo in 1 | 2 + // ~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // foo in 1 | 2 + // ~~~~~ + // ``` + expression_l: Loc; + + constructor( + lhs: Node, + rhs: Node, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents matching with renaming into specified local variable (i.e. `case 1; in Integer => a; end`) + export class MatchAs { + // Pattern that is used for matching + value: Node; + // Variable that is assigned if matched (see `MatchVar` node) + as: Node; + // Location of the `=>` operator + // + // ```text + // case 1; in Integer => a; end + // ~~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // case 1; in Integer => a; end + // ~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + value: Node, + as: Node, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents implicit matching using `if /regex/` + // + // ```text + // if /.*/ + // puts 'true' + // else + // puts 'false' + // end + // ``` + // Prints "false". + // + // Under the hood this construction matches regex against `$_`, so the following works: + // ```text + // $_ = 'match_me' + // if /match_me/ + // puts 'true' + // else + // puts 'false' + // end + // ``` + // this code prints "true". + export class MatchCurrentLine { + // Given regex + re: Node; + // Location of the regex + // + // ```text + // if /re/; end + // ~~~~ + // ``` + // + // Technically this location is redundant, but keeping it is the only way to + // have the same interface for all nodes. + expression_l: Loc; + + constructor( + re: Node, + expression_l: Loc, + ); + } + // Represents empty hash pattern that is used in pattern matching (i.e. `in **nil`) + export class MatchNilPattern { + // Location of the `**` operator + // + // ```text + // in **nil + // ~~ + // ``` + operator_l: Loc; + // Location of the name + // + // ```text + // in **nil + // ~~~ + // ``` + name_l: Loc; + // Location of the full expression + // + // ```text + // in **nil + // ~~~~~ + // ``` + expression_l: Loc; + + constructor( + operator_l: Loc, + name_l: Loc, + expression_l: Loc, + ); + } + // Represents a one-line pattern matching that can throw an error (i.e. `foo => pattern`) + export class MatchPattern { + // Value that is used for matching + value: Node; + // Pattern that is used for matching + pattern: Node; + // Location of the `=>` operator + // + // ```text + // foo => pattern + // ~~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // foo => pattern + // ~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + value: Node, + pattern: Node, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents a one-line pattern matching that never throws but returns true/false (i.e. `foo in pattern`) + export class MatchPatternP { + // Value that is used for matching + value: Node; + // Pattern that is used for matching + pattern: Node; + // Location of the `in` operator + // + // ```text + // foo in pattern + // ~~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // foo in pattern + // ~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + value: Node, + pattern: Node, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents a wildcard pattern used in pattern matching (i.e. `in *foo`) + export class MatchRest { + // Name of the variable name + // + // `None` if there's no name (i.e. `in *`) + name: Node | null; + // Location of the `*` operator + // + // ```text + // case foo; in *bar; end + // ~ + // ``` + operator_l: Loc; + // Location of the `*` operator + // + // ```text + // case foo; in *bar; end + // ~~~~ + // ``` + expression_l: Loc; + + constructor( + name: Node | null, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents matching with assignment into a local variable (i.e. `pattern => var`) + export class MatchVar { + // Name of the variable that is assigned if matching succeeds + name: string; + // Location of the name + // + // ```text + // case foo; in pattern => bar; end + // ~~~ + // ``` + // + // **Note** it can also be produced by a hash pattern + // + // ```text + // case foo; in { a: }; end + // ~ + // ``` + name_l: Loc; + // Location of the full expression + // + // ```text + // case foo; in pattern => bar; end + // ~~~ + // ``` + // + // **Note** it can also be produced by a hash pattern + // + // ```text + // case foo; in { a: }; end + // ~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + name_l: Loc, + expression_l: Loc, + ); + } + // Represents matching a regex that produces local variables (i.e. `/(?bar)/ =~ 'bar'`) + // + // Each named group in regex declares a local variable. + export class MatchWithLvasgn { + // Regex that is used for matching + re: Node; + // Value that is used for matching + value: Node; + // Location of the `=~` operatir + // + // ```text + // /(?bar)/ =~ 'bar' + // ~~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // /(?bar)/ =~ 'bar' + // ~~~~~~~~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + re: Node, + value: Node, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents left hand statement of the mass-assignment (i.e. `foo, bar` in `foo, bar = 1, 2`) + export class Mlhs { + // A list of items that are assigned + items: Node[]; + // Location of the open parenthesis + // + // ```text + // (a, b) = 1, 2 + // ~ + // ``` + // + // `None` if there are no parentheses + begin_l: Loc | null; + // Location of the closing parenthesis + // + // ```text + // (a, b) = 1, 2 + // ~ + // ``` + // + // `None` if there are no parentheses + end_l: Loc | null; + // Location of the full expression + // + // ```text + // (a, b) = 1, 2 + // ~~~~~~ + // ``` + expression_l: Loc; + + constructor( + items: Node[], + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents module declaration using `module` keyword + export class Module { + // Name of the module + name: Node; + // Body of the module + // + // `None` if module has no body + body: Node | null; + // Location of the `module` keyword + // + // ```text + // module M; end + // ~~~~~~ + // ``` + keyword_l: Loc; + // Location of the `end` keyword + // + // ```text + // module M; end + // ~~~ + // ``` + end_l: Loc; + // Location of the full expression + // + // ```text + // module M; end + // ~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + name: Node, + body: Node | null, + keyword_l: Loc, + end_l: Loc, + expression_l: Loc, + ); + } + // Represents `next` keyword + export class Next { + // Arguments given to `next` + args: Node[]; + // Location of the `next` keyword + // + // ```text + // next 42 + // ~~~~ + // ``` + keyword_l: Loc; + // Location of the full expression + // + // ```text + // next(42) + // ~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + args: Node[], + keyword_l: Loc, + expression_l: Loc, + ); + } + // Represents `nil` literal + export class Nil { + // Location of the `nil` keyword + // + // ```text + // nil + // ~~~ + // ``` + expression_l: Loc; + + constructor( + expression_l: Loc, + ); + } + // Represents numeric global variable (e.g. `$1`) + export class NthRef { + // Name of the variable, `String("1")` for `$1` + name: string; + // Location of the full expression + // + // ```text + // $1 + // ~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + expression_l: Loc, + ); + } + // Represents a block that takes numbered parameters (i.e. `proc { _1 }`) + export class Numblock { + // Method call that takes a block + call: Node; + // Number of parameters that block takes + numargs: number; + // Block body + body: Node; + // Location of the open brace + // + // ```text + // proc { _1 } + // ~ + // ``` + begin_l: Loc; + // Location of the closing brace + // + // ```text + // proc { _1 } + // ~ + // ``` + end_l: Loc; + // Location of the open brace + // + // ```text + // proc { _1 } + // ~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + call: Node, + numargs: number, + body: Node, + begin_l: Loc, + end_l: Loc, + expression_l: Loc, + ); + } + // Represents an operation with assignment (e.g. `a += 1`) + export class OpAsgn { + // Left hand statement of the assignment + recv: Node; + // Operator, can be one of: + // 1. `+=` + // 2. `-=` + // 3. `*=` + // 4. `/=` + // 5. `|=` + // 6. `&=` + // 7. `>>=` + // 8. `<<=` + // 9. `%=` + // 10. `^=` + // 11. `**=` + operator: string; + // Right hand statement of the assignment + value: Node; + // Location of the operator + // + // ```text + // a.b <<= c + // ~~~ + // ``` + operator_l: Loc; + // Location of the operator + // + // ```text + // a.b <<= c + // ~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + recv: Node, + operator: string, + value: Node, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents optional positional argument (i.e. `foo` in `m(foo = 1)`) + export class Optarg { + // Name of the argument + name: string; + // Default value of the argument + default_: Node; + // Location of the argument name + // + // ```text + // def m(foo = 1); end + // ~~~ + // ``` + name_l: Loc; + // Location of the `=` operator + // + // ```text + // def m(foo = 1); end + // ~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // def m(foo = 1); end + // ~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + default_: Node, + name_l: Loc, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents `foo || bar` (or `foo or bar`) statement. + export class Or { + // Left hand statement + lhs: Node; + // Right hand statement + rhs: Node; + // Location of the `||`/`or` operator + // + // ```text + // foo || bar + // ~~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // foo || bar + // ~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + lhs: Node, + rhs: Node, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents `lhs ||= rhs` assignment + export class OrAsgn { + // Left hand statement + recv: Node; + // Right hand statement + value: Node; + // Location of the `||=` operator + // + // ```text + // foo ||= bar + // ~~~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // foo ||= bar + // ~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + recv: Node, + value: Node, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents a key/value pair (e.g. a part of the `Hash` node) + export class Pair { + // Key of the pair + key: Node; + // Value of the pair + value: Node; + // Location of the `:` or `=>` operator + // + // ```text + // { foo: bar } + // ~ + // + // { :foo => bar } + // ~~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // { foo: bar } + // ~~~~~~~~ + // + // { :foo => bar } + // ~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + key: Node, + value: Node, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents a pattern based on a "pinned" variable (e.g. `^foo`) + export class Pin { + // Variable that is pinned + var_: Node; + // Location of the `^` operator + // + // ```text + // case foo; in ^bar; end + // ~ + // ``` + selector_l: Loc; + // Location of the full expression + // + // ```text + // case foo; in ^bar; end + // ~~~~ + // ``` + expression_l: Loc; + + constructor( + var_: Node, + selector_l: Loc, + expression_l: Loc, + ); + } + // Represents `END { .. }` statement + export class Postexe { + // Body of the block + body: Node | null; + // Location of the `END` keyword + // + // ```text + // END { 42 } + // ~~~ + // ``` + keyword_l: Loc; + // Location of the open parenthesis + // + // ```text + // END { 42 } + // ~ + // ``` + begin_l: Loc; + // Location of the closing parenthesis + // + // ```text + // END { 42 } + // ~ + // ``` + end_l: Loc; + // Location of the full expression + // + // ```text + // END { 42 } + // ~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + body: Node | null, + keyword_l: Loc, + begin_l: Loc, + end_l: Loc, + expression_l: Loc, + ); + } + // Represents `BEGIN { ... }` statement + export class Preexe { + // Body of the block + body: Node | null; + // Location of the `BEGIN` keyword + // + // ```text + // BEGIN { 42 } + // ~~~~~ + // ``` + keyword_l: Loc; + // Location of the open parenthesis + // + // ```text + // BEGIN { 42 } + // ~ + // ``` + begin_l: Loc; + // Location of the closing parenthesis + // + // ```text + // BEGIN { 42 } + // ~ + // ``` + end_l: Loc; + // Location of the full expression + // + // ```text + // BEGIN { 42 } + // ~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + body: Node | null, + keyword_l: Loc, + begin_l: Loc, + end_l: Loc, + expression_l: Loc, + ); + } + // Represents a sole block argument (e.g. `|foo|`) + // + // Block that takes a single array argument automatically expands it. + // Adding trailing comma after block argument disables this behavior (and then the only argument is emitted as `Arg`). + export class Procarg0 { + // Parts of the sole block argument. + // + // `proc { |(a, b)| }` also counts as a sole argument, so this list may contain: + // 1. A single `Arg` node (for `proc { |a| }` case) + // 2. Multiple `Arg` nodes (for `proc { |(a, b, c)| }` case) + args: Node[]; + // Location of the open parenthesis + // + // ```text + // proc { |(foo, bar)| } + // ~ + // ``` + // + // `None` if there's only one argument + begin_l: Loc | null; + // Location of the open parenthesis + // + // ```text + // proc { |(foo, bar)| } + // ~ + // ``` + // + // `None` if there's only one argument + end_l: Loc | null; + // Location of the full expression + // + // ```text + // proc { |(foo, bar)| } + // ~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + args: Node[], + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents rational literal (e.g. `1r`) + export class Rational { + // String value of the literal, `String("1r")` for `1r` + value: string; + // Location of the unary `-` (but not `+`) + // + // ```text + // -1r + // ~ + // ``` + operator_l: Loc | null; + // Location of the full expression + // + // ```text + // -1r + // ~~~ + // ``` + expression_l: Loc; + + constructor( + value: string, + operator_l: Loc | null, + expression_l: Loc, + ); + } + // Represents `redo` keyword + export class Redo { + // Location of the full expression + // + // ```text + // redo + // ~~~~ + // ``` + expression_l: Loc; + + constructor( + expression_l: Loc, + ); + } + // Represents regex literal (e.g. `/foo/`) + export class Regexp { + // A list of static and dynamic regex parts + parts: Node[]; + // Regex options. + // + // `None` if regex has no explicit flags + options: Node | null; + // Location of the regex begin + // + // ```text + // /foo/ + // ~ + // + // %r{foo} + // ~~ + // ``` + begin_l: Loc; + // Location of the regex end + // + // ```text + // /foo/ + // ~ + // + // %r{foo} + // ~ + // ``` + end_l: Loc; + // Location of the full expression + // + // ```text + // /foo/mix + // ~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + parts: Node[], + options: Node | null, + begin_l: Loc, + end_l: Loc, + expression_l: Loc, + ); + } + // Represents flags of the regex literal (i.e. `mix` for `/foo/mix`) + export class RegOpt { + // A list of flags + options: string | null; + // Location of the full expression + // + // ```text + // /foo/mix + // ~~~ + // ``` + expression_l: Loc; + + constructor( + options: string | null, + expression_l: Loc, + ); + } + // Represents a `rescue` block + export class Rescue { + // Body of the block that is wrapped into `rescue` (i.e. the part that may throw an error) + body: Node | null; + // A list of `rescue` handlers (see `RescueBody` node) + rescue_bodies: Node[]; + // Else branch. + // + // `None` if there's no `else` branch + else_: Node | null; + // Location of the `else` keyword + // + // ```text + // begin; 1; rescue StandardError => e; 2; else; 3; end + // ~~~~ + // ``` + // + // `None` if there's no `else` branch + else_l: Loc | null; + // Location of the full expression + // + // ```text + // begin; 1; rescue StandardError => e; 2; else; 3; end + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // ``` + // + // **Note**: `begin/end` keywords belong to `KwBegin` node + expression_l: Loc; + + constructor( + body: Node | null, + rescue_bodies: Node[], + else_: Node | null, + else_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a single `rescue` handler (i.e. `rescue E => e ...`) + export class RescueBody { + // A list of exception classes + // + // `None` if no classes specified (i.e. `rescue => e; ...` or just `rescue; ...`) + exc_list: Node | null; + // Variable that captures exception + // + // `None` if no variable specified (i.e. `rescue E; ...` or just `rescue; ... `) + exc_var: Node | null; + // Body of the handler + body: Node | null; + // Location of the `rescue` keyword + // + // ```text + // begin; 1; rescue E => e; 2; end + // ~~~~~~ + // ``` + keyword_l: Loc; + // Location of the `=>` operator + // + // ```text + // begin; 1; rescue E => e; 2; end + // ~~ + // ``` + // + // `None` if exception is not captured. + assoc_l: Loc | null; + // Location of the `then` keyword + // + // ```text + // begin; 1; rescue E => e then; 2; end + // ~~~~ + // ``` + // + // `then` is optional, so `begin_l` can be `None` + begin_l: Loc | null; + // Location of the full expression + // + // ```text + // begin; 1; rescue E => e then; 2; end + // ~~~~~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + exc_list: Node | null, + exc_var: Node | null, + body: Node | null, + keyword_l: Loc, + assoc_l: Loc | null, + begin_l: Loc | null, + expression_l: Loc, + ); + } + // Represents positional rest argument (i.e. `*foo` in `def m(*foo); end`) + export class Restarg { + // Name of the argument. + // + // `None` if argument has no name (i.e. `def m(*); end`) + name: string | null; + // Location of the `*` operator + // + // ```text + // def m(*foo); end + // ~ + // ``` + operator_l: Loc; + // Location of the argument name + // + // ```text + // def m(*foo); end + // ~~~ + // ``` + name_l: Loc | null; + // Location of the full expression + // + // ```text + // def m(*foo); end + // ~~~~ + // ``` + expression_l: Loc; + + constructor( + name: string | null, + operator_l: Loc, + name_l: Loc | null, + expression_l: Loc, + ); + } + // Represents `retry` keyword + export class Retry { + // Location of the `retry` keyword + // + // ```text + // retry + // ~~~~~ + // ``` + expression_l: Loc; + + constructor( + expression_l: Loc, + ); + } + // Represents `return` keyword + export class Return { + // A list of values that is returned + args: Node[]; + // Location of the `return` keyword + // + // ```text + // return 1, 2 + // ~~~~~~ + // ``` + keyword_l: Loc; + // Location of the full expression + // + // ```text + // return 1, 2 + // ~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + args: Node[], + keyword_l: Loc, + expression_l: Loc, + ); + } + // Represents opening a singleton class (i.e. `class << foo; ... end;`) + export class SClass { + // Expression that is used to get a singleton class + // + // `Lvar("foo")` for `class << foo; end` + expr: Node; + // Body of the block + body: Node | null; + // Location of the `class` keyword + // + // ```text + // class << foo; end + // ~~~~~ + // ``` + keyword_l: Loc; + // Location of the `<<` operator + // + // ```text + // class << foo; end + // ~~ + // ``` + operator_l: Loc; + // Location of the `end` keyword + // + // ```text + // class << foo; end + // ~~~ + // ``` + end_l: Loc; + // Location of the full expression + // + // ```text + // class << foo; end + // ~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + expr: Node, + body: Node | null, + keyword_l: Loc, + operator_l: Loc, + end_l: Loc, + expression_l: Loc, + ); + } + // Represents `self` keyword + export class Self_ { + // Location of the `self` keyword + // + // ```text + // self + // ~~~~ + // ``` + expression_l: Loc; + + constructor( + expression_l: Loc, + ); + } + // Represents a method call (e.g. `foo.bar(42)`) + export class Send { + // Receiver of the method call + // + // `None` for implicit method call (e.g. `foo(42)`) + recv: Node | null; + // Name of the method that is called + method_name: string; + // A list of arguments + args: Node[]; + // Location of the `.` operator + // + // ```text + // foo.bar(42) + // ~ + // ``` + // + // `None` for implicit method call (e.g. `foo(42)`) + dot_l: Loc | null; + // Location of the method name + // + // ```text + // foo.bar(42) + // ~~~ + // ``` + // + // `None` in a very special case when method call is implicit (i.e. `foo.(42)`) + selector_l: Loc | null; + // Location of open parenthesis + // + // ```text + // foo(42) + // ~ + // ``` + // + // `None` if there are no parentheses + begin_l: Loc | null; + // Location of closing parenthesis + // + // ```text + // foo(42) + // ~ + // ``` + // + // `None` if there are no parentheses + end_l: Loc | null; + // Location of the operator if method is a setter + // + // ```text + // foo.bar = 42 + // ~ + // ``` + // + // `None` otherwise + operator_l: Loc | null; + // Location of the full expression + // + // ```text + // foo.bar(42) + // ~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + recv: Node | null, + method_name: string, + args: Node[], + dot_l: Loc | null, + selector_l: Loc | null, + begin_l: Loc | null, + end_l: Loc | null, + operator_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a special block argument that "shadows" outer variable (i.e. `|;foo|`) + export class Shadowarg { + // Name of the argument + name: string; + // Location of the argument + // + // ```text + // proc { |;foo|} + // ~~~ + // ``` + expression_l: Loc; + + constructor( + name: string, + expression_l: Loc, + ); + } + // Represents an arguments splat (i.e. `*bar` in a call like `foo(*bar)`) + export class Splat { + // Value that is converted to array + value: Node | null; + // Location of the `*` operator + // + // ```text + // foo(*bar) + // ~ + // ``` + operator_l: Loc; + // Location of the full expression + // + // ```text + // foo(*bar) + // ~~~~ + // ``` + expression_l: Loc; + + constructor( + value: Node | null, + operator_l: Loc, + expression_l: Loc, + ); + } + // Represents a plain non-interpolated string literal (e.g. `"foo"`) + export class Str { + // Value of the string literal + // + // Note that it's a `StringValue`, not a `String`. + // The reason is that you can get UTF-8 incompatible strings + // from a valid UTF-8 source using escape sequences like `"\xFF"` + // + // These "\", "x", "F", "F" chars are valid separately, but together + // they construct a char with code = 255 that is invalid for UTF-8. + // + // You can use `to_string_lossy` or `to_string` methods to get a raw string value. + value: Uint8Array; + // Location of the string begin + // + // ```text + // "foo" + // ~ + // ``` + // + // `None` if string literal is a part of the words array (like `%w[foo bar baz]`) + begin_l: Loc | null; + // Location of the string begin + // + // ```text + // "foo" + // ~ + // ``` + // + // `None` if string literal is a part of the words array (like `%w[foo bar baz]`) + end_l: Loc | null; + // Location of the full expression + // + // ```text + // "foo" + // ~~~~~ + // ``` + expression_l: Loc; + + constructor( + value: Uint8Array, + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a `super` keyword + export class Super { + // A list of arguments given to `super` + args: Node[]; + // Location of the `super` keyword + // + // ```text + // super(1, 2) + // ~~~~~ + // ``` + keyword_l: Loc; + // Location of the open parenthesis + // + // ```text + // super(1, 2) + // ~ + // ``` + // + // `None` if there are no parentheses + begin_l: Loc | null; + // Location of the closing parenthesis + // + // ```text + // super(1, 2) + // ~ + // ``` + // + // `None` if there are no parentheses + end_l: Loc | null; + // Location of the full expression + // + // ```text + // super(1, 2) + // ~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + args: Node[], + keyword_l: Loc, + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a plain symbol literal (i.e. `:foo`) + // + // Note that `:` in `{ foo: bar }` belongs to a `pair` node. + export class Sym { + // Value of the symbol literal + // + // Note that it's a `StringValue`, not a `String`. + // The reason is that you can get UTF-8 incompatible strings + // from a valid UTF-8 source using escape sequences like `"\xFF"` + // + // These "\", "x", "F", "F" chars are valid separately, but together + // they construct a char with code = 255 that is invalid for UTF-8. + // + // You can use `to_string_lossy` or `to_string` methods to get a raw symbol value. + name: Uint8Array; + // Location of the symbol begin + // + // ```text + // :foo + // ~ + // ``` + // + // `None` if symbol is a label (`{ foo: 1 }`) or a part of the symbols array (`%i[foo bar baz]`) + begin_l: Loc | null; + // Location of the symbol end + // + // ```text + // { 'foo': 1 } + // ~ + // ``` + // + // `None` if symbol is **not** a string label (`:foo`) or a part of the symbols array (`%i[foo bar baz]`) + end_l: Loc | null; + // Location of the full expression + // + // ```text + // :foo + // ~~~~ + // + // { foo: 1 } + // ~~~~ + // + // %i[foo] + // ~~~ + // ``` + expression_l: Loc; + + constructor( + name: Uint8Array, + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a `true` literal + export class True { + // Location of the `true` keyword + // + // ```text + // true + // ~~~~ + // ``` + expression_l: Loc; + + constructor( + expression_l: Loc, + ); + } + // Represents an `undef` keyword (e.g. `undef foo, :bar`) + export class Undef { + // A list of names to `undef` + names: Node[]; + // Location the `undef` keyword + // + // ```text + // undef foo, :bar + // ~~~~~ + // ``` + keyword_l: Loc; + // Location of the full expression + // + // ```text + // undef :foo, bar + // ~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + names: Node[], + keyword_l: Loc, + expression_l: Loc, + ); + } + // Represents an `unless` guard used in pattern matching (i.e. `in pattern unless guard`) + export class UnlessGuard { + // Condition of the guard, `Lvar("foo")` in `in pattern unless guard` + cond: Node; + // Location of the `unless` keyword + // + // ```text + // case foo; in pattern unless cond; end + // ~~~~~~ + // ``` + keyword_l: Loc; + // Location of the full expression + // + // ```text + // case foo; in pattern unless cond; end + // ~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + cond: Node, + keyword_l: Loc, + expression_l: Loc, + ); + } + // Represents `until` loop + export class Until { + // Condition of the loop + cond: Node; + // Body of the loop. + // + // `None` if body is empty + body: Node | null; + // Location of the `until` keyword + // + // ```text + // until cond do; foo; end + // ~~~~~ + // ``` + keyword_l: Loc; + // Location of the `do` keyword + // + // ```text + // until cond do; foo; end + // ~~ + // ``` + // + // `do` is optional, and so `begin_l` can be `None` + begin_l: Loc | null; + // Location of the `end` keyword + // + // ```text + // until cond do; foo; end + // ~~~ + // ``` + // + // `None` if loop is a modifier (i.e. `foo until bar`) + end_l: Loc | null; + // Location of the full expression + // + // ```text + // until cond do; foo; end + // ~~~~~~~~~~~~~~~~~~~~~~~ + // + // foo until bar + // ~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + cond: Node, + body: Node | null, + keyword_l: Loc, + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a post-until loop + // + // ```text + // begin + // foo + // end until bar + // ``` + export class UntilPost { + // Condition of the loop + cond: Node; + // Body of the loop + body: Node; + // Location of the `until` keyword + // + // ```text + // begin; foo; end until bar + // ~~~~~ + // ``` + keyword_l: Loc; + // Location of the `until` keyword + // + // ```text + // begin; foo; end until bar + // ~~~~~~~~~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + cond: Node, + body: Node, + keyword_l: Loc, + expression_l: Loc, + ); + } + // Represents a branch of the `case` statement (i.e. `when foo`) + export class When { + // A list of values to compare/match against + patterns: Node[]; + // Body of the `when` branch + body: Node | null; + // Location of the `when` keyword + // + // ```text + // case foo; when bar; end + // ~~~~ + // ``` + keyword_l: Loc; + // Location of the `then` keyword + // + // ```text + // case foo; when bar then baz; end + // ~~~~ + // ``` + // + // `then` is optional, and so `begin_l` can be `None` + begin_l: Loc; + // Location of the full expression + // + // ```text + // case foo; when bar then baz; end + // ~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + patterns: Node[], + body: Node | null, + keyword_l: Loc, + begin_l: Loc, + expression_l: Loc, + ); + } + // Represents `while` loop + export class While { + // Condition of the loop + cond: Node; + // Body of the loop. + // + // `None` if body is empty + body: Node | null; + // Location of the `while` keyword + // + // ```text + // while cond do; foo; end + // ~~~~~ + // ``` + keyword_l: Loc; + // Location of the `do` keyword + // + // ```text + // while cond do; foo; end + // ~~ + // ``` + // + // `do` is optional, and so `begin_l` can be `None` + begin_l: Loc | null; + // Location of the `end` keyword + // + // ```text + // while cond do; foo; end + // ~~~ + // ``` + // + // `None` if loop is a modifier (i.e. `foo while bar`) + end_l: Loc | null; + // Location of the full expression + // + // ```text + // while cond do; foo; end + // ~~~~~~~~~~~~~~~~~~~~~~~ + // + // foo while bar + // ~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + cond: Node, + body: Node | null, + keyword_l: Loc, + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a post-while loop + // + // ```text + // begin + // foo + // end while bar + // ``` + export class WhilePost { + // Condition of the loop + cond: Node; + // Body of the loop + body: Node; + // Location of the `while` keyword + // + // ```text + // begin; foo; end while bar + // ~~~~~ + // ``` + keyword_l: Loc; + // Location of the `while` keyword + // + // ```text + // begin; foo; end while bar + // ~~~~~~~~~~~~~~~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + cond: Node, + body: Node, + keyword_l: Loc, + expression_l: Loc, + ); + } + // Represents a executable here-document literal (both with and without interpolation) + // + // It's similar to `Xstr` in terms of abstract syntax tree, but has different source maps. + export class XHeredoc { + // A list of string parts (static literals and interpolated expressions) + parts: Node[]; + // Location of the executable here-document body + // + // ```text + // <<-`HERE`\n a\n #{42}\nHERE + // ~~~~~~~~~~~~~~~ + // ``` + heredoc_body_l: Loc; + // Location of the executable here-document end + // + // ```text + // <<-`HERE`\n a\n #{42}\nHERE + // ~~~~ + // ``` + heredoc_end_l: Loc; + // Location of the executable here-document identifier + // + // ```text + // <<-`HERE`\n a\n #{42}\nHERE + // ~~~~~~~ + // ``` + // + // **Note**: This is the only node (with `Heredoc`) that has `expression_l` smaller that all other sub-locations merged. + // The reason for that is that it's possible to add more code after here-document ID: + // + // ```text + // <<-`HERE` + "rest" + // content + // HERE + // ``` + expression_l: Loc; + + constructor( + parts: Node[], + heredoc_body_l: Loc, + heredoc_end_l: Loc, + expression_l: Loc, + ); + } + // Represents an executable string (i.e. `` `sh #{script_name}` ``) + export class Xstr { + // A list of string parts (static literals and interpolated expressions) + parts: Node[]; + // Location of the string begin + // + // ```text + // `#{foo}` + // ~ + // + // %X{#{foo}} + // ~~~ + // ``` + begin_l: Loc; + // Location of the string end + // + // ```text + // `#{foo}` + // ~ + // + // %X{#{foo}} + // ~ + // ``` + end_l: Loc; + // Location of the full expression + // + // ```text + // `#{foo}` + // ~~~~~~~~ + // + // %X{#{foo}} + // ~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + parts: Node[], + begin_l: Loc, + end_l: Loc, + expression_l: Loc, + ); + } + // Represents an `yield` keyword + export class Yield { + // A list of arguments given to `yield` + args: Node[]; + // Location of the `yield` keyword + // + // ```text + // yield 1, 2 + // ~~~~~ + // ``` + keyword_l: Loc; + // Location of the open parenthesis + // + // ```text + // yield(1, 2) + // ~ + // ``` + // + // `None` if there are no parentheses + begin_l: Loc | null; + // Location of the closing parenthesis + // + // ```text + // yield(1, 2) + // ~ + // ``` + // + // `None` if there are no parentheses + end_l: Loc | null; + // Location of the full expression + // + // ```text + // yield(1, 2) + // ~~~~~~~~~~~ + // ``` + expression_l: Loc; + + constructor( + args: Node[], + keyword_l: Loc, + begin_l: Loc | null, + end_l: Loc | null, + expression_l: Loc, + ); + } + // Represents a `super` call without arguments and parentheses + // + // It's different from `super()` as it implicitly forwards current arguments + export class ZSuper { + // Location of the `super` keyword + // + // ```text + // super + // ~~~~~ + // ``` + expression_l: Loc; + + constructor( + expression_l: Loc, + ); + } +} + +export class DiagnosticMessage {} + +export namespace messages { + // Emitted for code + // + // ```text + // 1.2.3 + // ``` + export class FractionAfterNumeric { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // foo.2 + // ``` + export class NoDigitsAfterDot { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // %k[foo] + // ``` + export class UnknownTypeOfPercentString { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // 0b + // ``` + export class NumericLiteralWithoutDigits { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // %w[foo bar + // ``` + export class UnterminatedList { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // /foo + // ``` + export class UnterminatedRegexp { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // "foo + // ``` + export class UnterminatedString { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // %s + // // ^ EOF, not " + // + // ``` + export class UnterminatedQuotedString { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // "\ufoo" + // ``` + export class InvalidUnicodeEscape { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // "\u{999999}" + // ``` + export class TooLargeUnicodeCodepoint { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // "\u{d800}" + // ``` + export class InvalidUnicodeCodepoint { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // ?\u{41 42} + // ``` + export class MultipleCodepointAtSingleChar { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // "\M-" + // ``` + export class InvalidEscapeCharacter { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // "\xZZ" + // ``` + export class InvalidHexEscape { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // <<-HERE + // ``` + export class UnterminatedHeredoc { + // Heredoc identifier + heredoc_id: string; + + constructor( + heredoc_id: string, + ); + } + // Emitted for code like + // + // ```text + // <<-"HERE + // ``` + export class UnterminatedHeredocId { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // eval("foo \r = 42") + // ``` + export class SlashRAtMiddleOfLine { + + constructor( + ); + } + // Emitted for code like (only in $VERBOSE mode) + // + // ```text + // foo **arg + // ``` + export class DStarInterpretedAsArgPrefix { + + constructor( + ); + } + // Emitted for code like (only in $VERBOSE mode) + // + // ```text + // foo *arg + // ``` + export class StarInterpretedAsArgPrefix { + + constructor( + ); + } + // Emitted for code like (only in $VERBOSE mode) + // + // ```text + // foo &arg + // ``` + export class AmpersandInterpretedAsArgPrefix { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // range = 1... + // ``` + export class TripleDotAtEol { + + constructor( + ); + } + // Emitted for code like (only in $VERBOSE mode) + // + // ```text + // def m (a, b, c); end + // ``` + export class ParenthesesIterpretedAsArglist { + + constructor( + ); + } + // Emitted for code like (only in $VERBOSE mode) + // + // ```text + // m +foo + // ``` + export class AmbiguousFirstArgument { + // Operator that is ambiguous + operator: number; + + constructor( + operator: number, + ); + } + // Emitted for code like (only in $VERBOSE mode) + // + // ```text + // 1 *2 + // ``` + export class AmbiguousOperator { + // Operator that is ambiguous + operator: string; + // Interpretation of this operator + interpreted_as: string; + + constructor( + operator: string, + interpreted_as: string, + ); + } + // Emitted for code like + // + // ```text + // "\M- " + // ``` + export class InvalidCharacterSyntax { + // Valid syntax sugestions + suggestion: string; + + constructor( + suggestion: string, + ); + } + // Emitted for code like + // + // ```text + // 09 + // ``` + export class InvalidOctalDigit { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // 0_a + // ``` + export class TrailingCharInNumber { + // Invalid trailing char + c: number; + + constructor( + c: number, + ); + } + // Emitted for code like + // + // ```text + // =begin + // ``` + export class EmbeddedDocumentMeetsEof { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // eval("\x01foo") + // ``` + export class InvalidChar { + // char + c: number; + + constructor( + c: number, + ); + } + // It is unknown how to trigger this error. + // Code that triggers it in MRI can be dead. + export class IncompleteCharacterSyntax { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // $ + // ``` + export class GvarWithoutId { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // $@ + // ``` + export class InvalidGvarName { + // char after `$` + c: number; + + constructor( + c: number, + ); + } + // Emitted for code like + // + // ```text + // @ + // ``` + export class IvarWithoutId { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // @1 + // ``` + export class InvalidIvarName { + // char after `@` + c: number; + + constructor( + c: number, + ); + } + // Emitted for code like + // + // ```text + // @@ + // ``` + export class CvarWithoutId { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // @@1 + // ``` + export class InvalidCvarName { + // char after `@@` + c: number; + + constructor( + c: number, + ); + } + // Emitted for code like + // + // ```text + // /re/foo + // ``` + export class UnknownRegexOptions { + // Concatenated unknown options + options: string; + + constructor( + options: string, + ); + } + // Emitted for code like + // + // ```text + // "\u{1234" + // ``` + export class UnterminatedUnicodeEscape { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // # encoding: foo + // ``` + export class EncodingError { + // Error from decoder + error: string; + + constructor( + error: string, + ); + } + // Emitter for code like + // + // ```text + // eval("\xFF = 42") + // ``` + export class InvalidMultibyteChar { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // a ?AA : 2 + // ``` + export class AmbiguousTernaryOperator { + // Source of the condition expression + condition: string; + + constructor( + condition: string, + ); + } + // Emitted for code like + // + // ```text + // m /foo/ + // ``` + export class AmbiguousRegexp { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // begin; else; end + // ``` + export class ElseWithoutRescue { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // def f; BEGIN{}; end + // ``` + export class BeginNotAtTopLevel { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // alias $a $1 + // ``` + export class AliasNthRef { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // *a&.x = 0 + // ``` + export class CsendInsideMasgn { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // module foo; end + // ``` + export class ClassOrModuleNameMustBeConstant { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // def foo=() = 42 + // ``` + export class EndlessSetterDefinition { + + constructor( + ); + } + // Emitted for any code that produces invalid sequence of tokens + export class UnexpectedToken { + // Name of the token + token_name: string; + + constructor( + token_name: string, + ); + } + // Emitted for code like + // + // ```text + // def a; class Foo; end; end + // ``` + export class ClassDefinitionInMethodBody { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // def a; module Foo; end; end + // ``` + export class ModuleDefinitionInMethodBody { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // class A; return; end + // ``` + export class InvalidReturnInClassOrModuleBody { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // def foo(Abc); end + // ``` + export class ConstArgument { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // def foo(@abc); end + // ``` + export class IvarArgument { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // def foo($abc); end + // ``` + export class GvarArgument { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // def foo(@@abc); end + // ``` + export class CvarArgument { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // case 0; in ^a; true; end + // ``` + export class NoSuchLocalVariable { + // Variable name + var_name: string; + + constructor( + var_name: string, + ); + } + // Emitted for code like + // + // ```text + // m { |a| _1 } + // ``` + export class OrdinaryParamDefined { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // foo { _1; bar { _2 }; } + // ``` + export class NumparamUsed { + + constructor( + ); + } + // Emitted for code like (only in $VERBOSE mode) + // + // ```text + // if + // 42 + // end + // ``` + export class TokAtEolWithoutExpression { + // Name of the token + token_name: string; + + constructor( + token_name: string, + ); + } + // Emitted for code like + // + // ```text + // { foo?: } + // # or + // { foo!: } + // ``` + export class InvalidIdToGet { + // Identifier + identifier: string; + + constructor( + identifier: string, + ); + } + // Emitted for code like + // + // ```text + // def foo *rest, ... + // end + // ``` + export class ForwardArgAfterRestarg { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // def foo(); bar(&); end + // ``` + export class NoAnonymousBlockarg { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // def m; END {}; end + // ``` + export class EndInMethod { + + constructor( + ); + } + // Emitted for code like (only in $VERBOSE mode) + // + // ```text + // a < b < c + // ``` + export class ComparisonAfterComparison { + // Source of the first comparison + comparison: string; + + constructor( + comparison: string, + ); + } + // Emitted for code like + // + // ```text + // { 42 => value, 42 => another_value } + // ``` + export class DuplicateHashKey { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // def m(foo = foo) end + // ``` + export class CircularArgumentReference { + // Name of the argument + arg_name: string; + + constructor( + arg_name: string, + ); + } + // Emitted for code like + // + // ```text + // def m; FOO = 1; end + // ``` + export class DynamicConstantAssignment { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // self = foo + // ``` + export class CantAssignToSelf { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // nil = foo + // ``` + export class CantAssignToNil { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // true = foo + // ``` + export class CantAssignToTrue { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // false = foo + // ``` + export class CantAssignToFalse { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // __FILE__ = foo + // ``` + export class CantAssignToFile { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // __LINE__ = foo + // ``` + export class CantAssignToLine { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // __ENCODING__ = foo + // ``` + export class CantAssignToEncoding { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // proc {_1; _1 = nil} + // ``` + export class CantAssignToNumparam { + // Source of the numbered parameter + numparam: string; + + constructor( + numparam: string, + ); + } + // Emitted for code like + // + // ```text + // $1 = foo + // ``` + export class CantSetVariable { + // Source of the read-only variable that is assigned + var_name: string; + + constructor( + var_name: string, + ); + } + // Emitted for code like + // + // ```text + // yield(&foo) + // ``` + export class BlockGivenToYield { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // fun(&bar) do end + // ``` + export class BlockAndBlockArgGiven { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // case a; in "#{a}": 1; end + // ``` + export class SymbolLiteralWithInterpolation { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // _1 = 1 + // ``` + export class ReservedForNumparam { + // Numbered parameter that is treated as a local variable + numparam: string; + + constructor( + numparam: string, + ); + } + // Emitted for code like + // + // ```text + // case a; in a?:; end + // ``` + export class KeyMustBeValidAsLocalVariable { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // case 0; in a, a; end + // ``` + export class DuplicateVariableName { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // case 0; in a: 1, a: 2; end + // ``` + export class DuplicateKeyName { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // def (1).foo; end + // ``` + export class SingletonLiteral { + + constructor( + ); + } + // Emitted for code like (only in $VERBOSE mode) + // + // ```text + // $100 + // ``` + export class NthRefIsTooBig { + // Source of the nth_ref that is techincally a regular global variable + nth_ref: string; + + constructor( + nth_ref: string, + ); + } + // Emitted for code like + // + // ```text + // def foo(aa, aa); end + // ``` + export class DuplicatedArgumentName { + + constructor( + ); + } + // Emitted for code like + // + // ```text + // /[/ + // ``` + export class RegexError { + // Error from Onigurama engine + error: string; + + constructor( + error: string, + ); + } + // Emitted for code like + // + // ```text + // %I"x .\xc3." + // ``` + export class InvalidSymbol { + // Source of the symbol + symbol: string; + + constructor( + symbol: string, + ); + } + // Emitted for code like + // + // ```text + // a = return + // ``` + export class VoidValueExpression { + + constructor( + ); + } +} + +export namespace tokens { + export const id_to_name: { [K in number]: string }; + export const name_to_id: { [K in string]: number }; +} diff --git a/js/index.js b/js/index.js new file mode 100644 index 0000000..480ef44 --- /dev/null +++ b/js/index.js @@ -0,0 +1,31 @@ +const { parse } = require("./lib_ruby_parser_wasm"); +const { Node, nodes } = require("./nodes"); +const { Message, messages } = require("./messages"); +const { + ParserResult, + Token, + Comment, + MagicComment, + DecodedInput, + SourceLine, + Diagnostic, + Loc, + Bytes, +} = require("./types"); + +module.exports = { + parse, + nodes, + Node, + Message, + messages, + ParserResult, + Token, + Comment, + MagicComment, + DecodedInput, + SourceLine, + Diagnostic, + Loc, + Bytes, +}; diff --git a/js/package.json b/js/package.json new file mode 100644 index 0000000..4e63b90 --- /dev/null +++ b/js/package.json @@ -0,0 +1,19 @@ +{ + "name": "lib-ruby-parser-wasm-bindings", + "collaborators": [ + "Ilya Bylich " + ], + "version": "0.1.0", + "files": [ + "lib_ruby_parser_wasm_bg.wasm", + "lib_ruby_parser_wasm.js", + "index.js", + "index.d.ts", + "types.js", + "nodes.js", + "messages.js", + "tokens.js" + ], + "types": "index.d.ts", + "main": "index.js" +} diff --git a/js/types-old.js b/js/types-old.js new file mode 100644 index 0000000..bf825aa --- /dev/null +++ b/js/types-old.js @@ -0,0 +1,91 @@ +class Loc { + constructor(begin, end) { + this.begin = begin; + this.end = end; + } +} + +class Bytes { + constructor(raw) { + this.raw = raw; + } +} + +class Token { + constructor(token_type, token_value, loc) { + this.token_type = token_type; + this.token_value = token_value; + this.loc = loc; + } +} + +class Diagnostic { + constructor(level, message, loc) { + this.level = level; + this.message = message; + this.loc = loc; + } +} + +class Comment { + constructor(location, kind) { + this.location = location; + this.kind = kind; + } +} + +class MagicComment { + constructor(kind, key_l, value_l) { + this.kind = kind; + this.key_l = key_l; + this.value_l = value_l; + } +} + +class DecodedInput { + constructor(name, lines, bytes) { + this.name = name; + this.lines = lines; + this.bytes = bytes; + } +} + +class SourceLine { + constructor(start, end, ends_with_eof) { + this.start = start; + this.end = end; + this.ends_with_eof = ends_with_eof; + } +} + +class ParserResult { + constructor( + ast, + tokens, + diagnostics, + comments, + magic_comments, + input, + ) { + this.ast = ast; + this.tokens = tokens; + this.diagnostics = diagnostics; + this.comments = comments; + this.magic_comments = magic_comments; + this.input = input; + } +} + +root.Loc = Loc; +root.Bytes = Bytes; +root.Token = Token; +root.Diagnostic = Diagnostic; +root.Comment = Comment; +root.MagicComment = MagicComment; +root.DecodedInput = DecodedInput; +root.SourceLine = SourceLine; +root.ParserResult = ParserResult; + +function lib_ruby_parser__now() { + return performance.now(); +} diff --git a/js/types.js b/js/types.js index bf825aa..710665c 100644 --- a/js/types.js +++ b/js/types.js @@ -1,91 +1,88 @@ class Loc { - constructor(begin, end) { - this.begin = begin; - this.end = end; - } + constructor(begin, end) { + this.begin = begin; + this.end = end; + } } class Bytes { - constructor(raw) { - this.raw = raw; - } + constructor(raw) { + this.raw = raw; + } } -class Token { - constructor(token_type, token_value, loc) { - this.token_type = token_type; - this.token_value = token_value; - this.loc = loc; - } +class ParserResult { + constructor(ast, tokens, diagnostics, comments, magic_comments, input) { + this.ast = ast; + this.tokens = tokens; + this.diagnostics = diagnostics; + this.comments = comments; + this.magic_comments = magic_comments; + this.input = input; + } } -class Diagnostic { - constructor(level, message, loc) { - this.level = level; - this.message = message; - this.loc = loc; - } +class Token { + constructor(token_type, token_value, loc, lex_state_before, lex_state_after) { + this.token_type = token_type; + this.token_value = token_value; + this.loc = loc; + this.lex_state_before = lex_state_before; + this.lex_state_after = lex_state_after; + } + + name() { + return tokens.id_to_name[this.token_type]; + } } class Comment { - constructor(location, kind) { - this.location = location; - this.kind = kind; - } + constructor(loc, kind) { + this.loc = loc; + this.kind = kind; + } } class MagicComment { - constructor(kind, key_l, value_l) { - this.kind = kind; - this.key_l = key_l; - this.value_l = value_l; - } + constructor(kind, key_l, value_l) { + this.kind = kind; + this.key_l = key_l; + this.value_l = value_l; + } } class DecodedInput { - constructor(name, lines, bytes) { - this.name = name; - this.lines = lines; - this.bytes = bytes; - } + constructor(name, lines, bytes) { + this.name = name; + this.lines = lines; + this.bytes = bytes; + } } class SourceLine { - constructor(start, end, ends_with_eof) { - this.start = start; - this.end = end; - this.ends_with_eof = ends_with_eof; - } + constructor(start, end, ends_with_eof) { + this.start = start; + this.end = end; + this.ends_with_eof = ends_with_eof; + } } -class ParserResult { - constructor( - ast, - tokens, - diagnostics, - comments, - magic_comments, - input, - ) { - this.ast = ast; - this.tokens = tokens; - this.diagnostics = diagnostics; - this.comments = comments; - this.magic_comments = magic_comments; - this.input = input; - } +class Diagnostic { + constructor(level, message, loc) { + this.level = level; + this.message = message; + this.loc = loc; + } } -root.Loc = Loc; -root.Bytes = Bytes; -root.Token = Token; -root.Diagnostic = Diagnostic; -root.Comment = Comment; -root.MagicComment = MagicComment; -root.DecodedInput = DecodedInput; -root.SourceLine = SourceLine; -root.ParserResult = ParserResult; - -function lib_ruby_parser__now() { - return performance.now(); -} +module.exports = { + ParserResult, + Token, + Comment, + MagicComment, + DecodedInput, + SourceLine, + Diagnostic, + Loc, + Bytes, +}; diff --git a/tests/build.mk b/tests/build.mk index 08fdda6..6b07eac 100644 --- a/tests/build.mk +++ b/tests/build.mk @@ -1,5 +1,5 @@ tests/no-modules: build/web-lib-ruby-parser.js node tests/test-web.js -tests/nodejs: build/nodejs-lib-ruby-parser.js +tests/nodejs: build/nodejs node tests/test-node.js diff --git a/tests/test-node.js b/tests/test-node.js index f1123b2..3917181 100644 --- a/tests/test-node.js +++ b/tests/test-node.js @@ -1,207 +1,195 @@ -const LibRubyParser = require('../build/nodejs-lib-ruby-parser.js'); -const assert = require('assert').strict; +const LibRubyParser = require("../build/nodejs"); +const assert = require("assert").strict; function test_ast() { - const actual = LibRubyParser.parse("2 + 2").ast; - const expected = new LibRubyParser.nodes.Send( - // recv - new LibRubyParser.nodes.Int( - '2', - undefined, - new LibRubyParser.Loc(0, 1) - ), - // method_name - '+', - // args, - [ - new LibRubyParser.nodes.Int( - // value - '2', - // operator_l - undefined, - // expression_l - new LibRubyParser.Loc(4, 5), - ) - ], - // dot_l - undefined, - // selector_l - new LibRubyParser.Loc(2, 3), - // begin_l - undefined, - // end_l - undefined, + const actual = LibRubyParser.parse("2 + 2").ast; + const expected = new LibRubyParser.nodes.Send( + // recv + new LibRubyParser.nodes.Int("2", undefined, new LibRubyParser.Loc(0, 1)), + // method_name + "+", + // args, + [ + new LibRubyParser.nodes.Int( + // value + "2", // operator_l undefined, - // expression_l, - new LibRubyParser.Loc(0, 5), - ); - assert.deepEqual(actual, expected); + // expression_l + new LibRubyParser.Loc(4, 5) + ), + ], + // dot_l + undefined, + // selector_l + new LibRubyParser.Loc(2, 3), + // begin_l + undefined, + // end_l + undefined, + // operator_l + undefined, + // expression_l, + new LibRubyParser.Loc(0, 5) + ); + assert.deepEqual(actual, expected); } function test_tokens() { - const actual = LibRubyParser.parse('foo 42').tokens; - const expected = [ - new LibRubyParser.Token( - 307, - new LibRubyParser.Bytes(new TextEncoder("utf-8").encode("foo")), - new LibRubyParser.Loc(0, 3), - ), - new LibRubyParser.Token( - 314, - new LibRubyParser.Bytes(new TextEncoder("utf-8").encode("42")), - new LibRubyParser.Loc(4, 6), - ), - new LibRubyParser.Token( - 0, - new LibRubyParser.Bytes(Uint8Array.from([])), - new LibRubyParser.Loc(6, 6), - ), - ]; - assert.deepEqual(actual, expected); + const actual = LibRubyParser.parse("foo 42").tokens; + const expected = [ + new LibRubyParser.Token( + 307, + new TextEncoder("utf-8").encode("foo"), + new LibRubyParser.Loc(0, 3) + ), + new LibRubyParser.Token( + 314, + new TextEncoder("utf-8").encode("42"), + new LibRubyParser.Loc(4, 6) + ), + new LibRubyParser.Token( + 0, + Uint8Array.from([]), + new LibRubyParser.Loc(6, 6) + ), + ]; + assert.deepEqual(actual, expected); } function test_diagnostics() { - const actual = LibRubyParser.parse('def foo; BAR = 1; end').diagnostics; - const expected = [ - new LibRubyParser.Diagnostic( - 'error', - new LibRubyParser.messages.DynamicConstantAssignment(), - new LibRubyParser.Loc(9, 12), - ) - ]; - assert.deepEqual(actual, expected); + const actual = LibRubyParser.parse("def foo; BAR = 1; end").diagnostics; + const expected = [ + new LibRubyParser.Diagnostic( + "error", + new LibRubyParser.messages.DynamicConstantAssignment(), + new LibRubyParser.Loc(9, 12) + ), + ]; + assert.deepEqual(actual, expected); } function test_comments() { - const actual = LibRubyParser.parse("# foo\n=begin\nbar\n=end").comments; - const expected = [ - new LibRubyParser.Comment( - new LibRubyParser.Loc(0, 6), - 'inline' - ), - new LibRubyParser.Comment( - new LibRubyParser.Loc(6, 21), - 'document' - ), - ]; - assert.deepEqual(actual, expected); + const actual = LibRubyParser.parse("# foo\n=begin\nbar\n=end").comments; + const expected = [ + new LibRubyParser.Comment(new LibRubyParser.Loc(0, 6), "inline"), + new LibRubyParser.Comment(new LibRubyParser.Loc(6, 21), "document"), + ]; + assert.deepEqual(actual, expected); } function test_magic_comments() { - const actual = LibRubyParser.parse(` + const actual = LibRubyParser.parse(` # encoding: utf-8 # warn_indent: false # frozen_string_literal: true # shareable_constant_value: foo `).magic_comments; - const expected = [ - new LibRubyParser.MagicComment( - 'encoding', - new LibRubyParser.Loc(3, 11), - new LibRubyParser.Loc(13, 18), - ), - new LibRubyParser.MagicComment( - 'warn_indent', - new LibRubyParser.Loc(21, 32), - new LibRubyParser.Loc(34, 39), - ), - new LibRubyParser.MagicComment( - 'frozen_string_literal', - new LibRubyParser.Loc(42, 63), - new LibRubyParser.Loc(65, 69), - ), - new LibRubyParser.MagicComment( - 'shareable_constant_value', - new LibRubyParser.Loc(72, 96), - new LibRubyParser.Loc(98, 101), - ), - ]; - assert.deepEqual(actual, expected); + const expected = [ + new LibRubyParser.MagicComment( + "encoding", + new LibRubyParser.Loc(3, 11), + new LibRubyParser.Loc(13, 18) + ), + new LibRubyParser.MagicComment( + "warn_indent", + new LibRubyParser.Loc(21, 32), + new LibRubyParser.Loc(34, 39) + ), + new LibRubyParser.MagicComment( + "frozen_string_literal", + new LibRubyParser.Loc(42, 63), + new LibRubyParser.Loc(65, 69) + ), + new LibRubyParser.MagicComment( + "shareable_constant_value", + new LibRubyParser.Loc(72, 96), + new LibRubyParser.Loc(98, 101) + ), + ]; + assert.deepEqual(actual, expected); } function test_input() { - const actual = LibRubyParser.parse('2 + 2', 'foo.rb').input; - const expected = new LibRubyParser.DecodedInput( - 'foo.rb', - [ - new LibRubyParser.SourceLine(0, 5, true), - ], - new TextEncoder("utf-8").encode("2 + 2") - ); - assert.deepEqual(actual, expected); + const actual = LibRubyParser.parse("2 + 2", "foo.rb").input; + const expected = new LibRubyParser.DecodedInput( + "foo.rb", + [new LibRubyParser.SourceLine(0, 5, true)], + new TextEncoder("utf-8").encode("2 + 2") + ); + assert.deepEqual(actual, expected); } function test_decoder_ok() { - let decoder_called = false; - let yielded_encoding = null; - let yielded_input = null; - - const input = '# encoding: two-is-three\n2 + 2'; - const output = LibRubyParser.parse( - input, - null, - (encoding, input) => { - decoder_called = true; - - yielded_encoding = encoding; - yielded_input = input; - - // decode back and replace 2 -> 3 - input = new TextDecoder().decode(input); - input = input.replace(/2/g, '3'); - - return new TextEncoder("utf-8").encode(input); - } - ); - assert(decoder_called); - assert.equal(yielded_encoding, 'two-is-three'); - assert.deepEqual(yielded_input, new TextEncoder("utf-8").encode(input)); - - assert.equal(output.ast.recv.value, '3'); - assert.equal(output.ast.args[0].value, '3'); - assert.deepEqual(output.tokens[0].token_value.raw, new TextEncoder("utf-8").encode('3')); - assert.deepEqual(output.tokens[2].token_value.raw, new TextEncoder("utf-8").encode('3')); + let decoder_called = false; + let yielded_encoding = null; + let yielded_input = null; + + const input = "# encoding: two-is-three\n2 + 2"; + const output = LibRubyParser.parse(input, null, (encoding, input) => { + decoder_called = true; + + yielded_encoding = encoding; + yielded_input = input; + + // decode back and replace 2 -> 3 + input = new TextDecoder().decode(input); + input = input.replace(/2/g, "3"); + + return new TextEncoder("utf-8").encode(input); + }); + assert(decoder_called); + assert.equal(yielded_encoding, "two-is-three"); + assert.deepEqual(yielded_input, new TextEncoder("utf-8").encode(input)); + + assert.equal(output.ast.recv.value, "3"); + assert.equal(output.ast.args[0].value, "3"); + assert.deepEqual( + output.tokens[0].token_value, + new TextEncoder("utf-8").encode("3") + ); + assert.deepEqual( + output.tokens[2].token_value, + new TextEncoder("utf-8").encode("3") + ); } function test_decoder_err() { - let decoder_called = false; - let yielded_encoding = null; - let yielded_input = null; - - const input = '# encoding: two-is-three\n2 + 2'; - const output = LibRubyParser.parse( - input, - null, - (encoding, input) => { - decoder_called = true; - - yielded_encoding = encoding; - yielded_input = input; - - throw `JS[Unsupported encoding ${encoding}]`; - } - ); - assert.equal(yielded_encoding, 'two-is-three'); - assert.deepEqual(yielded_input, new TextEncoder("utf-8").encode(input)); - - assert.equal(output.ast, undefined); - assert.deepEqual(output.tokens, [ - new LibRubyParser.Token( - 0, - new LibRubyParser.Bytes(Uint8Array.from([])), - new LibRubyParser.Loc(0, 1) - ) - ]); - assert.deepEqual(output.diagnostics, [ - new LibRubyParser.Diagnostic( - 'error', - new LibRubyParser.messages.EncodingError('DecodingError("JS[Unsupported encoding two-is-three]")'), - new LibRubyParser.Loc(12, 24) - ) - ]); - assert.deepEqual(output.comments, []); - assert.deepEqual(output.magic_comments, []); + let decoder_called = false; + let yielded_encoding = null; + let yielded_input = null; + + const input = "# encoding: two-is-three\n2 + 2"; + const output = LibRubyParser.parse(input, null, (encoding, input) => { + decoder_called = true; + + yielded_encoding = encoding; + yielded_input = input; + + throw `JS[Unsupported encoding ${encoding}]`; + }); + assert.equal(yielded_encoding, "two-is-three"); + assert.deepEqual(yielded_input, new TextEncoder("utf-8").encode(input)); + + assert.equal(output.ast, undefined); + assert.deepEqual(output.tokens, [ + new LibRubyParser.Token( + 0, + Uint8Array.from([]), + new LibRubyParser.Loc(0, 1) + ), + ]); + assert.deepEqual(output.diagnostics, [ + new LibRubyParser.Diagnostic( + "error", + new LibRubyParser.messages.EncodingError( + 'DecodingError("JS[Unsupported encoding two-is-three]")' + ), + new LibRubyParser.Loc(12, 24) + ), + ]); + assert.deepEqual(output.comments, []); + assert.deepEqual(output.magic_comments, []); } test_ast();