Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into query-docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Xanewok committed Apr 11, 2024
2 parents dc3eab1 + ab3688b commit c846917
Show file tree
Hide file tree
Showing 134 changed files with 1,991 additions and 1,476 deletions.
5 changes: 5 additions & 0 deletions .changeset/pink-bats-lie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nomicfoundation/slang": minor
---

Changed the cst.NodeType in TS to use more descriptive string values rather than 0/1 integers
5 changes: 5 additions & 0 deletions .changeset/poor-lemons-hammer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nomicfoundation/slang": patch
---

Support arbitrary ASCII escape sequences in string literals until 0.4.25
5 changes: 5 additions & 0 deletions .changeset/tidy-vans-enjoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nomicfoundation/slang": patch
---

support string literals in version pragmas
5 changes: 5 additions & 0 deletions .changeset/warm-maps-give.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nomicfoundation/slang": minor
---

Add RuleNode/TokenNode::toJSON() in the TypeScript API
19 changes: 5 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ members = [
"crates/solidity/testing/sanctuary",
"crates/solidity/testing/snapshots",
"crates/solidity/testing/solc",
"crates/solidity/testing/utils",

"crates/testlang/inputs/language",
"crates/testlang/outputs/cargo/slang_testlang_node_addon",
Expand Down Expand Up @@ -64,7 +63,6 @@ solidity_spec = { path = "crates/solidity/outputs/spec" }
solidity_testing_sanctuary = { path = "crates/solidity/testing/sanctuary" }
solidity_testing_snapshots = { path = "crates/solidity/testing/snapshots" }
solidity_testing_solc = { path = "crates/solidity/testing/solc" }
solidity_testing_utils = { path = "crates/solidity/testing/utils" }

slang_testlang = { path = "crates/testlang/outputs/cargo/slang_testlang" }
slang_testlang_node_addon = { path = "crates/testlang/outputs/cargo/slang_testlang_node_addon" }
Expand Down
3 changes: 2 additions & 1 deletion crates/codegen/parser/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ napi = { workspace = true, optional = true }
napi-derive = { workspace = true, optional = true }
nom = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true, optional = true }
strum = { workspace = true }
strum_macros = { workspace = true }

# Since the source code is copied over as-is, we need to retain exact feature set with target crate,
# i.e `slang_solidity_node_addon`.
[features]
default = ["slang_napi_interfaces"]
slang_napi_interfaces = ["dep:napi", "dep:napi-derive"]
slang_napi_interfaces = ["dep:napi", "dep:napi-derive", "dep:serde_json"]

[lints]
workspace = true
Expand Down
99 changes: 63 additions & 36 deletions crates/codegen/parser/runtime/src/napi_interface/cst.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::rc::Rc;

use napi::bindgen_prelude::Env;
use napi::JsObject;
use napi::Either;
use napi_derive::napi;

use crate::napi_interface::cursor::Cursor;
Expand All @@ -10,12 +9,28 @@ use crate::napi_interface::{
RuleKind, RustNode, RustRuleNode, RustTextIndex, RustTokenNode, TokenKind,
};

#[napi(namespace = "cst")]
#[napi(namespace = "cst", string_enum)]
pub enum NodeType {
Rule,
Token,
}

impl RustNode {
/// Converts the node into `napi` wrapper for `RuleNode | TokenNode` JS object.
pub fn into_js_either_node(self) -> Either<RuleNode, TokenNode> {
match self {
RustNode::Rule(rule) => Either::A(RuleNode(rule)),
RustNode::Token(token) => Either::B(TokenNode(token)),
}
}
}

impl From<RustNode> for Either<RuleNode, TokenNode> {
fn from(value: RustNode) -> Self {
value.into_js_either_node()
}
}

#[napi(namespace = "cst")]
pub struct RuleNode(pub(crate) Rc<RustRuleNode>);

Expand Down Expand Up @@ -50,11 +65,11 @@ impl RuleNode {
}

#[napi(ts_return_type = "Array<cst.Node>", catch_unwind)]
pub fn children(&self, env: Env) -> Vec<JsObject> {
pub fn children(&self) -> Vec<Either<RuleNode, TokenNode>> {
self.0
.children
.iter()
.map(|child| child.to_js(env))
.map(|child| child.node.clone().into())
.collect()
}

Expand All @@ -68,10 +83,45 @@ impl RuleNode {
.into()
}

#[napi(catch_unwind, js_name = "toJSON")]
/// Serialize the token node to JSON.
pub fn to_json(&self) -> String {
serde_json::to_string(&self.0).unwrap()
}

#[napi(catch_unwind)]
pub fn unparse(&self) -> String {
self.0.clone().unparse()
}

// Expose the children as a hidden (non-enumerable, don't generate type definition)
// property that's eagerly evaluated (getter) for an inspected parent object in the debugger context.
#[napi(
enumerable = false,
configurable = false,
writable = false,
getter,
js_name = "__children", // Needed; otherwise, the property name would shadow `children`.
skip_typescript,
catch_unwind
)]
pub fn __children(&self) -> Vec<Either<RuleNode, TokenNode>> {
Self::children(self)
}

// Similarly, expose the eagerly evaluated unparsed text in the debugger context.
#[napi(
enumerable = false,
configurable = false,
writable = false,
getter,
js_name = "__text",
skip_typescript,
catch_unwind
)]
pub fn __text(&self) -> String {
self.unparse()
}
}

#[napi(namespace = "cst")]
Expand Down Expand Up @@ -107,6 +157,14 @@ impl TokenNode {
self.0.text.clone()
}

#[napi(catch_unwind, js_name = "toJSON")]
/// Serialize the token node to JSON.
///
/// This method is intended for debugging purposes and may not be stable.
pub fn to_json(&self) -> String {
serde_json::to_string(&self.0).unwrap()
}

#[napi(ts_return_type = "cursor.Cursor", catch_unwind)]
pub fn create_cursor(
&self,
Expand All @@ -117,34 +175,3 @@ impl TokenNode {
.into()
}
}

pub(crate) trait ToJS {
fn to_js(&self, env: Env) -> JsObject;
}

impl ToJS for Rc<RustRuleNode> {
fn to_js(&self, env: Env) -> JsObject {
RuleNode(self.clone())
.into_instance(env)
.expect("Class constructor to be defined by #[napi]")
.as_object(env)
}
}

impl ToJS for Rc<RustTokenNode> {
fn to_js(&self, env: Env) -> JsObject {
TokenNode(self.clone())
.into_instance(env)
.expect("Class constructor to be defined by #[napi]")
.as_object(env)
}
}

impl ToJS for RustNode {
fn to_js(&self, env: Env) -> JsObject {
match self {
RustNode::Rule(rust_rule_node) => rust_rule_node.to_js(env),
RustNode::Token(rust_token_node) => rust_token_node.to_js(env),
}
}
}
18 changes: 7 additions & 11 deletions crates/codegen/parser/runtime/src/napi_interface/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
// The functions are meant to be definitions for export, so they're not really used
#![allow(clippy::return_self_not_must_use)]

use cst::ToJS;
use napi::bindgen_prelude::Env;
use napi::JsObject;
use napi::Either;
use napi_derive::napi;
use text_index::{TextIndex, TextRange};

use crate::napi_interface::{cst, text_index, NodeLabel, RuleKind, RustCursor, TokenKind};
use crate::napi_interface::cst::{self, RuleNode, TokenNode};
use crate::napi_interface::{text_index, NodeLabel, RuleKind, RustCursor, TokenKind};

#[napi(namespace = "cursor")]
pub struct Cursor(pub(super) RustCursor);
Expand Down Expand Up @@ -53,8 +52,8 @@ impl Cursor {
}

#[napi(ts_return_type = "cst.Node", catch_unwind)]
pub fn node(&self, env: Env) -> JsObject {
self.0.node().to_js(env)
pub fn node(&self) -> Either<RuleNode, TokenNode> {
self.0.node().into()
}

#[napi(getter, ts_return_type = "kinds.NodeLabel", catch_unwind)]
Expand All @@ -79,11 +78,8 @@ impl Cursor {
}

#[napi(ts_return_type = "Array<cst.RuleNode>", catch_unwind)]
pub fn ancestors(&self, env: Env) -> Vec<JsObject> {
self.0
.ancestors()
.map(|rust_rule_node| rust_rule_node.to_js(env))
.collect()
pub fn ancestors(&self) -> Vec<cst::RuleNode> {
self.0.ancestors().map(cst::RuleNode).collect()
}

#[napi(catch_unwind)]
Expand Down
10 changes: 5 additions & 5 deletions crates/codegen/parser/runtime/src/napi_interface/parse_output.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use cst::ToJS;
use napi::bindgen_prelude::Env;
use napi::Either;
use napi_derive::napi;

use crate::napi_interface::{cst, cursor, parse_error, RustParseOutput};
use crate::napi_interface::cst::{RuleNode, TokenNode};
use crate::napi_interface::{cursor, parse_error, RustParseOutput};

#[napi(namespace = "parse_output")]
pub struct ParseOutput(RustParseOutput);
Expand All @@ -16,8 +16,8 @@ impl From<RustParseOutput> for ParseOutput {
#[napi(namespace = "parse_output")]
impl ParseOutput {
#[napi(ts_return_type = "cst.Node", catch_unwind)]
pub fn tree(&self, env: Env) -> napi::JsObject {
self.0.tree().to_js(env)
pub fn tree(&self) -> Either<RuleNode, TokenNode> {
self.0.tree().into()
}

#[napi(ts_return_type = "Array<parse_error.ParseError>", catch_unwind)]
Expand Down
29 changes: 19 additions & 10 deletions crates/codegen/parser/runtime/src/napi_interface/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
// The functions are meant to be definitions for export, so they're not really used
#![allow(clippy::return_self_not_must_use)]

use napi::{Env, JsObject};
use std::collections::HashMap;

use napi::bindgen_prelude::ClassInstance;
use napi::Env;
use napi_derive::napi;

use crate::napi_interface::cursor::Cursor;
Expand Down Expand Up @@ -36,22 +39,28 @@ pub struct QueryResultIterator(RustQueryResultIterator);
pub struct QueryResult {
pub query_number: u32,
#[napi(ts_type = "{ [key: string]: cursor.Cursor[] }")]
pub bindings: JsObject,
pub bindings: HashMap<String, Vec<ClassInstance<Cursor>>>,
}

impl QueryResult {
fn new(env: Env, result: RustQueryResult) -> napi::Result<Self> {
#[allow(clippy::cast_possible_truncation)]
let query_number = result.query_number as u32;
let mut bindings = env.create_object()?;
// transer all of the bindings eagerly on the assumption
// transfer all of the bindings eagerly on the assumption
// that they've all been explicitly requested.
for (key, value) in result.bindings {
bindings.set_named_property(
&key,
value.into_iter().map(|x| x.into()).collect::<Vec<Cursor>>(),
)?;
}
let bindings = result
.bindings
.into_iter()
.map(|(key, values)| {
let instances = values
.into_iter()
.map(|cursor| Cursor(cursor).into_instance(env))
.collect::<napi::Result<_>>()?;

Ok((key, instances))
})
.collect::<napi::Result<_>>()?;

Ok(Self {
query_number,
bindings,
Expand Down
Loading

0 comments on commit c846917

Please sign in to comment.