From 2ec122016c667cdead001b0bef3c1f30992f8721 Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Tue, 15 Oct 2024 19:01:21 -0700 Subject: [PATCH] Add map literals Map literals share the brackets with lists, but they use colons to delimit keys and values. Signed-off-by: Alek Ratzloff --- src/ast.rs | 19 ++++++++++++++- src/compiler.rs | 22 +++++++++++++++++ src/compiler/visitors.rs | 19 +++++++++++++++ src/obj/ty.rs | 4 ++++ src/parser.rs | 51 +++++++++++++++++++++++++++++++++++++--- tools/genast.py | 5 +++- 6 files changed, 115 insertions(+), 5 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index dad8da9..73ddbd9 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,5 +1,5 @@ // This is an auto-generated file. Any changes made to this file may be overwritten. -// This file was created at: 2024-10-07 10:41:44 +// This file was created at: 2024-10-14 20:16:29 #![allow(dead_code)] use std::fmt::Debug; use std::any::Any; @@ -17,6 +17,7 @@ pub trait ExprVisitor { fn visit_primary_expr(&mut self, expr: &PrimaryExpr) -> Result<(), Error>; fn visit_function_expr(&mut self, expr: &FunctionExpr) -> Result<(), Error>; fn visit_list_expr(&mut self, expr: &ListExpr) -> Result<(), Error>; + fn visit_map_expr(&mut self, expr: &MapExpr) -> Result<(), Error>; } pub trait Expr: Debug + Any { @@ -153,6 +154,22 @@ impl Expr for ListExpr { fn as_any_ref(&self) -> &dyn Any { self } } +#[derive(Debug)] +pub struct MapExpr { + pub lbracket: Token, + pub pairs: Vec<(ExprP, ExprP)>, + pub rbracket: Token, +} + +impl Expr for MapExpr { + fn accept(&self, visitor: &mut dyn ExprVisitor) -> Result<(), Error> { + visitor.visit_map_expr(self) + } + + fn as_any(self: Box) -> Box { self } + fn as_any_ref(&self) -> &dyn Any { self } +} + pub trait StmtVisitor { fn visit_import_stmt(&mut self, stmt: &ImportStmt) -> Result<(), Error>; fn visit_expr_stmt(&mut self, stmt: &ExprStmt) -> Result<(), Error>; diff --git a/src/compiler.rs b/src/compiler.rs index 25772b2..300ab68 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -958,4 +958,26 @@ impl ExprVisitor for Compiler<'_> { Ok(()) } + + fn visit_map_expr(&mut self, expr: &MapExpr) -> Result<()> { + let line = expr_line_number(expr); + let map_global = self.get_global("Map").unwrap(); + let insert_constant = self.insert_constant(Str::create("insert"))?; + + self.emit(line, Op::GetGlobal(map_global)); + self.emit(line, Op::Call(0)); + + for (key, value) in &expr.pairs { + self.emit(line, Op::Dup); + self.emit(line, Op::GetAttr(insert_constant)); + + self.compile_expr(key)?; + self.compile_expr(value)?; + + self.emit(line, Op::Call(2)); + self.emit(line, Op::Pop); + } + + Ok(()) + } } diff --git a/src/compiler/visitors.rs b/src/compiler/visitors.rs index b21fa77..83cd73f 100644 --- a/src/compiler/visitors.rs +++ b/src/compiler/visitors.rs @@ -125,6 +125,12 @@ impl ExprVisitor for LineNumber { self.update_end(expr.rbracket.line); Ok(()) } + + fn visit_map_expr(&mut self, expr: &MapExpr) -> Result<()> { + self.update_start(expr.lbracket.line); + self.update_end(expr.rbracket.line); + Ok(()) + } } pub(super) fn expr_line_number(expr: &dyn Expr) -> LineRange { @@ -282,6 +288,11 @@ impl ExprVisitor for LocalAssignCollector { // there shouldn't be any local assignments inside of a list expression Ok(()) } + + fn visit_map_expr(&mut self, _expr: &MapExpr) -> Result<()> { + // there shouldn't be any local assignments inside of a map expression + Ok(()) + } } #[derive(Default)] @@ -405,4 +416,12 @@ impl ExprVisitor for LocalNameCollector { } Ok(()) } + + fn visit_map_expr(&mut self, expr: &MapExpr) -> Result<()> { + for (key, value) in &expr.pairs { + key.accept(self)?; + value.accept(self)?; + } + Ok(()) + } } diff --git a/src/obj/ty.rs b/src/obj/ty.rs index 4721b9e..a8d7bc0 100644 --- a/src/obj/ty.rs +++ b/src/obj/ty.rs @@ -144,6 +144,10 @@ pub fn init_types() { }); )* })* + + $({ + $name.borrow_mut().set_attr("name", Str::create(stringify!($name))); + })* }}; } diff --git a/src/parser.rs b/src/parser.rs index 17effaf..e02b1be 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -931,7 +931,7 @@ impl Parser { } Ok(expr) } else if self.mat(TokenKind::LBracket)? { - self.list() + self.list_or_map() } else { Err(self.error(format!("unexpected token {:?}", self.current.kind))) } @@ -986,12 +986,34 @@ impl Parser { Ok(()) } - fn list(&mut self) -> Result { + fn list_or_map(&mut self) -> Result { let lbracket = self.prev.clone().unwrap(); let mut exprs = Vec::new(); + // check if it's a map + if self.mat(TokenKind::Colon)? { + let rbracket = self + .expect("expected ']' after empty map body", TokenKind::RBracket)? + .clone(); + return Ok(Box::new(MapExpr { + lbracket, + pairs: vec![], + rbracket, + })); + } + if !self.check(TokenKind::RBracket) { - exprs.push(self.expr()?); + let expr = self.expr()?; + + // check if it's a map + if self.mat(TokenKind::Colon)? { + let value = self.expr()?; + let pairs = vec![(expr, value)]; + return self.finish_map(lbracket, pairs); + } + + exprs.push(expr); + while self.mat(TokenKind::Comma)? { // allow trailing comma if self.check(TokenKind::RBracket) { @@ -1011,6 +1033,29 @@ impl Parser { rbracket, })) } + + fn finish_map(&mut self, lbracket: Token, mut pairs: Vec<(ExprP, ExprP)>) -> Result { + while self.mat(TokenKind::Comma)? { + // trailing comma + if self.check(TokenKind::RBracket) { + break; + } + let key = self.expr()?; + self.expect("expected ':' after map key", TokenKind::Colon)?; + let value = self.expr()?; + pairs.push((key, value)); + } + + let rbracket = self + .expect("expect ']' after map pairs", TokenKind::RBracket)? + .clone(); + + Ok(Box::new(MapExpr { + lbracket, + pairs, + rbracket, + })) + } } #[cfg(test)] diff --git a/tools/genast.py b/tools/genast.py index 3e21e51..10c201e 100755 --- a/tools/genast.py +++ b/tools/genast.py @@ -229,7 +229,10 @@ GENERATE = [ "rbrace: Token", ], ) - .add_struct("List", ["lbracket: Token", "exprs: Vec", "rbracket: Token"]), + .add_struct("List", ["lbracket: Token", "exprs: Vec", "rbracket: Token"]) + .add_struct( + "Map", ["lbracket: Token", "pairs: Vec<(ExprP, ExprP)>", "rbracket: Token"] + ), # Stmt GenerateGroup("Stmt") .add_struct("Import", ["import_kw: Token", "what: Vec", "module: Token"])