quantum_queries/src/vm/parsing/mod.rs

363 lines
12 KiB
Rust

pub mod ast;
use self::ast::*;
use pest::Parser;
#[derive(Parser)]
#[grammar = "vm/parsing/grammar.pest"]
struct ExpressionParser;
pub fn parse_relation(expression: &str) -> Result<ast::BooleanExpression, String> {
let parse_result = ExpressionParser::parse(Rule::relation, expression);
if let Err(e) = parse_result {
return Err(parse_error_to_human(expression, e));
}
let main_expr = parse_result
.unwrap()
.next()
.unwrap()
.into_inner()
.next()
.unwrap();
let main_expr = match main_expr.as_rule() {
Rule::boolean_expression => parse_boolean_expression(main_expr),
_ => unreachable!(),
};
Ok(main_expr)
}
pub fn parse_arithmetic(expression: &str) -> Result<ast::ArithmeticExpression, String> {
let parse_result = ExpressionParser::parse(Rule::conjecture, expression);
if let Err(e) = parse_result {
return Err(parse_error_to_human(expression, e));
}
let main_expr = parse_result
.unwrap()
.next()
.unwrap()
.into_inner()
.next()
.unwrap();
let main_expr = match main_expr.as_rule() {
Rule::arithmetic_expression => parse_arithmetic_expression(main_expr),
_ => unreachable!(),
};
Ok(main_expr)
}
fn parse_error_to_human(expression: &str, error: pest::error::Error<Rule>) -> String {
let mut error_msg = String::new();
error_msg.push_str("Invalid expression:\n");
match error.line_col {
pest::error::LineColLocation::Pos((line, col)) => {
with_caret(error.line(), line, col, "In ", &mut error_msg);
error_msg.push('\n');
}
pest::error::LineColLocation::Span((sline, scol), (eline, ecol)) => {
let (start_line, end_line) = {
let mut lines = expression.lines();
(lines.nth(sline).unwrap(), lines.nth(eline - sline).unwrap())
};
with_caret(start_line, sline, scol, "Starting at ", &mut error_msg);
with_caret(end_line, eline, ecol, "until ", &mut error_msg);
error_msg.push('\n');
}
}
match error.variant {
pest::error::ErrorVariant::ParsingError {
positives,
negatives,
} => {
match positives.len() {
0 => {}
1 => {
error_msg.push_str("Expected ");
error_msg.push_str(rule_as_text(&positives[0]));
error_msg.push('.');
}
2 => {
error_msg.push_str("Expected either ");
error_msg.push_str(rule_as_text(&positives[0]));
error_msg.push_str("or ");
error_msg.push_str(rule_as_text(&positives[1]));
error_msg.push('.');
}
_ => {
error_msg.push_str("Expected one of ");
for rule in &positives[..positives.len() - 1] {
error_msg.push_str(rule_as_text(rule));
error_msg.push_str(", ");
}
error_msg.push_str("or ");
error_msg.push_str(rule_as_text(&positives.last().unwrap()));
error_msg.push('.');
}
}
match negatives.len() {
0 => {}
1 => {
error_msg.push_str("Did not expect ");
error_msg.push_str(rule_as_text(&negatives[0]));
error_msg.push('.');
}
_ => {
error_msg.push_str("Did not expect any of ");
for rule in &negatives[..negatives.len() - 1] {
error_msg.push_str(rule_as_text(rule));
error_msg.push_str(", ");
}
error_msg.push_str("nor ");
error_msg.push_str(rule_as_text(&negatives.last().unwrap()));
error_msg.push('.');
}
}
}
pest::error::ErrorVariant::CustomError { message } => {
error_msg.push_str(&message);
}
}
return error_msg;
}
fn parse_boolean_expression(rule: pest::iterators::Pair<Rule>) -> BooleanExpression {
let inner = rule.into_inner().next().unwrap();
match inner.as_rule() {
Rule::binary_boolean_conjunction => BooleanExpression::BinaryBooleanConjunction(Box::new(
parse_binary_boolean_conjunction(inner),
)),
Rule::unary_boolean_conjunction => BooleanExpression::UnaryBooleanConjunction(Box::new(
parse_unary_boolean_conjunction(inner),
)),
Rule::comparison_expression => {
BooleanExpression::ComparisonConjunction(Box::new(parse_comparison_expression(inner)))
}
_ => unreachable!(),
}
}
fn parse_comparison_expression(rule: pest::iterators::Pair<Rule>) -> ComparisonConjunction {
let mut inner = rule.into_inner();
let operator = parse_comparison_operator(inner.next().unwrap());
let left_operand = parse_arithmetic_operand(inner.next().unwrap());
let right_operand = parse_arithmetic_operand(inner.next().unwrap());
let conjunction = ComparisonConjunction {
operator,
left_operand,
right_operand,
};
conjunction
}
fn parse_comparison_operator(rule: pest::iterators::Pair<Rule>) -> ComparisonOperator {
match rule.as_str().trim() {
">=" => ComparisonOperator::GreaterOrEqual,
"<=" => ComparisonOperator::LessOrEqual,
">" => ComparisonOperator::GreaterThan,
"<" => ComparisonOperator::LessThan,
"=" => ComparisonOperator::Equal,
"!=" => ComparisonOperator::NotEqual,
_ => unreachable!(),
}
}
fn parse_binary_arithmetic_conjunction(
rule: pest::iterators::Pair<Rule>,
) -> BinaryArithmeticConjunction {
let mut inner = rule.into_inner();
let operator = parse_binary_arithmetic_operator(inner.next().unwrap());
let left_operand = parse_arithmetic_operand(inner.next().unwrap());
let right_operand = parse_arithmetic_operand(inner.next().unwrap());
let conjunction = BinaryArithmeticConjunction {
operator,
left_operand,
right_operand,
};
conjunction
}
fn parse_arithmetic_operand(rule: pest::iterators::Pair<Rule>) -> ArithmeticOperand {
let inner = rule.into_inner().next().unwrap();
match inner.as_rule() {
Rule::number_literal => {
let inner = inner.as_str().trim().parse::<i64>().unwrap();
ArithmeticOperand::Literal(inner)
}
Rule::arithmetic_expression => {
ArithmeticOperand::Expression(Box::new(parse_arithmetic_expression(inner)))
}
_ => unreachable!(),
}
}
fn parse_arithmetic_expression(rule: pest::iterators::Pair<Rule>) -> ArithmeticExpression {
let inner = rule.into_inner().next().unwrap();
match inner.as_rule() {
Rule::variable => ArithmeticExpression::Variable(parse_variable(inner)),
Rule::unary_arithmetic_conjunction => ArithmeticExpression::UnaryArithmeticConjunction(
Box::new(parse_unary_arithmetic_conjunction(inner)),
),
Rule::binary_arithmetic_conjunction => ArithmeticExpression::BinaryArithmeticConjunction(
Box::new(parse_binary_arithmetic_conjunction(inner)),
),
_ => unreachable!(),
}
}
fn parse_unary_arithmetic_conjunction(
rule: pest::iterators::Pair<Rule>,
) -> UnaryArithmeticConjunction {
let mut inner = rule.into_inner();
let operator = parse_unary_arithmetic_operator(inner.next().unwrap());
let operand = parse_arithmetic_operand(inner.next().unwrap());
let conjunction = UnaryArithmeticConjunction { operator, operand };
conjunction
}
fn parse_unary_arithmetic_operator(
rule: pest::iterators::Pair<Rule>,
) -> ast::UnaryArithmeticOperator {
match rule.as_str().trim() {
"neg" => ast::UnaryArithmeticOperator::Negative,
"ham" => ast::UnaryArithmeticOperator::Ham,
"sqrt" => ast::UnaryArithmeticOperator::Sqrt,
_ => unreachable!(),
}
}
fn parse_variable(rule: pest::iterators::Pair<Rule>) -> ast::Variable {
match rule.as_str().trim() {
"x" => ast::Variable::X,
"y" => ast::Variable::Y,
"n" => ast::Variable::N,
"p" => ast::Variable::P,
"k" => ast::Variable::K,
_ => unreachable!(),
}
}
fn parse_binary_arithmetic_operator(rule: pest::iterators::Pair<Rule>) -> BinaryArithmeticOperator {
match rule.as_str().trim() {
"*" => BinaryArithmeticOperator::Times,
"/" => BinaryArithmeticOperator::Divide,
"+" => BinaryArithmeticOperator::Plus,
"-" => BinaryArithmeticOperator::Minus,
"^" => BinaryArithmeticOperator::Xor,
"pow" => BinaryArithmeticOperator::Pow,
_ => unreachable!(),
}
}
fn parse_unary_boolean_conjunction(rule: pest::iterators::Pair<Rule>) -> UnaryBooleanConjunction {
let mut inner = rule.into_inner();
let operator = parse_unary_boolean_operator(inner.next().unwrap());
let operand = parse_boolean_expression(inner.next().unwrap());
let conjunction = UnaryBooleanConjunction { operator, operand };
conjunction
}
fn parse_unary_boolean_operator(rule: pest::iterators::Pair<Rule>) -> UnaryBooleanOperator {
match rule.as_str().trim() {
"not" => UnaryBooleanOperator::Not,
_ => unreachable!(),
}
}
fn parse_binary_boolean_conjunction(rule: pest::iterators::Pair<Rule>) -> BinaryBooleanConjunction {
let mut inner = rule.into_inner();
let operator = parse_binary_boolean_operator(inner.next().unwrap());
let left_operand = parse_boolean_expression(inner.next().unwrap());
let right_operand = parse_boolean_expression(inner.next().unwrap());
let conjunction = ast::BinaryBooleanConjunction {
operator,
left_operand,
right_operand,
};
conjunction
}
fn parse_binary_boolean_operator(rule: pest::iterators::Pair<Rule>) -> ast::BinaryBooleanOperator {
match rule.as_str().trim() {
"and" => ast::BinaryBooleanOperator::And,
"or" => ast::BinaryBooleanOperator::Or,
"xor" => ast::BinaryBooleanOperator::Xor,
_ => unreachable!(),
}
}
fn with_caret(line: &str, line_number: usize, column: usize, preamble: &str, into: &mut String) {
into.push_str(&format!("{}l.{}: {}\n", preamble, line_number, line));
let padding = preamble.len() + 2 + (line_number % 10) + 2;
for _ in 0..(column - 1 + padding) {
into.push(' ');
}
into.push('^');
}
fn rule_as_text(rule: &Rule) -> &'static str {
match rule {
Rule::EOI => "end of input",
Rule::WHITESPACE => "whitespace",
Rule::relation => "relation (boolean) expression",
Rule::conjecture => "conjecture (arithmetic) expression",
Rule::boolean_expression => "boolean expression",
Rule::binary_boolean_conjunction => "binary boolean relation",
Rule::binary_boolean_operator => "binary boolean operator",
Rule::unary_boolean_conjunction => "unary boolean expression",
Rule::boolean_unary_operator => "unary boolean operator",
Rule::binary_arithmetic_conjunction => "binary arithmetic expression",
Rule::binary_arithmetic_operator => "binary arithmetic operator",
Rule::arithmetic_operand => "arithmetic value",
Rule::number_literal => "number literal",
Rule::arithmetic_expression => "arithmetic expression",
Rule::variable => "x, y, n, or p",
Rule::unary_arithmetic_conjunction => "unary arithmetic expression",
Rule::unary_arithmetic_operator => "unary arithmetic operator",
Rule::comparison_expression => "arithmetic comparison",
Rule::comparison_operator => "arithmetic comparison operator",
}
}
#[test]
fn parse_bad() {
match parse_relation("< ham x cheese") {
Err(e) => {
println!("{}", e);
}
Ok(ast) => {
println!("{:?}", ast);
panic!();
}
}
}
#[test]
fn parse_test() {
match parse_relation("< ham x 3") {
Err(e) => {
println!("{}", e);
panic!();
}
Ok(ast) => {
println!("{:#?}", ast)
}
}
}
#[test]
fn parse_weird_test() {
match parse_relation("and and < x 1 != 1 y < ham x k") {
Err(e) => {
println!("{}", e);
panic!();
}
Ok(ast) => {
println!("{:?}", ast)
}
}
}