From 77aa4f4d5e43014fcff169c2d472ade7bbfe04ff Mon Sep 17 00:00:00 2001
From: Matthew Hambrecht
Date: Sun, 10 Aug 2025 14:29:55 -0400
Subject: [PATCH 1/5] Cleaned conditional literal, renamed unit tests
---
src/language/parser/conditional.rs | 65 +++++++++++++++++++++++++-----
src/language/parser/get.rs | 9 +----
tests/transpiler_tests.rs | 38 ++++++++---------
tests/validator_tests.rs | 32 +++++++--------
4 files changed, 92 insertions(+), 52 deletions(-)
diff --git a/src/language/parser/conditional.rs b/src/language/parser/conditional.rs
index aad9058..58e5bf5 100644
--- a/src/language/parser/conditional.rs
+++ b/src/language/parser/conditional.rs
@@ -1,4 +1,5 @@
use std::fmt;
+
use crate::{
language::{
parser::helpers::{
@@ -435,6 +436,58 @@ fn recurse_down(
}
impl ConditionNode {
+ /// Reconstructs conditional literal from provided tokens
+ /// and bounds.
+ fn reconstruct_literal (
+ tokens: &Vec,
+ start_idx: usize,
+ end_idx: usize,
+ ) -> String {
+ // Reconstruct literal
+ let mut i: usize = start_idx;
+ let mut literal = String::new();
+
+ while i < end_idx {
+ match tokens[i].token_type {
+ TokenType::OpenParen | TokenType::CloseParen => {
+ literal.push_str(&tokens[i].lexeme);
+ i += 1;
+ }
+ TokenType::And | TokenType::Or => {
+ literal.push_str(&format!(" {} ", &tokens[i].lexeme));
+ i += 1;
+ }
+ TokenType::Identifier => {
+ let mut j = i;
+ let mut parts = Vec::new();
+
+ while parts.len() < 3
+ {
+ parts.push(if tokens[j].token_type == TokenType::Equal {
+ "=".to_string()
+ } else {
+ tokens[j].lexeme.clone()
+ }
+ );
+ j += 1;
+ }
+
+ if !parts.is_empty() {
+ literal.push_str(&parts.join(" "));
+ }
+
+ i = j;
+ }
+ _ => {
+ literal.push_str(&tokens[i].lexeme);
+ i += 1;
+ }
+ }
+ }
+
+ literal
+ }
+
/// Takes current node type and given the current location in the
/// query defined by the borrowed index, makes an attempt to parse
/// this node and associated subnodes for the Abstract Syntax Tree.
@@ -482,20 +535,12 @@ impl ConditionNode {
return Err("Conditional had unclosed parentheses".to_string());
}
+
return Ok(
ConditionNode {
_condition: ret,
_depth: depth,
- _literal: {
- tokens[start_idx..*idx].iter()
- .map(|v| if v.token_type == TokenType::Equal {
- "="
- } else {
- v.lexeme.as_str()
- })
- .collect::>()
- .join(" ")
- }
+ _literal: ConditionNode::reconstruct_literal(tokens, start_idx, *idx)
}
)
}
diff --git a/src/language/parser/get.rs b/src/language/parser/get.rs
index 444246c..4241ca9 100644
--- a/src/language/parser/get.rs
+++ b/src/language/parser/get.rs
@@ -388,12 +388,7 @@ impl FilterNode {
Some(FilterNode {
condition: condition_node,
- _literal: {
- tokens[start_idx..*idx].iter()
- .map(|v| v.lexeme.as_str())
- .collect::>()
- .join(" ")
- },
+ _literal: tokens[start_idx].lexeme.clone(),
_depth: depth
}));
}
@@ -408,7 +403,7 @@ impl FilterNode {
&self
) -> (String, String) {
(
- colorize(&self._literal, AnsiColor::Cyan),
+ colorize(&format!("{} {}", self._literal, self.condition.transpile_raw()), AnsiColor::Cyan),
colorize(&format!("WHERE {}", self.condition.transpile_color()), AnsiColor::Cyan)
)
}
diff --git a/tests/transpiler_tests.rs b/tests/transpiler_tests.rs
index 09f7f6f..124c758 100644
--- a/tests/transpiler_tests.rs
+++ b/tests/transpiler_tests.rs
@@ -3,28 +3,28 @@ use eaql::transpiler::engine;
// Database Query Tests (Validator)
// Normal
#[test]
-fn integration_test_db_create_normal() {
+fn transpile_integration_test_db_create_normal() {
// Create keyword tests
assert_eq!(engine("create database test;"), Ok("CREATE DATABASE test;".to_string()));
assert_eq!(engine("make database test."), Ok("CREATE DATABASE test;".to_string()));
}
#[test]
-fn integration_test_db_use_normal() {
+fn transpile_integration_test_db_use_normal() {
// Use keyword tests
assert_eq!(engine("use database test;"), Ok("USE DATABASE test;".to_string()));
assert_eq!(engine("enter database test;"), Ok("USE DATABASE test;".to_string()));
}
#[test]
-fn integration_test_db_show_normal() {
+fn transpile_integration_test_db_show_normal() {
// Show keyword tests
assert_eq!(engine("show database;"), Ok("SHOW DATABASES;".to_string()));
assert_eq!(engine("list databases."), Ok("SHOW DATABASES;".to_string()));
}
#[test]
-fn integration_test_db_destroy_normal() {
+fn transpile_integration_test_db_destroy_normal() {
// Destroy
assert_eq!(engine("remove database db1!"), Ok("DROP DATABASE db1;".to_string()));
assert_eq!(engine("destroy database db1!"), Ok("DROP DATABASE db1;".to_string()));
@@ -37,7 +37,7 @@ fn integration_test_db_destroy_normal() {
// Error
#[test]
-fn integration_test_db_create_error() {
+fn transpile_integration_test_db_create_error() {
// Generic Error Test
assert!(engine("create database test").is_err());
assert!(engine("Create the test!").is_err());
@@ -45,7 +45,7 @@ fn integration_test_db_create_error() {
}
#[test]
-fn integration_test_db_use_error() {
+fn transpile_integration_test_db_use_error() {
// Generic Error Test
assert!(engine("use database test").is_err());
assert!(engine("Enter test!").is_err());
@@ -53,7 +53,7 @@ fn integration_test_db_use_error() {
}
#[test]
-fn integration_test_db_show_error() {
+fn transpile_integration_test_db_show_error() {
// Generic Error Test
assert!(engine("show database").is_err());
assert!(engine("show!").is_err());
@@ -61,7 +61,7 @@ fn integration_test_db_show_error() {
}
#[test]
-fn integration_test_db_destroy_error() {
+fn transpile_integration_test_db_destroy_error() {
// Generic Error Test
assert!(engine("delete databases db1, db2, db3").is_err());
assert!(engine("Delete db1!").is_err());
@@ -72,7 +72,7 @@ fn integration_test_db_destroy_error() {
// Table Accessor Query Tests (Validator)
// Normal
#[test]
-fn integration_test_table_accessor_normal_get() {
+fn transpile_integration_test_table_accessor_normal_get() {
// Test "wildcard" keywords
assert_eq!(engine("get all from test_table;"), Ok("SELECT * FROM test_table;".to_string()));
assert_eq!(engine("get any from test_table;"), Ok("SELECT * FROM test_table;".to_string()));
@@ -89,7 +89,7 @@ fn integration_test_table_accessor_normal_get() {
}
#[test]
-fn integration_test_table_accessor_normal_filter() {
+fn transpile_integration_test_table_accessor_normal_filter() {
// Test "filter entrance" keywords
assert_eq!(engine("get all from test_table where id = 3;"), Ok("SELECT * FROM test_table WHERE id = 3;".to_string()));
assert_eq!(engine("get all from test_table wherever id = 3;"), Ok("SELECT * FROM test_table WHERE id = 3;".to_string()));
@@ -99,14 +99,14 @@ fn integration_test_table_accessor_normal_filter() {
assert_eq!(engine("get all from test_table where id = 3 and price = 2.0."),
Ok("SELECT * FROM test_table WHERE id = 3 and price = 2.0;".to_string()));
assert_eq!(engine("get all from test_table where id = 3 or (price <= 2 and name is \"3\")!"),
- Ok("SELECT * FROM test_table WHERE id = 3 or ( price <= 2 and name = \"3\" );".to_string()));
+ Ok("SELECT * FROM test_table WHERE id = 3 or (price <= 2 and name = \"3\");".to_string()));
assert_eq!(engine("get all from test_table where (price < 3 or name is \"test\" and (id = 3 or (value < 4 and time >= 5)));"),
- Ok("SELECT * FROM test_table WHERE ( price < 3 or name = \"test\" and ( id = 3 or ( value < 4 and time >= 5 ) ) );".to_string()));
+ Ok("SELECT * FROM test_table WHERE (price < 3 or name = \"test\" and (id = 3 or (value < 4 and time >= 5)));".to_string()));
}
#[test]
-fn integration_test_table_accessor_normal_postprocessor() {
+fn transpile_integration_test_table_accessor_normal_postprocessor() {
// Test "post-processor entrance" keywords
assert_eq!(engine("get all from test_table then limit 5;"), Ok("SELECT * FROM test_table LIMIT 5;".to_string()));
assert_eq!(engine("get all from test_table afterwords limit 5;"), Ok("SELECT * FROM test_table LIMIT 5;".to_string()));
@@ -114,12 +114,12 @@ fn integration_test_table_accessor_normal_postprocessor() {
// Test post-processor w/ filter
assert_eq!(engine("get all from test_table where id = 3 or (price <= 2 and name is \"3\") then limit 5."),
- Ok("SELECT * FROM test_table WHERE id = 3 or ( price <= 2 and name = \"3\" ) LIMIT 5;".to_string()));
+ Ok("SELECT * FROM test_table WHERE id = 3 or (price <= 2 and name = \"3\") LIMIT 5;".to_string()));
}
#[test]
-fn integration_test_table_accessor_normal_postprocessor_limit() {
+fn transpile_integration_test_table_accessor_normal_postprocessor_limit() {
// Test "post-processor limit" keywords
assert_eq!(engine("get all from test_table then limit 5;"), Ok("SELECT * FROM test_table LIMIT 5;".to_string()));
assert_eq!(engine("get all from test_table then limit it to 5;"), Ok("SELECT * FROM test_table LIMIT 5;".to_string()));
@@ -127,7 +127,7 @@ fn integration_test_table_accessor_normal_postprocessor_limit() {
// Error
#[test]
-fn integration_test_table_accessor_error_get() {
+fn transpile_integration_test_table_accessor_error_get() {
// Test improper tokens
assert!(engine("get all from \"test_table\";").is_err());
assert!(engine("get all from test_table").is_err());
@@ -139,7 +139,7 @@ fn integration_test_table_accessor_error_get() {
}
#[test]
-fn integration_test_table_accessor_error_filter() {
+fn transpile_integration_test_table_accessor_error_filter() {
// Test bad conditions
assert!(engine("get all from test_table where id = 3").is_err());
assert!(engine("get all from test_table where id is equal to 3;").is_err());
@@ -152,7 +152,7 @@ fn integration_test_table_accessor_error_filter() {
}
#[test]
-fn integration_test_table_accessor_error_postprocessor() {
+fn transpile_integration_test_table_accessor_error_postprocessor() {
// Generic tests
assert!(engine("get all from test_table then limit 5").is_err());
@@ -164,7 +164,7 @@ fn integration_test_table_accessor_error_postprocessor() {
}
#[test]
-fn integration_test_table_accessor_error_postprocessor_limit() {
+fn transpile_integration_test_table_accessor_error_postprocessor_limit() {
// Test bad limit
assert!(engine("get all from test_table then limit = 5;").is_err());
assert!(engine("get all from test_table then limit id;").is_err());
diff --git a/tests/validator_tests.rs b/tests/validator_tests.rs
index d699a53..15fb5d8 100644
--- a/tests/validator_tests.rs
+++ b/tests/validator_tests.rs
@@ -3,28 +3,28 @@ use eaql::validator::engine;
// Database Query Tests (Validator)
// Normal
#[test]
-fn integration_test_db_create_normal() {
+fn validator_integration_test_db_create_normal() {
// Create keyword tests
assert_eq!(engine("create database test;"), true);
assert_eq!(engine("make database test."), true);
}
#[test]
-fn integration_test_db_use_normal() {
+fn validator_integration_test_db_use_normal() {
// Use keyword tests
assert_eq!(engine("use database test;"), true);
assert_eq!(engine("enter database test;"), true);
}
#[test]
-fn integration_test_db_show_normal() {
+fn validator_integration_test_db_show_normal() {
// Show keyword tests
assert_eq!(engine("show database;"), true);
assert_eq!(engine("list databases."), true);
}
#[test]
-fn integration_test_db_destroy_normal() {
+fn validator_integration_test_db_destroy_normal() {
// Destroy
assert_eq!(engine("remove database db1!"), true);
assert_eq!(engine("destroy database db1!"), true);
@@ -37,7 +37,7 @@ fn integration_test_db_destroy_normal() {
// Error
#[test]
-fn integration_test_db_create_error() {
+fn validator_integration_test_db_create_error() {
// Generic Error Test
assert_eq!(engine("create database test"), false);
assert_eq!(engine("Create the test!"), false);
@@ -45,7 +45,7 @@ fn integration_test_db_create_error() {
}
#[test]
-fn integration_test_db_use_error() {
+fn validator_integration_test_db_use_error() {
// Generic Error Test
assert_eq!(engine("use database test"), false);
assert_eq!(engine("Enter test!"), false);
@@ -53,7 +53,7 @@ fn integration_test_db_use_error() {
}
#[test]
-fn integration_test_db_show_error() {
+fn validator_integration_test_db_show_error() {
// Generic Error Test
assert_eq!(engine("show database"), false);
assert_eq!(engine("show!"), false);
@@ -61,7 +61,7 @@ fn integration_test_db_show_error() {
}
#[test]
-fn integration_test_db_destroy_error() {
+fn validator_integration_test_db_destroy_error() {
// Generic Error Test
assert_eq!(engine("delete databases db1, db2, db3"), false);
assert_eq!(engine("Delete db1!"), false);
@@ -72,7 +72,7 @@ fn integration_test_db_destroy_error() {
// Table Accessor Query Tests (Validator)
// Normal
#[test]
-fn integration_test_table_accessor_normal_get() {
+fn validator_integration_test_table_accessor_normal_get() {
// Test "wildcard" keywords
assert_eq!(engine("get all from test_table;"), true);
assert_eq!(engine("get any from test_table;"), true);
@@ -89,7 +89,7 @@ fn integration_test_table_accessor_normal_get() {
}
#[test]
-fn integration_test_table_accessor_normal_filter() {
+fn validator_integration_test_table_accessor_normal_filter() {
// Test "filter entrance" keywords
assert_eq!(engine("get all from test_table where id = 3;"), true);
assert_eq!(engine("get all from test_table wherever id = 3;"), true);
@@ -103,7 +103,7 @@ fn integration_test_table_accessor_normal_filter() {
#[test]
-fn integration_test_table_accessor_normal_postprocessor() {
+fn validator_integration_test_table_accessor_normal_postprocessor() {
// Test "post-processor entrance" keywords
assert_eq!(engine("get all from test_table then limit 5;"), true);
assert_eq!(engine("get all from test_table afterwords limit 5;"), true);
@@ -115,7 +115,7 @@ fn integration_test_table_accessor_normal_postprocessor() {
#[test]
-fn integration_test_table_accessor_normal_postprocessor_limit() {
+fn validator_integration_test_table_accessor_normal_postprocessor_limit() {
// Test "post-processor limit" keywords
assert_eq!(engine("get all from test_table then limit 5;"), true);
assert_eq!(engine("get all from test_table then limit it to 5;"), true);
@@ -123,7 +123,7 @@ fn integration_test_table_accessor_normal_postprocessor_limit() {
// Error
#[test]
-fn integration_test_table_accessor_error_get() {
+fn validator_integration_test_table_accessor_error_get() {
// Test improper tokens
assert_eq!(engine("get all from \"test_table\";"), false);
assert_eq!(engine("get all from test_table"), false);
@@ -135,7 +135,7 @@ fn integration_test_table_accessor_error_get() {
}
#[test]
-fn integration_test_table_accessor_error_filter() {
+fn validator_integration_test_table_accessor_error_filter() {
// Test bad conditions
assert_eq!(engine("get all from test_table where id = 3"), false);
assert_eq!(engine("get all from test_table where id is equal to 3;"), false);
@@ -148,7 +148,7 @@ fn integration_test_table_accessor_error_filter() {
}
#[test]
-fn integration_test_table_accessor_error_postprocessor() {
+fn validator_integration_test_table_accessor_error_postprocessor() {
// Generic tests
assert_eq!(engine("get all from test_table then limit 5"), false);
@@ -160,7 +160,7 @@ fn integration_test_table_accessor_error_postprocessor() {
}
#[test]
-fn integration_test_table_accessor_error_postprocessor_limit() {
+fn validator_integration_test_table_accessor_error_postprocessor_limit() {
// Test bad limit
assert_eq!(engine("get all from test_table then limit = 5;"), false);
assert_eq!(engine("get all from test_table then limit id;"), false);
From 9fcc86662552da18a1d6260cfa6d4273ff89a405 Mon Sep 17 00:00:00 2001
From: Matthew Hambrecht
Date: Sun, 10 Aug 2025 14:46:35 -0400
Subject: [PATCH 2/5] Cleaned column list literal
---
src/language/parser/get.rs | 33 +++++++++++++++++++++++++--------
1 file changed, 25 insertions(+), 8 deletions(-)
diff --git a/src/language/parser/get.rs b/src/language/parser/get.rs
index 4241ca9..d8db02e 100644
--- a/src/language/parser/get.rs
+++ b/src/language/parser/get.rs
@@ -245,7 +245,7 @@ impl TableNode {
}
impl ColumnNode {
- pub fn recurse_build(
+ fn recurse_build(
tokens: &Vec,
cols: &mut Vec,
idx: &mut usize,
@@ -279,6 +279,28 @@ make sure they're in a valid list notation.".to_string()
);
}
+ /// Reconstructs original literal from list of tokens and
+ /// provided bounds.
+ fn reconstruct_literal(
+ tokens: &Vec,
+ start_idx: usize,
+ end_idx: usize
+ ) -> String {
+ let mut literal = String::new();
+
+ literal.push_str(&format!("{} ", &tokens[start_idx].lexeme).to_string());
+
+ for v in &tokens[start_idx + 1..end_idx] {
+ match v.token_type {
+ TokenType::Comma => literal.push_str(", "),
+ TokenType::And => literal.push_str(" and "),
+ _ => literal.push_str(&v.lexeme),
+ };
+ }
+
+ literal
+ }
+
/// Takes current node type and given the current location in the
/// query defined by the borrowed index, makes an attempt to parse
/// this node and associated subnodes for the Abstract Syntax Tree.
@@ -316,12 +338,7 @@ make sure they're in a valid list notation.".to_string()
is_wildcard: false,
column_names: column_names,
- _literal: {
- tokens[start_idx..*idx].iter()
- .map(|v| v.lexeme.as_str())
- .collect::>()
- .join(" ")
- },
+ _literal: ColumnNode::reconstruct_literal(tokens, start_idx, *idx),
_depth: depth
});
}
@@ -645,7 +662,7 @@ mod tests {
],
is_wildcard: false,
- _literal: "get id , cost and time".to_string(),
+ _literal: "get id, cost and time".to_string(),
_depth: 0
};
let mut idx: usize = 1;
From 42928ec667a64532eeabea75fba84b12aa331043 Mon Sep 17 00:00:00 2001
From: Matthew Hambrecht
Date: Sun, 10 Aug 2025 14:57:19 -0400
Subject: [PATCH 3/5] Added linting test
---
.github/workflows/code-format.yaml | 26 ++++++++++++++++++++++++++
README.md | 1 +
2 files changed, 27 insertions(+)
create mode 100644 .github/workflows/code-format.yaml
diff --git a/.github/workflows/code-format.yaml b/.github/workflows/code-format.yaml
new file mode 100644
index 0000000..1ac206a
--- /dev/null
+++ b/.github/workflows/code-format.yaml
@@ -0,0 +1,26 @@
+name: Linting Tests
+
+on:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+
+env:
+ CARGO_TERM_COLOR: always
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Setup Formatter
+ run: rustup component add rustfmt
+
+ - name: Run Composite Build Action
+ uses: ./.github/actions/build
+
+ - name: Run Format Test
+ run: cargo fmt --all -- --check
\ No newline at end of file
diff --git a/README.md b/README.md
index c3389a4..e37c0e2 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,7 @@
+
**EAQL (English Augmented Query Language)** is a simplified, English-like query language designed as natural-language alternative to SQL. It's built for learners, educators, and tinkerers.
From 0e326f7ba233333bd0494d6d100622ee56081ded Mon Sep 17 00:00:00 2001
From: Matthew Hambrecht
Date: Sun, 10 Aug 2025 14:59:21 -0400
Subject: [PATCH 4/5] Fixed workflow perms
---
.github/workflows/code-format.yaml | 3 +++
.github/workflows/doc-tests.yaml | 3 +++
.github/workflows/integration-tests.yaml | 3 +++
.github/workflows/unit-tests.yaml | 3 +++
4 files changed, 12 insertions(+)
diff --git a/.github/workflows/code-format.yaml b/.github/workflows/code-format.yaml
index 1ac206a..63b1066 100644
--- a/.github/workflows/code-format.yaml
+++ b/.github/workflows/code-format.yaml
@@ -1,5 +1,8 @@
name: Linting Tests
+permissions:
+ contents: read
+
on:
push:
branches: [ "main" ]
diff --git a/.github/workflows/doc-tests.yaml b/.github/workflows/doc-tests.yaml
index 76e06f4..c4aace1 100644
--- a/.github/workflows/doc-tests.yaml
+++ b/.github/workflows/doc-tests.yaml
@@ -1,5 +1,8 @@
name: Document Tests
+permissions:
+ contents: read
+
on:
push:
branches: [ "main" ]
diff --git a/.github/workflows/integration-tests.yaml b/.github/workflows/integration-tests.yaml
index 2b57ddf..4eef85a 100644
--- a/.github/workflows/integration-tests.yaml
+++ b/.github/workflows/integration-tests.yaml
@@ -1,5 +1,8 @@
name: Integration Tests
+permissions:
+ contents: read
+
on:
push:
branches: [ "main" ]
diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml
index c4ad040..4ccbdad 100644
--- a/.github/workflows/unit-tests.yaml
+++ b/.github/workflows/unit-tests.yaml
@@ -1,5 +1,8 @@
name: Unit Tests
+permissions:
+ contents: read
+
on:
push:
branches: [ "main" ]
From 001f22be78a312ff7c44be648508924bb73be271 Mon Sep 17 00:00:00 2001
From: Matthew Hambrecht
Date: Sun, 10 Aug 2025 15:08:24 -0400
Subject: [PATCH 5/5] Code cleanup for lint
---
.github/workflows/code-format.yaml | 4 +-
.github/workflows/doc-tests.yaml | 4 +-
.github/workflows/integration-tests.yaml | 4 +-
.github/workflows/unit-tests.yaml | 4 +-
README.md | 1 +
src/bin/repl.rs | 15 +-
src/language/lexer.rs | 404 +++++---------
src/language/mod.rs | 2 +-
src/language/parser/conditional.rs | 649 +++++++++++------------
src/language/parser/database.rs | 536 +++++++------------
src/language/parser/get.rs | 505 +++++++-----------
src/language/parser/helpers.rs | 57 +-
src/language/parser/mod.rs | 4 +-
src/language/parser/parser.rs | 95 ++--
src/language/parser/postprocessor.rs | 205 +++----
src/language/tokens.rs | 68 ++-
src/lib.rs | 4 +-
src/transpiler/mod.rs | 2 +-
src/transpiler/transpiler.rs | 30 +-
src/utils/io.rs | 6 +-
src/utils/logger.rs | 10 +-
src/utils/mod.rs | 6 +-
src/utils/query.rs | 23 +-
src/validator/mod.rs | 2 +-
src/validator/validator.rs | 29 +-
tests/transpiler_tests.rs | 174 ++++--
tests/validator_tests.rs | 70 ++-
27 files changed, 1252 insertions(+), 1661 deletions(-)
diff --git a/.github/workflows/code-format.yaml b/.github/workflows/code-format.yaml
index 63b1066..5fb5633 100644
--- a/.github/workflows/code-format.yaml
+++ b/.github/workflows/code-format.yaml
@@ -1,4 +1,4 @@
-name: Linting Tests
+name: Linting
permissions:
contents: read
@@ -13,7 +13,7 @@ env:
CARGO_TERM_COLOR: always
jobs:
- build:
+ test:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
diff --git a/.github/workflows/doc-tests.yaml b/.github/workflows/doc-tests.yaml
index c4aace1..e15687e 100644
--- a/.github/workflows/doc-tests.yaml
+++ b/.github/workflows/doc-tests.yaml
@@ -1,4 +1,4 @@
-name: Document Tests
+name: Document
permissions:
contents: read
@@ -13,7 +13,7 @@ env:
CARGO_TERM_COLOR: always
jobs:
- build:
+ test:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
diff --git a/.github/workflows/integration-tests.yaml b/.github/workflows/integration-tests.yaml
index 4eef85a..0db6d55 100644
--- a/.github/workflows/integration-tests.yaml
+++ b/.github/workflows/integration-tests.yaml
@@ -1,4 +1,4 @@
-name: Integration Tests
+name: Integration
permissions:
contents: read
@@ -13,7 +13,7 @@ env:
CARGO_TERM_COLOR: always
jobs:
- build:
+ test:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml
index 4ccbdad..a8643a6 100644
--- a/.github/workflows/unit-tests.yaml
+++ b/.github/workflows/unit-tests.yaml
@@ -1,4 +1,4 @@
-name: Unit Tests
+name: Unit
permissions:
contents: read
@@ -13,7 +13,7 @@ env:
CARGO_TERM_COLOR: always
jobs:
- build:
+ test:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
diff --git a/README.md b/README.md
index e37c0e2..3382043 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@
+
**EAQL (English Augmented Query Language)** is a simplified, English-like query language designed as natural-language alternative to SQL. It's built for learners, educators, and tinkerers.
diff --git a/src/bin/repl.rs b/src/bin/repl.rs
index d28a894..2a3a4a4 100644
--- a/src/bin/repl.rs
+++ b/src/bin/repl.rs
@@ -1,12 +1,8 @@
-use eaql::{
- transpiler,
- validator,
- utils,
-};
+use eaql::{transpiler, utils, validator};
fn main() {
let args: Vec = std::env::args().collect();
-
+
utils::help::display_logo();
if args.len() < 2 {
@@ -17,7 +13,8 @@ fn main() {
match args[args.len() - 1].as_str() {
"transpile" => transpiler::repl_loop(),
"query_test" => validator::repl_loop(),
- _ => utils::help::display_help(
- Some(format!("Invalid Testing CLI Argument -> {}, see usage!", args[2]).as_str()))
+ _ => utils::help::display_help(Some(
+ format!("Invalid Testing CLI Argument -> {}, see usage!", args[2]).as_str(),
+ )),
}
-}
\ No newline at end of file
+}
diff --git a/src/language/lexer.rs b/src/language/lexer.rs
index 42cc811..d47110c 100644
--- a/src/language/lexer.rs
+++ b/src/language/lexer.rs
@@ -1,28 +1,17 @@
-use std::fmt;
-use crate::{
- language::{
- tokens::{
- Token,
- TokenType,
- IDENTIFER_STOPS,
- SINGLE_DOUBLE_START_TOKENS,
- SINGLE_START_TOKENS,
- SYSTEM_KEYWORDS
- }
- }
+use crate::language::tokens::{
+ IDENTIFER_STOPS, SINGLE_DOUBLE_START_TOKENS, SINGLE_START_TOKENS, SYSTEM_KEYWORDS, Token,
+ TokenType,
};
+use std::fmt;
#[derive(Debug)]
pub struct Lexer {
- pub tokens: Vec
+ pub tokens: Vec,
}
impl Lexer {
// Look ahead for one token
- fn peek_one(
- query: &String,
- current: &usize
- ) -> Option {
+ fn peek_one(query: &String, current: &usize) -> Option {
if current + 1 >= query.len() {
return None;
}
@@ -30,18 +19,14 @@ impl Lexer {
return query.chars().nth(*current + 1);
}
- fn peek_decimal(
- query: &String,
- start: &mut usize,
- current: &mut usize,
- ) {
+ fn peek_decimal(query: &String, start: &mut usize, current: &mut usize) {
loop {
if *current >= query.len() {
// Fix edge case where query ends with decimal
- // (i.e. 2. becomes a number literal but should be a number and eoq token)
+ // (i.e. 2. becomes a number literal but should be a number and eoq token)
if *current > 0 {
let prev = query.as_bytes()[*current - 1];
-
+
if matches!(prev, b'.' | b'!' | b';') {
*current -= 1;
}
@@ -51,7 +36,7 @@ impl Lexer {
}
*current += 1;
-
+
let slice = &query.as_bytes()[*start..*current];
let s = match std::str::from_utf8(slice) {
Ok(s) => s,
@@ -60,7 +45,7 @@ impl Lexer {
return;
}
};
-
+
if s.parse::().is_err() {
*current -= 1;
return;
@@ -68,11 +53,7 @@ impl Lexer {
}
}
- fn peek_string(
- query: &String,
- current: &mut usize,
- token_type: &mut TokenType
- ) -> () {
+ fn peek_string(query: &String, current: &mut usize, token_type: &mut TokenType) -> () {
loop {
if *current + 1 >= query.len() {
*token_type = TokenType::UnknownToken;
@@ -80,7 +61,7 @@ impl Lexer {
}
*current += 1;
-
+
if query.chars().nth(*current).unwrap() == '\"' {
return;
}
@@ -98,8 +79,7 @@ impl Lexer {
return;
}
- if IDENTIFER_STOPS.contains(
- &query.chars().nth(*current).unwrap()) {
+ if IDENTIFER_STOPS.contains(&query.chars().nth(*current).unwrap()) {
let tmp = &query[*start..*current];
if let Some(keyword_token) = SYSTEM_KEYWORDS.get(tmp.to_lowercase().as_str()) {
@@ -113,46 +93,37 @@ impl Lexer {
}
}
- fn handle_single_token(
- _query: &String,
- c: char,
- current: &mut usize
- ) -> Result {
+ fn handle_single_token(_query: &String, c: char, current: &mut usize) -> Result {
let token_type: TokenType = match c {
n if [';', '!', '.'].contains(&n) => TokenType::EoqToken,
')' => TokenType::CloseParen,
'(' => TokenType::OpenParen,
',' => TokenType::Comma,
- _ => TokenType::UnknownToken
+ _ => TokenType::UnknownToken,
};
*current += 1;
- return Ok(Token::new(
- token_type,
- &"".to_string(),
- &c.to_string()
- ));
+ return Ok(Token::new(token_type, &"".to_string(), &c.to_string()));
}
- /* Handle slightly complex tokens like '>' which might be
+ /* Handle slightly complex tokens like '>' which might be
succeeded by '=' */
fn handle_single_double_token(
query: &String,
c: char,
- current: &mut usize
+ current: &mut usize,
) -> Result {
let slice_start = *current;
let peeked_token: Option = Lexer::peek_one(query, current);
- let token_type: TokenType =
- if c == '>' {
+ let token_type: TokenType = if c == '>' {
if peeked_token == Some('=') {
*current += 2;
TokenType::Gte
- } else {
+ } else {
*current += 1;
TokenType::Gt
- }
+ }
} else if c == '<' {
if peeked_token == Some('=') {
*current += 2;
@@ -173,7 +144,7 @@ impl Lexer {
return Ok(Token::new(
token_type,
&"".to_string(),
- &query[slice_start..slice_end].to_string()
+ &query[slice_start..slice_end].to_string(),
));
}
@@ -181,62 +152,51 @@ impl Lexer {
query: &String,
c: char,
current: &mut usize,
- start: &mut usize
+ start: &mut usize,
) -> Result {
let mut token_type: TokenType;
let literal: String;
let slice_start: usize = *current;
- if c == ' ' { // Whitespace
+ if c == ' ' {
+ // Whitespace
token_type = TokenType::WhitespaceToken;
literal = " ".to_string()
- } else if c.is_digit(10) ||
- (
- c == '-' &&
- Lexer::peek_one(query, ¤t).is_some_and(|x: char| x.is_digit(10))
- ) { // Number literals
+ } else if c.is_digit(10)
+ || (c == '-' && Lexer::peek_one(query, ¤t).is_some_and(|x: char| x.is_digit(10)))
+ {
+ // Number literals
token_type = TokenType::NumberLiteral;
- if c == '-' &&
- Lexer::peek_one(query, ¤t).is_some_and(|x: char| x.is_digit(10)) {
+ if c == '-' && Lexer::peek_one(query, ¤t).is_some_and(|x: char| x.is_digit(10)) {
*current += 1;
}
- Lexer::peek_decimal(
- query,
- start,
- current);
-
+ Lexer::peek_decimal(query, start, current);
+
literal = query[*start..*current].to_string();
*current -= 1;
} else if c == '\"' {
token_type = TokenType::StringLiteral;
- Lexer::peek_string(
- query,
- current,
- &mut token_type
- );
+ Lexer::peek_string(query, current, &mut token_type);
if token_type == TokenType::UnknownToken {
*current += 1; // We weren't able to increment in the loop
return Ok(Token::new(
token_type,
&"".to_string(),
- &query[slice_start..*current].to_string()
+ &query[slice_start..*current].to_string(),
));
}
-
+
literal = query[*start + 1..*current].to_string();
- } else { // This is where we handle an identifier, or keyword
+ } else {
+ // This is where we handle an identifier, or keyword
token_type = TokenType::Identifier;
- Lexer::peek_identifier(
- query,
- start,
- current,
- &mut token_type);
-
+ Lexer::peek_identifier(query, start, current, &mut token_type);
+
// Keywords don't need literals
literal = if token_type == TokenType::Identifier {
query[*start..*current].to_string()
@@ -253,33 +213,19 @@ impl Lexer {
return Ok(Token::new(
token_type,
&literal,
- &query[slice_start..slice_end].to_string()
+ &query[slice_start..slice_end].to_string(),
));
}
- fn next_token(
- query: &String,
- current: &mut usize,
- start: &mut usize,
- ) -> Result {
+ fn next_token(query: &String, current: &mut usize, start: &mut usize) -> Result {
let c: char = query.chars().nth(*current).unwrap();
if SINGLE_START_TOKENS.contains(&c) {
- return Lexer::handle_single_token(
- query,
- c,
- current);
+ return Lexer::handle_single_token(query, c, current);
} else if SINGLE_DOUBLE_START_TOKENS.contains(&c) {
- return Lexer::handle_single_double_token(
- query,
- c,
- current);
+ return Lexer::handle_single_double_token(query, c, current);
} else {
- return Lexer::handle_default(
- query,
- c,
- current,
- start)
+ return Lexer::handle_default(query, c, current, start);
}
}
@@ -290,11 +236,8 @@ impl Lexer {
let mut current: usize = 0;
while current < query.len() {
- let token: Result = Lexer::next_token(
- query,
- &mut current,
- &mut start);
-
+ let token: Result = Lexer::next_token(query, &mut current, &mut start);
+
if token.is_err() {
warnings.push(token.err().unwrap());
} else {
@@ -307,28 +250,25 @@ impl Lexer {
Ok(Lexer {
tokens: toks
.into_iter()
- .filter(|x: &Token| x.token_type != TokenType::WhitespaceToken &&
- x.token_type != TokenType::NullToken
- )
- .collect()
+ .filter(|x: &Token| {
+ x.token_type != TokenType::WhitespaceToken
+ && x.token_type != TokenType::NullToken
+ })
+ .collect(),
})
}
}
impl fmt::Display for Lexer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "Lexer {{ Tokens: {:#?} }}",
- self.tokens)
+ write!(f, "Lexer {{ Tokens: {:#?} }}", self.tokens)
}
}
-pub fn scan_tokens(query: &String) -> Result{
+pub fn scan_tokens(query: &String) -> Result {
Lexer::new(query)
}
-
// Begin Lexer Tests
#[cfg(test)]
@@ -343,36 +283,12 @@ mod tests {
assert!(!test_lexer.is_err());
let expected: Vec = vec![
- Token::new(
- TokenType::OpenParen,
- &"".to_string(),
- &"(".to_string(),
- ),
- Token::new(
- TokenType::CloseParen,
- &"".to_string(),
- &")".to_string(),
- ),
- Token::new(
- TokenType::EoqToken,
- &"".to_string(),
- &"!".to_string(),
- ),
- Token::new(
- TokenType::EoqToken,
- &"".to_string(),
- &".".to_string(),
- ),
- Token::new(
- TokenType::EoqToken,
- &"".to_string(),
- &";".to_string(),
- ),
- Token::new(
- TokenType::Comma,
- &"".to_string(),
- &",".to_string(),
- ),
+ Token::new(TokenType::OpenParen, &"".to_string(), &"(".to_string()),
+ Token::new(TokenType::CloseParen, &"".to_string(), &")".to_string()),
+ Token::new(TokenType::EoqToken, &"".to_string(), &"!".to_string()),
+ Token::new(TokenType::EoqToken, &"".to_string(), &".".to_string()),
+ Token::new(TokenType::EoqToken, &"".to_string(), &";".to_string()),
+ Token::new(TokenType::Comma, &"".to_string(), &",".to_string()),
];
assert_eq!(expected, test_lexer.unwrap().tokens);
@@ -382,35 +298,15 @@ mod tests {
fn unit_test_basic_single_double_tokens() {
let input: String = "<><=>==".to_string();
let test_lexer: Result = Lexer::new(&input);
-
+
assert!(!test_lexer.is_err());
let expected: Vec = vec![
- Token::new(
- TokenType::Lt,
- &"".to_string(),
- &"<".to_string(),
- ),
- Token::new(
- TokenType::Gt,
- &"".to_string(),
- &">".to_string(),
- ),
- Token::new(
- TokenType::Lte,
- &"".to_string(),
- &"<=".to_string(),
- ),
- Token::new(
- TokenType::Gte,
- &"".to_string(),
- &">=".to_string(),
- ),
- Token::new(
- TokenType::Equal,
- &"".to_string(),
- &"=".to_string(),
- ),
+ Token::new(TokenType::Lt, &"".to_string(), &"<".to_string()),
+ Token::new(TokenType::Gt, &"".to_string(), &">".to_string()),
+ Token::new(TokenType::Lte, &"".to_string(), &"<=".to_string()),
+ Token::new(TokenType::Gte, &"".to_string(), &">=".to_string()),
+ Token::new(TokenType::Equal, &"".to_string(), &"=".to_string()),
];
assert_eq!(expected, test_lexer.unwrap().tokens);
@@ -423,13 +319,11 @@ mod tests {
assert!(!test_lexer.is_err());
- let expected: Vec = vec![
- Token::new(
- TokenType::StringLiteral,
- &"Hi1234".to_string(),
- &"\"Hi1234\"".to_string(),
- ),
- ];
+ let expected: Vec = vec![Token::new(
+ TokenType::StringLiteral,
+ &"Hi1234".to_string(),
+ &"\"Hi1234\"".to_string(),
+ )];
assert_eq!(expected, test_lexer.unwrap().tokens);
}
@@ -441,13 +335,11 @@ mod tests {
assert!(!test_lexer.is_err());
- let expected: Vec = vec![
- Token::new(
- TokenType::UnknownToken,
- &"".to_string(),
- &"\"Hi1234".to_string(),
- ),
- ];
+ let expected: Vec = vec![Token::new(
+ TokenType::UnknownToken,
+ &"".to_string(),
+ &"\"Hi1234".to_string(),
+ )];
assert_eq!(expected, test_lexer.unwrap().tokens);
}
@@ -456,16 +348,14 @@ mod tests {
fn unit_test_basic_string_literal_error_2() {
let input: String = "\"".to_string();
let test_lexer: Result = Lexer::new(&input);
-
+
assert!(!test_lexer.is_err());
-
- let expected: Vec = vec![
- Token::new(
- TokenType::UnknownToken,
- &"".to_string(),
- &"\"".to_string(),
- ),
- ];
+
+ let expected: Vec = vec![Token::new(
+ TokenType::UnknownToken,
+ &"".to_string(),
+ &"\"".to_string(),
+ )];
assert_eq!(expected, test_lexer.unwrap().tokens);
}
@@ -477,13 +367,11 @@ mod tests {
assert!(!test_lexer.is_err());
- let expected: Vec = vec![
- Token::new(
- TokenType::StringLiteral,
- &"".to_string(),
- &"\"\"".to_string(),
- ),
- ];
+ let expected: Vec = vec![Token::new(
+ TokenType::StringLiteral,
+ &"".to_string(),
+ &"\"\"".to_string(),
+ )];
assert_eq!(expected, test_lexer.unwrap().tokens);
}
@@ -495,13 +383,11 @@ mod tests {
assert!(!test_lexer.is_err());
- let expected: Vec = vec![
- Token::new(
- TokenType::NumberLiteral,
- &"1234".to_string(),
- &"1234".to_string(),
- ),
- ];
+ let expected: Vec = vec![Token::new(
+ TokenType::NumberLiteral,
+ &"1234".to_string(),
+ &"1234".to_string(),
+ )];
assert_eq!(expected, test_lexer.unwrap().tokens);
}
@@ -510,20 +396,16 @@ mod tests {
fn unit_test_edge_number_literal_decimal() {
let input: String = "12.".to_string();
let test_lexer: Result = Lexer::new(&input);
-
+
assert!(!test_lexer.is_err());
-
+
let expected: Vec = vec![
Token::new(
TokenType::NumberLiteral,
&"12".to_string(),
&"12".to_string(),
),
- Token::new(
- TokenType::EoqToken,
- &"".to_string(),
- &".".to_string(),
- ),
+ Token::new(TokenType::EoqToken, &"".to_string(), &".".to_string()),
];
assert_eq!(expected, test_lexer.unwrap().tokens);
@@ -533,16 +415,14 @@ mod tests {
fn unit_test_basic_number_literal_decimal() {
let input: String = "12.34".to_string();
let test_lexer: Result = Lexer::new(&input);
-
+
assert!(!test_lexer.is_err());
-
- let expected: Vec = vec![
- Token::new(
- TokenType::NumberLiteral,
- &"12.34".to_string(),
- &"12.34".to_string(),
- ),
- ];
+
+ let expected: Vec = vec![Token::new(
+ TokenType::NumberLiteral,
+ &"12.34".to_string(),
+ &"12.34".to_string(),
+ )];
assert_eq!(expected, test_lexer.unwrap().tokens);
}
@@ -554,13 +434,11 @@ mod tests {
assert!(!test_lexer.is_err());
- let expected: Vec = vec![
- Token::new(
- TokenType::NumberLiteral,
- &"-12.34".to_string(),
- &"-12.34".to_string(),
- ),
- ];
+ let expected: Vec = vec![Token::new(
+ TokenType::NumberLiteral,
+ &"-12.34".to_string(),
+ &"-12.34".to_string(),
+ )];
assert_eq!(expected, test_lexer.unwrap().tokens);
}
@@ -569,35 +447,23 @@ mod tests {
fn unit_test_basic_keyword_literal_1() {
let input: String = "get all from place.".to_string();
let test_lexer: Result = Lexer::new(&input);
-
+
assert!(!test_lexer.is_err());
let expected: Vec = vec![
- Token::new(
- TokenType::Get,
- &"".to_string(),
- &"get".to_string(),
- ),
+ Token::new(TokenType::Get, &"".to_string(), &"get".to_string()),
Token::new(
TokenType::WildcardKeyword,
&"".to_string(),
&"all".to_string(),
),
- Token::new(
- TokenType::From,
- &"".to_string(),
- &"from".to_string(),
- ),
+ Token::new(TokenType::From, &"".to_string(), &"from".to_string()),
Token::new(
TokenType::Identifier,
&"place".to_string(),
&"place".to_string(),
),
- Token::new(
- TokenType::EoqToken,
- &"".to_string(),
- &".".to_string(),
- ),
+ Token::new(TokenType::EoqToken, &"".to_string(), &".".to_string()),
];
assert_eq!(expected, test_lexer.unwrap().tokens);
@@ -605,27 +471,21 @@ mod tests {
#[test]
fn unit_test_basic_keyword_literal_2() {
- let input: String = "retrieve everything from place whenever name is \"Coffee\" and cost >= 2.43!".to_string();
+ let input: String =
+ "retrieve everything from place whenever name is \"Coffee\" and cost >= 2.43!"
+ .to_string();
let test_lexer: Result = Lexer::new(&input);
-
+
assert!(!test_lexer.is_err());
let expected: Vec = vec![
- Token::new(
- TokenType::Get,
- &"".to_string(),
- &"retrieve".to_string(),
- ),
+ Token::new(TokenType::Get, &"".to_string(), &"retrieve".to_string()),
Token::new(
TokenType::WildcardKeyword,
&"".to_string(),
&"everything".to_string(),
),
- Token::new(
- TokenType::From,
- &"".to_string(),
- &"from".to_string(),
- ),
+ Token::new(TokenType::From, &"".to_string(), &"from".to_string()),
Token::new(
TokenType::Identifier,
&"place".to_string(),
@@ -641,43 +501,27 @@ mod tests {
&"name".to_string(),
&"name".to_string(),
),
- Token::new(
- TokenType::Equal,
- &"".to_string(),
- &"is".to_string(),
- ),
+ Token::new(TokenType::Equal, &"".to_string(), &"is".to_string()),
Token::new(
TokenType::StringLiteral,
&"Coffee".to_string(),
&"\"Coffee\"".to_string(),
),
- Token::new(
- TokenType::And,
- &"".to_string(),
- &"and".to_string()
- ),
+ Token::new(TokenType::And, &"".to_string(), &"and".to_string()),
Token::new(
TokenType::Identifier,
&"cost".to_string(),
- &"cost".to_string()
- ),
- Token::new(
- TokenType::Gte,
- &"".to_string(),
- &">=".to_string()
+ &"cost".to_string(),
),
+ Token::new(TokenType::Gte, &"".to_string(), &">=".to_string()),
Token::new(
TokenType::NumberLiteral,
&"2.43".to_string(),
&"2.43".to_string(),
),
- Token::new(
- TokenType::EoqToken,
- &"".to_string(),
- &"!".to_string(),
- ),
+ Token::new(TokenType::EoqToken, &"".to_string(), &"!".to_string()),
];
assert_eq!(expected, test_lexer.unwrap().tokens);
}
-}
\ No newline at end of file
+}
diff --git a/src/language/mod.rs b/src/language/mod.rs
index 7111d2b..113ec24 100644
--- a/src/language/mod.rs
+++ b/src/language/mod.rs
@@ -1,3 +1,3 @@
+pub mod lexer;
pub mod parser;
pub mod tokens;
-pub mod lexer;
\ No newline at end of file
diff --git a/src/language/parser/conditional.rs b/src/language/parser/conditional.rs
index 58e5bf5..15b91c0 100644
--- a/src/language/parser/conditional.rs
+++ b/src/language/parser/conditional.rs
@@ -2,14 +2,10 @@ use std::fmt;
use crate::{
language::{
- parser::helpers::{
- get_tab, valid_until_warning, validate_length
- },
- tokens::{
- Token, TokenType
- }
+ parser::helpers::{get_tab, valid_until_warning, validate_length},
+ tokens::{Token, TokenType},
},
- utils::logger
+ utils::logger,
};
#[derive(Debug, PartialEq)]
@@ -24,7 +20,7 @@ pub struct ConditionNode {
pub enum ConditionChild {
Op(Box),
Expr(Box),
- Bool(Box)
+ Bool(Box),
}
#[derive(Debug, PartialEq)]
@@ -34,7 +30,7 @@ pub struct OperandNode {
_ls: ConditionChild,
_rs: ConditionChild,
- _depth: u16
+ _depth: u16,
}
#[derive(Debug, PartialEq)]
@@ -43,26 +39,24 @@ pub struct ExpressionNode {
_comparison_operator: Token,
_literal: Token,
- _depth: u16
+ _depth: u16,
}
#[derive(Debug, PartialEq)]
pub struct BoolNode {
_value: bool,
- _depth: u16
+ _depth: u16,
}
-fn update_depths(
- node: &mut ConditionChild
-) -> () {
+fn update_depths(node: &mut ConditionChild) -> () {
match node {
ConditionChild::Op(state) => {
state._depth += 1;
update_depths(&mut state._ls);
update_depths(&mut state._rs);
return;
- },
+ }
ConditionChild::Bool(state) => state._depth += 1,
ConditionChild::Expr(state) => state._depth += 1,
}
@@ -78,32 +72,34 @@ fn handle_open_paren(
closing_or: &mut bool,
) -> Result {
let mut ret: ConditionChild = ConditionChild::Op(Box::new(OperandNode {
- _type: "OR".to_string(),
- _depth: depth,
- _ls: match recurse_down(
- tokens,
- idx,
- depth + 1,
- "OR".to_string(),
- finished,
- closing_paren,
- opened_paren,
- closing_or) {
- Ok(node) => node,
- Err(msg) => return Err(msg)
- },
- _rs: match recurse_down(
- tokens,
- idx,
- depth + 1,
- "OR".to_string(),
- finished,
- closing_paren,
- opened_paren,
- closing_or) {
- Ok(node) => node,
- Err(msg) => return Err(msg)
- }
+ _type: "OR".to_string(),
+ _depth: depth,
+ _ls: match recurse_down(
+ tokens,
+ idx,
+ depth + 1,
+ "OR".to_string(),
+ finished,
+ closing_paren,
+ opened_paren,
+ closing_or,
+ ) {
+ Ok(node) => node,
+ Err(msg) => return Err(msg),
+ },
+ _rs: match recurse_down(
+ tokens,
+ idx,
+ depth + 1,
+ "OR".to_string(),
+ finished,
+ closing_paren,
+ opened_paren,
+ closing_or,
+ ) {
+ Ok(node) => node,
+ Err(msg) => return Err(msg),
+ },
}));
match tokens[*idx].token_type {
@@ -117,27 +113,26 @@ fn handle_open_paren(
update_depths(&mut ret);
}
- return Ok(ConditionChild::Op(Box::new(
- OperandNode {
- _type: "AND".to_string(),
- _ls: ret,
- _rs: match recurse_down(
- tokens,
- idx,
- depth + 1,
- "AND".to_string(),
- finished,
- closing_paren,
- opened_paren,
- closing_or) {
- Ok(node) => node,
- Err(msg) => return Err(msg)
- },
-
- _depth: depth
- }
- )));
- },
+ return Ok(ConditionChild::Op(Box::new(OperandNode {
+ _type: "AND".to_string(),
+ _ls: ret,
+ _rs: match recurse_down(
+ tokens,
+ idx,
+ depth + 1,
+ "AND".to_string(),
+ finished,
+ closing_paren,
+ opened_paren,
+ closing_or,
+ ) {
+ Ok(node) => node,
+ Err(msg) => return Err(msg),
+ },
+
+ _depth: depth,
+ })));
+ }
TokenType::Or => {
*idx += 1;
*closing_paren = false;
@@ -147,53 +142,59 @@ fn handle_open_paren(
if logger::LOG_LEVEL <= logger::DEBUG.0 {
update_depths(&mut ret);
}
-
- return Ok(ConditionChild::Op(Box::new(
- OperandNode {
- _type: "OR".to_string(),
- _ls: ret,
- _rs: match recurse_down(
- tokens,
- idx,
- depth + 1,
- "OR".to_string(),
- finished,
- closing_paren,
- opened_paren,
- closing_or) {
- Ok(node) => node,
- Err(msg) => return Err(msg)
- },
-
- _depth: depth
- }
- )));
- },
+
+ return Ok(ConditionChild::Op(Box::new(OperandNode {
+ _type: "OR".to_string(),
+ _ls: ret,
+ _rs: match recurse_down(
+ tokens,
+ idx,
+ depth + 1,
+ "OR".to_string(),
+ finished,
+ closing_paren,
+ opened_paren,
+ closing_or,
+ ) {
+ Ok(node) => node,
+ Err(msg) => return Err(msg),
+ },
+
+ _depth: depth,
+ })));
+ }
TokenType::CloseParen => {
if *opened_paren == 0 {
- return Err("Closing parentheses found with unmatched opening in conditional!".to_string());
+ return Err(
+ "Closing parentheses found with unmatched opening in conditional!".to_string(),
+ );
}
*opened_paren -= 1;
*idx += 1;
-
+
return Ok(ret);
- },
+ }
TokenType::PostProcessorEntrance => {
*closing_paren = false;
*finished = true;
-
+
return Ok(ret);
- },
+ }
TokenType::EoqToken => {
*closing_paren = false;
*finished = true;
return Ok(ret);
- },
- _ => return Err(format!("Something went wrong parsing a nested conditional: Expected a closing parentheses, \
+ }
+ _ => {
+ return Err(format!(
+ "Something went wrong parsing a nested conditional: Expected a closing parentheses, \
post-processor entrance, end-of-query, 'and' or 'or', \
- but got '{}' instead.", tokens[*idx].lexeme))
+ but got '{}' instead.",
+ tokens[*idx].lexeme
+ ));
+ }
}
}
@@ -207,32 +208,34 @@ fn handle_and(
closing_or: &mut bool,
) -> Result {
Ok(ConditionChild::Op(Box::new(OperandNode {
- _type: "AND".to_string(),
- _depth: depth,
- _ls: match recurse_down(
- tokens,
- idx,
- depth + 1,
- "AND".to_string(),
- finished,
- closing_paren,
- opened_paren,
- closing_or) {
- Ok(node) => node,
- Err(msg) => return Err(msg)
- },
- _rs: match recurse_down(
- tokens,
- idx,
- depth + 1,
- "AND".to_string(),
- finished,
- closing_paren,
- opened_paren,
- closing_or) {
- Ok(node) => node,
- Err(msg) => return Err(msg)
- }
+ _type: "AND".to_string(),
+ _depth: depth,
+ _ls: match recurse_down(
+ tokens,
+ idx,
+ depth + 1,
+ "AND".to_string(),
+ finished,
+ closing_paren,
+ opened_paren,
+ closing_or,
+ ) {
+ Ok(node) => node,
+ Err(msg) => return Err(msg),
+ },
+ _rs: match recurse_down(
+ tokens,
+ idx,
+ depth + 1,
+ "AND".to_string(),
+ finished,
+ closing_paren,
+ opened_paren,
+ closing_or,
+ ) {
+ Ok(node) => node,
+ Err(msg) => return Err(msg),
+ },
})))
}
@@ -245,56 +248,50 @@ fn handle_literal(
opened_paren: &mut u16,
closing_or: &mut bool,
) -> Result {
- let ls: ConditionChild = ConditionChild::Expr(
- Box::new(match ExpressionNode::parse(tokens, idx, depth + 1) {
+ let ls: ConditionChild = ConditionChild::Expr(Box::new(
+ match ExpressionNode::parse(tokens, idx, depth + 1) {
Ok(node) => node,
- Err(msg) => return Err(msg)
- })
- );
+ Err(msg) => return Err(msg),
+ },
+ ));
let rs: ConditionChild = match recurse_down(
- tokens,
- idx,
- depth + 1,
- "AND".to_string(),
- finished,
- closing_paren,
- opened_paren,
- closing_or) {
+ tokens,
+ idx,
+ depth + 1,
+ "AND".to_string(),
+ finished,
+ closing_paren,
+ opened_paren,
+ closing_or,
+ ) {
Ok(node) => node,
- Err(msg) => return Err(msg)
+ Err(msg) => return Err(msg),
};
- return Ok(
- ConditionChild::Op(Box::new(OperandNode {
- _type: "AND".to_string(),
- _depth: depth,
- _ls: ls,
- _rs: rs
- }))
- );
+ return Ok(ConditionChild::Op(Box::new(OperandNode {
+ _type: "AND".to_string(),
+ _depth: depth,
+ _ls: ls,
+ _rs: rs,
+ })));
}
-fn handle_close(
- parent_node: &String,
- depth: u16
-) -> ConditionChild {
+fn handle_close(parent_node: &String, depth: u16) -> ConditionChild {
// AND default to true, OR defaults to false
if *parent_node == "AND" {
- ConditionChild::Bool(Box::new(
- BoolNode { _value: true, _depth: depth }
- ))
+ ConditionChild::Bool(Box::new(BoolNode {
+ _value: true,
+ _depth: depth,
+ }))
} else {
- ConditionChild::Bool(Box::new(
- BoolNode { _value: false, _depth: depth }
- ))
+ ConditionChild::Bool(Box::new(BoolNode {
+ _value: false,
+ _depth: depth,
+ }))
}
}
-fn handle_or(
- closing_or: &mut bool,
- parent_node: &String,
- depth: u16,
-) -> ConditionChild {
+fn handle_or(closing_or: &mut bool, parent_node: &String, depth: u16) -> ConditionChild {
*closing_or = true;
handle_close(&parent_node, depth)
}
@@ -321,54 +318,84 @@ fn parse_child(
match tokens[*idx].token_type {
TokenType::And => {
*idx += 1;
- return handle_and(tokens, idx, depth, finished, closing_paren, opened_paren, closing_or);
- },
+ return handle_and(
+ tokens,
+ idx,
+ depth,
+ finished,
+ closing_paren,
+ opened_paren,
+ closing_or,
+ );
+ }
TokenType::Or => {
*idx += 1;
- return Ok(handle_or(closing_or, &parent_node, depth))
- },
+ return Ok(handle_or(closing_or, &parent_node, depth));
+ }
TokenType::OpenParen => {
*idx += 1;
*opened_paren += 1;
- return handle_open_paren(tokens, idx, depth, finished, closing_paren, opened_paren, closing_or);
- },
+ return handle_open_paren(
+ tokens,
+ idx,
+ depth,
+ finished,
+ closing_paren,
+ opened_paren,
+ closing_or,
+ );
+ }
TokenType::CloseParen => {
*idx += 1;
if *opened_paren == 0 {
- return Err("Closing parentheses found with unmatched opening in conditional!".to_string());
+ return Err(
+ "Closing parentheses found with unmatched opening in conditional!".to_string(),
+ );
}
*opened_paren -= 1;
- return Ok(handle_close_paren(closing_paren, &parent_node, depth))
- },
+ return Ok(handle_close_paren(closing_paren, &parent_node, depth));
+ }
TokenType::PostProcessorEntrance => {
if *closing_paren {
- return Err("Found end of conditional, but there are unclosed parentheses!".to_string())
+ return Err(
+ "Found end of conditional, but there are unclosed parentheses!".to_string(),
+ );
}
*finished = true;
- return Ok(handle_close(&parent_node, depth))
- },
+ return Ok(handle_close(&parent_node, depth));
+ }
TokenType::EoqToken => {
if *closing_paren {
- return Err("Found end of conditional, but there are unclosed parentheses!".to_string())
+ return Err(
+ "Found end of conditional, but there are unclosed parentheses!".to_string(),
+ );
}
*finished = true;
- return Ok(handle_close(&parent_node, depth))
- },
+ return Ok(handle_close(&parent_node, depth));
+ }
TokenType::Identifier => {
- return handle_literal(tokens, idx, depth, finished, closing_paren, opened_paren, closing_or);
- },
+ return handle_literal(
+ tokens,
+ idx,
+ depth,
+ finished,
+ closing_paren,
+ opened_paren,
+ closing_or,
+ );
+ }
_ => {
return Err(format!(
"Unexpected token found while parsing conditional expression -> {}",
tokens[*idx].lexeme
- ))
- }
+ ));
+ }
}
}
@@ -397,52 +424,57 @@ fn recurse_down(
if parent_node == "OR" {
*closing_or = false;
- return Ok(
- ConditionChild::Op(Box::new(OperandNode {
- _type: "OR".to_string(),
- _depth: depth,
- _ls: match recurse_down(
- tokens,
- idx,
- depth + 1,
- "OR".to_string(),
- finished,
- closing_paren,
- opened_paren,
- closing_or) {
- Ok(node) => node,
- Err(msg) => return Err(msg)
- },
- _rs: match recurse_down(
- tokens,
- idx,
- depth + 1,
- "OR".to_string(),
- finished,
- closing_paren,
- opened_paren,
- closing_or) {
- Ok(node) => node,
- Err(msg) => return Err(msg)
- }
- }))
- );
+ return Ok(ConditionChild::Op(Box::new(OperandNode {
+ _type: "OR".to_string(),
+ _depth: depth,
+ _ls: match recurse_down(
+ tokens,
+ idx,
+ depth + 1,
+ "OR".to_string(),
+ finished,
+ closing_paren,
+ opened_paren,
+ closing_or,
+ ) {
+ Ok(node) => node,
+ Err(msg) => return Err(msg),
+ },
+ _rs: match recurse_down(
+ tokens,
+ idx,
+ depth + 1,
+ "OR".to_string(),
+ finished,
+ closing_paren,
+ opened_paren,
+ closing_or,
+ ) {
+ Ok(node) => node,
+ Err(msg) => return Err(msg),
+ },
+ })));
}
return Ok(handle_close(&parent_node, depth));
}
- return parse_child(tokens, idx, depth, parent_node, finished, closing_paren, opened_paren, closing_or);
+ return parse_child(
+ tokens,
+ idx,
+ depth,
+ parent_node,
+ finished,
+ closing_paren,
+ opened_paren,
+ closing_or,
+ );
}
impl ConditionNode {
/// Reconstructs conditional literal from provided tokens
/// and bounds.
- fn reconstruct_literal (
- tokens: &Vec,
- start_idx: usize,
- end_idx: usize,
- ) -> String {
+ fn reconstruct_literal(tokens: &Vec, start_idx: usize, end_idx: usize) -> String {
// Reconstruct literal
let mut i: usize = start_idx;
let mut literal = String::new();
@@ -461,14 +493,12 @@ impl ConditionNode {
let mut j = i;
let mut parts = Vec::new();
- while parts.len() < 3
- {
+ while parts.len() < 3 {
parts.push(if tokens[j].token_type == TokenType::Equal {
"=".to_string()
} else {
tokens[j].lexeme.clone()
- }
- );
+ });
j += 1;
}
@@ -487,14 +517,14 @@ impl ConditionNode {
literal
}
-
+
/// Takes current node type and given the current location in the
/// query defined by the borrowed index, makes an attempt to parse
/// this node and associated subnodes for the Abstract Syntax Tree.
pub fn parse(
tokens: &Vec,
idx: &mut usize,
- depth: u16
+ depth: u16,
) -> Result {
let mut finished: bool = false;
let mut closing_paren: bool = false;
@@ -506,59 +536,54 @@ impl ConditionNode {
_type: "OR".to_string(),
_depth: depth + 1,
_ls: match recurse_down(
- tokens,
- idx,
- depth + 2,
- "OR".to_string(),
- &mut finished,
- &mut closing_paren,
- &mut opened_paren,
- &mut closing_or) {
+ tokens,
+ idx,
+ depth + 2,
+ "OR".to_string(),
+ &mut finished,
+ &mut closing_paren,
+ &mut opened_paren,
+ &mut closing_or,
+ ) {
Ok(node) => node,
- Err(msg) => return Err(msg)
+ Err(msg) => return Err(msg),
},
_rs: match recurse_down(
- tokens,
- idx,
- depth + 2,
- "OR".to_string(),
- &mut finished,
- &mut closing_paren,
- &mut opened_paren,
- &mut closing_or) {
+ tokens,
+ idx,
+ depth + 2,
+ "OR".to_string(),
+ &mut finished,
+ &mut closing_paren,
+ &mut opened_paren,
+ &mut closing_or,
+ ) {
Ok(node) => node,
- Err(msg) => return Err(msg)
- }
+ Err(msg) => return Err(msg),
+ },
}));
-
+
if opened_paren != 0 {
return Err("Conditional had unclosed parentheses".to_string());
}
-
- return Ok(
- ConditionNode {
- _condition: ret,
- _depth: depth,
- _literal: ConditionNode::reconstruct_literal(tokens, start_idx, *idx)
- }
- )
+ return Ok(ConditionNode {
+ _condition: ret,
+ _depth: depth,
+ _literal: ConditionNode::reconstruct_literal(tokens, start_idx, *idx),
+ });
}
/// Outputs current AST node transpiled with color
/// and it's raw query counterpart. Output are used by
/// the Transpiler REPL.
- pub fn transpile_color(
- &self
- ) -> String {
- format!("{}", self._literal)
+ pub fn transpile_color(&self) -> String {
+ format!("{}", self._literal)
}
/// Outputs current AST node transpiled to raw SQL
- pub fn transpile_raw(
- &self
- ) -> String {
- format!("{}", self._literal)
+ pub fn transpile_raw(&self) -> String {
+ format!("{}", self._literal)
}
}
@@ -569,7 +594,8 @@ impl ExpressionNode {
pub fn parse(
tokens: &Vec,
idx: &mut usize,
- depth: u16) -> Result {
+ depth: u16,
+ ) -> Result {
validate_length(tokens, &(*idx + 2), true)?;
let identifier: Token;
@@ -582,36 +608,38 @@ impl ExpressionNode {
} else {
return Err(valid_until_warning(tokens, idx));
}
-
+
if vec![
TokenType::Equal,
TokenType::Lte,
TokenType::Lt,
TokenType::Gt,
- TokenType::Gte
- ].contains(&tokens[*idx].token_type) {
+ TokenType::Gte,
+ ]
+ .contains(&tokens[*idx].token_type)
+ {
comparison_operator = tokens[*idx].clone();
*idx += 1;
} else {
return Err(valid_until_warning(tokens, idx));
}
-
+
if tokens[*idx].token_type == TokenType::StringLiteral
- || tokens[*idx].token_type == TokenType::NumberLiteral {
+ || tokens[*idx].token_type == TokenType::NumberLiteral
+ {
literal = tokens[*idx].clone();
*idx += 1;
} else {
return Err(valid_until_warning(tokens, idx));
}
-
+
return Ok(ExpressionNode {
_identifier: identifier,
_comparison_operator: comparison_operator,
_literal: literal,
- _depth: depth
- }
- )
+ _depth: depth,
+ });
}
}
@@ -630,7 +658,7 @@ impl fmt::Display for ConditionNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
-"\n{}(Condition){}{}",
+ "\n{}(Condition){}{}",
get_tab(self._depth),
get_tab(self._depth + 1),
self._condition
@@ -642,7 +670,7 @@ impl fmt::Display for OperandNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
-"\n{}(Operand::{}){}{}",
+ "\n{}(Operand::{}){}{}",
get_tab(self._depth),
self._type,
self._ls,
@@ -653,12 +681,7 @@ impl fmt::Display for OperandNode {
impl fmt::Display for BoolNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
-"\n{}(Bool::{})",
- get_tab(self._depth),
- self._value,
- )
+ write!(f, "\n{}(Bool::{})", get_tab(self._depth), self._value,)
}
}
@@ -666,7 +689,7 @@ impl fmt::Display for ExpressionNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
-"\n{}(Expression)
+ "\n{}(Expression)
{}variable: {:?}
{}operator: {:?}
{}value: {:?}",
@@ -681,7 +704,7 @@ impl fmt::Display for ExpressionNode {
}
}
-// Begin Conditional Tests
+// Begin Conditional Tests
#[cfg(test)]
mod tests {
use super::*;
@@ -689,85 +712,43 @@ mod tests {
#[test]
fn unit_test_expression_parsing_normal() {
let input: Vec = vec![
- Token::new(
- TokenType::Identifier,
- &"".to_string(),
- &"id".to_string(),
- ),
- Token::new(
- TokenType::Equal,
- &"".to_string(),
- &"is".to_string(),
- ),
- Token::new(
- TokenType::NumberLiteral,
- &"5".to_string(),
- &"5".to_string(),
- )
+ Token::new(TokenType::Identifier, &"".to_string(), &"id".to_string()),
+ Token::new(TokenType::Equal, &"".to_string(), &"is".to_string()),
+ Token::new(TokenType::NumberLiteral, &"5".to_string(), &"5".to_string()),
];
let mut idx: usize = 0;
let depth: u16 = 0;
let expected: ExpressionNode = ExpressionNode {
- _identifier: Token::new(
- TokenType::Identifier,
- &"".to_string(),
- &"id".to_string(),
- ),
- _comparison_operator: Token::new(
- TokenType::Equal,
- &"".to_string(),
- &"is".to_string(),
- ),
- _literal: Token::new(
- TokenType::NumberLiteral,
- &"5".to_string(),
- &"5".to_string(),
- ),
-
- _depth: 0
+ _identifier: Token::new(TokenType::Identifier, &"".to_string(), &"id".to_string()),
+ _comparison_operator: Token::new(TokenType::Equal, &"".to_string(), &"is".to_string()),
+ _literal: Token::new(TokenType::NumberLiteral, &"5".to_string(), &"5".to_string()),
+
+ _depth: 0,
};
- match ExpressionNode::parse(
- &input,
- &mut idx,
- depth) {
- Ok(val) => {
- assert_eq!(expected, val)
- },
- Err(err) => assert!(false, "Output errored out -> {}", err)
+ match ExpressionNode::parse(&input, &mut idx, depth) {
+ Ok(val) => {
+ assert_eq!(expected, val)
+ }
+ Err(err) => assert!(false, "Output errored out -> {}", err),
}
}
#[test]
fn unit_test_expression_parsing_error() {
let input: Vec = vec![
- Token::new(
- TokenType::Identifier,
- &"".to_string(),
- &"id".to_string(),
- ),
- Token::new(
- TokenType::Equal,
- &"".to_string(),
- &"is".to_string(),
- ),
- Token::new(
- TokenType::Get,
- &"".to_string(),
- &"get".to_string(),
- )
+ Token::new(TokenType::Identifier, &"".to_string(), &"id".to_string()),
+ Token::new(TokenType::Equal, &"".to_string(), &"is".to_string()),
+ Token::new(TokenType::Get, &"".to_string(), &"get".to_string()),
];
let mut idx: usize = 0;
let depth: u16 = 0;
- match ExpressionNode::parse(
- &input,
- &mut idx,
- depth) {
- Ok(_val) => assert!(false, "Output expected to error!"),
- Err(err) => assert!(true, "Output errored out -> {}", err)
+ match ExpressionNode::parse(&input, &mut idx, depth) {
+ Ok(_val) => assert!(false, "Output expected to error!"),
+ Err(err) => assert!(true, "Output errored out -> {}", err),
}
}
-}
\ No newline at end of file
+}
diff --git a/src/language/parser/database.rs b/src/language/parser/database.rs
index 5b3a588..d230c72 100644
--- a/src/language/parser/database.rs
+++ b/src/language/parser/database.rs
@@ -5,50 +5,43 @@ This handles our database mutators and accessors
Make the database db_name.
*/
-
-use std::{fmt, usize};
use crate::{
language::{
parser::{
- helpers::{
- get_tab, validate_length, peek_one
- },
+ helpers::{get_tab, peek_one, validate_length},
parser::ImpliedAction,
- }, tokens::{
- Token, TokenType
- }
+ },
+ tokens::{Token, TokenType},
},
utils::{
- colors::{
- AnsiColor,
- colorize,
- },
- logger
- }
+ colors::{AnsiColor, colorize},
+ logger,
+ },
};
+use std::{fmt, usize};
#[derive(Debug)]
-pub struct DatabaseNode {
+pub struct DatabaseNode {
pub _create: Option,
pub _destroy: Option,
pub _use: Option,
pub _show: Option,
_literal: String,
- _depth: u16
+ _depth: u16,
}
#[derive(Debug, PartialEq)]
pub struct ShowNode {
- _depth: u16
-}
+ _depth: u16,
+}
#[derive(Debug, PartialEq)]
pub struct DestroyNode {
databases: Vec,
_literal: String,
- _depth: u16
+ _depth: u16,
}
#[derive(Debug, PartialEq)]
@@ -56,7 +49,7 @@ pub struct CreateNode {
name: String,
_literal: String,
- _depth: u16
+ _depth: u16,
}
#[derive(Debug, PartialEq)]
@@ -64,22 +57,18 @@ pub struct UseNode {
name: String,
_literal: String,
- _depth: u16
+ _depth: u16,
}
impl DatabaseNode {
- pub fn parse(
+ pub fn parse(
tokens: &Vec,
idx: &mut usize,
depth: u16,
action: ImpliedAction,
- ) -> Result{
- validate_length(
- tokens ,
- idx,
- true)?;
+ ) -> Result {
+ validate_length(tokens, idx, true)?;
-
let mut database_node: DatabaseNode = DatabaseNode {
_create: None,
_destroy: None,
@@ -87,60 +76,53 @@ impl DatabaseNode {
_show: None,
_literal: {
- tokens[*idx - 2..*idx].iter()
+ tokens[*idx - 2..*idx]
+ .iter()
.map(|v| v.lexeme.as_str())
.collect::>()
.join(" ")
- },
- _depth: depth
+ },
+ _depth: depth,
};
match action {
ImpliedAction::Create => {
- database_node._create = match CreateNode::parse(
- tokens,
- idx,
- depth + 1
- ) {
+ database_node._create = match CreateNode::parse(tokens, idx, depth + 1) {
Ok(state) => Some(state),
- Err(msg) => return Err(msg)
- }},
+ Err(msg) => return Err(msg),
+ }
+ }
ImpliedAction::Delete => {
- database_node._destroy = match DestroyNode::parse(
- tokens,
- idx,
- depth + 1
- ) {
+ database_node._destroy = match DestroyNode::parse(tokens, idx, depth + 1) {
Ok(state) => Some(state),
- Err(msg) => return Err(msg)
- }},
+ Err(msg) => return Err(msg),
+ }
+ }
ImpliedAction::Show => {
- database_node._show = match ShowNode::parse(
- tokens,
- idx,
- depth + 1
- ) {
+ database_node._show = match ShowNode::parse(tokens, idx, depth + 1) {
Ok(state) => Some(state),
- Err(msg) => return Err(msg)
- }},
+ Err(msg) => return Err(msg),
+ }
+ }
ImpliedAction::Use => {
- database_node._use = match UseNode::parse(
- tokens,
- idx,
- depth + 1
- ) {
+ database_node._use = match UseNode::parse(tokens, idx, depth + 1) {
Ok(state) => Some(state),
- Err(msg) => return Err(msg)
- }},
- _ => return Err(format!(
- "Got unexpected action requested for database -> '{:?}'", action)
- )
- };
+ Err(msg) => return Err(msg),
+ }
+ }
+ _ => {
+ return Err(format!(
+ "Got unexpected action requested for database -> '{:?}'",
+ action
+ ));
+ }
+ };
if tokens[*idx].token_type != TokenType::EoqToken {
return Err(format!(
"Unexpected token '{}', expected end-of-query token by this point.",
- tokens[*idx].lexeme))
+ tokens[*idx].lexeme
+ ));
}
return Ok(database_node);
@@ -149,44 +131,27 @@ impl DatabaseNode {
/// Outputs current AST node transpiled with color
/// and it's raw query counterpart. Output are used by
/// the Transpiler REPL.
- pub fn transpile_color(
- &self,
- ) -> (String, String) {
- let pair: (String, (String, String)) = match (
- &self._create,
- &self._destroy,
- &self._show,
- &self._use,
- ) {
- (Some(op), _, _, _) => ("CREATE DATABASE ".to_string(), op.transpile_color()),
- (_, Some(op), _, _) => ("DROP DATABASE ".to_string(), op.transpile_color()),
- (_, _, Some(op), _) => ("SHOW DATABASES".to_string(), op.transpile_color()),
- (_, _, _, Some(op)) => ("USE DATABASE ".to_string(), op.transpile_color()),
- _ => logger::error("No database operation provided"),
- };
-
+ pub fn transpile_color(&self) -> (String, String) {
+ let pair: (String, (String, String)) =
+ match (&self._create, &self._destroy, &self._show, &self._use) {
+ (Some(op), _, _, _) => ("CREATE DATABASE ".to_string(), op.transpile_color()),
+ (_, Some(op), _, _) => ("DROP DATABASE ".to_string(), op.transpile_color()),
+ (_, _, Some(op), _) => ("SHOW DATABASES".to_string(), op.transpile_color()),
+ (_, _, _, Some(op)) => ("USE DATABASE ".to_string(), op.transpile_color()),
+ _ => logger::error("No database operation provided"),
+ };
+
(
- colorize(&self._literal, AnsiColor::Yellow) +
- if *&pair.1.0.len() != 0 { &" " } else { "" } +
- &pair.1.0,
- format!(
- "{}{}",
- colorize(&pair.0, AnsiColor::Yellow),
- &pair.1.1
- )
+ colorize(&self._literal, AnsiColor::Yellow)
+ + if *&pair.1.0.len() != 0 { &" " } else { "" }
+ + &pair.1.0,
+ format!("{}{}", colorize(&pair.0, AnsiColor::Yellow), &pair.1.1),
)
}
/// Outputs current AST node transpile to raw SQL.
- pub fn transpile_raw(
- &self
- ) -> String {
- match (
- &self._create,
- &self._destroy,
- &self._show,
- &self._use,
- ) {
+ pub fn transpile_raw(&self) -> String {
+ match (&self._create, &self._destroy, &self._show, &self._use) {
(Some(op), _, _, _) => "CREATE DATABASE ".to_string() + &op.transpile_raw(),
(_, Some(op), _, _) => "DROP DATABASE ".to_string() + &op.transpile_raw(),
(_, _, Some(op), _) => "SHOW DATABASES".to_string() + &op.transpile_raw(),
@@ -197,34 +162,31 @@ impl DatabaseNode {
}
impl CreateNode {
- pub fn parse(
- tokens: &Vec,
- idx: &mut usize,
- depth: u16,
- ) -> Result {
+ pub fn parse(tokens: &Vec, idx: &mut usize, depth: u16) -> Result {
validate_length(tokens, idx, true)?;
-
+
if tokens[*idx].token_type != TokenType::Identifier {
- return Err(format!("Expected identifier, got '{:?}' instead!", tokens[*idx].token_type));
+ return Err(format!(
+ "Expected identifier, got '{:?}' instead!",
+ tokens[*idx].token_type
+ ));
}
*idx += 1;
validate_length(tokens, idx, true)?;
-
+
return Ok(CreateNode {
name: tokens[*idx - 1].literal.clone(),
_literal: tokens[*idx - 1].lexeme.clone(),
- _depth: depth
- })
+ _depth: depth,
+ });
}
/// Outputs current AST node transpiled with color
/// and it's raw query counterpart. Output are used by
/// the Transpiler REPL.
- pub fn transpile_color(
- &self
- ) -> (String, String) {
+ pub fn transpile_color(&self) -> (String, String) {
(
colorize(&self._literal.as_str(), AnsiColor::Blue),
colorize(&self.name.as_str(), AnsiColor::Blue),
@@ -232,9 +194,7 @@ impl CreateNode {
}
/// Outputs current AST node transpiled to raw SQL.
- pub fn transpile_raw(
- &self
- ) -> String {
+ pub fn transpile_raw(&self) -> String {
self.name.clone()
}
}
@@ -246,116 +206,96 @@ impl DestroyNode {
idx: &mut usize,
) -> Result<(), String> {
let next_token: TokenType = peek_one(tokens, &idx);
- if
- next_token != TokenType::And &&
- next_token != TokenType::Comma
- {
+ if next_token != TokenType::And && next_token != TokenType::Comma {
if tokens[*idx].token_type == TokenType::Identifier {
dbs.push(tokens[*idx].literal.clone());
*idx += 1;
return Ok(());
}
- } else if
- next_token == TokenType::And ||
- next_token == TokenType::Comma
- {
+ } else if next_token == TokenType::And || next_token == TokenType::Comma {
if tokens[*idx].token_type == TokenType::Identifier {
dbs.push(tokens[*idx].literal.clone());
*idx += 2;
-
- DestroyNode::recurse_build(
- tokens,
- dbs,
- idx
- )?;
+
+ DestroyNode::recurse_build(tokens, dbs, idx)?;
return Ok(());
}
}
-
+
return Err("Something went wrong parsing database names, \
-make sure they're in a valid list notation.".to_string()
- );
+make sure they're in a valid list notation."
+ .to_string());
}
/// Takes current node type and given the current location in the
/// query defined by the borrowed index, makes an attempt to parse
/// this node and associated subnodes for the Abstract Syntax Tree.
- pub fn parse(
- tokens: &Vec,
- idx: &mut usize,
- depth: u16,
- ) -> Result {
+ pub fn parse(tokens: &Vec, idx: &mut usize, depth: u16) -> Result {
validate_length(tokens, idx, true)?;
- let start_idx: usize = *idx;
+ let start_idx: usize = *idx;
let mut db_names: Vec = vec![];
DestroyNode::recurse_build(tokens, &mut db_names, idx)?;
validate_length(tokens, idx, true)?;
-
+
return Ok(DestroyNode {
databases: db_names,
-
+
_literal: {
- tokens[start_idx..*idx].iter()
+ tokens[start_idx..*idx]
+ .iter()
.map(|v| v.lexeme.as_str())
.collect::>()
.join(" ")
},
- _depth: depth
- })
+ _depth: depth,
+ });
}
/// Outputs current AST node transpiled with color
/// and it's raw query counterpart. Output are used by
/// the Transpiler REPL.
- pub fn transpile_color(
- &self
- ) -> (String, String) {
+ pub fn transpile_color(&self) -> (String, String) {
(
colorize(&self._literal.as_str(), AnsiColor::Blue),
- colorize( &self.databases.join(", "), AnsiColor::Blue),
+ colorize(&self.databases.join(", "), AnsiColor::Blue),
)
}
/// Outputs current AST node transpile to raw SQL.
- pub fn transpile_raw(
- &self
- ) -> String {
+ pub fn transpile_raw(&self) -> String {
self.databases.join(", ")
}
}
impl UseNode {
- pub fn parse(
- tokens: &Vec,
- idx: &mut usize,
- depth: u16,
- ) -> Result {
+ pub fn parse(tokens: &Vec, idx: &mut usize, depth: u16) -> Result {
validate_length(tokens, idx, true)?;
-
+
if tokens[*idx].token_type != TokenType::Identifier {
- return Err(format!("Expected identifier, got '{:?}' instead!", tokens[*idx].token_type));
+ return Err(format!(
+ "Expected identifier, got '{:?}' instead!",
+ tokens[*idx].token_type
+ ));
}
*idx += 1;
validate_length(tokens, idx, true)?;
-
+
return Ok(UseNode {
name: tokens[*idx - 1].literal.clone(),
_literal: tokens[*idx - 1].lexeme.clone(),
- _depth: depth
- })
+ _depth: depth,
+ });
}
/// Outputs current AST node transpiled with color
/// and it's raw query counterpart. Output are used by
/// the Transpiler REPL.
- pub fn transpile_color(
- &self
- ) -> (String, String) {
+ pub fn transpile_color(&self) -> (String, String) {
(
colorize(&self._literal.as_str(), AnsiColor::Blue),
colorize(&self.name.as_str(), AnsiColor::Blue),
@@ -363,42 +303,27 @@ impl UseNode {
}
/// Outputs current AST node transpiled to raw SQL.
- pub fn transpile_raw(
- &self
- ) -> String {
+ pub fn transpile_raw(&self) -> String {
self.name.clone()
}
}
impl ShowNode {
- pub fn parse(
- tokens: &Vec,
- idx: &mut usize,
- depth: u16,
- ) -> Result {
+ pub fn parse(tokens: &Vec, idx: &mut usize, depth: u16) -> Result {
validate_length(tokens, idx, true)?;
-
- return Ok(ShowNode {
- _depth: depth
- })
+
+ return Ok(ShowNode { _depth: depth });
}
/// Outputs current AST node transpiled with color
/// and it's raw query counterpart. Output are used by
/// the Transpiler REPL.
- pub fn transpile_color(
- &self
- ) -> (String, String) {
- (
- "".to_string(),
- "".to_string(),
- )
+ pub fn transpile_color(&self) -> (String, String) {
+ ("".to_string(), "".to_string())
}
-
+
/// Outputs current AST node transpiled to raw SQL.
- pub fn transpile_raw(
- &self
- ) -> String {
+ pub fn transpile_raw(&self) -> String {
"".to_string()
}
}
@@ -408,7 +333,7 @@ impl fmt::Display for DatabaseNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
-"\n{}(DatabaseNode){}{}{}{}",
+ "\n{}(DatabaseNode){}{}{}{}",
get_tab(self._depth),
self._create
.as_ref()
@@ -426,7 +351,6 @@ impl fmt::Display for DatabaseNode {
.as_ref()
.map(|v| v as &dyn fmt::Display)
.unwrap_or(&""),
-
)
}
}
@@ -435,7 +359,7 @@ impl fmt::Display for CreateNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
-"\n{}(CreateNode)
+ "\n{}(CreateNode)
{}name: {}",
get_tab(self._depth),
get_tab(self._depth + 1),
@@ -448,7 +372,7 @@ impl fmt::Display for DestroyNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
-"\n{}(DestroyNode)
+ "\n{}(DestroyNode)
{}databases: {:?}",
get_tab(self._depth),
get_tab(self._depth + 1),
@@ -461,7 +385,7 @@ impl fmt::Display for UseNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
-"\n{}(UseNode)
+ "\n{}(UseNode)
{}name: {}",
get_tab(self._depth),
get_tab(self._depth + 1),
@@ -472,44 +396,32 @@ impl fmt::Display for UseNode {
impl fmt::Display for ShowNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
-"\n{}(ShowNode)",
- get_tab(self._depth),
- )
+ write!(f, "\n{}(ShowNode)", get_tab(self._depth),)
}
}
-
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unit_test_show_normal() {
- let input: Vec = vec![
- Token::new(
- TokenType::EoqToken,
- &"".to_string(),
- &"!".to_string(),
- ),
- ];
+ let input: Vec = vec![Token::new(
+ TokenType::EoqToken,
+ &"".to_string(),
+ &"!".to_string(),
+ )];
let mut idx: usize = 0;
let depth: u16 = 0;
- let expected: ShowNode = ShowNode {
- _depth: depth
- };
+ let expected: ShowNode = ShowNode { _depth: depth };
- match ShowNode::parse(
- &input,
- &mut idx,
- depth) {
- Ok(val) => assert_eq!(val, expected),
- Err(err) => assert!(false, "Output errored out -> {}", err)
+ match ShowNode::parse(&input, &mut idx, depth) {
+ Ok(val) => assert_eq!(val, expected),
+ Err(err) => assert!(false, "Output errored out -> {}", err),
}
}
-
+
#[test]
fn unit_test_use_normal() {
let input: Vec = vec![
@@ -518,11 +430,7 @@ mod tests {
&"test_db".to_string(),
&"test_db".to_string(),
),
- Token::new(
- TokenType::EoqToken,
- &"".to_string(),
- &"!".to_string(),
- ),
+ Token::new(TokenType::EoqToken, &"".to_string(), &"!".to_string()),
];
let mut idx: usize = 0;
@@ -530,45 +438,35 @@ mod tests {
let expected: UseNode = UseNode {
name: "test_db".to_string(),
_literal: "test_db".to_string(),
- _depth: depth
+ _depth: depth,
};
- match UseNode::parse(
- &input,
- &mut idx,
- depth) {
- Ok(val) => assert_eq!(val, expected),
- Err(err) => assert!(true, "Output errored out -> {}", err)
+ match UseNode::parse(&input, &mut idx, depth) {
+ Ok(val) => assert_eq!(val, expected),
+ Err(err) => assert!(true, "Output errored out -> {}", err),
}
}
-
+
#[test]
fn unit_test_use_error() {
let input: Vec = vec![
- Token::new(
- TokenType::Get,
- &"get".to_string(),
- &"get".to_string(),
- ),
- Token::new(
- TokenType::EoqToken,
- &"".to_string(),
- &"!".to_string(),
- ),
+ Token::new(TokenType::Get, &"get".to_string(), &"get".to_string()),
+ Token::new(TokenType::EoqToken, &"".to_string(), &"!".to_string()),
];
let mut idx: usize = 0;
let depth: u16 = 0;
- match UseNode::parse(
- &input,
- &mut idx,
- depth) {
- Ok(val) => assert!(false, "Output was expected to error but returned -> {}", val),
- Err(_) => assert!(true, "Output expected to error and did.")
- }
+ match UseNode::parse(&input, &mut idx, depth) {
+ Ok(val) => assert!(
+ false,
+ "Output was expected to error but returned -> {}",
+ val
+ ),
+ Err(_) => assert!(true, "Output expected to error and did."),
+ }
}
-
+
#[test]
fn unit_test_create_normal() {
let input: Vec = vec![
@@ -577,11 +475,7 @@ mod tests {
&"test_db".to_string(),
&"test_db".to_string(),
),
- Token::new(
- TokenType::EoqToken,
- &"".to_string(),
- &"!".to_string(),
- ),
+ Token::new(TokenType::EoqToken, &"".to_string(), &"!".to_string()),
];
let mut idx: usize = 0;
@@ -589,43 +483,33 @@ mod tests {
let expected: CreateNode = CreateNode {
name: "test_db".to_string(),
_literal: "test_db".to_string(),
- _depth: depth
+ _depth: depth,
};
- match CreateNode::parse(
- &input,
- &mut idx,
- depth) {
- Ok(val) => assert_eq!(val, expected),
- Err(err) => assert!(false, "Output errored out -> {}", err)
+ match CreateNode::parse(&input, &mut idx, depth) {
+ Ok(val) => assert_eq!(val, expected),
+ Err(err) => assert!(false, "Output errored out -> {}", err),
}
}
-
+
#[test]
fn unit_test_create_error() {
let input: Vec = vec![
- Token::new(
- TokenType::Get,
- &"get".to_string(),
- &"get".to_string(),
- ),
- Token::new(
- TokenType::EoqToken,
- &"".to_string(),
- &"!".to_string(),
- ),
+ Token::new(TokenType::Get, &"get".to_string(), &"get".to_string()),
+ Token::new(TokenType::EoqToken, &"".to_string(), &"!".to_string()),
];
let mut idx: usize = 0;
let depth: u16 = 0;
- match CreateNode::parse(
- &input,
- &mut idx,
- depth) {
- Ok(val) => assert!(false, "Output was expected to error but returned -> {}", val),
- Err(_) => assert!(true, "Output expected to error and did.")
- }
+ match CreateNode::parse(&input, &mut idx, depth) {
+ Ok(val) => assert!(
+ false,
+ "Output was expected to error but returned -> {}",
+ val
+ ),
+ Err(_) => assert!(true, "Output expected to error and did."),
+ }
}
#[test]
@@ -636,11 +520,7 @@ mod tests {
&"test_db".to_string(),
&"test_db".to_string(),
),
- Token::new(
- TokenType::EoqToken,
- &"".to_string(),
- &"!".to_string(),
- ),
+ Token::new(TokenType::EoqToken, &"".to_string(), &"!".to_string()),
];
let mut idx: usize = 0;
@@ -648,18 +528,15 @@ mod tests {
let expected: DestroyNode = DestroyNode {
databases: vec!["test_db".to_string()],
_literal: "test_db".to_string(),
- _depth: depth
+ _depth: depth,
};
- match DestroyNode::parse(
- &input,
- &mut idx,
- depth) {
- Ok(val) => assert_eq!(val, expected),
- Err(err) => assert!(false, "Output errored out -> {}", err)
+ match DestroyNode::parse(&input, &mut idx, depth) {
+ Ok(val) => assert_eq!(val, expected),
+ Err(err) => assert!(false, "Output errored out -> {}", err),
}
}
-
+
#[test]
fn unit_test_destroy_normal_multiple() {
let input: Vec = vec![
@@ -668,31 +545,19 @@ mod tests {
&"test_db_1".to_string(),
&"test_db_1".to_string(),
),
- Token::new(
- TokenType::Comma,
- &",".to_string(),
- &",".to_string(),
- ),
+ Token::new(TokenType::Comma, &",".to_string(), &",".to_string()),
Token::new(
TokenType::Identifier,
&"test_db_2".to_string(),
&"test_db_2".to_string(),
),
- Token::new(
- TokenType::Comma,
- &",".to_string(),
- &",".to_string(),
- ),
+ Token::new(TokenType::Comma, &",".to_string(), &",".to_string()),
Token::new(
TokenType::Identifier,
&"test_db_3".to_string(),
&"test_db_3".to_string(),
),
- Token::new(
- TokenType::EoqToken,
- &"".to_string(),
- &"!".to_string(),
- ),
+ Token::new(TokenType::EoqToken, &"".to_string(), &"!".to_string()),
];
let mut idx: usize = 0;
@@ -702,17 +567,14 @@ mod tests {
"test_db_1".to_string(),
"test_db_2".to_string(),
"test_db_3".to_string(),
- ],
+ ],
_literal: "test_db_1 , test_db_2 , test_db_3".to_string(),
- _depth: depth
+ _depth: depth,
};
- match DestroyNode::parse(
- &input,
- &mut idx,
- depth) {
- Ok(val) => assert_eq!(val, expected),
- Err(err) => assert!(false, "Output errored out -> {}", err)
+ match DestroyNode::parse(&input, &mut idx, depth) {
+ Ok(val) => assert_eq!(val, expected),
+ Err(err) => assert!(false, "Output errored out -> {}", err),
}
}
@@ -724,16 +586,8 @@ mod tests {
&"test_db_1".to_string(),
&"test_db_1".to_string(),
),
- Token::new(
- TokenType::Comma,
- &",".to_string(),
- &",".to_string(),
- ),
- Token::new(
- TokenType::Comma,
- &",".to_string(),
- &",".to_string(),
- ),
+ Token::new(TokenType::Comma, &",".to_string(), &",".to_string()),
+ Token::new(TokenType::Comma, &",".to_string(), &",".to_string()),
Token::new(
TokenType::Identifier,
&"test_db_2".to_string(),
@@ -744,38 +598,32 @@ mod tests {
let mut idx: usize = 0;
let depth: u16 = 0;
- match DestroyNode::parse(
- &input,
- &mut idx,
- depth) {
- Ok(val) => assert!(false, "Output was expected to error but returned -> {}", val),
- Err(_) => assert!(true, "Output expected to error and did.")
- }
+ match DestroyNode::parse(&input, &mut idx, depth) {
+ Ok(val) => assert!(
+ false,
+ "Output was expected to error but returned -> {}",
+ val
+ ),
+ Err(_) => assert!(true, "Output expected to error and did."),
+ }
}
#[test]
fn unit_test_destroy_error() {
let input: Vec = vec![
- Token::new(
- TokenType::Get,
- &"get".to_string(),
- &"get".to_string(),
- ),
- Token::new(
- TokenType::EoqToken,
- &"".to_string(),
- &"!".to_string(),
- ),
+ Token::new(TokenType::Get, &"get".to_string(), &"get".to_string()),
+ Token::new(TokenType::EoqToken, &"".to_string(), &"!".to_string()),
];
let mut idx: usize = 0;
let depth: u16 = 0;
- match DestroyNode::parse(
- &input,
- &mut idx,
- depth) {
- Ok(val) => assert!(false, "Output was expected to error but returned -> {}", val),
- Err(_) => assert!(true, "Output expected to error and did.")
- }
+ match DestroyNode::parse(&input, &mut idx, depth) {
+ Ok(val) => assert!(
+ false,
+ "Output was expected to error but returned -> {}",
+ val
+ ),
+ Err(_) => assert!(true, "Output expected to error and did."),
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/language/parser/get.rs b/src/language/parser/get.rs
index d8db02e..3b51883 100644
--- a/src/language/parser/get.rs
+++ b/src/language/parser/get.rs
@@ -1,23 +1,15 @@
-use std::{fmt, usize};
use crate::{
language::{
parser::{
conditional::ConditionNode,
- helpers::{
- get_tab, peek_one, validate_length
- },
- postprocessor::{
- PostProcessorNode
- },
- }, tokens::{
- Token, TokenType
- }
+ helpers::{get_tab, peek_one, validate_length},
+ postprocessor::PostProcessorNode,
+ },
+ tokens::{Token, TokenType},
},
- utils::colors::{
- colorize,
- AnsiColor
- }
+ utils::colors::{AnsiColor, colorize},
};
+use std::{fmt, usize};
#[derive(Debug)]
pub struct GetNode {
@@ -26,7 +18,7 @@ pub struct GetNode {
_filter: Option,
_postprocessor: Option,
- _depth: u16
+ _depth: u16,
}
#[derive(Debug, PartialEq)]
@@ -34,8 +26,8 @@ pub struct TableNode {
table_name: String,
_literal: String,
- _depth: u16
-}
+ _depth: u16,
+}
#[derive(Debug, PartialEq)]
pub struct ColumnNode {
@@ -43,7 +35,7 @@ pub struct ColumnNode {
is_wildcard: bool,
_literal: String,
- _depth: u16
+ _depth: u16,
}
#[derive(Debug, PartialEq)]
@@ -51,86 +43,66 @@ pub struct FilterNode {
condition: ConditionNode,
_literal: String,
- _depth: u16
+ _depth: u16,
}
impl GetNode {
- pub fn parse(
- tokens: &Vec,
- idx: &mut usize,
- depth: u16) -> Result{
- validate_length(
- tokens,
- idx,
- true)?;
-
- let columns: ColumnNode = match ColumnNode::parse(
- tokens,
- idx,
- depth + 1
- ) {
+ pub fn parse(tokens: &Vec, idx: &mut usize, depth: u16) -> Result {
+ validate_length(tokens, idx, true)?;
+
+ let columns: ColumnNode = match ColumnNode::parse(tokens, idx, depth + 1) {
Ok(column) => column,
- Err(err) => return Err(err)
+ Err(err) => return Err(err),
};
- let table: TableNode = match TableNode::parse(
- tokens,
- idx,
- depth + 1
- ) {
+ let table: TableNode = match TableNode::parse(tokens, idx, depth + 1) {
Ok(table) => table,
Err(err) => return Err(err),
};
- let filter: Option = match FilterNode::parse(
- tokens,
- idx,
- depth + 1
- ) {
+ let filter: Option = match FilterNode::parse(tokens, idx, depth + 1) {
Ok(filter) => filter,
Err(err) => return Err(err),
};
- let postprocessor: Option = match PostProcessorNode::parse(
- tokens,
- idx,
- depth + 1
- ) {
- Ok(postprocessor) => postprocessor,
- Err(msg) => return Err(msg)
- };
+ let postprocessor: Option =
+ match PostProcessorNode::parse(tokens, idx, depth + 1) {
+ Ok(postprocessor) => postprocessor,
+ Err(msg) => return Err(msg),
+ };
validate_length(tokens, idx, true)?;
if tokens[*idx].token_type != TokenType::EoqToken {
- return Err(format!("Unexpected token '{}', expected end-of-query token by this point.", tokens[*idx].lexeme))
+ return Err(format!(
+ "Unexpected token '{}', expected end-of-query token by this point.",
+ tokens[*idx].lexeme
+ ));
}
Ok(GetNode {
_table: table,
_columns: columns,
_filter: filter,
- _postprocessor: postprocessor,
+ _postprocessor: postprocessor,
- _depth: depth
+ _depth: depth,
})
-}
+ }
/// Outputs current AST node transpiled with color
/// and it's raw query counterpart. Output are used by
/// the Transpiler REPL.
- pub fn transpile_color(
- &self,
- ) -> (String, String) {
+ pub fn transpile_color(&self) -> (String, String) {
let columns: (String, String) = self._columns.transpile_color();
let table: (String, String) = self._table.transpile_color();
let filter: Option<(String, String)> = match &self._filter {
Some(filter) => Some(filter.transpile_color()),
- None => None
+ None => None,
};
let postprocessor: Option<(String, String)> = match &self._postprocessor {
Some(postprocessor) => Some(postprocessor.transpile_color()),
- None => None
+ None => None,
};
(
@@ -140,36 +112,34 @@ impl GetNode {
filter.as_ref().map(|f| f.0.clone()),
postprocessor.as_ref().map(|f| f.0.clone()),
]
- .into_iter()
- .flatten()
- .collect::>()
- .join(" "),
+ .into_iter()
+ .flatten()
+ .collect::>()
+ .join(" "),
[
Some(columns.1),
Some(table.1),
filter.as_ref().map(|f| f.1.clone()),
postprocessor.as_ref().map(|f| f.1.clone()),
]
- .into_iter()
- .flatten()
- .collect::>()
- .join(" "),
+ .into_iter()
+ .flatten()
+ .collect::>()
+ .join(" "),
)
}
/// Outputs current AST node transpiled to raw SQL
- pub fn transpile_raw(
- &self
- ) -> String {
+ pub fn transpile_raw(&self) -> String {
let columns: String = self._columns.transpile_raw();
let table: String = self._table.transpile_raw();
let filter: Option = match &self._filter {
Some(filter) => Some(filter.transpile_raw()),
- None => None
+ None => None,
};
let postprocessor: Option = match &self._postprocessor {
Some(postprocessor) => Some(postprocessor.transpile_raw()),
- None => None
+ None => None,
};
[
@@ -178,10 +148,10 @@ impl GetNode {
filter.as_ref().map(|f| f.clone()),
postprocessor.as_ref().map(|f| f.clone()),
]
- .into_iter()
- .flatten()
- .collect::>()
- .join(" ")
+ .into_iter()
+ .flatten()
+ .collect::>()
+ .join(" ")
}
}
@@ -189,15 +159,12 @@ impl TableNode {
/// Takes current node type and given the current location in the
/// query defined by the borrowed index, makes an attempt to parse
/// this node and associated subnodes for the Abstract Syntax Tree.
- pub fn parse(
- tokens: &Vec,
- idx: &mut usize,
- depth: u16
- ) -> Result {
+ pub fn parse(tokens: &Vec, idx: &mut usize, depth: u16) -> Result {
let start_idx: usize = *idx;
- if tokens[*idx].token_type == TokenType::From &&
- peek_one(tokens, idx) == TokenType::Identifier {
+ if tokens[*idx].token_type == TokenType::From
+ && peek_one(tokens, idx) == TokenType::Identifier
+ {
*idx += 1;
} else {
return Err(format!(
@@ -208,38 +175,33 @@ impl TableNode {
*idx += 1;
- return Ok(
- TableNode {
- table_name: tokens[*idx - 1].literal.clone(),
-
- _literal: {
- tokens[start_idx..*idx].iter()
- .map(|v| v.lexeme.as_str())
- .collect::>()
- .join(" ")
- },
+ return Ok(TableNode {
+ table_name: tokens[*idx - 1].literal.clone(),
+ _literal: {
+ tokens[start_idx..*idx]
+ .iter()
+ .map(|v| v.lexeme.as_str())
+ .collect::>()
+ .join(" ")
+ },
- _depth: depth
+ _depth: depth,
});
}
/// Outputs current AST node transpiled with color
/// and it's raw query counterpart. Output are used by
/// the Transpiler REPL.
- pub fn transpile_color(
- &self
- ) -> (String, String) {
+ pub fn transpile_color(&self) -> (String, String) {
(
colorize(&self._literal, AnsiColor::Blue),
- colorize(&format!("FROM {}", self.table_name), AnsiColor::Blue)
+ colorize(&format!("FROM {}", self.table_name), AnsiColor::Blue),
)
}
/// Outputs current AST node transpiled to raw SQL
- pub fn transpile_raw(
- &self
- ) -> String {
+ pub fn transpile_raw(&self) -> String {
format!("FROM {}", self.table_name)
}
}
@@ -251,41 +213,31 @@ impl ColumnNode {
idx: &mut usize,
) -> Result<(), String> {
let next_token: TokenType = peek_one(tokens, &idx);
- if next_token != TokenType::And &&
- next_token != TokenType::Comma {
+ if next_token != TokenType::And && next_token != TokenType::Comma {
if tokens[*idx].token_type == TokenType::Identifier {
cols.push(tokens[*idx].literal.clone());
*idx += 1;
return Ok(());
}
- } else if next_token == TokenType::And ||
- next_token == TokenType::Comma {
+ } else if next_token == TokenType::And || next_token == TokenType::Comma {
if tokens[*idx].token_type == TokenType::Identifier {
cols.push(tokens[*idx].literal.clone());
*idx += 2;
-
- ColumnNode::recurse_build(
- tokens,
- cols,
- idx
- )?;
+
+ ColumnNode::recurse_build(tokens, cols, idx)?;
return Ok(());
}
}
-
+
return Err("Something went wrong parsing column names, \
-make sure they're in a valid list notation.".to_string()
- );
+make sure they're in a valid list notation."
+ .to_string());
}
/// Reconstructs original literal from list of tokens and
/// provided bounds.
- fn reconstruct_literal(
- tokens: &Vec,
- start_idx: usize,
- end_idx: usize
- ) -> String {
+ fn reconstruct_literal(tokens: &Vec, start_idx: usize, end_idx: usize) -> String {
let mut literal = String::new();
literal.push_str(&format!("{} ", &tokens[start_idx].lexeme).to_string());
@@ -304,13 +256,9 @@ make sure they're in a valid list notation.".to_string()
/// Takes current node type and given the current location in the
/// query defined by the borrowed index, makes an attempt to parse
/// this node and associated subnodes for the Abstract Syntax Tree.
- pub fn parse(
- tokens: &Vec,
- idx: &mut usize,
- depth: u16
- ) -> Result {
+ pub fn parse(tokens: &Vec, idx: &mut usize, depth: u16) -> Result {
// We subtract 1 from this because Get keyword has been processed already
- let start_idx: usize = *idx - 1;
+ let start_idx: usize = *idx - 1;
if tokens[*idx].token_type == TokenType::WildcardKeyword {
*idx += 1;
@@ -320,52 +268,47 @@ make sure they're in a valid list notation.".to_string()
column_names: vec![],
_literal: {
- tokens[start_idx..*idx].iter()
+ tokens[start_idx..*idx]
+ .iter()
.map(|v| v.lexeme.as_str())
.collect::>()
.join(" ")
- },
- _depth: depth
+ },
+ _depth: depth,
});
}
let mut column_names: Vec = vec![];
-
+
ColumnNode::recurse_build(tokens, &mut column_names, idx)?;
- return Ok(
- ColumnNode {
- is_wildcard: false,
- column_names: column_names,
+ return Ok(ColumnNode {
+ is_wildcard: false,
+ column_names: column_names,
- _literal: ColumnNode::reconstruct_literal(tokens, start_idx, *idx),
- _depth: depth
+ _literal: ColumnNode::reconstruct_literal(tokens, start_idx, *idx),
+ _depth: depth,
});
}
/// Outputs current AST node transpiled with color
/// and it's raw query counterpart. Output are used by
/// the Transpiler REPL.
- pub fn transpile_color(
- &self
- ) -> (String, String) {
+ pub fn transpile_color(&self) -> (String, String) {
let columns: String = if self.is_wildcard {
"*".to_string()
} else {
self.column_names.join(", ")
};
-
(
colorize(&self._literal, AnsiColor::Yellow),
- colorize(&format!("SELECT {}", columns), AnsiColor::Yellow)
+ colorize(&format!("SELECT {}", columns), AnsiColor::Yellow),
)
}
/// Outputs current AST node transpiled to raw SQL
- pub fn transpile_raw(
- &self
- ) -> String {
+ pub fn transpile_raw(&self) -> String {
let columns: String = if self.is_wildcard {
"*".to_string()
} else {
@@ -383,30 +326,24 @@ impl FilterNode {
pub fn parse(
tokens: &Vec,
idx: &mut usize,
- depth: u16) -> Result