Started qmcc rust core libary
This commit is contained in:
1
core-qmcc/.gitignore
vendored
Normal file
1
core-qmcc/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
125
core-qmcc/Cargo.lock
generated
Normal file
125
core-qmcc/Cargo.lock
generated
Normal 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
8
core-qmcc/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "core-qmcc"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
logos = "0.15.1"
|
||||||
|
anyhow = "1.0.100"
|
||||||
113
core-qmcc/src/logic_expression/ast.rs
Normal file
113
core-qmcc/src/logic_expression/ast.rs
Normal 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 }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
114
core-qmcc/src/logic_expression/lexer.rs
Normal file
114
core-qmcc/src/logic_expression/lexer.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
4
core-qmcc/src/logic_expression/mod.rs
Normal file
4
core-qmcc/src/logic_expression/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
pub mod lexer;
|
||||||
|
mod parser;
|
||||||
|
mod parser_fsm;
|
||||||
|
mod ast;
|
||||||
367
core-qmcc/src/logic_expression/parser.rs
Normal file
367
core-qmcc/src/logic_expression/parser.rs
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
213
core-qmcc/src/logic_expression/parser_fsm.rs
Normal file
213
core-qmcc/src/logic_expression/parser_fsm.rs
Normal 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
6
core-qmcc/src/main.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
mod logic_expression;
|
||||||
|
mod test;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
0
core-qmcc/src/test.rs
Normal file
0
core-qmcc/src/test.rs
Normal file
Reference in New Issue
Block a user