summaryrefslogtreecommitdiffstats
path: root/crates
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2024-04-28 12:02:28 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2024-04-28 13:18:32 +0200
commit6d09f6c30d05a8da65fdfc7be8ccb11ee069eb2b (patch)
treeaed432374b4ef14621d2c0ce4b16658e053f41d2 /crates
parent1ddda7de7458ac4bfc67936ffb129eb52f0bed72 (diff)
downloadrebel-6d09f6c30d05a8da65fdfc7be8ccb11ee069eb2b.tar
rebel-6d09f6c30d05a8da65fdfc7be8ccb11ee069eb2b.zip
rebel-parse: ast: add post-parse validation
Diffstat (limited to 'crates')
-rw-r--r--crates/rebel-lang/examples/repl.rs14
-rw-r--r--crates/rebel-parse/src/ast.rs110
2 files changed, 124 insertions, 0 deletions
diff --git a/crates/rebel-lang/examples/repl.rs b/crates/rebel-lang/examples/repl.rs
index e4c2550..185155b 100644
--- a/crates/rebel-lang/examples/repl.rs
+++ b/crates/rebel-lang/examples/repl.rs
@@ -96,6 +96,13 @@ fn main() -> rustyline::Result<()> {
match &stmt {
rebel_parse::ast::BodyStmt::Assign { dest: _, expr } => {
+ match expr.validate() {
+ Ok(_) => (),
+ Err(err) => {
+ println!("Validation error: {err:?}");
+ continue;
+ }
+ };
match Type::ast_expr_type(&ctx, expr) {
Ok(_) => (),
Err(err) => {
@@ -105,6 +112,13 @@ fn main() -> rustyline::Result<()> {
};
}
rebel_parse::ast::BodyStmt::Expr { expr } => {
+ match expr.validate() {
+ Ok(_) => (),
+ Err(err) => {
+ println!("Validation error: {err:?}");
+ continue;
+ }
+ };
match Type::ast_expr_type(&ctx, expr) {
Ok(_) => (),
Err(err) => {
diff --git a/crates/rebel-parse/src/ast.rs b/crates/rebel-parse/src/ast.rs
index 90c02f5..1dcbdd9 100644
--- a/crates/rebel-parse/src/ast.rs
+++ b/crates/rebel-parse/src/ast.rs
@@ -1,3 +1,5 @@
+use std::collections::HashSet;
+
use crate::token;
pub type Recipe<'a> = Vec<RecipeStmt<'a>>;
@@ -121,6 +123,60 @@ impl<'a> Expr<'a> {
pub(crate) fn paren(expr: Expr<'a>) -> Self {
Expr::Paren(Box::new(expr))
}
+
+ pub fn validate(&self) -> Result<(), ValidationError> {
+ match self {
+ Expr::Binary { left, op, right } => {
+ left.validate()?;
+ right.validate()?;
+
+ if op.is_comparision()
+ && (left.is_binary_comparison() || right.is_binary_comparison())
+ {
+ return Err(ValidationError::NeedsParens);
+ }
+ Ok(())
+ }
+ Expr::Unary { op: _, expr } => expr.validate(),
+ Expr::Apply { expr, params } => {
+ for param in params {
+ param.validate()?;
+ }
+ expr.validate()
+ }
+ Expr::Method {
+ expr,
+ method: _,
+ params,
+ } => {
+ for param in params {
+ param.validate()?;
+ }
+ expr.validate()
+ }
+ Expr::Index { expr, index } => {
+ index.validate()?;
+ expr.validate()
+ }
+ Expr::Field { expr, field: _ } => expr.validate(),
+ Expr::Paren(expr) => expr.validate(),
+ Expr::Path(_) => Ok(()),
+ Expr::Literal(lit) => lit.validate(),
+ }
+ }
+
+ fn is_binary_comparison(&self) -> bool {
+ let Expr::Binary {
+ left: _,
+ op,
+ right: _,
+ } = self
+ else {
+ return false;
+ };
+
+ op.is_comparision()
+ }
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -166,6 +222,46 @@ impl<'a> Literal<'a> {
let value = u64::from_str_radix(&digits, radix).or(Err("number"))?;
Ok(Literal::Int(value))
}
+
+ fn validate(&self) -> Result<(), ValidationError> {
+ match self {
+ Literal::Unit => Ok(()),
+ Literal::Bool(_) => Ok(()),
+ Literal::Int(_) => Ok(()),
+ Literal::Str { pieces, kind: _ } => {
+ for piece in pieces {
+ match piece {
+ StrPiece::Chars(_) => {}
+ StrPiece::Escape(_) => {}
+ StrPiece::Interp(expr) => expr.validate()?,
+ }
+ }
+ Ok(())
+ }
+ Literal::Tuple(elems) => {
+ for elem in elems {
+ elem.validate()?;
+ }
+ Ok(())
+ }
+ Literal::Array(elems) => {
+ for elem in elems {
+ elem.validate()?;
+ }
+ Ok(())
+ }
+ Literal::Map(entries) => {
+ let mut keys = HashSet::new();
+ for MapEntry { key, value } in entries {
+ if !keys.insert(key) {
+ return Err(ValidationError::DuplicateKey);
+ }
+ value.validate()?;
+ }
+ Ok(())
+ }
+ }
+ }
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -220,6 +316,14 @@ pub enum OpBinary {
Gt,
}
+impl OpBinary {
+ fn is_comparision(self) -> bool {
+ use OpBinary::*;
+
+ matches!(self, Eq | Lt | Le | Ne | Ge | Gt)
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Path<'a> {
pub components: Vec<Ident<'a>>,
@@ -229,3 +333,9 @@ pub struct Path<'a> {
pub struct Ident<'a> {
pub name: &'a str,
}
+
+#[derive(Debug, Clone, Copy)]
+pub enum ValidationError {
+ DuplicateKey,
+ NeedsParens,
+}