fix(argus): parse errors

This commit is contained in:
Anand Balakrishnan 2023-10-11 17:49:56 -07:00
parent 9d0f7ef2ee
commit 019797f344
6 changed files with 64 additions and 67 deletions

View file

@ -14,6 +14,7 @@ ariadne = { version = "0.3.0", optional = true }
chumsky = { version = "1.0.0-alpha.6", features = ["default", "label"] }
derive_more = "0.99.17"
enum_dispatch = "0.3.12"
hashbrown = "0.14.1"
itertools = "0.11"
num-traits = "0.2.16"
paste = "1.0.14"

View file

@ -1,6 +1,6 @@
//! Expression tree for Argus specifications
use std::collections::HashSet;
use hashbrown::HashMap;
mod bool_expr;
pub mod iter;
@ -13,7 +13,7 @@ pub use num_expr::*;
pub use traits::*;
use self::iter::AstIter;
use crate::{ArgusResult, Error};
use crate::{ArgusResult, Error, Type};
/// A trait representing expressions
#[enum_dispatch]
@ -155,7 +155,7 @@ pub enum Expr {
/// definitions for variables.
#[derive(Clone, Debug, Default)]
pub struct ExprBuilder {
declarations: HashSet<String>,
pub(crate) declarations: HashMap<String, Type>,
}
impl ExprBuilder {
@ -188,37 +188,33 @@ impl ExprBuilder {
/// Declare a boolean variable
pub fn bool_var(&mut self, name: String) -> ArgusResult<BoolExpr> {
if self.declarations.insert(name.clone()) {
Ok((BoolVar { name }).into())
} else {
Err(Error::IdentifierRedeclaration)
match self.declarations.insert(name.clone(), Type::Bool) {
None | Some(Type::Bool) => Ok((BoolVar { name }).into()),
_ => Err(Error::IdentifierRedeclaration),
}
}
/// Declare a integer variable
pub fn int_var(&mut self, name: String) -> ArgusResult<NumExpr> {
if self.declarations.insert(name.clone()) {
Ok((IntVar { name }).into())
} else {
Err(Error::IdentifierRedeclaration)
match self.declarations.insert(name.clone(), Type::Int) {
None | Some(Type::Int) => Ok((IntVar { name }).into()),
_ => Err(Error::IdentifierRedeclaration),
}
}
/// Declare a unsigned integer variable
pub fn uint_var(&mut self, name: String) -> ArgusResult<NumExpr> {
if self.declarations.insert(name.clone()) {
Ok((UIntVar { name }).into())
} else {
Err(Error::IdentifierRedeclaration)
match self.declarations.insert(name.clone(), Type::UInt) {
None | Some(Type::UInt) => Ok((UIntVar { name }).into()),
_ => Err(Error::IdentifierRedeclaration),
}
}
/// Declare a floating point variable
pub fn float_var(&mut self, name: String) -> ArgusResult<NumExpr> {
if self.declarations.insert(name.clone()) {
Ok((FloatVar { name }).into())
} else {
Err(Error::IdentifierRedeclaration)
match self.declarations.insert(name.clone(), Type::Float) {
None | Some(Type::Float) => Ok((FloatVar { name }).into()),
_ => Err(Error::IdentifierRedeclaration),
}
}

View file

@ -16,8 +16,39 @@ pub use crate::core::{expr, signals};
pub use crate::parser::parse_str;
pub use crate::semantics::{BooleanSemantics, QuantitativeSemantics, Trace};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub(crate) enum Type {
#[default]
Unknown,
Bool,
UInt,
Int,
Float,
}
impl Type {
/// Get the lowest common supertype for the given types to a common
fn get_common_cast(self, other: Self) -> Self {
use Type::*;
match (self, other) {
(Unknown, other) | (other, Unknown) => other,
(Bool, ty) | (ty, Bool) => ty,
(UInt, Int) => Float,
(UInt, Float) => Float,
(Int, UInt) => Float,
(Int, Float) => Float,
(Float, UInt) => Float,
(Float, Int) => Float,
(lhs, rhs) => {
assert_eq!(lhs, rhs);
rhs
}
}
}
}
/// Errors generated by all Argus components.
#[derive(Error, Debug)]
#[derive(Error, Debug, PartialEq, Eq)]
pub enum Error {
/// An identifier has been redeclared in a specification.
///

View file

@ -87,13 +87,13 @@ pub fn lexer<'src>() -> impl Parser<'src, &'src str, Output<'src>, Error<'src>>
// A parser for numbers
let digits = text::digits(10).to_slice();
let frac = just('.').then(digits);
let frac = just('.').then(digits.or_not());
let exp = just('e').or(just('E')).then(one_of("+-").or_not()).then(digits);
let floating_number = just('-')
.or_not()
.then(digits)
.then(digits.or_not())
.then(choice((frac.then(exp).to_slice(), frac.to_slice(), exp.to_slice())))
// .then(frac.or_not())
// .then(exp.or_not())
@ -104,13 +104,9 @@ pub fn lexer<'src>() -> impl Parser<'src, &'src str, Output<'src>, Error<'src>>
let signed_int = one_of("+-")
// .or_not()
.then(digits)
.then(frac.not().or(exp.not()))
.to_slice()
.map(|s: &str| Token::Int(s.parse().unwrap()));
let unsigned_int = digits
.then(frac.not().or(exp.not()))
.to_slice()
.map(|s: &str| Token::UInt(s.parse().unwrap()));
let unsigned_int = digits.to_slice().map(|s: &str| Token::UInt(s.parse().unwrap()));
let number = choice((floating_number, signed_int, unsigned_int));

View file

@ -10,6 +10,8 @@ use chumsky::prelude::Rich;
use lexer::{lexer, Token};
use syntax::{parser, Expr, Interval};
use crate::Type;
/// Parse a string expression into a concrete Argus expression.
pub fn parse_str(src: &str) -> Result<crate::core::expr::Expr, Vec<Rich<'_, String>>> {
use chumsky::prelude::{Input, Parser};
@ -79,26 +81,26 @@ fn ast_to_expr<'tokens, 'src: 'tokens>(
ctx: &mut ExprBuilder,
) -> Result<crate::core::expr::Expr, Rich<'tokens, Token<'src>, lexer::Span>> {
match ast {
Expr::Error => unreachable!("Errors should have been caught by parser"),
Expr::Error => Err(Rich::custom(span, "Errors should have been caught by parser")),
Expr::Bool(value) => Ok(ctx.bool_const(*value).into()),
Expr::Int(value) => Ok(ctx.int_const(*value).into()),
Expr::UInt(value) => Ok(ctx.uint_const(*value).into()),
Expr::Float(value) => Ok(ctx.float_const(*value).into()),
Expr::Var { name, kind } => match kind {
syntax::Type::Unknown => Err(Rich::custom(span, "All variables must have defined type by now.")),
syntax::Type::Bool => ctx
Type::Unknown => Err(Rich::custom(span, "All variables must have defined type by now.")),
Type::Bool => ctx
.bool_var(name.to_string())
.map(|var| var.into())
.map_err(|err| Rich::custom(span, err.to_string())),
syntax::Type::UInt => ctx
Type::UInt => ctx
.uint_var(name.to_string())
.map(|var| var.into())
.map_err(|err| Rich::custom(span, err.to_string())),
syntax::Type::Int => ctx
Type::Int => ctx
.int_var(name.to_string())
.map(|var| var.into())
.map_err(|err| Rich::custom(span, err.to_string())),
syntax::Type::Float => ctx
Type::Float => ctx
.float_var(name.to_string())
.map(|var| var.into())
.map_err(|err| Rich::custom(span, err.to_string())),

View file

@ -3,40 +3,10 @@ use chumsky::prelude::*;
use chumsky::Parser;
use super::lexer::{Span, Token};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum Type {
#[default]
Unknown,
Bool,
UInt,
Int,
Float,
}
impl Type {
/// Get the lowest common supertype for the given types to a common
fn get_common_cast(self, other: Self) -> Self {
use Type::*;
match (self, other) {
(Unknown, other) | (other, Unknown) => other,
(Bool, ty) | (ty, Bool) => ty,
(UInt, Int) => Float,
(UInt, Float) => Float,
(Int, UInt) => Float,
(Int, Float) => Float,
(Float, UInt) => Float,
(Float, Int) => Float,
(lhs, rhs) => {
assert_eq!(lhs, rhs);
rhs
}
}
}
}
use crate::Type;
#[derive(Debug, Clone, PartialEq)]
pub struct Interval<'src> {
pub(crate) struct Interval<'src> {
pub a: Option<Box<Spanned<Expr<'src>>>>,
pub b: Option<Box<Spanned<Expr<'src>>>>,
}
@ -98,7 +68,7 @@ impl BinaryOps {
}
#[derive(Debug, Clone, PartialEq)]
pub enum Expr<'src> {
pub(crate) enum Expr<'src> {
Error,
Bool(bool),
Int(i64),
@ -258,7 +228,7 @@ fn num_expr_parser<'tokens, 'src: 'tokens>(
})
}
pub fn parser<'tokens, 'src: 'tokens>(
pub(crate) fn parser<'tokens, 'src: 'tokens>(
) -> impl Parser<'tokens, ParserInput<'tokens, 'src>, Spanned<Expr<'src>>, Error<'tokens, 'src>> + Clone {
let interval = {
let num_literal = select! {
@ -327,6 +297,7 @@ pub fn parser<'tokens, 'src: 'tokens>(
[],
|span| (Expr::Error, span),
)))
.or(expr)
.boxed();
let not_op = just(Token::Not)