From 97d43e497bb41dcb2a8bad7743254b61a920c489 Mon Sep 17 00:00:00 2001 From: Apisit Ritreungroj Date: Wed, 22 Mar 2023 15:15:28 +0700 Subject: [PATCH] update: parser code --- src/ast/mod.rs | 4 + src/ast/project.rs | 6 +- src/ast/schema.rs | 2 +- src/ast/table.rs | 27 +++- src/parser/mod.rs | 225 ++++++++++++++------------- tests/out/general_schema.in.ron | 11 +- tests/out/header_color_tables.in.ron | 11 +- tests/out/project.in.ron | 11 +- tests/out/table_element.in.ron | 11 +- tests/out/table_settings.in.ron | 58 ++++--- 10 files changed, 213 insertions(+), 153 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index b4d5ab3..99b4a10 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1,7 +1,11 @@ +use std::ops::Range; + use pest::Span; use pest::iterators::Pair; use crate::parser::Rule; +type SpanRange = Range; + pub mod enums; pub mod indexes; pub mod refs; diff --git a/src/ast/project.rs b/src/ast/project.rs index dc385f8..11ef2d0 100644 --- a/src/ast/project.rs +++ b/src/ast/project.rs @@ -1,6 +1,6 @@ -use std::{str::FromStr, ops::Range}; +use std::str::FromStr; -use pest::Span; +use super::*; #[derive(Debug, PartialEq, Eq, Clone, Default)] pub enum DatabaseType { @@ -27,7 +27,7 @@ impl FromStr for DatabaseType { #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct ProjectBlock { - pub span_range: Range, + pub span_range: SpanRange, pub name: String, pub database_type: DatabaseType, pub note: Option diff --git a/src/ast/schema.rs b/src/ast/schema.rs index f527cfe..f8469c9 100644 --- a/src/ast/schema.rs +++ b/src/ast/schema.rs @@ -4,7 +4,7 @@ use super::*; #[derive(Debug, PartialEq, Clone)] pub struct SchemaBlock<'a> { - pub span_range: Range, + pub span_range: SpanRange, /// Input source content. pub input: &'a str, /// Overall description of the project. This is optional. The file must contain one or zero 'Project' block. diff --git a/src/ast/table.rs b/src/ast/table.rs index bf0834d..1597250 100644 --- a/src/ast/table.rs +++ b/src/ast/table.rs @@ -1,21 +1,21 @@ -use std::{ops::Range, str::FromStr, collections::HashMap}; +use std::str::FromStr; use super::*; #[derive(Debug, PartialEq, Clone, Default)] pub struct TableBlock { - pub span_range: Range, + pub span_range: SpanRange, pub cols: Vec, pub ident: TableIdent, pub note: Option, pub indexes: Option, - pub settings: Option>, + pub settings: Option>, pub meta_indexer: TableIndexer } #[derive(Debug, PartialEq, Clone, Default)] pub struct ColumnType { - pub span_range: Range, + pub span_range: SpanRange, pub type_name: ColumnTypeName, pub args: Vec, pub arrays: Vec>, @@ -23,7 +23,7 @@ pub struct ColumnType { #[derive(Debug, PartialEq, Clone, Default)] pub struct TableColumn { - pub span_range: Range, + pub span_range: SpanRange, pub name: String, pub r#type: ColumnType, pub settings: ColumnSettings, @@ -47,6 +47,19 @@ pub enum Value { Null } +impl FromStr for Value { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "true" => Ok(Value::Bool(true)), + "false" => Ok(Value::Bool(false)), + "null" => Ok(Value::Null), + _ => Err(()), + } + } +} + impl ToString for Value { fn to_string(&self) -> String { match self { @@ -182,7 +195,7 @@ impl FromStr for ColumnTypeName { #[derive(Debug, PartialEq, Clone, Default)] pub struct ColumnSettings { - pub span_range: Range, + pub span_range: SpanRange, pub is_pk: bool, pub is_unique: bool, pub is_nullable: bool, @@ -194,7 +207,7 @@ pub struct ColumnSettings { #[derive(Debug, PartialEq, Clone, Default)] pub struct TableIdent { - pub span_range: Range, + pub span_range: SpanRange, pub name: String, pub schema: Option, pub alias: Option diff --git a/src/parser/mod.rs b/src/parser/mod.rs index c8614af..2e34a27 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,6 +1,5 @@ mod err; -use std::collections::HashMap; use std::str::FromStr; use err::*; @@ -25,16 +24,17 @@ struct DBMLParser; pub fn parse(input: &str) -> ParserResult { let pairs = DBMLParser::parse(Rule::schema, input)?; - for pair in pairs { - match pair.as_rule() { - Rule::schema => { - return Ok(parse_schema(pair, input)?); - }, - _ => throw_rules(&[Rule::schema], pair)? - } - } + let pair = pairs + .into_iter() + .next() + .ok_or_else(|| unreachable!("unhandled parsing error!"))?; - unreachable!("unhandled parsing error!"); + match pair.as_rule() { + Rule::schema => { + return Ok(parse_schema(pair, input)?); + }, + _ => throw_rules(&[Rule::schema], pair)? + } } fn parse_schema<'a>(pair: Pair, input: &'a str) -> ParserResult> { @@ -96,7 +96,9 @@ fn parse_project_decl(pair: Pair) -> ParserResult { } fn parse_project_stmt(pair: Pair) -> ParserResult<(String, String)> { - pair.into_inner().try_fold((String::new(), String::new()), |mut acc, p1| { + let init = (String::new(), String::new()); + + pair.into_inner().try_fold(init, |mut acc, p1| { match p1.as_rule() { Rule::var => acc.0 = p1.as_str().to_string(), Rule::string_value => acc.1 = parse_string_value(p1)?, @@ -147,8 +149,10 @@ fn parse_table_decl(pair: Pair) -> ParserResult { }) } -fn parse_table_settings(pair: Pair) -> ParserResult> { - pair.into_inner().try_fold(HashMap::new(), |mut acc, p2| { +fn parse_table_settings(pair: Pair) -> ParserResult> { + let init = vec![]; + + pair.into_inner().try_fold(init, |mut acc, p2| { match p2.as_rule() { Rule::table_attribute => { let p2_cloned = p2.clone(); @@ -172,7 +176,7 @@ fn parse_table_settings(pair: Pair) -> ParserResult _ => throw_rules(&[Rule::table_attribute], p2_cloned)? }; - acc.insert(s_key, s_val); + acc.push((s_key, s_val)); }, _ => throw_rules(&[Rule::table_attribute], p2)?, } @@ -556,16 +560,17 @@ fn parse_note_inline(pair: Pair) -> ParserResult { } fn parse_indexes_decl(pair: Pair) -> ParserResult { - for p1 in pair.into_inner() { - match p1.as_rule() { - Rule::indexes_block => { - return parse_indexes_block(p1) - }, - _ => throw_rules(&[Rule::indexes_block], p1)?, - } + let p1 = pair + .into_inner() + .next() + .ok_or_else(|| unreachable!("something went wrong parsing indexes_decl!"))?; + + match p1.as_rule() { + Rule::indexes_block => { + parse_indexes_block(p1) + }, + _ => throw_rules(&[Rule::indexes_block], p1)?, } - - unreachable!("something went wrong parsing indexes_decl!") } fn parse_indexes_block(pair: Pair) -> ParserResult { @@ -598,28 +603,32 @@ fn parse_indexes_single_multi(pair: Pair) -> ParserResult { } fn parse_indexes_ident(pair: Pair) -> ParserResult { - for p1 in pair.into_inner() { - match p1.as_rule() { - Rule::ident => { - let value = parse_ident(p1)?; - return Ok(IndexesColumnType::String(value)) - }, - Rule::backquoted_quoted_string => { - for p2 in p1.into_inner() { - match p2.as_rule() { - Rule::backquoted_quoted_value => { - let value = p2.as_str().to_string(); - return Ok(IndexesColumnType::Expr(value)) - }, - _ => throw_rules(&[Rule::backquoted_quoted_value], p2)?, - } - } - }, - _ => throw_rules(&[Rule::ident, Rule::backquoted_quoted_string], p1)?, - } + let p1 = pair + .into_inner() + .next() + .ok_or_else(|| unreachable!("something went wrong at indexes_ident"))?; + + match p1.as_rule() { + Rule::ident => { + let value = parse_ident(p1)?; + Ok(IndexesColumnType::String(value)) + }, + Rule::backquoted_quoted_string => { + let p2 = p1 + .into_inner() + .next() + .ok_or_else(|| unreachable!("something went wrong at indexes_ident"))?; + + match p2.as_rule() { + Rule::backquoted_quoted_value => { + let value = p2.as_str().to_string(); + Ok(IndexesColumnType::Expr(value)) + }, + _ => throw_rules(&[Rule::backquoted_quoted_value], p2)?, + } + }, + _ => throw_rules(&[Rule::ident, Rule::backquoted_quoted_string], p1)?, } - - unreachable!("something went wrong at indexes_ident"); } fn parse_indexes_settings(pair: Pair) -> ParserResult { @@ -697,57 +706,60 @@ fn parse_string_value(pair: Pair) -> ParserResult { } fn parse_value(pair: Pair) -> ParserResult { - for p1 in pair.into_inner() { - match p1.as_rule() { - Rule::string_value => { - let value = parse_string_value(p1)?; - - return Ok(Value::String(value)); - }, - Rule::number_value => { - for p2 in p1.into_inner() { - match p2.as_rule() { - Rule::decimal => { - return match p2.as_str().parse::() { - Ok(val) => Ok(Value::Decimal(val)), - Err(err) => throw_msg(err.to_string(), p2)?, - }; - }, - Rule::integer => { - return match p2.as_str().parse::() { - Ok(val) => Ok(Value::Integer(val)), - Err(err) => throw_msg(err.to_string(), p2)?, - }; - }, - _ => throw_rules(&[Rule::decimal, Rule::integer], p2)?, + let p1 = pair + .into_inner() + .next() + .ok_or_else(|| unreachable!("something went wrong at value!"))?; + + match p1.as_rule() { + Rule::string_value => { + let value = parse_string_value(p1)?; + + Ok(Value::String(value)) + }, + Rule::number_value => { + let p2 = p1 + .into_inner() + .next() + .ok_or_else(|| unreachable!("something went wrong at value!"))?; + + match p2.as_rule() { + Rule::decimal => { + match p2.as_str().parse::() { + Ok(val) => Ok(Value::Decimal(val)), + Err(err) => throw_msg(err.to_string(), p2)?, } - } - }, - Rule::boolean_value => { - return match p1.as_str() { - "true" => Ok(Value::Bool(true)), - "false" => Ok(Value::Bool(false)), - "null" => Ok(Value::Null), - _ => throw_msg(format!("'{}' is incompatible with boolean value", p1.as_str()), p1)?, - } - }, - Rule::hex_value => { - return Ok(Value::HexColor(p1.as_str().to_string())) - }, - Rule::backquoted_quoted_string => { - return Ok(Value::Expr(p1.into_inner().as_str().to_string())) - }, - _ => throw_rules(&[ - Rule::string_value, - Rule::number_value, - Rule::boolean_value, - Rule::hex_value, - Rule::backquoted_quoted_string - ], p1)?, - } + }, + Rule::integer => { + match p2.as_str().parse::() { + Ok(val) => Ok(Value::Integer(val)), + Err(err) => throw_msg(err.to_string(), p2)?, + } + }, + _ => throw_rules(&[Rule::decimal, Rule::integer], p2)?, + } + }, + Rule::boolean_value => { + if let Ok(v) = Value::from_str(p1.as_str()) { + Ok(v) + } else { + throw_msg(format!("'{}' is incompatible with boolean value", p1.as_str()), p1)? + } + }, + Rule::hex_value => { + Ok(Value::HexColor(p1.as_str().to_string())) + }, + Rule::backquoted_quoted_string => { + Ok(Value::Expr(p1.into_inner().as_str().to_string())) + }, + _ => throw_rules(&[ + Rule::string_value, + Rule::number_value, + Rule::boolean_value, + Rule::hex_value, + Rule::backquoted_quoted_string + ], p1)?, } - - unreachable!("something went wrong at value!") } fn parse_decl_ident(pair: Pair) -> ParserResult<(Option, String)> { @@ -774,17 +786,18 @@ fn parse_decl_ident(pair: Pair) -> ParserResult<(Option, String)> } fn parse_ident(pair: Pair) -> ParserResult { - for p1 in pair.into_inner() { - return match p1.as_rule() { - Rule::var => { - Ok(p1.as_str().to_string()) - }, - Rule::double_quoted_string => { - Ok(p1.into_inner().as_str().to_string()) - }, - _ => throw_rules(&[Rule::var, Rule::double_quoted_string], p1)?, - } + let p1 = pair + .into_inner() + .next() + .ok_or_else(|| unreachable!("something went wrong at ident!"))?; + + match p1.as_rule() { + Rule::var => { + Ok(p1.as_str().to_string()) + }, + Rule::double_quoted_string => { + Ok(p1.into_inner().as_str().to_string()) + }, + _ => throw_rules(&[Rule::var, Rule::double_quoted_string], p1)?, } - - unreachable!("something went wrong at ident!") -} +} \ No newline at end of file diff --git a/tests/out/general_schema.in.ron b/tests/out/general_schema.in.ron index 5635ff8..b7b7deb 100644 --- a/tests/out/general_schema.in.ron +++ b/tests/out/general_schema.in.ron @@ -104,11 +104,14 @@ SchemaBlock { note: None, indexes: None, settings: Some( - { - "headercolor": HexColor( - "#fff", + [ + ( + "headercolor", + HexColor( + "#fff", + ), ), - }, + ], ), meta_indexer: TableIndexer { pk_list: [], diff --git a/tests/out/header_color_tables.in.ron b/tests/out/header_color_tables.in.ron index d10a434..f6fdec2 100644 --- a/tests/out/header_color_tables.in.ron +++ b/tests/out/header_color_tables.in.ron @@ -104,11 +104,14 @@ SchemaBlock { note: None, indexes: None, settings: Some( - { - "HeaderColor": HexColor( - "#0065ab", + [ + ( + "HeaderColor", + HexColor( + "#0065ab", + ), ), - }, + ], ), meta_indexer: TableIndexer { pk_list: [], diff --git a/tests/out/project.in.ron b/tests/out/project.in.ron index bcc5180..e5b1b4d 100644 --- a/tests/out/project.in.ron +++ b/tests/out/project.in.ron @@ -113,11 +113,14 @@ SchemaBlock { note: None, indexes: None, settings: Some( - { - "headercolor": HexColor( - "#fff", + [ + ( + "headercolor", + HexColor( + "#fff", + ), ), - }, + ], ), meta_indexer: TableIndexer { pk_list: [], diff --git a/tests/out/table_element.in.ron b/tests/out/table_element.in.ron index eabcbeb..8e7dd2f 100644 --- a/tests/out/table_element.in.ron +++ b/tests/out/table_element.in.ron @@ -127,11 +127,14 @@ SchemaBlock { }, ), settings: Some( - { - "note": String( - "note in table settings", + [ + ( + "note", + String( + "note in table settings", + ), ), - }, + ], ), meta_indexer: TableIndexer { pk_list: [], diff --git a/tests/out/table_settings.in.ron b/tests/out/table_settings.in.ron index 2d9e59c..2ea5e4d 100644 --- a/tests/out/table_settings.in.ron +++ b/tests/out/table_settings.in.ron @@ -60,11 +60,14 @@ SchemaBlock { note: None, indexes: None, settings: Some( - { - "headerColor": HexColor( - "#555", + [ + ( + "headerColor", + HexColor( + "#555", + ), ), - }, + ], ), meta_indexer: TableIndexer { pk_list: [], @@ -129,11 +132,14 @@ SchemaBlock { note: None, indexes: None, settings: Some( - { - "note": String( - "name is required", + [ + ( + "note", + String( + "name is required", + ), ), - }, + ], ), meta_indexer: TableIndexer { pk_list: [], @@ -220,14 +226,20 @@ SchemaBlock { note: None, indexes: None, settings: Some( - { - "note": String( - "product must have price", + [ + ( + "headerColor", + HexColor( + "#17DACC", + ), ), - "headerColor": HexColor( - "#17DACC", + ( + "note", + String( + "product must have price", + ), ), - }, + ], ), meta_indexer: TableIndexer { pk_list: [], @@ -336,14 +348,20 @@ SchemaBlock { note: None, indexes: None, settings: Some( - { - "note": String( - "merchants sell a lot", + [ + ( + "headerColor", + HexColor( + "#08DAFF", + ), ), - "headerColor": HexColor( - "#08DAFF", + ( + "note", + String( + "merchants sell a lot", + ), ), - }, + ], ), meta_indexer: TableIndexer { pk_list: [],