Started qmcc rust core libary

This commit is contained in:
alexander
2025-11-24 07:32:55 +01:00
parent a9f67f420e
commit b0742b6062
10 changed files with 951 additions and 0 deletions

1
core-qmcc/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

125
core-qmcc/Cargo.lock generated Normal file
View File

@@ -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"

8
core-qmcc/Cargo.toml Normal file
View File

@@ -0,0 +1,8 @@
[package]
name = "core-qmcc"
version = "0.1.0"
edition = "2024"
[dependencies]
logos = "0.15.1"
anyhow = "1.0.100"

View File

@@ -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<ASTNode>, left: Box<ASTNode> },
UnaryOperator { op: UnaryOperator, right: Box<ASTNode>}
}
enum ASTNodeUnfinished {
BinaryOperation { op: BinaryOperator, right: Option<Box<ASTNode>>, left: Option<Box<ASTNode>> },
UnaryOperator { op: UnaryOperator, right: Option<Box<ASTNode>>}
}
impl ASTNode {
fn from_table(table: &Vec<ExpressionEntry>, entry: usize) -> Self {
let unfinished_stack: Vec<ASTNodeUnfinished> = 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 }
];
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,4 @@
pub mod lexer;
mod parser;
mod parser_fsm;
mod ast;

View File

@@ -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<String, i32>, Vec<ExpressionEntry>, RangeFrom<usize>), ParserError>
{
let mut literal_name_map: HashMap<String, i32> = HashMap::new();
let mut literal_id_counter: i32 = 0;
let mut expr_table: Vec<ExpressionEntry> = Vec::new();
let mut expr_table_operators: Vec<ExpressionEntry> = 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<ExpressionEntry>, operators_range: RangeFrom<usize>) -> 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<ExpressionId>) = 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),
}
}
}

View File

@@ -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<BraceKind>
}
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<ParsingFSMInputs>, expected_actions: Vec<ParsingFSMAction>) {
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
]
);
}
}

6
core-qmcc/src/main.rs Normal file
View File

@@ -0,0 +1,6 @@
mod logic_expression;
mod test;
fn main() {
println!("Hello, world!");
}

0
core-qmcc/src/test.rs Normal file
View File