summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2024-05-01 00:51:26 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2024-05-01 10:42:48 +0200
commitd267148ba107e8f7e622e1722c03c80abb51a65f (patch)
treee37c61e7c867d3b8a58b1a2a29361442bc513b27
parent23f2fb9ddca6801388b3e4debb66d1db673ec38c (diff)
downloadrebel-d267148ba107e8f7e622e1722c03c80abb51a65f.tar
rebel-d267148ba107e8f7e622e1722c03c80abb51a65f.zip
rebel-parse: handle more pattern types
Add support for parsing parentheses, field access and index operations in patterns, and distinguish let and destructuring pattern.
-rw-r--r--crates/rebel-lang/src/scope.rs30
-rw-r--r--crates/rebel-parse/src/ast/expr.rs19
-rw-r--r--crates/rebel-parse/src/ast/mod.rs16
-rw-r--r--crates/rebel-parse/src/ast/pat.rs15
-rw-r--r--crates/rebel-parse/src/grammar/recipe.rs24
5 files changed, 80 insertions, 24 deletions
diff --git a/crates/rebel-lang/src/scope.rs b/crates/rebel-lang/src/scope.rs
index 4b47435..3f2e682 100644
--- a/crates/rebel-lang/src/scope.rs
+++ b/crates/rebel-lang/src/scope.rs
@@ -42,7 +42,14 @@ impl Default for Context {
}
impl Context {
- pub fn local_ident<'a>(path: &'a ast::Path) -> Option<ast::Ident<'a>> {
+ fn pat_ident<'a>(pat: &pat::Pat<'a>) -> ast::Ident<'a> {
+ match pat {
+ pat::Pat::Paren(subpat) => Self::pat_ident(subpat),
+ pat::Pat::Ident(ident) => *ident,
+ }
+ }
+
+ fn local_ident<'a>(path: &'a ast::Path) -> Option<ast::Ident<'a>> {
if path.root != ast::PathRoot::Relative {
return None;
}
@@ -82,8 +89,7 @@ impl Context {
ast::BlockStmt::Let { dest, expr } => {
let ast::TypedPat { pat, typ } = dest.as_ref();
- let pat::Pat::Path(dest_path) = pat;
- let dest_ident = Context::local_ident(dest_path).ok_or(TypeError)?;
+ let dest_ident = Self::pat_ident(pat);
let explicit_type = if let Some(typ) = typ {
Type::ast_type(self, typ)?
@@ -98,7 +104,6 @@ impl Context {
};
// TODO: Lexical scoping
- // TODO: Free fixed array length
if dest_ident.name != "_" {
self.vars.insert(
@@ -112,8 +117,11 @@ impl Context {
}
}
ast::BlockStmt::Assign { dest, expr } => {
- let pat::Pat::Path(dest_path) = dest.as_ref();
- let dest_ident = Context::local_ident(dest_path).ok_or(TypeError)?;
+ let pat::DestrPat::Path(dest_path) = dest.as_ref() else {
+ // TODO: Other pattern types
+ return Err(TypeError);
+ };
+ let dest_ident = Self::local_ident(dest_path).ok_or(TypeError)?;
let expr_type = Type::ast_expr_type(self, expr)?;
@@ -144,8 +152,7 @@ impl Context {
ast::BlockStmt::Let { dest, expr } => {
let ast::TypedPat { pat, typ } = dest.as_ref();
- let pat::Pat::Path(dest_path) = pat;
- let dest_ident = Context::local_ident(dest_path).expect("Type error during eval");
+ let dest_ident = Self::pat_ident(pat);
let explicit_type = if let Some(typ) = typ {
Type::ast_type(self, typ).expect("Type error during eval")
@@ -180,8 +187,11 @@ impl Context {
}
}
ast::BlockStmt::Assign { dest, expr } => {
- let pat::Pat::Path(dest_path) = dest.as_ref();
- let dest_ident = Context::local_ident(dest_path).expect("Type error during eval");
+ let pat::DestrPat::Path(dest_path) = dest.as_ref() else {
+ // TODO: Other pattern types
+ panic!("Type error during eval");
+ };
+ let dest_ident = Self::local_ident(dest_path).expect("Type error during eval");
let value = Value::eval(self, expr)?;
let expr_type = value.typ().or(Err(EvalError))?;
diff --git a/crates/rebel-parse/src/ast/expr.rs b/crates/rebel-parse/src/ast/expr.rs
index 4e386e7..fda37e8 100644
--- a/crates/rebel-parse/src/ast/expr.rs
+++ b/crates/rebel-parse/src/ast/expr.rs
@@ -1,6 +1,6 @@
use std::collections::HashSet;
-use super::{Ident, Path, ValidationError};
+use super::{DestrPat, Ident, Path, ValidationError};
use crate::token;
pub use token::StrKind;
@@ -142,6 +142,23 @@ impl<'a> Expr<'a> {
}
}
+impl<'a> From<&DestrPat<'a>> for Expr<'a> {
+ fn from(value: &DestrPat<'a>) -> Self {
+ match value {
+ DestrPat::Index { pat, index } => Expr::Index {
+ expr: Box::new(pat.as_ref().into()),
+ index: index.clone(),
+ },
+ DestrPat::Field { pat, field } => Expr::Field {
+ expr: Box::new(pat.as_ref().into()),
+ field: *field,
+ },
+ DestrPat::Paren(pat) => Expr::Paren(Box::new(pat.as_ref().into())),
+ DestrPat::Path(path) => Expr::Path(path.clone()),
+ }
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Literal<'a> {
Unit,
diff --git a/crates/rebel-parse/src/ast/mod.rs b/crates/rebel-parse/src/ast/mod.rs
index eb6a7bd..1afa9a1 100644
--- a/crates/rebel-parse/src/ast/mod.rs
+++ b/crates/rebel-parse/src/ast/mod.rs
@@ -2,10 +2,12 @@ pub mod expr;
pub mod pat;
pub mod typ;
-use expr::*;
-use pat::Pat;
+use expr::Expr;
+use pat::DestrPat;
use typ::Type;
+use self::pat::Pat;
+
pub type Recipe<'a> = Vec<RecipeStmt<'a>>;
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -58,7 +60,7 @@ pub enum BlockStmt<'a> {
expr: Option<Box<Expr<'a>>>,
},
Assign {
- dest: Box<Pat<'a>>,
+ dest: Box<DestrPat<'a>>,
expr: Box<Expr<'a>>,
},
Expr {
@@ -76,16 +78,14 @@ impl<'a> BlockStmt<'a> {
}
pub(crate) fn assign(
- dest: Pat<'a>,
- op: Option<OpBinary>,
+ dest: DestrPat<'a>,
+ op: Option<expr::OpBinary>,
swapped: bool,
expr: Expr<'a>,
) -> Self {
let expr = match op {
Some(op) => {
- let dest_expr = match &dest {
- Pat::Path(path) => Expr::Path(path.clone()),
- };
+ let dest_expr = Expr::from(&dest);
if swapped {
Expr::binary(expr, op, dest_expr)
} else {
diff --git a/crates/rebel-parse/src/ast/pat.rs b/crates/rebel-parse/src/ast/pat.rs
index a376956..d1baf68 100644
--- a/crates/rebel-parse/src/ast/pat.rs
+++ b/crates/rebel-parse/src/ast/pat.rs
@@ -2,5 +2,20 @@ use super::*;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Pat<'a> {
+ Paren(Box<Pat<'a>>),
+ Ident(Ident<'a>),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum DestrPat<'a> {
+ Index {
+ pat: Box<DestrPat<'a>>,
+ index: Box<Expr<'a>>,
+ },
+ Field {
+ pat: Box<DestrPat<'a>>,
+ field: Ident<'a>,
+ },
+ Paren(Box<DestrPat<'a>>),
Path(Path<'a>),
}
diff --git a/crates/rebel-parse/src/grammar/recipe.rs b/crates/rebel-parse/src/grammar/recipe.rs
index 68429cb..8903eec 100644
--- a/crates/rebel-parse/src/grammar/recipe.rs
+++ b/crates/rebel-parse/src/grammar/recipe.rs
@@ -2,7 +2,7 @@ use crate::{
ast::{
self,
expr::{self, Expr},
- pat::Pat,
+ pat::{DestrPat, Pat},
typ::{self, Type},
},
token::*,
@@ -40,10 +40,10 @@ peg::parser! {
/ [Token::Keyword(Keyword::Let)] dest:typed_pat() {
ast::BlockStmt::let_assign(dest, None)
}
- / dest:pat() op:assign_op() expr:expr() {
+ / dest:destr_pat() op:assign_op() expr:expr() {
ast::BlockStmt::assign(dest, op, false, expr)
}
- / dest:pat() p2('=', '+') expr:expr() {
+ / dest:destr_pat() p2('=', '+') expr:expr() {
ast::BlockStmt::assign(dest, Some(Add), true, expr)
}
/ expr:expr() {
@@ -79,8 +79,22 @@ peg::parser! {
typ::Literal::Struct(entries)
}
- pub rule pat() -> Pat<'a>
- = path:path() { Pat::Path(path) }
+ pub rule pat() -> ast::pat::Pat<'a>
+ = p('(') pat:pat() p(')') { Pat::Paren(Box::new(pat)) }
+ / ident:ident() { Pat::Ident(ident) }
+
+ pub rule destr_pat() -> DestrPat<'a> = precedence! {
+ pat:@ p('[') index:expr() p(']') {
+ DestrPat::Index { pat: Box::new(pat), index: Box::new(index) }
+ }
+ --
+ pat:@ p('.') field:field() {
+ DestrPat::Field { pat: Box::new(pat), field }
+ }
+ --
+ p('(') pat:destr_pat() p(')') { DestrPat::Paren(Box::new(pat)) }
+ path:path() { DestrPat::Path(path) }
+ }
rule struct_field_typ() -> typ::StructField<'a>
= field:field() p(':') typ:typ() {