Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

pub mod language;
pub mod parser;
pub mod syntax_utils;
pub mod tokenizer;

pub use language::{DdlogLanguage, SyntaxKind};
pub use parser::{Parsed, ast, parse};
pub use syntax_utils::parse_parenthesized_list;
pub use tokenizer::{Span, tokenize};
91 changes: 91 additions & 0 deletions src/parser/ast/function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//!
//! AST wrapper for function declarations and definitions.
Comment on lines +1 to +2
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Expand the module documentation.

The module documentation is too brief and violates the coding guidelines requiring comprehensive module-level comments that explain the module's purpose and utility.

Apply this diff to provide a complete module documentation:

-//!
-//! AST wrapper for function declarations and definitions.
+//! AST wrapper for function declarations and definitions.
+//!
+//! This module provides a typed wrapper around DDlog function syntax nodes,
+//! enabling structured access to function components such as the name,
+//! extern status, parameter list with types, and return type. Functions
+//! in DDlog can be either external (extern) functions implemented outside
+//! the language or internal functions defined within DDlog rules.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//!
//! AST wrapper for function declarations and definitions.
//! AST wrapper for function declarations and definitions.
//
//! This module provides a typed wrapper around DDlog function syntax nodes,
//! enabling structured access to function components such as the name,
//! extern status, parameter list with types, and return type. Functions
//! in DDlog can be either external (extern) functions implemented outside
//! the language or internal functions defined within DDlog rules.
🤖 Prompt for AI Agents
In src/parser/ast/function.rs at the beginning of the file (lines 1-2), the
module documentation is too brief and does not meet the coding guidelines.
Expand the module-level comment to provide a comprehensive explanation of the
module's purpose, detailing that it serves as an AST wrapper for function
declarations and definitions, and describe its utility within the parser. Ensure
the documentation is clear, informative, and follows the project's style for
module comments.

//!
//! Provides access to function names, parameters, return types and whether the
//! function is declared as `extern`.

use super::AstNode;
use crate::{DdlogLanguage, SyntaxKind};

/// Typed wrapper for a function declaration or definition.
#[derive(Debug, Clone)]
pub struct Function {
pub(crate) syntax: rowan::SyntaxNode<DdlogLanguage>,
}

impl Function {
/// Name of the function if present.
#[must_use]
pub fn name(&self) -> Option<String> {
self.syntax
.children_with_tokens()
.skip_while(|e| !matches!(e.kind(), SyntaxKind::K_FUNCTION))
.skip(1)
.find_map(|e| match e {
rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::T_IDENT => {
Some(t.text().to_string())
}
_ => None,
})
}

/// Returns `true` if declared with the `extern` keyword.
#[must_use]
pub fn is_extern(&self) -> bool {
self.syntax
.children_with_tokens()
.any(|e| e.kind() == SyntaxKind::K_EXTERN)
}

/// Parameters as pairs of name and type.
#[must_use]
pub fn parameters(&self) -> Vec<(String, String)> {
let (pairs, errors) =
super::parse_utils::parse_name_type_pairs(self.syntax.children_with_tokens());
if !errors.is_empty() {
log::debug!("Parsing errors in function parameters: {errors:?}");
}
pairs
}

/// Return type text if specified.
#[must_use]
pub fn return_type(&self) -> Option<String> {
let mut iter = self.syntax.children_with_tokens().peekable();
let mut depth = 0usize;
for e in &mut iter {
match e.kind() {
SyntaxKind::T_LPAREN => depth += 1,
SyntaxKind::T_RPAREN => {
depth -= 1;
if depth == 0 {
break;
}
}
_ => {}
}
}

super::parse_utils::parse_type_after_colon(&mut iter)
}
}

impl_ast_node!(Function);

#[cfg(test)]
mod tests {

use crate::parse;
#[expect(clippy::expect_used, reason = "Using expect for clearer test failures")]
#[test]
fn function_name() {
let parsed = parse("function foo() {}");
let func = parsed
.root()
.functions()
.first()
.cloned()
.expect("function missing");
assert_eq!(func.name(), Some("foo".into()));
}
}
101 changes: 101 additions & 0 deletions src/parser/ast/import.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//!
//! AST wrapper for `import` statements in `DDlog`.
//!
//! This module provides the `Import` struct for reading module paths such as
//! `foo::bar` and optional aliases introduced with the `as` keyword. It allows
//! consumers to navigate import statements without reimplementing token logic.

use super::AstNode;
use crate::{DdlogLanguage, SyntaxKind};

/// Typed wrapper for an `import` statement.
#[derive(Debug, Clone)]
pub struct Import {
pub(crate) syntax: rowan::SyntaxNode<DdlogLanguage>,
}

impl Import {
/// The module path text as written in the source.
#[must_use]
pub fn path(&self) -> String {
self.syntax
.children_with_tokens()
.skip_while(|e| !matches!(e.kind(), SyntaxKind::K_IMPORT))
.skip(1)
.take_while(|e| !matches!(e.kind(), SyntaxKind::K_AS))
.filter_map(|e| match e {
rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::T_IDENT => {
Some(t.text().to_string())
}
rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::T_COLON_COLON => {
Some("::".to_string())
}
_ => None,
})
.collect::<String>()
}

/// The alias assigned with `as`, if any.
#[must_use]
pub fn alias(&self) -> Option<String> {
self.syntax
.children_with_tokens()
.skip_while(|e| !matches!(e.kind(), SyntaxKind::K_AS))
.skip(1)
.find_map(|e| match e {
rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::T_IDENT => {
Some(t.text().to_string())
}
_ => None,
})
}
}

impl_ast_node!(Import);

#[cfg(test)]
mod tests {

use crate::parse;
#[test]
fn parses_simple_import() {
let parsed = parse("import foo");
#[expect(clippy::expect_used, reason = "Using expect for clearer test failures")]
let first = parsed
.root()
.imports()
.first()
.cloned()
.expect("import missing");
assert_eq!(first.path(), "foo");
assert!(first.alias().is_none());
}

#[test]
fn parses_import_with_alias() {
let parsed = parse("import foo::bar as baz");
#[expect(clippy::expect_used, reason = "Using expect for clearer test failures")]
let first = parsed
.root()
.imports()
.first()
.cloned()
.expect("import missing");
assert_eq!(first.path(), "foo::bar");
assert_eq!(first.alias(), Some("baz".to_string()));
}

#[test]
fn parses_complex_path() {
let parsed = parse("import std::collections::HashMap");
#[expect(clippy::expect_used, reason = "Using expect for clearer test failures")]
let first = parsed
.root()
.imports()
.first()
.cloned()
.expect("import missing");
assert_eq!(first.path(), "std::collections::HashMap");
assert!(first.alias().is_none());
}
}
65 changes: 65 additions & 0 deletions src/parser/ast/index.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//!
//! AST wrapper for index declarations.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
//!
//! This module provides a typed wrapper around `DDlog` index syntax nodes,
//! enabling structured access to the optional name, target relation and column
//! expressions used to build the index.

use super::AstNode;
use crate::{DdlogLanguage, SyntaxKind};

/// Typed wrapper for an index declaration.
#[derive(Debug, Clone)]
pub struct Index {
pub(crate) syntax: rowan::SyntaxNode<DdlogLanguage>,
}

impl Index {
/// Name of the index if present.
#[must_use]
pub fn name(&self) -> Option<String> {
self.find_identifier_after_keyword(SyntaxKind::K_INDEX)
}

/// Target relation name.
#[must_use]
pub fn relation(&self) -> Option<String> {
self.find_identifier_after_keyword(SyntaxKind::K_ON)
}

fn find_identifier_after_keyword(&self, keyword: SyntaxKind) -> Option<String> {
let mut iter = self.syntax.children_with_tokens();
if !super::skip_to_match(&mut iter, |k| k == keyword) {
return None;
}
super::take_first_ident(iter)
}

/// Column expressions included in the index.
#[must_use]
pub fn columns(&self) -> Vec<String> {
Comment thread
leynos marked this conversation as resolved.
crate::syntax_utils::parse_parenthesized_list(self.syntax.children_with_tokens())
}
}

impl_ast_node!(Index);

#[cfg(test)]
mod tests {

use crate::parse;

#[expect(clippy::expect_used, reason = "Using expect for clearer test failures")]
#[test]
fn index_columns() {
let parsed = parse("index I on R(lower(name))");
let idx = parsed
.root()
.indexes()
.first()
.cloned()
.expect("index missing");
assert_eq!(idx.relation(), Some("R".into()));
assert_eq!(idx.columns(), vec!["lower(name)".to_string()]);
}
}
Loading