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"] } chumsky = { version = "1.0.0-alpha.6", features = ["default", "label"] }
derive_more = "0.99.17" derive_more = "0.99.17"
enum_dispatch = "0.3.12" enum_dispatch = "0.3.12"
hashbrown = "0.14.1"
itertools = "0.11" itertools = "0.11"
num-traits = "0.2.16" num-traits = "0.2.16"
paste = "1.0.14" paste = "1.0.14"

View file

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

View file

@ -16,8 +16,39 @@ pub use crate::core::{expr, signals};
pub use crate::parser::parse_str; pub use crate::parser::parse_str;
pub use crate::semantics::{BooleanSemantics, QuantitativeSemantics, Trace}; 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. /// Errors generated by all Argus components.
#[derive(Error, Debug)] #[derive(Error, Debug, PartialEq, Eq)]
pub enum Error { pub enum Error {
/// An identifier has been redeclared in a specification. /// 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 // A parser for numbers
let digits = text::digits(10).to_slice(); 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 exp = just('e').or(just('E')).then(one_of("+-").or_not()).then(digits);
let floating_number = just('-') let floating_number = just('-')
.or_not() .or_not()
.then(digits) .then(digits.or_not())
.then(choice((frac.then(exp).to_slice(), frac.to_slice(), exp.to_slice()))) .then(choice((frac.then(exp).to_slice(), frac.to_slice(), exp.to_slice())))
// .then(frac.or_not()) // .then(frac.or_not())
// .then(exp.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("+-") let signed_int = one_of("+-")
// .or_not() // .or_not()
.then(digits) .then(digits)
.then(frac.not().or(exp.not()))
.to_slice() .to_slice()
.map(|s: &str| Token::Int(s.parse().unwrap())); .map(|s: &str| Token::Int(s.parse().unwrap()));
let unsigned_int = digits let unsigned_int = digits.to_slice().map(|s: &str| Token::UInt(s.parse().unwrap()));
.then(frac.not().or(exp.not()))
.to_slice()
.map(|s: &str| Token::UInt(s.parse().unwrap()));
let number = choice((floating_number, signed_int, unsigned_int)); let number = choice((floating_number, signed_int, unsigned_int));

View file

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

View file

@ -3,40 +3,10 @@ use chumsky::prelude::*;
use chumsky::Parser; use chumsky::Parser;
use super::lexer::{Span, Token}; use super::lexer::{Span, Token};
use crate::Type;
#[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
}
}
}
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Interval<'src> { pub(crate) struct Interval<'src> {
pub a: Option<Box<Spanned<Expr<'src>>>>, pub a: Option<Box<Spanned<Expr<'src>>>>,
pub b: Option<Box<Spanned<Expr<'src>>>>, pub b: Option<Box<Spanned<Expr<'src>>>>,
} }
@ -98,7 +68,7 @@ impl BinaryOps {
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Expr<'src> { pub(crate) enum Expr<'src> {
Error, Error,
Bool(bool), Bool(bool),
Int(i64), 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 { ) -> impl Parser<'tokens, ParserInput<'tokens, 'src>, Spanned<Expr<'src>>, Error<'tokens, 'src>> + Clone {
let interval = { let interval = {
let num_literal = select! { let num_literal = select! {
@ -327,6 +297,7 @@ pub fn parser<'tokens, 'src: 'tokens>(
[], [],
|span| (Expr::Error, span), |span| (Expr::Error, span),
))) )))
.or(expr)
.boxed(); .boxed();
let not_op = just(Token::Not) let not_op = just(Token::Not)