conlang: Split assignment into plain Assign and assign-with-Modify

This commit is contained in:
John 2024-05-19 14:31:30 -05:00
parent 8d8928b8a8
commit f330a7eaa5
8 changed files with 140 additions and 78 deletions

View File

@ -346,8 +346,10 @@ pub enum ExprKind {
/// An empty expression: `(` `)` /// An empty expression: `(` `)`
#[default] #[default]
Empty, Empty,
/// An [Assign]ment expression: [`Expr`] ([`AssignKind`] [`Expr`])\+ /// An [Assign]ment expression: [`Expr`] (`=` [`Expr`])\+
Assign(Assign), Assign(Assign),
/// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
Modify(Modify),
/// A [Binary] expression: [`Expr`] ([`BinaryKind`] [`Expr`])\+ /// A [Binary] expression: [`Expr`] ([`BinaryKind`] [`Expr`])\+
Binary(Binary), Binary(Binary),
/// A [Unary] expression: [`UnaryKind`]\* [`Expr`] /// A [Unary] expression: [`UnaryKind`]\* [`Expr`]
@ -394,14 +396,18 @@ pub enum ExprKind {
/// An [Assign]ment expression: [`Expr`] ([`AssignKind`] [`Expr`])\+ /// An [Assign]ment expression: [`Expr`] ([`AssignKind`] [`Expr`])\+
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Assign { 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)>, pub parts: Box<(ExprKind, ExprKind)>,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum AssignKind { pub enum ModifyKind {
/// Standard Assignment with no read-back
Plain,
And, And,
Or, Or,
Xor, Xor,

View File

@ -420,6 +420,7 @@ mod display {
match self { match self {
ExprKind::Empty => "()".fmt(f), ExprKind::Empty => "()".fmt(f),
ExprKind::Assign(v) => v.fmt(f), ExprKind::Assign(v) => v.fmt(f),
ExprKind::Modify(v) => v.fmt(f),
ExprKind::Binary(v) => v.fmt(f), ExprKind::Binary(v) => v.fmt(f),
ExprKind::Unary(v) => v.fmt(f), ExprKind::Unary(v) => v.fmt(f),
ExprKind::Member(v) => v.fmt(f), ExprKind::Member(v) => v.fmt(f),
@ -445,26 +446,32 @@ mod display {
} }
impl Display for Assign { 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 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { kind, parts } = self; let Self { kind, parts } = self;
write!(f, "{} {kind} {}", parts.0, parts.1) 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 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
AssignKind::Plain => "=", ModifyKind::Mul => "*=",
AssignKind::Mul => "*=", ModifyKind::Div => "/=",
AssignKind::Div => "/=", ModifyKind::Rem => "%=",
AssignKind::Rem => "%=", ModifyKind::Add => "+=",
AssignKind::Add => "+=", ModifyKind::Sub => "-=",
AssignKind::Sub => "-=", ModifyKind::And => "&=",
AssignKind::And => "&=", ModifyKind::Or => "|=",
AssignKind::Or => "|=", ModifyKind::Xor => "^=",
AssignKind::Xor => "^=", ModifyKind::Shl => "<<=",
AssignKind::Shl => "<<=", ModifyKind::Shr => ">>=",
AssignKind::Shr => ">>=",
} }
.fmt(f) .fmt(f)
} }
@ -749,6 +756,7 @@ mod convert {
} }
impl From for ExprKind { impl From for ExprKind {
Assign => ExprKind::Assign, Assign => ExprKind::Assign,
Modify => ExprKind::Modify,
Binary => ExprKind::Binary, Binary => ExprKind::Binary,
Unary => ExprKind::Unary, Unary => ExprKind::Unary,
Member => ExprKind::Member, Member => ExprKind::Member,

View File

@ -234,14 +234,19 @@ pub trait Fold {
or_fold_expr_kind(self, kind) or_fold_expr_kind(self, kind)
} }
fn fold_assign(&mut self, a: Assign) -> Assign { fn fold_assign(&mut self, a: Assign) -> Assign {
let Assign { kind, parts } = a; let Assign { parts } = a;
let (head, tail) = *parts; let (head, tail) = *parts;
Assign { Assign { parts: Box::new((self.fold_expr_kind(head), self.fold_expr_kind(tail))) }
kind: self.fold_assign_kind(kind), }
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))), 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 kind
} }
fn fold_binary(&mut self, b: Binary) -> Binary { fn fold_binary(&mut self, b: Binary) -> Binary {
@ -522,6 +527,7 @@ pub fn or_fold_expr_kind<F: Fold + ?Sized>(folder: &mut F, kind: ExprKind) -> Ex
match kind { match kind {
ExprKind::Empty => ExprKind::Empty, ExprKind::Empty => ExprKind::Empty,
ExprKind::Assign(a) => ExprKind::Assign(folder.fold_assign(a)), 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::Binary(b) => ExprKind::Binary(folder.fold_binary(b)),
ExprKind::Unary(u) => ExprKind::Unary(folder.fold_unary(u)), ExprKind::Unary(u) => ExprKind::Unary(folder.fold_unary(u)),
ExprKind::Member(m) => ExprKind::Member(folder.fold_member(m)), ExprKind::Member(m) => ExprKind::Member(folder.fold_member(m)),

View File

@ -202,13 +202,19 @@ pub trait Visit<'a>: Sized {
or_visit_expr_kind(self, e) or_visit_expr_kind(self, e)
} }
fn visit_assign(&mut self, a: &'a Assign) { fn visit_assign(&mut self, a: &'a Assign) {
let Assign { kind, parts } = a; let Assign { parts } = a;
let (head, tail) = parts.as_ref(); let (head, tail) = parts.as_ref();
self.visit_assign_kind(kind);
self.visit_expr_kind(head); self.visit_expr_kind(head);
self.visit_expr_kind(tail); 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) { fn visit_binary(&mut self, b: &'a Binary) {
let Binary { kind, parts } = b; let Binary { kind, parts } = b;
let (head, tail) = parts.as_ref(); 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 { match e {
ExprKind::Empty => {} ExprKind::Empty => {}
ExprKind::Assign(a) => visitor.visit_assign(a), ExprKind::Assign(a) => visitor.visit_assign(a),
ExprKind::Modify(m) => visitor.visit_modify(m),
ExprKind::Binary(b) => visitor.visit_binary(b), ExprKind::Binary(b) => visitor.visit_binary(b),
ExprKind::Unary(u) => visitor.visit_unary(u), ExprKind::Unary(u) => visitor.visit_unary(u),
ExprKind::Member(m) => visitor.visit_member(m), ExprKind::Member(m) => visitor.visit_member(m),

View File

@ -128,6 +128,7 @@ impl Interpret for ExprKind {
match self { match self {
ExprKind::Empty => Ok(ConValue::Empty), ExprKind::Empty => Ok(ConValue::Empty),
ExprKind::Assign(v) => v.interpret(env), ExprKind::Assign(v) => v.interpret(env),
ExprKind::Modify(v) => v.interpret(env),
ExprKind::Binary(v) => v.interpret(env), ExprKind::Binary(v) => v.interpret(env),
ExprKind::Unary(v) => v.interpret(env), ExprKind::Unary(v) => v.interpret(env),
ExprKind::Member(v) => v.interpret(env), ExprKind::Member(v) => v.interpret(env),
@ -151,17 +152,17 @@ impl Interpret for ExprKind {
} }
} }
} }
impl Interpret for Assign {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn evaluate_place_expr<'e>(
let Assign { kind: op, parts } = self; env: &'e mut Environment,
let (head, tail) = parts.borrow(); expr: &ExprKind,
// Resolve the head pattern ) -> IResult<(&'e mut Option<ConValue>, Sym)> {
let head = match &head { match expr {
ExprKind::Path(Path { parts, .. }) if parts.len() == 1 => { ExprKind::Path(Path { parts, .. }) if parts.len() == 1 => {
match parts.last().expect("parts should not be empty") { match parts.last().expect("parts should not be empty") {
PathPart::SuperKw => Err(Error::NotAssignable)?, PathPart::SuperKw => Err(Error::NotAssignable),
PathPart::SelfKw => todo!("Assignment to `self`"), PathPart::SelfKw => todo!("Assignment to `self`"),
PathPart::Ident(Identifier(s)) => s, PathPart::Ident(Identifier(s)) => env.get_mut(*s).map(|v| (v, *s)),
} }
} }
ExprKind::Index(_) => todo!("Assignment to an index operation"), ExprKind::Index(_) => todo!("Assignment to an index operation"),
@ -169,41 +170,53 @@ impl Interpret for Assign {
ExprKind::Empty | ExprKind::Group(_) | ExprKind::Tuple(_) => { ExprKind::Empty | ExprKind::Group(_) | ExprKind::Tuple(_) => {
todo!("Pattern Destructuring?") todo!("Pattern Destructuring?")
} }
_ => Err(Error::NotAssignable)?, _ => Err(Error::NotAssignable),
}; }
// Get the initializer and the tail }
let init = tail.interpret(env)?;
let target = env.get_mut(*head)?;
if let AssignKind::Plain = op { impl Interpret for Assign {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Assign { parts } = self;
let (head, tail) = parts.borrow();
let init = tail.interpret(env)?;
// Resolve the head pattern
let target = evaluate_place_expr(env, head)?;
use std::mem::discriminant as variant; use std::mem::discriminant as variant;
// runtime typecheck // runtime typecheck
match target { match target.0 {
Some(value) if variant(value) == variant(&init) => { Some(value) if variant(value) == variant(&init) => {
*value = init; *value = init;
} }
value @ None => *value = Some(init), value @ None => *value = Some(init),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError)?,
} }
return Ok(ConValue::Empty); Ok(ConValue::Empty)
} }
let Some(target) = target else { }
return Err(Error::NotInitialized(*head)); impl Interpret for Modify {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Modify { kind: op, parts } = self;
let (head, tail) = parts.borrow();
// Get the initializer and the tail
let init = tail.interpret(env)?;
// Resolve the head pattern
let target = evaluate_place_expr(env, head)?;
let (Some(target), _) = target else {
return Err(Error::NotInitialized(target.1));
}; };
match op { match op {
AssignKind::Add => target.add_assign(init)?, ModifyKind::Add => target.add_assign(init),
AssignKind::Sub => target.sub_assign(init)?, ModifyKind::Sub => target.sub_assign(init),
AssignKind::Mul => target.mul_assign(init)?, ModifyKind::Mul => target.mul_assign(init),
AssignKind::Div => target.div_assign(init)?, ModifyKind::Div => target.div_assign(init),
AssignKind::Rem => target.rem_assign(init)?, ModifyKind::Rem => target.rem_assign(init),
AssignKind::And => target.bitand_assign(init)?, ModifyKind::And => target.bitand_assign(init),
AssignKind::Or => target.bitor_assign(init)?, ModifyKind::Or => target.bitor_assign(init),
AssignKind::Xor => target.bitxor_assign(init)?, ModifyKind::Xor => target.bitxor_assign(init),
AssignKind::Shl => target.shl_assign(init)?, ModifyKind::Shl => target.shl_assign(init),
AssignKind::Shr => target.shr_assign(init)?, ModifyKind::Shr => target.shr_assign(init),
_ => (), }?;
}
Ok(ConValue::Empty) Ok(ConValue::Empty)
} }
} }

View File

@ -911,7 +911,7 @@ impl<'t> Parser<'t> {
continue; 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"); let (before, after) = prec.infix().expect("should have a precedence");
if before < power { if before < power {
break; break;
@ -919,9 +919,21 @@ impl<'t> Parser<'t> {
self.consume_peeked(); self.consume_peeked();
let tail = self.exprkind(after)?; let tail = self.exprkind(after)?;
head = Assign { kind, parts: (head, tail).into() }.into(); head = Modify { kind, parts: (head, tail).into() }.into();
continue; 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; break;
} }
Ok(head) Ok(head)
@ -1206,8 +1218,8 @@ impl Precedence {
} }
} }
} }
impl From<AssignKind> for Precedence { impl From<ModifyKind> for Precedence {
fn from(_value: AssignKind) -> Self { fn from(_value: ModifyKind) -> Self {
Precedence::Assign Precedence::Assign
} }
} }
@ -1254,8 +1266,7 @@ operator! {
At => At, At => At,
Tilde => Tilde, Tilde => Tilde,
}; };
from_assign(Punct => AssignKind) { from_modify(Punct => ModifyKind) {
Eq => Plain,
AmpEq => And, AmpEq => And,
BarEq => Or, BarEq => Or,
XorEq => Xor, XorEq => Xor,

View File

@ -389,6 +389,7 @@ pub mod yamlify {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
match self { match self {
ExprKind::Assign(k) => k.yaml(y), ExprKind::Assign(k) => k.yaml(y),
ExprKind::Modify(k) => k.yaml(y),
ExprKind::Binary(k) => k.yaml(y), ExprKind::Binary(k) => k.yaml(y),
ExprKind::Unary(k) => k.yaml(y), ExprKind::Unary(k) => k.yaml(y),
ExprKind::Member(k) => k.yaml(y), ExprKind::Member(k) => k.yaml(y),
@ -415,14 +416,22 @@ pub mod yamlify {
} }
impl Yamlify for Assign { impl Yamlify for Assign {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { kind, parts } = self; let Self { parts } = self;
y.key("Assign") 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("kind", kind)
.pair("head", &parts.0) .pair("head", &parts.0)
.pair("tail", &parts.1); .pair("tail", &parts.1);
} }
} }
impl Yamlify for AssignKind { impl Yamlify for ModifyKind {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
y.value(self); y.value(self);
} }

View File

@ -82,7 +82,8 @@ Bool = "true" | "false" ;
Expr = Assign ; 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 ; *) (* Binary = Compare | Range | Logic | Bitwise | Shift | Factor | Term ; *)
Compare = Range (CompareOp Range )* ; Compare = Range (CompareOp Range )* ;
@ -131,7 +132,8 @@ Break = "break" Expr ;
Return = "return" Expr ; Return = "return" Expr ;
Continue = "continue" ; Continue = "continue" ;
AssignKind = '=' | '+=' | '-=' | '*=' | '/=' | '&=' | '|=' | '^=' |'<<=' |'>>=' ; AssignKind = '=' ;
ModifyKind = '+=' | '-=' | '*=' | '/=' | '&=' | '|=' | '^=' |'<<=' |'>>=' ;
CompareOp = '<' | '<=' | '==' | '!=' | '>=' | '>' ; CompareOp = '<' | '<=' | '==' | '!=' | '>=' | '>' ;
RangeOp = '..' | '..=' ; RangeOp = '..' | '..=' ;