From f330a7eaa52039d527a034f29acd51ae8c7f4d79 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 19 May 2024 14:31:30 -0500 Subject: [PATCH] conlang: Split assignment into plain Assign and assign-with-Modify --- compiler/cl-ast/src/ast.rs | 16 ++-- compiler/cl-ast/src/ast_impl.rs | 32 +++++--- compiler/cl-ast/src/ast_visitor/fold.rs | 14 +++- compiler/cl-ast/src/ast_visitor/visit.rs | 13 +++- compiler/cl-interpret/src/interpret.rs | 99 ++++++++++++++---------- compiler/cl-parser/src/parser.rs | 25 ++++-- compiler/cl-repl/examples/yaml.rs | 13 +++- grammar.ebnf | 6 +- 8 files changed, 140 insertions(+), 78 deletions(-) diff --git a/compiler/cl-ast/src/ast.rs b/compiler/cl-ast/src/ast.rs index 3239501..b73e7a3 100644 --- a/compiler/cl-ast/src/ast.rs +++ b/compiler/cl-ast/src/ast.rs @@ -346,8 +346,10 @@ pub enum ExprKind { /// An empty expression: `(` `)` #[default] Empty, - /// An [Assign]ment expression: [`Expr`] ([`AssignKind`] [`Expr`])\+ + /// An [Assign]ment expression: [`Expr`] (`=` [`Expr`])\+ Assign(Assign), + /// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+ + Modify(Modify), /// A [Binary] expression: [`Expr`] ([`BinaryKind`] [`Expr`])\+ Binary(Binary), /// A [Unary] expression: [`UnaryKind`]\* [`Expr`] @@ -394,14 +396,18 @@ pub enum ExprKind { /// An [Assign]ment expression: [`Expr`] ([`AssignKind`] [`Expr`])\+ #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Assign { - pub kind: AssignKind, + pub parts: Box<(ExprKind, ExprKind)>, +} + +/// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+ +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Modify { + pub kind: ModifyKind, pub parts: Box<(ExprKind, ExprKind)>, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum AssignKind { - /// Standard Assignment with no read-back - Plain, +pub enum ModifyKind { And, Or, Xor, diff --git a/compiler/cl-ast/src/ast_impl.rs b/compiler/cl-ast/src/ast_impl.rs index 67cdb1a..05cd25e 100644 --- a/compiler/cl-ast/src/ast_impl.rs +++ b/compiler/cl-ast/src/ast_impl.rs @@ -420,6 +420,7 @@ mod display { match self { ExprKind::Empty => "()".fmt(f), ExprKind::Assign(v) => v.fmt(f), + ExprKind::Modify(v) => v.fmt(f), ExprKind::Binary(v) => v.fmt(f), ExprKind::Unary(v) => v.fmt(f), ExprKind::Member(v) => v.fmt(f), @@ -445,26 +446,32 @@ mod display { } impl Display for Assign { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { parts } = self; + write!(f, "{} = {}", parts.0, parts.1) + } + } + + impl Display for Modify { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { kind, parts } = self; write!(f, "{} {kind} {}", parts.0, parts.1) } } - impl Display for AssignKind { + impl Display for ModifyKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - AssignKind::Plain => "=", - AssignKind::Mul => "*=", - AssignKind::Div => "/=", - AssignKind::Rem => "%=", - AssignKind::Add => "+=", - AssignKind::Sub => "-=", - AssignKind::And => "&=", - AssignKind::Or => "|=", - AssignKind::Xor => "^=", - AssignKind::Shl => "<<=", - AssignKind::Shr => ">>=", + ModifyKind::Mul => "*=", + ModifyKind::Div => "/=", + ModifyKind::Rem => "%=", + ModifyKind::Add => "+=", + ModifyKind::Sub => "-=", + ModifyKind::And => "&=", + ModifyKind::Or => "|=", + ModifyKind::Xor => "^=", + ModifyKind::Shl => "<<=", + ModifyKind::Shr => ">>=", } .fmt(f) } @@ -749,6 +756,7 @@ mod convert { } impl From for ExprKind { Assign => ExprKind::Assign, + Modify => ExprKind::Modify, Binary => ExprKind::Binary, Unary => ExprKind::Unary, Member => ExprKind::Member, diff --git a/compiler/cl-ast/src/ast_visitor/fold.rs b/compiler/cl-ast/src/ast_visitor/fold.rs index a2f9926..3a68371 100644 --- a/compiler/cl-ast/src/ast_visitor/fold.rs +++ b/compiler/cl-ast/src/ast_visitor/fold.rs @@ -234,14 +234,19 @@ pub trait Fold { or_fold_expr_kind(self, kind) } fn fold_assign(&mut self, a: Assign) -> Assign { - let Assign { kind, parts } = a; + let Assign { parts } = a; let (head, tail) = *parts; - Assign { - kind: self.fold_assign_kind(kind), + Assign { parts: Box::new((self.fold_expr_kind(head), self.fold_expr_kind(tail))) } + } + fn fold_modify(&mut self, m: Modify) -> Modify { + let Modify { kind, parts } = m; + let (head, tail) = *parts; + Modify { + kind: self.fold_modify_kind(kind), parts: Box::new((self.fold_expr_kind(head), self.fold_expr_kind(tail))), } } - fn fold_assign_kind(&mut self, kind: AssignKind) -> AssignKind { + fn fold_modify_kind(&mut self, kind: ModifyKind) -> ModifyKind { kind } fn fold_binary(&mut self, b: Binary) -> Binary { @@ -522,6 +527,7 @@ pub fn or_fold_expr_kind(folder: &mut F, kind: ExprKind) -> Ex match kind { ExprKind::Empty => ExprKind::Empty, ExprKind::Assign(a) => ExprKind::Assign(folder.fold_assign(a)), + ExprKind::Modify(m) => ExprKind::Modify(folder.fold_modify(m)), ExprKind::Binary(b) => ExprKind::Binary(folder.fold_binary(b)), ExprKind::Unary(u) => ExprKind::Unary(folder.fold_unary(u)), ExprKind::Member(m) => ExprKind::Member(folder.fold_member(m)), diff --git a/compiler/cl-ast/src/ast_visitor/visit.rs b/compiler/cl-ast/src/ast_visitor/visit.rs index dbf8000..f0487c4 100644 --- a/compiler/cl-ast/src/ast_visitor/visit.rs +++ b/compiler/cl-ast/src/ast_visitor/visit.rs @@ -202,13 +202,19 @@ pub trait Visit<'a>: Sized { or_visit_expr_kind(self, e) } fn visit_assign(&mut self, a: &'a Assign) { - let Assign { kind, parts } = a; + let Assign { parts } = a; let (head, tail) = parts.as_ref(); - self.visit_assign_kind(kind); self.visit_expr_kind(head); self.visit_expr_kind(tail); } - fn visit_assign_kind(&mut self, _kind: &'a AssignKind) {} + fn visit_modify(&mut self, m: &'a Modify) { + let Modify { kind, parts } = m; + let (head, tail) = parts.as_ref(); + self.visit_modify_kind(kind); + self.visit_expr_kind(head); + self.visit_expr_kind(tail); + } + fn visit_modify_kind(&mut self, _kind: &'a ModifyKind) {} fn visit_binary(&mut self, b: &'a Binary) { let Binary { kind, parts } = b; let (head, tail) = parts.as_ref(); @@ -437,6 +443,7 @@ pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) { match e { ExprKind::Empty => {} ExprKind::Assign(a) => visitor.visit_assign(a), + ExprKind::Modify(m) => visitor.visit_modify(m), ExprKind::Binary(b) => visitor.visit_binary(b), ExprKind::Unary(u) => visitor.visit_unary(u), ExprKind::Member(m) => visitor.visit_member(m), diff --git a/compiler/cl-interpret/src/interpret.rs b/compiler/cl-interpret/src/interpret.rs index e7945a5..51cae1c 100644 --- a/compiler/cl-interpret/src/interpret.rs +++ b/compiler/cl-interpret/src/interpret.rs @@ -128,6 +128,7 @@ impl Interpret for ExprKind { match self { ExprKind::Empty => Ok(ConValue::Empty), ExprKind::Assign(v) => v.interpret(env), + ExprKind::Modify(v) => v.interpret(env), ExprKind::Binary(v) => v.interpret(env), ExprKind::Unary(v) => v.interpret(env), ExprKind::Member(v) => v.interpret(env), @@ -151,59 +152,71 @@ impl Interpret for ExprKind { } } } + +fn evaluate_place_expr<'e>( + env: &'e mut Environment, + expr: &ExprKind, +) -> IResult<(&'e mut Option, Sym)> { + match expr { + ExprKind::Path(Path { parts, .. }) if parts.len() == 1 => { + match parts.last().expect("parts should not be empty") { + PathPart::SuperKw => Err(Error::NotAssignable), + PathPart::SelfKw => todo!("Assignment to `self`"), + PathPart::Ident(Identifier(s)) => env.get_mut(*s).map(|v| (v, *s)), + } + } + ExprKind::Index(_) => todo!("Assignment to an index operation"), + ExprKind::Path(_) => todo!("Path expression resolution (IMPORTANT)"), + ExprKind::Empty | ExprKind::Group(_) | ExprKind::Tuple(_) => { + todo!("Pattern Destructuring?") + } + _ => Err(Error::NotAssignable), + } +} + impl Interpret for Assign { fn interpret(&self, env: &mut Environment) -> IResult { - let Assign { kind: op, parts } = self; + let Assign { parts } = self; let (head, tail) = parts.borrow(); + let init = tail.interpret(env)?; // Resolve the head pattern - let head = match &head { - ExprKind::Path(Path { parts, .. }) if parts.len() == 1 => { - match parts.last().expect("parts should not be empty") { - PathPart::SuperKw => Err(Error::NotAssignable)?, - PathPart::SelfKw => todo!("Assignment to `self`"), - PathPart::Ident(Identifier(s)) => s, - } + let target = evaluate_place_expr(env, head)?; + use std::mem::discriminant as variant; + // runtime typecheck + match target.0 { + Some(value) if variant(value) == variant(&init) => { + *value = init; } - ExprKind::Index(_) => todo!("Assignment to an index operation"), - ExprKind::Path(_) => todo!("Path expression resolution (IMPORTANT)"), - ExprKind::Empty | ExprKind::Group(_) | ExprKind::Tuple(_) => { - todo!("Pattern Destructuring?") - } - _ => Err(Error::NotAssignable)?, - }; + value @ None => *value = Some(init), + _ => Err(Error::TypeError)?, + } + Ok(ConValue::Empty) + } +} +impl Interpret for Modify { + fn interpret(&self, env: &mut Environment) -> IResult { + let Modify { kind: op, parts } = self; + let (head, tail) = parts.borrow(); // Get the initializer and the tail let init = tail.interpret(env)?; - let target = env.get_mut(*head)?; - - if let AssignKind::Plain = op { - use std::mem::discriminant as variant; - // runtime typecheck - match target { - Some(value) if variant(value) == variant(&init) => { - *value = init; - } - value @ None => *value = Some(init), - _ => Err(Error::TypeError)?, - } - return Ok(ConValue::Empty); - } - let Some(target) = target else { - return Err(Error::NotInitialized(*head)); + // Resolve the head pattern + let target = evaluate_place_expr(env, head)?; + let (Some(target), _) = target else { + return Err(Error::NotInitialized(target.1)); }; match op { - AssignKind::Add => target.add_assign(init)?, - AssignKind::Sub => target.sub_assign(init)?, - AssignKind::Mul => target.mul_assign(init)?, - AssignKind::Div => target.div_assign(init)?, - AssignKind::Rem => target.rem_assign(init)?, - AssignKind::And => target.bitand_assign(init)?, - AssignKind::Or => target.bitor_assign(init)?, - AssignKind::Xor => target.bitxor_assign(init)?, - AssignKind::Shl => target.shl_assign(init)?, - AssignKind::Shr => target.shr_assign(init)?, - _ => (), - } + ModifyKind::Add => target.add_assign(init), + ModifyKind::Sub => target.sub_assign(init), + ModifyKind::Mul => target.mul_assign(init), + ModifyKind::Div => target.div_assign(init), + ModifyKind::Rem => target.rem_assign(init), + ModifyKind::And => target.bitand_assign(init), + ModifyKind::Or => target.bitor_assign(init), + ModifyKind::Xor => target.bitxor_assign(init), + ModifyKind::Shl => target.shl_assign(init), + ModifyKind::Shr => target.shr_assign(init), + }?; Ok(ConValue::Empty) } } diff --git a/compiler/cl-parser/src/parser.rs b/compiler/cl-parser/src/parser.rs index 66b9408..bc42ae2 100644 --- a/compiler/cl-parser/src/parser.rs +++ b/compiler/cl-parser/src/parser.rs @@ -911,7 +911,7 @@ impl<'t> Parser<'t> { continue; } - if let Some((kind, prec)) = from_assign(op) { + if let Some((kind, prec)) = from_modify(op) { let (before, after) = prec.infix().expect("should have a precedence"); if before < power { break; @@ -919,9 +919,21 @@ impl<'t> Parser<'t> { self.consume_peeked(); let tail = self.exprkind(after)?; - head = Assign { kind, parts: (head, tail).into() }.into(); + head = Modify { kind, parts: (head, tail).into() }.into(); continue; } + + if let Punct::Eq = op { + let (before, after) = Precedence::Assign + .infix() + .expect("should have a precedence"); + if before < power { + break; + } + self.consume_peeked(); + let tail = self.exprkind(after)?; + head = Assign { parts: (head, tail).into() }.into(); + } break; } Ok(head) @@ -942,7 +954,7 @@ impl<'t> Parser<'t> { } } TokenKind::Literal => { - let name = self.literal()?; // TODO: Maybe restrict this to just + let name = self.literal()?; // TODO: Maybe restrict this to just Ok(MemberKind::Tuple(name)) } t => Err(self.error(Unexpected(t), PARSING)), @@ -1206,8 +1218,8 @@ impl Precedence { } } } -impl From for Precedence { - fn from(_value: AssignKind) -> Self { +impl From for Precedence { + fn from(_value: ModifyKind) -> Self { Precedence::Assign } } @@ -1254,8 +1266,7 @@ operator! { At => At, Tilde => Tilde, }; - from_assign(Punct => AssignKind) { - Eq => Plain, + from_modify(Punct => ModifyKind) { AmpEq => And, BarEq => Or, XorEq => Xor, diff --git a/compiler/cl-repl/examples/yaml.rs b/compiler/cl-repl/examples/yaml.rs index 2a45b43..64c80a9 100644 --- a/compiler/cl-repl/examples/yaml.rs +++ b/compiler/cl-repl/examples/yaml.rs @@ -389,6 +389,7 @@ pub mod yamlify { fn yaml(&self, y: &mut Yamler) { match self { ExprKind::Assign(k) => k.yaml(y), + ExprKind::Modify(k) => k.yaml(y), ExprKind::Binary(k) => k.yaml(y), ExprKind::Unary(k) => k.yaml(y), ExprKind::Member(k) => k.yaml(y), @@ -415,14 +416,22 @@ pub mod yamlify { } impl Yamlify for Assign { fn yaml(&self, y: &mut Yamler) { - let Self { kind, parts } = self; + let Self { parts } = self; y.key("Assign") + .pair("head", &parts.0) + .pair("tail", &parts.1); + } + } + impl Yamlify for Modify { + fn yaml(&self, y: &mut Yamler) { + let Self { kind, parts } = self; + y.key("Modify") .pair("kind", kind) .pair("head", &parts.0) .pair("tail", &parts.1); } } - impl Yamlify for AssignKind { + impl Yamlify for ModifyKind { fn yaml(&self, y: &mut Yamler) { y.value(self); } diff --git a/grammar.ebnf b/grammar.ebnf index e9a2887..e186c07 100644 --- a/grammar.ebnf +++ b/grammar.ebnf @@ -82,7 +82,8 @@ Bool = "true" | "false" ; Expr = Assign ; -Assign = Path (AssignKind Assign ) | Compare ; +Assign = Path (AssignKind Assign ) | Modify ; +Modify = Path (ModifyKind Assign ) | Compare ; (* Binary = Compare | Range | Logic | Bitwise | Shift | Factor | Term ; *) Compare = Range (CompareOp Range )* ; @@ -131,7 +132,8 @@ Break = "break" Expr ; Return = "return" Expr ; Continue = "continue" ; -AssignKind = '=' | '+=' | '-=' | '*=' | '/=' | '&=' | '|=' | '^=' |'<<=' |'>>=' ; +AssignKind = '=' ; +ModifyKind = '+=' | '-=' | '*=' | '/=' | '&=' | '|=' | '^=' |'<<=' |'>>=' ; CompareOp = '<' | '<=' | '==' | '!=' | '>=' | '>' ; RangeOp = '..' | '..=' ;