Add augmented assignment operators for set statements

This allows us to do things like

    self.foo += 5

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2024-10-07 11:05:39 -07:00
parent 365bee0554
commit 16ab9d718b
4 changed files with 81 additions and 40 deletions

View File

@@ -1,5 +1,5 @@
// This is an auto-generated file. Any changes made to this file may be overwritten. // This is an auto-generated file. Any changes made to this file may be overwritten.
// This file was created at: 2024-10-07 09:59:23 // This file was created at: 2024-10-07 10:41:44
#![allow(dead_code)] #![allow(dead_code)]
use std::fmt::Debug; use std::fmt::Debug;
use std::any::Any; use std::any::Any;
@@ -221,6 +221,7 @@ impl Stmt for AssignStmt {
pub struct SetStmt { pub struct SetStmt {
pub expr: ExprP, pub expr: ExprP,
pub name: Token, pub name: Token,
pub op: Token,
pub rhs: ExprP, pub rhs: ExprP,
} }

View File

@@ -39,6 +39,40 @@ fn unescape(s: &str) -> String {
.replace("\\\\", "\\") .replace("\\\\", "\\")
} }
static BIN_OP_NAMES: LazyLock<HashMap<TokenKind, &'static str>> = LazyLock::new(|| {
hash_map! {
TokenKind::Plus => "__add__",
TokenKind::Minus => "__sub__",
TokenKind::Star => "__mul__",
TokenKind::Slash => "__div__",
TokenKind::And => "__and__",
TokenKind::Or => "__or__",
TokenKind::BangEq => "__ne__",
TokenKind::EqEq => "__eq__",
TokenKind::Greater => "__gt__",
TokenKind::GreaterEq => "__ge__",
TokenKind::Less => "__lt__",
TokenKind::LessEq => "__le__",
}
});
static UNARY_OP_NAMES: LazyLock<HashMap<TokenKind, &'static str>> = LazyLock::new(|| {
hash_map! {
TokenKind::Plus => "__pos__",
TokenKind::Minus => "__neg__",
TokenKind::Bang => "__not__",
}
});
static AUG_ASSIGN_NAMES: LazyLock<HashMap<TokenKind, &'static str>> = LazyLock::new(|| {
hash_map! {
TokenKind::PlusEq => "__add__",
TokenKind::MinusEq => "__sub__",
TokenKind::StarEq => "__mul__",
TokenKind::SlashEq => "__div__",
}
});
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Scope // Scope
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -573,16 +607,8 @@ impl StmtVisitor for Compiler<'_> {
} }
// augmented assignment // augmented assignment
TokenKind::PlusEq | TokenKind::MinusEq | TokenKind::StarEq | TokenKind::SlashEq => { TokenKind::PlusEq | TokenKind::MinusEq | TokenKind::StarEq | TokenKind::SlashEq => {
static OP_NAMES: LazyLock<HashMap<TokenKind, &'static str>> = LazyLock::new(|| {
hash_map! {
TokenKind::PlusEq => "__add__",
TokenKind::MinusEq => "__sub__",
TokenKind::StarEq => "__mul__",
TokenKind::SlashEq => "__div__",
}
});
self.emit_lookup(line, name)?; self.emit_lookup(line, name)?;
let op_name = OP_NAMES let op_name = AUG_ASSIGN_NAMES
.get(&stmt.op.kind) .get(&stmt.op.kind)
.expect("invalid augmented assignment operator"); .expect("invalid augmented assignment operator");
let op_constant = self.insert_constant(Str::create(op_name))?; let op_constant = self.insert_constant(Str::create(op_name))?;
@@ -598,10 +624,35 @@ impl StmtVisitor for Compiler<'_> {
} }
fn visit_set_stmt(&mut self, stmt: &SetStmt) -> Result<()> { fn visit_set_stmt(&mut self, stmt: &SetStmt) -> Result<()> {
self.compile_expr(&stmt.expr)?;
let name = self.insert_constant(Str::create(&stmt.name.text))?; let name = self.insert_constant(Str::create(&stmt.name.text))?;
let line = stmt_line_number(stmt);
self.compile_expr(&stmt.expr)?;
match stmt.op.kind {
// normal assignment
TokenKind::Eq => {
self.compile_expr(&stmt.rhs)?;
self.emit(line, Op::SetAttr(name));
}
// augmented assignment
TokenKind::PlusEq | TokenKind::MinusEq | TokenKind::StarEq | TokenKind::SlashEq => {
//
self.emit(line, Op::Dup);
self.emit(line, Op::GetAttr(name));
let op = AUG_ASSIGN_NAMES
.get(&stmt.op.kind)
.expect("invalid augmented assign operator");
let op_constant = self.insert_constant(Str::create(op))?;
self.emit(line, Op::GetAttr(op_constant));
self.compile_expr(&stmt.rhs)?;
self.emit(line, Op::Call(1));
self.emit(line, Op::SetAttr(name));
}
_ => unreachable!(),
}
self.compile_expr(&stmt.rhs)?; self.compile_expr(&stmt.rhs)?;
self.emit(stmt_line_number(stmt), Op::SetAttr(name));
Ok(()) Ok(())
} }
@@ -677,23 +728,6 @@ impl StmtVisitor for Compiler<'_> {
impl ExprVisitor for Compiler<'_> { impl ExprVisitor for Compiler<'_> {
fn visit_binary_expr(&mut self, expr: &BinaryExpr) -> Result<()> { fn visit_binary_expr(&mut self, expr: &BinaryExpr) -> Result<()> {
static OP_NAMES: LazyLock<HashMap<TokenKind, &'static str>> = LazyLock::new(|| {
hash_map! {
TokenKind::Plus => "__add__",
TokenKind::Minus => "__sub__",
TokenKind::Star => "__mul__",
TokenKind::Slash => "__div__",
TokenKind::And => "__and__",
TokenKind::Or => "__or__",
TokenKind::BangEq => "__ne__",
TokenKind::EqEq => "__eq__",
TokenKind::Greater => "__gt__",
TokenKind::GreaterEq => "__ge__",
TokenKind::Less => "__lt__",
TokenKind::LessEq => "__le__",
}
});
self.compile_expr(&expr.lhs)?; self.compile_expr(&expr.lhs)?;
// short-circuit setup // short-circuit setup
@@ -711,7 +745,7 @@ impl ExprVisitor for Compiler<'_> {
} }
} }
let name = OP_NAMES let name = BIN_OP_NAMES
.get(&expr.op.kind) .get(&expr.op.kind)
.expect("invalid binary operator"); .expect("invalid binary operator");
let constant_id = self.insert_constant(Str::create(name))?; let constant_id = self.insert_constant(Str::create(name))?;
@@ -750,15 +784,10 @@ impl ExprVisitor for Compiler<'_> {
} }
fn visit_unary_expr(&mut self, expr: &UnaryExpr) -> Result<()> { fn visit_unary_expr(&mut self, expr: &UnaryExpr) -> Result<()> {
static OP_NAMES: LazyLock<HashMap<TokenKind, &'static str>> = LazyLock::new(|| {
hash_map! {
TokenKind::Plus => "__pos__",
TokenKind::Minus => "__neg__",
TokenKind::Bang => "__not__",
}
});
self.compile_expr(&expr.expr)?; self.compile_expr(&expr.expr)?;
let name = OP_NAMES.get(&expr.op.kind).expect("invalid unary operator"); let name = UNARY_OP_NAMES
.get(&expr.op.kind)
.expect("invalid unary operator");
let constant_id = self.insert_constant(Str::create(name))?; let constant_id = self.insert_constant(Str::create(name))?;
self.emit(expr_line_number(expr), Op::GetAttr(constant_id)); self.emit(expr_line_number(expr), Op::GetAttr(constant_id));
self.emit(expr_line_number(expr), Op::Call(0)); self.emit(expr_line_number(expr), Op::Call(0));

View File

@@ -587,13 +587,24 @@ impl Parser {
let expr = self.expr()?; let expr = self.expr()?;
let stmt: StmtP; let stmt: StmtP;
if expr.as_any_ref().downcast_ref::<GetExpr>().is_some() && self.mat(TokenKind::Eq)? { if expr.as_any_ref().downcast_ref::<GetExpr>().is_some()
&& mat!(
self,
TokenKind::Eq,
TokenKind::PlusEq,
TokenKind::MinusEq,
TokenKind::StarEq,
TokenKind::SlashEq
)
{
let op = self.prev.clone().unwrap();
let expr = expr.as_any().downcast::<GetExpr>().unwrap(); let expr = expr.as_any().downcast::<GetExpr>().unwrap();
let rhs = self.expr()?; let rhs = self.expr()?;
// unpack the GetExpr and turn it into a SetExpr instead // unpack the GetExpr and turn it into a SetExpr instead
stmt = Box::new(SetStmt { stmt = Box::new(SetStmt {
expr: expr.expr, expr: expr.expr,
name: expr.name, name: expr.name,
op,
rhs, rhs,
}); });
} else { } else {

View File

@@ -235,7 +235,7 @@ GENERATE = [
.add_struct("Import", ["import_kw: Token", "what: Vec<Token>", "module: Token"]) .add_struct("Import", ["import_kw: Token", "what: Vec<Token>", "module: Token"])
.add_struct("Expr", ["expr: ExprP"]) .add_struct("Expr", ["expr: ExprP"])
.add_struct("Assign", ["lhs: Token", "op: Token", "rhs: ExprP"]) .add_struct("Assign", ["lhs: Token", "op: Token", "rhs: ExprP"])
.add_struct("Set", ["expr: ExprP", "name: Token", "rhs: ExprP"]) .add_struct("Set", ["expr: ExprP", "name: Token", "op: Token", "rhs: ExprP"])
.add_struct("Block", ["lbrace: Token", "stmts: Vec<StmtP>", "rbrace: Token"]) .add_struct("Block", ["lbrace: Token", "stmts: Vec<StmtP>", "rbrace: Token"])
.add_struct("Return", ["return_kw: Token", "expr: Option<ExprP>"]) .add_struct("Return", ["return_kw: Token", "expr: Option<ExprP>"])
.add_struct( .add_struct(