Add map literals

Map literals share the brackets with lists, but they use colons to
delimit keys and values.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2024-10-15 19:01:21 -07:00
parent 07a59c8930
commit 2ec122016c
6 changed files with 115 additions and 5 deletions

View File

@@ -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<Self>) -> Box<dyn Any> { 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>;

View File

@@ -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(())
}
}

View File

@@ -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(())
}
}

View File

@@ -144,6 +144,10 @@ pub fn init_types() {
});
)*
})*
$({
$name.borrow_mut().set_attr("name", Str::create(stringify!($name)));
})*
}};
}

View File

@@ -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<ExprP> {
fn list_or_map(&mut self) -> Result<ExprP> {
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<ExprP> {
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)]