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