diff options
author | Matthias Schiffer <mschiffer@universe-factory.net> | 2024-05-01 00:51:26 +0200 |
---|---|---|
committer | Matthias Schiffer <mschiffer@universe-factory.net> | 2024-05-01 10:42:48 +0200 |
commit | d267148ba107e8f7e622e1722c03c80abb51a65f (patch) | |
tree | e37c61e7c867d3b8a58b1a2a29361442bc513b27 | |
parent | 23f2fb9ddca6801388b3e4debb66d1db673ec38c (diff) | |
download | rebel-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.rs | 30 | ||||
-rw-r--r-- | crates/rebel-parse/src/ast/expr.rs | 19 | ||||
-rw-r--r-- | crates/rebel-parse/src/ast/mod.rs | 16 | ||||
-rw-r--r-- | crates/rebel-parse/src/ast/pat.rs | 15 | ||||
-rw-r--r-- | crates/rebel-parse/src/grammar/recipe.rs | 24 |
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() { |