diff --git a/server/src/lang/ast/expr.rs b/server/src/lang/ast/expr.rs index 6bf56019..4eee7850 100644 --- a/server/src/lang/ast/expr.rs +++ b/server/src/lang/ast/expr.rs @@ -1,12 +1,12 @@ use std::rc::Rc; use id_arena::Id; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use super::{sql::{SqlSelect, SqlInsert, SqlDelete, SqlUpdate}, stmt::StmtId, Literal}; use crate::lang::token::{Span, Spanned, Token}; -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] pub enum Operation { Add, Subtract, @@ -23,7 +23,7 @@ pub enum Operation { Not, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub enum Expr { Select { query: SqlSelect, @@ -162,4 +162,12 @@ impl Spanned for Expr { } } -pub type ExprId = Id; +#[repr(transparent)] +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub struct ExprId(pub Id); + +impl Serialize for ExprId { + fn serialize(&self, serializer: S) -> Result { + self.0.index().serialize(serializer) + } +} diff --git a/server/src/lang/ast/mod.rs b/server/src/lang/ast/mod.rs index cd8a6964..9c018cba 100644 --- a/server/src/lang/ast/mod.rs +++ b/server/src/lang/ast/mod.rs @@ -1,4 +1,5 @@ use std::rc::Rc; +use serde::Serialize; use id_arena::Arena; use rustc_hash::FxHashMap; @@ -12,7 +13,7 @@ pub mod expr; pub mod sql; pub mod stmt; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Serialize)] pub enum Literal { Str(Rc), Num(f64), @@ -73,24 +74,24 @@ pub struct ParserArena { impl ParserArena { pub fn new() -> ParserArena { ParserArena { - expressions: Arena::::new(), + expressions:Arena::::new(), statements: Arena::::new(), } } pub fn expression(&mut self, expr: Expr) -> ExprId { - self.expressions.alloc(expr) + ExprId(self.expressions.alloc(expr)) } pub fn statement(&mut self, stmt: Stmt) -> StmtId { - self.statements.alloc(stmt) + StmtId(self.statements.alloc(stmt)) } pub fn get_expression(&self, idx: ExprId) -> &Expr { - &self.expressions[idx] + &self.expressions[idx.0] } pub fn get_statement(&self, idx: StmtId) -> &Stmt { - &self.statements[idx] + &self.statements[idx.0] } } diff --git a/server/src/lang/ast/sql.rs b/server/src/lang/ast/sql.rs index 517b0a94..75592cd5 100644 --- a/server/src/lang/ast/sql.rs +++ b/server/src/lang/ast/sql.rs @@ -1,17 +1,18 @@ use crate::lang::token::Token; +use serde::Serialize; use super::expr::ExprId; // Enums -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub enum SqlDistinct { ImplicitAll, All, Distinct, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub enum SqlJoinType { Left, LeftOuter, @@ -19,7 +20,7 @@ pub enum SqlJoinType { Inner, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub enum SqlCompoundOperator { Union, UnionAll, @@ -27,57 +28,57 @@ pub enum SqlCompoundOperator { Except, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub enum SqlOrdering { Asc, Desc, } // -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub struct SqlCollectionIdentifier { pub namespace: Option, pub name: Token, pub alias: Option, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub enum SqlExpr { Default(ExprId), } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub enum SqlProjection { All { collection: Option }, Expr { expr: SqlExpr, alias: Option }, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub struct SqlLimitClause { pub count: SqlExpr, pub offset: Option, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub struct SqlOrderByClause { pub expr: SqlExpr, pub ordering: SqlOrdering, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub struct SqlSelectCompound { pub operator: SqlCompoundOperator, pub core: SqlSelectCore, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub struct SqlJoin { pub join_type: SqlJoinType, pub subquery: SqlCollectionSubquery, pub join_constraint: Option, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub enum SqlCollectionSubquery { Group(Vec), Join(Box, Vec), @@ -88,7 +89,7 @@ pub enum SqlCollectionSubquery { }, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub struct SqlSelectCore { pub distinct: SqlDistinct, pub projection: Vec, @@ -98,7 +99,7 @@ pub struct SqlSelectCore { pub having: Option, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub struct SqlSelect { pub core: SqlSelectCore, pub compound: Vec, @@ -106,26 +107,26 @@ pub struct SqlSelect { pub limit: Option, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub enum SqlValues { Values(Vec), Select(SqlSelect) } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub struct SqlInsert { pub collection: SqlCollectionIdentifier, pub values: SqlValues, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub struct SqlUpdate { pub collection: SqlCollectionIdentifier, pub assignments: Vec, pub r#where: Option, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub struct SqlDelete { pub collection: SqlCollectionIdentifier, pub r#where: Option, diff --git a/server/src/lang/ast/stmt.rs b/server/src/lang/ast/stmt.rs index 73780885..d8e641a4 100644 --- a/server/src/lang/ast/stmt.rs +++ b/server/src/lang/ast/stmt.rs @@ -1,3 +1,5 @@ +use serde::Serialize; + use id_arena::Id; use crate::lang::token::{Span, Spanned, Token}; @@ -63,4 +65,12 @@ impl Spanned for Stmt { } } -pub type StmtId = Id; +#[repr(transparent)] +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub struct StmtId(pub Id); + +impl Serialize for StmtId { + fn serialize(&self, serializer: S) -> Result { + self.0.index().serialize(serializer) + } +} diff --git a/server/src/lang/serializer.rs b/server/src/lang/serializer.rs index 1089b6f7..47f6eea5 100644 --- a/server/src/lang/serializer.rs +++ b/server/src/lang/serializer.rs @@ -1,17 +1,6 @@ use serde_json::{json, Value}; -use crate::lang::ast::Literal; - -use super::{ - ast::{ - expr::{Expr, ExprId}, - sql::{SqlCollectionSubquery, SqlExpr, SqlProjection, SqlSelect, SqlSelectCore, SqlCollectionIdentifier, SqlValues}, - stmt::{Stmt, StmtId}, - SqlVisitor, Visitor, - }, - parser::Program, -}; -use std::rc::Rc; +use super::parser::Program; pub struct ProgramSerializer<'a> { pub program: &'a Program, @@ -22,10 +11,10 @@ impl<'a> ProgramSerializer<'a> { ProgramSerializer { program } } pub fn to_json(&self) -> Value { - json!(self.visit_stmt(self.program.root).unwrap()) + serde_json::to_value(&self.program.root).unwrap() } pub fn serialize(&self) -> String { - serde_json::to_string_pretty(&self.to_json()).unwrap() + serde_json::to_string_pretty(&self.program.root).unwrap() } } @@ -33,496 +22,4 @@ impl<'a> ToString for ProgramSerializer<'a> { fn to_string(&self) -> String { self.serialize().clone() } -} - -fn ser_collection_identifier(ident: &SqlCollectionIdentifier) -> Value { - json!({ - "type": "Collection", - "namespace": ident.namespace.as_ref().map(|token| token.lexeme.to_owned()), - "name": ident.name.lexeme, - "alias": ident.alias.as_ref().map(|token| token.lexeme.to_owned()) - }) -} - -impl<'a> SqlVisitor for ProgramSerializer<'a> { - fn visit_sql_expr(&self, sql_expr: &SqlExpr) -> Result { - let SqlExpr::Default(eidx) = sql_expr; - self.visit_expr(*eidx) - } - - fn visit_sql_select_core(&self, core: &SqlSelectCore) -> Result { - let core_distinct = json!(format!("{:?}", core.distinct)); - - let core_projection: Result = core - .projection - .iter() - .map(|x| match x { - SqlProjection::All { collection } => Ok(json!({ - "type": "All", - "collection": collection.as_ref().map(|token| token.lexeme.to_owned()) - })), - SqlProjection::Expr { expr, alias } => Ok(json!({ - "type": "Expr", - "expr": self.visit_sql_expr(&expr)?, - "alias": alias.as_ref().map(|token| token.lexeme.to_owned()) - })), - }) - .collect(); - - let core_from = core - .from - .as_ref() - .map(|x| self.visit_sql_subquery(&x)) - .unwrap_or_else(|| Ok(json!(serde_json::Value::Null))); - - let core_where = core - .r#where - .as_ref() - .map(|x| self.visit_sql_expr(&x)) - .unwrap_or_else(|| Ok(json!(serde_json::Value::Null))); - - let core_group_by = core - .group_by - .as_ref() - .map(|x| x.iter().map(|expr| self.visit_sql_expr(&expr)).collect()) - .unwrap_or_else(|| Ok(json!(serde_json::Value::Null))); - - let having = core - .having - .as_ref() - .map(|x| self.visit_sql_expr(&x)) - .unwrap_or_else(|| Ok(json!(serde_json::Value::Null))); - - Ok(json!({ - "distinct": core_distinct, - "projection": core_projection?, - "from": core_from?, - "where": core_where?, - "group_by": core_group_by?, - "having": having? - })) - } - - fn visit_sql_subquery(&self, subquery: &SqlCollectionSubquery) -> Result { - match subquery { - SqlCollectionSubquery::Collection (ident) => Ok(ser_collection_identifier(ident)), - SqlCollectionSubquery::Group(groups) => { - let subqueries: Result = groups - .iter() - .map(|x| Ok(self.visit_sql_subquery(x)?)) - .collect(); - Ok(json!({ - "type": "Group", - "subqueries": subqueries? - })) - } - SqlCollectionSubquery::Select { expr, alias } => Ok(json!({ - "type": "Select", - "expr": self.visit_expr(*expr)?, - "alias": alias.as_ref().map(|token| token.lexeme.to_owned()) - })), - SqlCollectionSubquery::Join(join_subquery, joins) => { - let joins_ser: Result = joins - .iter() - .map(|x| { - Ok(json!({ - "type": format!("{:?}", x.join_type), - "subquery": self.visit_sql_subquery(&x.subquery)?, - "constraint": x.join_constraint.as_ref().map(|y| { - self.visit_sql_expr(&y) - }).unwrap_or_else(|| Ok(json!(serde_json::Value::Null)))? - })) - }) - .collect(); - Ok(json!({ - "type": "Join", - "subquery": self.visit_sql_subquery(&join_subquery)?, - "joins": joins_ser? - })) - } - } - } - - fn visit_sql_select(&self, select: &SqlSelect) -> Result { - let core = self.visit_sql_select_core(&select.core); - - let compound: Result = select - .compound - .iter() - .map(|x| { - Ok(json!({ - "core": self.visit_sql_select_core(&x.core)?, - "operation": format!("{:?}", x.operator), - })) - }) - .collect(); - - let order_by: Result = select - .order_by - .as_ref() - .map(|x| { - x.iter() - .map(|order| { - let expr = self.visit_sql_expr(&order.expr)?; - Ok(json!({ - "expr": expr, - "ordering": format!("{:?}", order.ordering), - })) - }) - .collect() - }) - .unwrap_or_else(|| Ok(json!(serde_json::Value::Null))); - - let limit: Result = select - .limit - .as_ref() - .map(|x| { - let count_part = self.visit_sql_expr(&x.count)?; - - let offset_part = if x.offset.is_some() { - self.visit_sql_expr(&x.offset.as_ref().unwrap())? - } else { - json!(serde_json::Value::Null) - }; - - Ok(json!({ - "count": count_part, - "offset": offset_part - })) - }) - .unwrap_or_else(|| Ok(json!(serde_json::Value::Null))); - - Ok(json!({ - "core": core?, - "compound": compound?, - "order_by": order_by?, - "limit": limit? - })) - } - - fn visit_sql_insert(&self, sql_insert: &super::ast::sql::SqlInsert) -> Result { - let values = match &sql_insert.values { - SqlValues::Values(values) => { - let values_ser: Result = values - .iter() - .map(|x| self.visit_sql_expr(&x)) - .collect(); - Ok(json!({ - "type": "Values", - "values": values_ser? - })) - } - SqlValues::Select(select) => { - Ok(json!({ - "type": "Select", - "select": self.visit_sql_select(&select)? - })) - } - }; - let ident = ser_collection_identifier(&sql_insert.collection); - - Ok(json!({ - "collection": ident, - "values": values?, - })) - - } - - fn visit_sql_update(&self, sql_update: &super::ast::sql::SqlUpdate) -> Result { - let ident = ser_collection_identifier(&sql_update.collection); - - let assignments: Result = sql_update - .assignments - .iter() - .map(|x| self.visit_sql_expr(&x)) - .collect(); - - let r#where = sql_update - .r#where - .as_ref() - .map(|x| self.visit_sql_expr(&x)) - .unwrap_or_else(|| Ok(json!(serde_json::Value::Null))); - - Ok(json!({ - "collection": ident, - "assignments": assignments, - "where": r#where?, - })) - } - - fn visit_sql_delete(&self, sql_delete: &super::ast::sql::SqlDelete) -> Result { - let ident = ser_collection_identifier(&sql_delete.collection); - - let r#where = sql_delete - .r#where - .as_ref() - .map(|x| self.visit_sql_expr(&x)) - .unwrap_or_else(|| Ok(json!(serde_json::Value::Null))); - - Ok(json!({ - "collection": ident, - "where": r#where?, - })) - } - -} - -impl<'a> Visitor for ProgramSerializer<'a> { - fn visit_expr(&self, eidx: ExprId) -> Result { - // TODO: Remove clone here - let a = Rc::clone(&self.program.arena); - let e = a.get_expression(eidx); - - let matched: Value = match e { - Expr::Select { span: _, query } => json!({ - "type": "Expr::Select", - "value": self.visit_sql_select(query)?, - }), - Expr::Insert { command, span: _ } => { - json!({ - "type": "Expr::Insert", - "command": self.visit_sql_insert(command)?, - }) - }, - Expr::Update { command, span: _ } => { - json!({ - "type": "Expr::Update", - "command": self.visit_sql_update(command)?, - }) - }, - Expr::Delete { command, span: _ } => { - json!({ - "type": "Expr::Delete", - "command": self.visit_sql_delete(command)?, - }) - }, - Expr::Literal { - raw, - span: _, - value, - } => { - json!({ - "type": "Expr::Literal", - "value": match value { - Literal::Object(map) => { - json!({ - "type": "Object", - "value": map.keys().map(|item| json!({ - "key": item, - "value": self.visit_expr(*map.get(item).unwrap()).unwrap() - })).collect::>(), - }) - } - Literal::Array(items) => { - json!({ - "type": "Array", - "value": items.iter().map(|item| self.visit_expr(*item).unwrap()).collect::>(), - }) - }, - _ => json!(format!("{:?}", value)) - }, - "raw": raw, - }) - } - Expr::Grouping { expr, span: _ } => { - json!({ - "type": "Expr::Grouping", - "expr": self.visit_expr(*expr)?, - }) - } - Expr::Unary { - operation, - expr, - span: _, - } => { - json!({ - "type": "Expr::Unary", - "operation": operation, - "expr": self.visit_expr(*expr)?, - }) - } - Expr::Binary { - operation, - left, - right, - span: _, - } => { - json!({ - "type": "Expr::Binary", - "left": self.visit_expr(*left)?, - "operation": operation, - "right": self.visit_expr(*right)?, - }) - } - Expr::Variable { name, span: _ } => { - json!({ - "type": "Expr::Variable", - "name": name.lexeme.as_ref(), - }) - } - Expr::Assignment { dst, expr, span: _ } => { - json!({ - "type": "Expr::Assignment", - "dst": dst.lexeme.as_ref(), - "expr": self.visit_expr(*expr)?, - }) - } - Expr::Logical { - left, - operation, - right, - span: _, - } => { - json!({ - "type": "Expr::Logical", - "left": self.visit_expr(*left)?, - "operation": operation, - "right": self.visit_expr(*right)?, - }) - } - Expr::Call { - callee, - span: _, - args, - } => { - json!({ - "type": "Expr::Call", - "callee": self.visit_expr(*callee)?, - "args": args.iter().map(|arg| self.visit_expr(*arg).unwrap()).collect::>(), - }) - } - Expr::Function { - name, - parameters, - body, - span: _, - } => { - let fn_name = if name.is_some() { - name.as_ref().unwrap().lexeme.as_ref().unwrap().to_string() - } else { - "".to_string() - }; - json!({ - "type": "Expr::Function", - "name": fn_name, - "parameters": parameters.iter().map(|param| param.lexeme.as_ref()).collect::>(), - "body": body.iter().map(|stmt| self.visit_stmt(*stmt).unwrap()).collect::>(), - }) - } - Expr::Get { - object, - name, - span: _, - } => { - json!({ - "type": "Expr::Get", - "object": self.visit_expr(*object)?, - "name": name.lexeme.as_ref(), - }) - } - Expr::Set { - object, - name, - value, - span: _, - } => { - json!({ - "type": "Expr::Set", - "object": self.visit_expr(*object)?, - "name": name.lexeme.as_ref(), - "value": self.visit_expr(*value)?, - }) - } - }; - - Ok(matched) - } - - fn visit_stmt(&self, sidx: StmtId) -> Result { - // TODO: Remove clone here - let a = Rc::clone(&self.program.arena); - let s = a.get_statement(sidx); - let matched: Value = match s { - Stmt::Program { body, span: _ } => { - json!({ - "type": "Stmt::Program", - "body": body.iter().map(|stmt| self.visit_stmt(*stmt).unwrap()).collect::>(), - }) - } - Stmt::Block { body, span: _ } => { - json!({ - "type": "Stmt::Block", - "body": body.iter().map(|stmt| self.visit_stmt(*stmt).unwrap()).collect::>(), - }) - } - Stmt::Expression { expr, span: _ } => { - json!({ - "type": "Stmt::Expression", - "expr": self.visit_expr(*expr)?, - }) - } - Stmt::Declaration { dst, expr, span: _ } => { - json!({ - "type": "Stmt::Declaration", - "variable": dst.lexeme.as_ref().unwrap(), - "expr": self.visit_expr(*expr)?, - }) - } - Stmt::If { - condition, - body, - r#else_body, - span: _, - } => { - json!({ - "type": "Stmt::If", - "condition": self.visit_expr(*condition)?, - "body": self.visit_stmt(*body)?, - "else_body": if r#else_body.is_some() { - self.visit_stmt(r#else_body.unwrap())? - } else { - json!(serde_json::Value::Null) - }, - }) - } - Stmt::Loop { - condition, - body, - post, - span: _, - } => { - json!({ - "type": "Stmt::Loop", - "condition": if condition.is_some() { - self.visit_expr(condition.unwrap())? - } else { - json!(serde_json::Value::Null) - }, - "body": self.visit_stmt(*body)?, - "post": if post.is_some() { - self.visit_stmt(post.unwrap())? - } else { - json!(serde_json::Value::Null) - }, - }) - } - Stmt::Break { span: _ } => json!({ - "type": "Stmt::Break", - }), - Stmt::Continue { span: _ } => json!({ - "type": "Stmt::Continue", - }), - Stmt::Return { expr, span: _ } => { - json!({ - "type": "Stmt::Return", - "expr": if expr.is_some() { - self.visit_expr(expr.unwrap())? - } else { - json!(serde_json::Value::Null) - }, - }) - } - }; - Ok(matched) - } -} +} \ No newline at end of file diff --git a/server/src/lang/token.rs b/server/src/lang/token.rs index e83cc39d..9a2618ee 100644 --- a/server/src/lang/token.rs +++ b/server/src/lang/token.rs @@ -1,9 +1,9 @@ use phf::phf_map; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use super::ast::Literal; -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize)] pub enum Symbol { Comma, Colon, @@ -31,7 +31,7 @@ pub enum Symbol { LogicalOr, } -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize)] pub enum TokenType { Str, Num, @@ -49,7 +49,7 @@ pub enum TokenType { Eof, } -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize)] pub enum Keyword { Class, Else, @@ -66,7 +66,7 @@ pub enum Keyword { Loop, } -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize)] pub enum SqlKeyword { /* Bool, @@ -262,7 +262,7 @@ pub static SQL_KEYWORDS: phf::Map<&'static str, TokenType> = phf_map! { "WRITE" => skw!(SqlKeyword::Write), }; -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize)] pub struct Token { pub tok_type: TokenType, pub literal: Option, @@ -270,7 +270,7 @@ pub struct Token { pub span: Span, } -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize)] pub struct Span { pub start: usize, pub end: usize, diff --git a/server/src/runtime/resolver.rs b/server/src/runtime/resolver.rs index 8bdec215..5ba02ae6 100644 --- a/server/src/runtime/resolver.rs +++ b/server/src/runtime/resolver.rs @@ -3,12 +3,13 @@ use crate::lang::ast::stmt::{Stmt, StmtId}; use crate::lang::ast::{Literal, ParserArena, VisitorMut}; use crate::lang::token::Token; use crate::runtime::types::RV; +use id_arena::Id; use rustc_hash::FxHashMap; use std::rc::Rc; pub struct Resolver { scopes: Vec>, - locals: FxHashMap, + locals: FxHashMap, usize>, arena: Rc, } @@ -27,7 +28,7 @@ impl Resolver { } pub fn get_distance(&self, eid: ExprId) -> Option { - self.locals.get(&eid).copied() + self.locals.get(&eid.0).copied() } pub fn begin_scope(&mut self) { @@ -55,7 +56,7 @@ impl Resolver { pub fn resolve_local(&mut self, expr: ExprId, name: &Token) { for i in (0..self.scopes.len()).rev() { if self.scopes[i].contains_key(name.literal.as_ref().unwrap().as_str().unwrap()) { - self.locals.insert(expr, self.scopes.len() - 1 - i); + self.locals.insert(expr.0, self.scopes.len() - 1 - i); return; } }