From b0742b606208fddb5d8a262a3fd60cb19f183013 Mon Sep 17 00:00:00 2001 From: alexander Date: Mon, 24 Nov 2025 07:32:55 +0100 Subject: [PATCH] Started qmcc rust core libary --- core-qmcc/.gitignore | 1 + core-qmcc/Cargo.lock | 125 +++++++ core-qmcc/Cargo.toml | 8 + core-qmcc/src/logic_expression/ast.rs | 113 ++++++ core-qmcc/src/logic_expression/lexer.rs | 114 ++++++ core-qmcc/src/logic_expression/mod.rs | 4 + core-qmcc/src/logic_expression/parser.rs | 367 +++++++++++++++++++ core-qmcc/src/logic_expression/parser_fsm.rs | 213 +++++++++++ core-qmcc/src/main.rs | 6 + core-qmcc/src/test.rs | 0 10 files changed, 951 insertions(+) create mode 100644 core-qmcc/.gitignore create mode 100644 core-qmcc/Cargo.lock create mode 100644 core-qmcc/Cargo.toml create mode 100644 core-qmcc/src/logic_expression/ast.rs create mode 100644 core-qmcc/src/logic_expression/lexer.rs create mode 100644 core-qmcc/src/logic_expression/mod.rs create mode 100644 core-qmcc/src/logic_expression/parser.rs create mode 100644 core-qmcc/src/logic_expression/parser_fsm.rs create mode 100644 core-qmcc/src/main.rs create mode 100644 core-qmcc/src/test.rs diff --git a/core-qmcc/.gitignore b/core-qmcc/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/core-qmcc/.gitignore @@ -0,0 +1 @@ +/target diff --git a/core-qmcc/Cargo.lock b/core-qmcc/Cargo.lock new file mode 100644 index 0000000..959506c --- /dev/null +++ b/core-qmcc/Cargo.lock @@ -0,0 +1,125 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + +[[package]] +name = "core-qmcc" +version = "0.1.0" +dependencies = [ + "anyhow", + "logos", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "logos" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff472f899b4ec2d99161c51f60ff7075eeb3097069a36050d8037a6325eb8154" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "192a3a2b90b0c05b27a0b2c43eecdb7c415e29243acc3f89cc8247a5b693045c" +dependencies = [ + "beef", + "fnv", + "lazy_static", + "proc-macro2", + "quote", + "regex-syntax", + "rustc_version", + "syn", +] + +[[package]] +name = "logos-derive" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "605d9697bcd5ef3a42d38efc51541aa3d6a4a25f7ab6d1ed0da5ac632a26b470" +dependencies = [ + "logos-codegen", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" diff --git a/core-qmcc/Cargo.toml b/core-qmcc/Cargo.toml new file mode 100644 index 0000000..6da409d --- /dev/null +++ b/core-qmcc/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "core-qmcc" +version = "0.1.0" +edition = "2024" + +[dependencies] +logos = "0.15.1" +anyhow = "1.0.100" \ No newline at end of file diff --git a/core-qmcc/src/logic_expression/ast.rs b/core-qmcc/src/logic_expression/ast.rs new file mode 100644 index 0000000..b5ab2d9 --- /dev/null +++ b/core-qmcc/src/logic_expression/ast.rs @@ -0,0 +1,113 @@ +use std::fmt::Display; + +use crate::logic_expression::parser::ExpressionEntry; + + + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BinaryOperator { + And, Or, Xor +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum UnaryOperator { + Not +} + +#[derive(Debug, Clone)] +pub enum ASTNode { + Literal(i32), + LiteralValue(bool), + BinaryOperation { op: BinaryOperator, right: Box, left: Box }, + UnaryOperator { op: UnaryOperator, right: Box} +} + +enum ASTNodeUnfinished { + BinaryOperation { op: BinaryOperator, right: Option>, left: Option> }, + UnaryOperator { op: UnaryOperator, right: Option>} +} + +impl ASTNode { + fn from_table(table: &Vec, entry: usize) -> Self { + let unfinished_stack: Vec = Vec::new(); + + match table[entry] { + ExpressionEntry::Literal { id } => return ASTNode::Literal(id), + ExpressionEntry::LiteralValue { value } => return ASTNode::LiteralValue(value), + ExpressionEntry::Infix { expr_id, binding_strength, op, left, right } => { + + }, + ExpressionEntry::Prefix { expr_id, binding_strength, op, right } => todo!(), + }; + + match table[entry] { + + ExpressionEntry::Infix { op, left, right , ..} => { + ASTNode::BinaryOperation { op, + right: Box::new(Self::from_table(table, right as usize)), + left: Box::new(Self::from_table(table, left as usize)) + } + }, + ExpressionEntry::Prefix { op, right, .. } => { + ASTNode::UnaryOperator { op, + right: Box::new(Self::from_table(table, right as usize)), + } + }, + } + } +} + +impl Display for ASTNode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut stack: Vec<(&ASTNode, usize)> = Vec::new(); + stack.push((&self, 0)); + + while let Some((node, layer)) = stack.pop() { + match node { + ASTNode::Literal(literal_id) => + write!(f, "{}Lit {}\n", "\t".repeat(layer), literal_id)?, + ASTNode::LiteralValue(v) => + write!(f, "{}Val {}\n", "\t".repeat(layer), v)?, + ASTNode::BinaryOperation { op, left, right } => { + stack.push((left.as_ref(), layer + 1)); + stack.push((right.as_ref(), layer + 1)); + write!(f, "{}Op {:?}\n", "\t".repeat(layer), op)? + }, + ASTNode::UnaryOperator { op, right } => { + stack.push((right.as_ref(), layer + 1)); + write!(f, "{}Op {:?}\n", "\t".repeat(layer), op)? + } + } + }; + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use crate::logic_expression::{ast::UnaryOperator, ast::BinaryOperator, parser::ExpressionEntry}; + + #[test] + pub fn test_ast_build_from_table() { + let table = vec![ + ExpressionEntry::Literal { id: 0 }, + ExpressionEntry::Literal { id: 1 }, + ExpressionEntry::Literal { id: 1 }, + ExpressionEntry::Literal { id: 2 }, + ExpressionEntry::Literal { id: 3 }, + ExpressionEntry::Literal { id: 4 }, + ExpressionEntry::Literal { id: 0 }, + ExpressionEntry::Literal { id: 3 }, + ExpressionEntry::Infix { expr_id: 8, binding_strength: 3, op: BinaryOperator::And, left: 0, right: 13 }, + ExpressionEntry::Infix { expr_id: 9, binding_strength: 13, op: BinaryOperator::And, left: 1, right: 10 }, + ExpressionEntry::Infix { expr_id: 10, binding_strength: 13, op: BinaryOperator::And, left: 2, right: 11 }, + ExpressionEntry::Prefix { expr_id: 11, binding_strength: 15, op: UnaryOperator::Not, right: 3 }, + ExpressionEntry::Infix { expr_id: 12, binding_strength: 12, op: BinaryOperator::Or, left: 9, right: 4 }, + ExpressionEntry::Infix { expr_id: 13, binding_strength: 3, op: BinaryOperator::And, left: 12, right: 5 }, + ExpressionEntry::Infix { expr_id: 14, binding_strength: 2, op: BinaryOperator::Or, left: 8, right: 6 }, + ExpressionEntry::Infix { expr_id: 15, binding_strength: 1, op: BinaryOperator::Xor, left: 14, right: 16 }, + ExpressionEntry::Prefix { expr_id: 16, binding_strength: 5, op: UnaryOperator::Not, right: 7 } + ]; + } +} \ No newline at end of file diff --git a/core-qmcc/src/logic_expression/lexer.rs b/core-qmcc/src/logic_expression/lexer.rs new file mode 100644 index 0000000..2c09793 --- /dev/null +++ b/core-qmcc/src/logic_expression/lexer.rs @@ -0,0 +1,114 @@ +use logos::Logos; + +#[derive(Logos, Debug, PartialEq, Clone)] +#[logos(skip r"[ \t\n\f]+")] +pub enum LogicExpressionTokens { + #[regex(r"AND|and|\+|&{1,2}|∧", priority=10)] + OpAnd, + + #[regex(r"OR|or|\|{1,2}|∨|v", priority=10)] + OpOr, + + #[regex(r"XOR|xor|\^", priority=10)] + OpXor, + + #[regex(r"¬|not|NOT|~", priority=10)] + NotOp, + + #[regex(r"[a-zA-Z0-9]*")] + Literal, + + #[regex(r"\(|\[|\{")] + OpenBrace, + + #[regex(r"\)|\]|\}")] + CloseBrace, + + #[regex(r"1|true", priority=10)] + TrueConst, + + #[regex(r"0|false", priority=10)] + FalseConst +} + +// a or b and (c xor d) +// LITERAL(a) OP(OR) LITERAL(B) + +#[cfg(test)] +mod tests { + use logos::Logos; + use crate::logic_expression::lexer::LogicExpressionTokens; + + #[test] + fn test_lexer_logic_1() { + let mut lex = LogicExpressionTokens::lexer("A AND B OR C + D"); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::Literal))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::OpAnd))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::Literal))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::OpOr))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::Literal))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::OpAnd))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::Literal))); + assert_eq!(lex.next(), None); + } + + #[test] + fn test_lexer_not_and_parens() { + let mut lex = LogicExpressionTokens::lexer("not (A)"); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::NotOp))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::OpenBrace))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::Literal))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::CloseBrace))); + assert_eq!(lex.next(), None); + } + + #[test] + fn test_lexer_true_false() { + let mut lex = LogicExpressionTokens::lexer("1 AND false OR true"); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::TrueConst))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::OpAnd))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::FalseConst))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::OpOr))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::TrueConst))); + assert_eq!(lex.next(), None); + } + + #[test] + fn test_lexer_braces_variants() { + let mut lex = LogicExpressionTokens::lexer("[{A}]"); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::OpenBrace))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::OpenBrace))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::Literal))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::CloseBrace))); + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::CloseBrace))); + assert_eq!(lex.next(), None); + } + + #[test] + fn test_lexer_multichar_ops_and_literals() { + let mut lex = LogicExpressionTokens::lexer("A && B || C v D ∧ E ∨ F"); + // A + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::Literal))); + // && -> OpAnd + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::OpAnd))); + // B + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::Literal))); + // || -> OpOr + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::OpOr))); + // C + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::Literal))); + // v -> OpOr + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::OpOr))); + // D + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::Literal))); + // ∧ -> OpAnd + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::OpAnd))); + // E + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::Literal))); + // ∨ -> OpOr + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::OpOr))); + // F + assert_eq!(lex.next(), Some(Ok(LogicExpressionTokens::Literal))); + assert_eq!(lex.next(), None); + } +} \ No newline at end of file diff --git a/core-qmcc/src/logic_expression/mod.rs b/core-qmcc/src/logic_expression/mod.rs new file mode 100644 index 0000000..54cd46a --- /dev/null +++ b/core-qmcc/src/logic_expression/mod.rs @@ -0,0 +1,4 @@ +pub mod lexer; +mod parser; +mod parser_fsm; +mod ast; \ No newline at end of file diff --git a/core-qmcc/src/logic_expression/parser.rs b/core-qmcc/src/logic_expression/parser.rs new file mode 100644 index 0000000..ec1101e --- /dev/null +++ b/core-qmcc/src/logic_expression/parser.rs @@ -0,0 +1,367 @@ +use std::{collections::HashMap, ops::{RangeBounds, RangeFrom}}; + +use logos::Lexer; + +use crate::logic_expression::{ast::{BinaryOperator, UnaryOperator}, lexer::LogicExpressionTokens, parser_fsm::{ParsingFSM, ParsingFSMAction, map_tokens_to_fsm_input}}; + +#[derive(Debug)] +pub enum ParserError { + BracketMissingMatch { pos: usize }, + OperatorNotAllow { pos: usize }, + EmptyBracketPair { pos: usize } +} + +impl std::fmt::Display for ParserError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ParserError::BracketMissingMatch { pos} => write!(f, "BracketMissingMatch"), + ParserError::OperatorNotAllow { pos} => write!(f, "OperatorNotAllow"), + ParserError::EmptyBracketPair { pos} => write!(f, "EmptyBracketPair"), + } + } +} + +fn map_binary_operator_binding_strength(op: BinaryOperator) -> u32 { + match op { + BinaryOperator::And => 3, + BinaryOperator::Or => 2, + BinaryOperator::Xor => 1, + } +} + +fn map_unary_operator_binding_strength(op: UnaryOperator) -> u32 { + match op { + UnaryOperator::Not => 5, + } +} + + +type ExpressionId = i32; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ExpressionEntry { + Literal { + id: i32 + }, + LiteralValue { + value: bool + }, + Infix { + expr_id: ExpressionId, + binding_strength: u32, + op: BinaryOperator, + left: ExpressionId, + right: ExpressionId + }, + Prefix { + expr_id: ExpressionId, + binding_strength: u32, + op: UnaryOperator, + right: ExpressionId + } +} + +pub fn parse_to_expr_table<'a>(mut lexer: Lexer<'a, LogicExpressionTokens>, string: &str) -> Result<(HashMap, Vec, RangeFrom), ParserError> +{ + let mut literal_name_map: HashMap = HashMap::new(); + let mut literal_id_counter: i32 = 0; + + let mut expr_table: Vec = Vec::new(); + let mut expr_table_operators: Vec = Vec::new(); + let mut fsm = ParsingFSM::new(); + + while let Some(Ok(token)) = lexer.next() { + let token_span = lexer.span(); + let token_text = &string[token_span.clone()]; + let action = fsm.advance(map_tokens_to_fsm_input(&token, token_text)); + let brace_level = fsm.get_brace_level() * 10; + + let left_index = (expr_table.len() as i32) - 1; + let right_index = expr_table.len() as i32; + + match action { + ParsingFSMAction::InsertBinaryOp => { + expr_table_operators.push(ExpressionEntry::Infix { + expr_id: -1, + binding_strength: map_binary_operator_binding_strength(BinaryOperator::And) + brace_level, + op: BinaryOperator::And, + left: left_index, right: right_index + }); + } + ParsingFSMAction::None => {}, + ParsingFSMAction::ErrorBraceMissMatch => return Err(ParserError::BracketMissingMatch { pos: token_span.start }), + ParsingFSMAction::ErrorOperatorNotAllowed => return Err(ParserError::OperatorNotAllow { pos: token_span.start }), + ParsingFSMAction::ErrorCannotHaveEmptyBracePair => return Err(ParserError::EmptyBracketPair { pos: token_span.start }), + }; + + match &token { + LogicExpressionTokens::OpAnd => { + expr_table_operators.push(ExpressionEntry::Infix { + expr_id: -1, + binding_strength: map_binary_operator_binding_strength(BinaryOperator::And) + brace_level, + op: BinaryOperator::And, + left: left_index, right: right_index + }); + }, + LogicExpressionTokens::OpOr => { + expr_table_operators.push(ExpressionEntry::Infix { + expr_id: -1, + binding_strength: map_binary_operator_binding_strength(BinaryOperator::Or) + brace_level, + op: BinaryOperator::Or, + left: left_index, right: right_index + }); + }, + LogicExpressionTokens::OpXor => { + expr_table_operators.push(ExpressionEntry::Infix { + expr_id: -1, + binding_strength: map_binary_operator_binding_strength(BinaryOperator::Xor) + brace_level, + op: BinaryOperator::Xor, + left: left_index, right: right_index + }); + }, + LogicExpressionTokens::NotOp => { + expr_table_operators.push(ExpressionEntry::Prefix { + expr_id: -1, + binding_strength: map_unary_operator_binding_strength(UnaryOperator::Not) + brace_level, + op: UnaryOperator::Not, + right: right_index, + }); + }, + LogicExpressionTokens::Literal => { + let name = token_text.to_string(); + + let literal_id: i32 = match literal_name_map.get(&name) { + Some(i) => *i, + None => { + let id = literal_id_counter; + literal_id_counter += 1; + literal_name_map.insert(name, id); + id + }, + }; + + expr_table.push(ExpressionEntry::Literal { id: literal_id }); + }, + LogicExpressionTokens::OpenBrace => {}, + LogicExpressionTokens::CloseBrace => {}, + LogicExpressionTokens::TrueConst => { + expr_table.push(ExpressionEntry::LiteralValue { value: true }); + }, + LogicExpressionTokens::FalseConst => { + expr_table.push(ExpressionEntry::LiteralValue { value: true }); + }, + }; + }; + + let operators_range = expr_table.len()..; + + for entry in expr_table_operators { + let expr_id = expr_table.len() as ExpressionId; + + expr_table.push(match entry { + ExpressionEntry::Infix { expr_id: _, binding_strength, op, left, right } => { + ExpressionEntry::Infix { expr_id, binding_strength, op, left, right } + }, + ExpressionEntry::Prefix { expr_id: _, binding_strength, op, right } => { + ExpressionEntry::Prefix { expr_id, binding_strength, op, right } + }, + _ => entry + }); + }; + + Ok((literal_name_map, expr_table, operators_range)) +} + + +pub fn link_expr_table(expr_table: &mut Vec, operators_range: RangeFrom) -> usize { + // Sorting the table by bindings strength + let operators_range_clone = operators_range.clone(); + { + let expr_operators_partition = &mut expr_table[operators_range_clone]; + + expr_operators_partition.sort_by_key(|entry| { + match entry { + ExpressionEntry::Infix { binding_strength, .. } => *binding_strength, + ExpressionEntry::Prefix { binding_strength, ..} => *binding_strength, + _ => panic!("This should not happen") + } + }); + expr_operators_partition.reverse(); + } + + + let mut expr_table_index_iter = (operators_range.start..expr_table.len()).into_iter(); + + // Doing the replacement + while let Some(current_index) = expr_table_index_iter.next() && (current_index + 1) < expr_table.len() { + let entry = expr_table[current_index]; + let current_id = match entry { + ExpressionEntry::Literal { .. } => panic!("This should not happen"), + ExpressionEntry::LiteralValue { .. } => panic!("This should not happen"), + ExpressionEntry::Infix { expr_id, .. } => expr_id, + ExpressionEntry::Prefix { expr_id, .. } => expr_id, + }; + + let (replace1, replace2_opt): (ExpressionId, Option) = match entry { + ExpressionEntry::Infix { left, right, .. } => (left, Some(right)), + ExpressionEntry::Prefix { right, .. } => (right, None), + ExpressionEntry::Literal { .. } => panic!("This should not happen"), + ExpressionEntry::LiteralValue { .. } => panic!("This should not happen"), + }; + + + for other_entry in expr_table[current_index+1..].iter_mut() { + match other_entry { + ExpressionEntry::Literal { .. } => {}, + ExpressionEntry::LiteralValue { .. } => {}, + ExpressionEntry::Infix { left, right, .. } => { + if *left == replace1 { *left = current_id as i32; } + if *right == replace1 { *right = current_id as i32; } + + if let Some(replace2) = replace2_opt { + if *left == replace2 { *left = current_id as i32; } + if *right == replace2 { *right = current_id as i32; } + } + }, + ExpressionEntry::Prefix { right, .. } => { + if *right == replace1 { *right = current_id as i32; } + + if let Some(replace2) = replace2_opt { + if *right == replace2 { *right = current_id as i32; } + } + }, + } + + } + }; + + let entry_index = match expr_table.last().unwrap() { + ExpressionEntry::Literal { .. } => panic!("This should not happen"), + ExpressionEntry::LiteralValue { .. } => panic!("This should not happen"), + ExpressionEntry::Infix { expr_id, .. } => *expr_id, + ExpressionEntry::Prefix { expr_id, .. } => *expr_id, + } as usize; + + // Resorting Indices + let expr_operators_partition_copy = Vec::from(&expr_table[operators_range.clone()]); + + for expr in expr_operators_partition_copy { + match expr { + ExpressionEntry::Literal { .. } => {}, + ExpressionEntry::LiteralValue { .. } => {}, + infix_expr @ ExpressionEntry::Infix { expr_id, .. } => { + expr_table[expr_id as usize] = infix_expr; + }, + prefix_expr @ ExpressionEntry::Prefix { expr_id, .. } => { + expr_table[expr_id as usize] = prefix_expr; + }, + } + }; + + entry_index +} + +#[cfg(test)] +mod test { + use logos::Logos; + + use crate::logic_expression::{lexer::LogicExpressionTokens, parser::{BinaryOperator, UnaryOperator, ExpressionEntry, link_expr_table, parse_to_expr_table}}; + + #[test] + fn test_expression_table_creation() { + let source_string = "A AND B (B AND NOT C OR D) E OR A XOR NOT D"; + let test_lexer = LogicExpressionTokens::lexer(source_string); + let expr_table = parse_to_expr_table(test_lexer, source_string); + + let expected_expr_table = vec![ + ExpressionEntry::Literal { id: 0 }, + ExpressionEntry::Literal { id: 1 }, + ExpressionEntry::Literal { id: 1 }, + ExpressionEntry::Literal { id: 2 }, + ExpressionEntry::Literal { id: 3 }, + ExpressionEntry::Literal { id: 4 }, + ExpressionEntry::Literal { id: 0 }, + ExpressionEntry::Literal { id: 3 }, + ExpressionEntry::Infix { expr_id: 8, binding_strength: 3, op: BinaryOperator::And, left: 0, right: 1 }, + ExpressionEntry::Infix { expr_id: 9, binding_strength: 13, op: BinaryOperator::And, left: 1, right: 2 }, + ExpressionEntry::Infix { expr_id: 10, binding_strength: 13, op: BinaryOperator::And, left: 2, right: 3 }, + ExpressionEntry::Prefix { expr_id: 11, binding_strength: 15, op: UnaryOperator::Not, right: 3 }, + ExpressionEntry::Infix { expr_id: 12, binding_strength: 12, op: BinaryOperator::Or, left: 3, right: 4 }, + ExpressionEntry::Infix { expr_id: 13, binding_strength: 3, op: BinaryOperator::And, left: 4, right: 5 }, + ExpressionEntry::Infix { expr_id: 14, binding_strength: 2, op: BinaryOperator::Or, left: 5, right: 6 }, + ExpressionEntry::Infix { expr_id: 15, binding_strength: 1, op: BinaryOperator::Xor, left: 6, right: 7 }, + ExpressionEntry::Prefix { expr_id: 16, binding_strength: 5, op: UnaryOperator::Not, right: 7 } + ]; + + match expr_table { + Ok(( + literal_map, + expr_table, + operator_range + )) => { + assert_eq!(literal_map.len(), 5); + assert!(literal_map.contains_key("A")); + assert!(literal_map.contains_key("B")); + assert!(literal_map.contains_key("C")); + assert!(literal_map.contains_key("D")); + assert!(literal_map.contains_key("E")); + + assert_eq!(expr_table[operator_range].len(), 9, "Operator Partition as incorrect size!"); + assert_eq!(expr_table.len(), expected_expr_table.len(), "Expr_table should has incorrect size!"); + + expr_table.iter().zip(expected_expr_table).enumerate().for_each(|(index, (entry, expected))| { + assert_eq!(*entry, expected, "Index {:2}: Entry did not match!\nExpected: {:?}\nGot: {:?}", index, entry, expected); + }); + }, + Err(e) => assert!(false, "Result should be ok, got: Error {:?}", e), + } + } + + #[test] + fn test_expression_table_linking() { + let source_string = "A AND B (B AND NOT C OR D) E OR A XOR NOT D"; + let test_lexer = LogicExpressionTokens::lexer(source_string); + let expr_table = parse_to_expr_table(test_lexer, source_string); + + let expected_expr_table = vec![ + ExpressionEntry::Literal { id: 0 }, // 0 + ExpressionEntry::Literal { id: 1 }, // 1 + ExpressionEntry::Literal { id: 1 }, // 2 + ExpressionEntry::Literal { id: 2 }, // 3 + ExpressionEntry::Literal { id: 3 }, // 4 + ExpressionEntry::Literal { id: 4 }, // 5 + ExpressionEntry::Literal { id: 0 }, // 6 + ExpressionEntry::Literal { id: 3 }, // 7 + ExpressionEntry::Infix { expr_id: 8, binding_strength: 3, op: BinaryOperator::And, left: 0, right: 1 }, + ExpressionEntry::Infix { expr_id: 9, binding_strength: 13, op: BinaryOperator::And, left: 1, right: 1 }, + ExpressionEntry::Infix { expr_id: 10, binding_strength: 13, op: BinaryOperator::And, left: 2, right: 3 }, + ExpressionEntry::Prefix { expr_id: 11, binding_strength: 15, op: UnaryOperator::Not, right: 3 }, + ExpressionEntry::Infix { expr_id: 12, binding_strength: 12, op: BinaryOperator::Or, left: 3, right: 4 }, + ExpressionEntry::Infix { expr_id: 13, binding_strength: 3, op: BinaryOperator::And, left: 4, right: 5 }, + ExpressionEntry::Infix { expr_id: 14, binding_strength: 2, op: BinaryOperator::Or, left: 5, right: 6 }, + ExpressionEntry::Infix { expr_id: 15, binding_strength: 1, op: BinaryOperator::Xor, left: 6, right: 7 }, + ExpressionEntry::Prefix { expr_id: 16, binding_strength: 5, op: UnaryOperator::Not, right: 7 } + ]; + + match expr_table { + Ok(( + _literal_map, + mut expr_table, + operator_range + )) => { + assert_eq!(expr_table[operator_range.clone()].len(), 9, "Operator Partition as incorrect size!"); + assert_eq!(expr_table.len(), expected_expr_table.len(), "Expr_table should has incorrect size!"); + + let entry_index = link_expr_table(&mut expr_table, operator_range); + + assert_eq!(entry_index, 15, "Invalid Entry Index, should be 15!"); + + expr_table.iter().zip(expected_expr_table).enumerate().for_each(|(index, (entry, expected))| { + println!("{:2}: {:?}", index, entry); + // assert_eq!(*entry, expected, "Index {:2}: Entry did not match!\nExpected: {:?}\nGot: {:?}", index, entry, expected); + }); + }, + Err(e) => assert!(false, "Result should be ok, got: Error {:?}", e), + } + } +} \ No newline at end of file diff --git a/core-qmcc/src/logic_expression/parser_fsm.rs b/core-qmcc/src/logic_expression/parser_fsm.rs new file mode 100644 index 0000000..67c8669 --- /dev/null +++ b/core-qmcc/src/logic_expression/parser_fsm.rs @@ -0,0 +1,213 @@ +use crate::logic_expression::lexer::LogicExpressionTokens; + + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ParsingFSMInputs { + BraceOpen(BraceKind), + BraceClose(BraceKind), + UnaryOperator, + BinaryOperator, + Literal +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ParsingFSMState { + LastBraceOpen, + LastBraceClose, + LastUnaryOp, + LastBinaryOp, + LastLiteral, + Error +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ParsingFSMAction { + None, + InsertBinaryOp, + ErrorBraceMissMatch, + ErrorOperatorNotAllowed, + ErrorCannotHaveEmptyBracePair +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BraceKind { + Curly, Round, Square +} + +pub struct ParsingFSM { + state: ParsingFSMState, + brace_stack: Vec +} + +pub fn map_tokens_to_fsm_input(token: &LogicExpressionTokens, token_str: &str) -> ParsingFSMInputs { + match token { + LogicExpressionTokens::OpAnd => ParsingFSMInputs::BinaryOperator, + LogicExpressionTokens::OpOr => ParsingFSMInputs::BinaryOperator, + LogicExpressionTokens::OpXor => ParsingFSMInputs::BinaryOperator, + LogicExpressionTokens::NotOp => ParsingFSMInputs::UnaryOperator, + LogicExpressionTokens::Literal => ParsingFSMInputs::Literal, + LogicExpressionTokens::OpenBrace => { + ParsingFSMInputs::BraceOpen( + match token_str.chars().nth(0) { + Some('{') => BraceKind::Curly, + Some('[') => BraceKind::Square, + _ => BraceKind::Round + } + ) + }, + LogicExpressionTokens::CloseBrace => { + ParsingFSMInputs::BraceClose( + match token_str.chars().nth(0) { + Some('{') => BraceKind::Curly, + Some('[') => BraceKind::Square, + _ => BraceKind::Round + } + ) + }, + LogicExpressionTokens::TrueConst => ParsingFSMInputs::Literal, + LogicExpressionTokens::FalseConst => ParsingFSMInputs::Literal, + } +} + +impl ParsingFSM { + pub fn new() -> Self { + ParsingFSM { state: ParsingFSMState::LastBinaryOp, brace_stack: Vec::new() } + } + + pub fn get_state(&self) -> ParsingFSMState { + self.state + } + + pub fn get_brace_level(&self) -> u32 { + self.brace_stack.len() as u32 + } + + pub fn advance(&mut self, input: ParsingFSMInputs) -> ParsingFSMAction { + let (next_state, output): (ParsingFSMState, ParsingFSMAction) = match (self.state, input) { + (ParsingFSMState::LastLiteral, ParsingFSMInputs::BraceOpen(c)) => { + self.brace_stack.push(c); + (ParsingFSMState::LastBraceOpen, ParsingFSMAction::InsertBinaryOp) + }, + (ParsingFSMState::LastBinaryOp, ParsingFSMInputs::BraceOpen(c)) => { + self.brace_stack.push(c); + (ParsingFSMState::LastBraceOpen, ParsingFSMAction::None) + }, + (ParsingFSMState::LastUnaryOp, ParsingFSMInputs::BraceOpen(c)) => { + self.brace_stack.push(c); + (ParsingFSMState::LastBraceOpen, ParsingFSMAction::None) + }, + (ParsingFSMState::LastBraceOpen, ParsingFSMInputs::BraceOpen(c)) => { + self.brace_stack.push(c); + (ParsingFSMState::LastBraceOpen, ParsingFSMAction::None) + }, + + (ParsingFSMState::LastBraceClose, ParsingFSMInputs::BraceOpen(c)) => { + self.brace_stack.push(c); + (ParsingFSMState::LastBraceOpen, ParsingFSMAction::InsertBinaryOp) + }, + + (ParsingFSMState::LastLiteral, ParsingFSMInputs::BraceClose(c)) => { + match self.brace_stack.pop() { + Some(c_stack) => if c == c_stack { + (ParsingFSMState::LastBraceClose, ParsingFSMAction::None) + } else { + (ParsingFSMState::Error, ParsingFSMAction::ErrorBraceMissMatch) + }, + None => (ParsingFSMState::Error, ParsingFSMAction::ErrorBraceMissMatch), + } + }, + (ParsingFSMState::LastBinaryOp, ParsingFSMInputs::BraceClose(_)) => (ParsingFSMState::Error, ParsingFSMAction::ErrorOperatorNotAllowed), + (ParsingFSMState::LastUnaryOp, ParsingFSMInputs::BraceClose(_)) => (ParsingFSMState::Error, ParsingFSMAction::ErrorOperatorNotAllowed), + (ParsingFSMState::LastBraceOpen, ParsingFSMInputs::BraceClose(_)) => (ParsingFSMState::Error, ParsingFSMAction::ErrorCannotHaveEmptyBracePair), + (ParsingFSMState::LastBraceClose, ParsingFSMInputs::BraceClose(c)) => { + match self.brace_stack.pop() { + Some(c_stack) => if c == c_stack { + (ParsingFSMState::LastBraceClose, ParsingFSMAction::None) + } else { + (ParsingFSMState::Error, ParsingFSMAction::ErrorBraceMissMatch) + }, + None => (ParsingFSMState::Error, ParsingFSMAction::ErrorBraceMissMatch), + } + }, + + (ParsingFSMState::LastLiteral, ParsingFSMInputs::UnaryOperator) => (ParsingFSMState::LastUnaryOp, ParsingFSMAction::InsertBinaryOp), + (ParsingFSMState::LastBinaryOp, ParsingFSMInputs::UnaryOperator) => (ParsingFSMState::LastUnaryOp, ParsingFSMAction::None), + (ParsingFSMState::LastUnaryOp, ParsingFSMInputs::UnaryOperator) => (ParsingFSMState::LastUnaryOp, ParsingFSMAction::None), + (ParsingFSMState::LastBraceOpen, ParsingFSMInputs::UnaryOperator) => (ParsingFSMState::LastUnaryOp, ParsingFSMAction::None), + (ParsingFSMState::LastBraceClose, ParsingFSMInputs::UnaryOperator) => (ParsingFSMState::Error, ParsingFSMAction::InsertBinaryOp), + + (ParsingFSMState::LastLiteral, ParsingFSMInputs::BinaryOperator) => (ParsingFSMState::LastBinaryOp, ParsingFSMAction::None), + (ParsingFSMState::LastBinaryOp, ParsingFSMInputs::BinaryOperator) => (ParsingFSMState::Error, ParsingFSMAction::ErrorOperatorNotAllowed), + (ParsingFSMState::LastUnaryOp, ParsingFSMInputs::BinaryOperator) => (ParsingFSMState::Error, ParsingFSMAction::ErrorOperatorNotAllowed), + (ParsingFSMState::LastBraceOpen, ParsingFSMInputs::BinaryOperator) => (ParsingFSMState::Error, ParsingFSMAction::ErrorOperatorNotAllowed), + (ParsingFSMState::LastBraceClose, ParsingFSMInputs::BinaryOperator) => (ParsingFSMState::LastBinaryOp, ParsingFSMAction::None), + + (ParsingFSMState::LastLiteral, ParsingFSMInputs::Literal) => (ParsingFSMState::LastLiteral, ParsingFSMAction::InsertBinaryOp), + (ParsingFSMState::LastBinaryOp, ParsingFSMInputs::Literal) => (ParsingFSMState::LastLiteral, ParsingFSMAction::None), + (ParsingFSMState::LastUnaryOp, ParsingFSMInputs::Literal) => (ParsingFSMState::LastLiteral, ParsingFSMAction::None), + (ParsingFSMState::LastBraceOpen, ParsingFSMInputs::Literal) => (ParsingFSMState::LastLiteral, ParsingFSMAction::None), + (ParsingFSMState::LastBraceClose, ParsingFSMInputs::Literal) => (ParsingFSMState::LastLiteral, ParsingFSMAction::InsertBinaryOp), + + (ParsingFSMState::Error, _) => (ParsingFSMState::Error, ParsingFSMAction::None), + }; + + self.state = next_state; + output + } +} + + +#[cfg(test)] +mod test { + use crate::logic_expression::parser_fsm::{BraceKind, ParsingFSM, ParsingFSMAction::{self, *}, ParsingFSMInputs::{self, *}, ParsingFSMState}; + + pub fn test_fsm(inputs: Vec, expected_actions: Vec) { + let mut fsm = ParsingFSM::new(); + let res: Vec<(ParsingFSMInputs, u32, ParsingFSMAction, ParsingFSMState)> = inputs.iter().map(|i| { + let r = fsm.advance(*i); + let s = fsm.get_state(); + let brace_level = fsm.get_brace_level(); + (*i, brace_level, r, s) + }).collect(); + + for (pos, (exp, (i, brace_level, r, s))) in expected_actions.iter().zip(res).enumerate() { + assert_eq!(*exp, r, "[Step {}]: Expected Action '{:?}' got '{:?}' (state: '{:?}', input: '{:?}', brace_level: '{:?}')", pos, exp, r, s, i, brace_level); + } + + assert_eq!(fsm.get_brace_level(), 0, "Brace Level should be 0 after fsm ended! (got {})", fsm.get_brace_level()); + } + + #[test] + pub fn test_fsm_valid_1() { + test_fsm( + vec![ + Literal, BinaryOperator, Literal + ], + vec![ + None, None, None + ] + ); + } + + #[test] + pub fn test_fsm_valid_2() { + test_fsm( + vec![ + Literal, BinaryOperator, + BraceOpen(BraceKind::Curly), + Literal, BinaryOperator, Literal, BinaryOperator, Literal, Literal, + BraceClose(BraceKind::Curly), + Literal, UnaryOperator, Literal + ], + vec![ + None, None, + None, + None, None, None, None, None, InsertBinaryOp, + None, + InsertBinaryOp, InsertBinaryOp, None + ] + ); + } + + +} \ No newline at end of file diff --git a/core-qmcc/src/main.rs b/core-qmcc/src/main.rs new file mode 100644 index 0000000..3397eb3 --- /dev/null +++ b/core-qmcc/src/main.rs @@ -0,0 +1,6 @@ +mod logic_expression; +mod test; + +fn main() { + println!("Hello, world!"); +} diff --git a/core-qmcc/src/test.rs b/core-qmcc/src/test.rs new file mode 100644 index 0000000..e69de29