diff --git a/gold/src/ast/high.rs b/gold/src/ast/high.rs index 829f610..2586d46 100644 --- a/gold/src/ast/high.rs +++ b/gold/src/ast/high.rs @@ -1,7 +1,6 @@ -use std::fmt::Display; use std::rc::Rc; -use crate::{builtins::BUILTINS, error::{Reason, Span, Syntax}}; +use crate::{builtins::BUILTINS, error::{Reason, Span, Syntax}, types::LogicOp}; use crate::formatting::FormatSpec; use crate::error::{Action, Error, Tagged, Taggable}; @@ -9,7 +8,7 @@ use crate::Object; use crate::types::Key; use super::low; use super::scope::{SubScope, Scope, LocalScope}; -use crate::types::{UnOp, BinOp, Res}; +use crate::types::{UnOp, BinOp, Res, EagerOp}; // Utility // ---------------------------------------------------------------- @@ -438,7 +437,7 @@ impl Lower for ArgElement { #[derive(Debug, Clone, PartialEq)] pub enum Transform { /// Unary operator - UnOp(Tagged), + UnOp(Tagged>), /// Binary operator with right operand BinOp(Tagged, Box>), @@ -455,7 +454,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::Index.tag(loc), Box::new(subscript)) + Transform::BinOp(BinOp::Eager(EagerOp::Index).tag(loc), Box::new(subscript)) } /// Construct an exponentiation transform. @@ -465,7 +464,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::Power.tag(loc), Box::new(exponent)) + Transform::BinOp(BinOp::Eager(EagerOp::Power).tag(loc), Box::new(exponent)) } /// Construct a multiplication transform. @@ -475,7 +474,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::Multiply.tag(loc), Box::new(multiplicand)) + Transform::BinOp(BinOp::Eager(EagerOp::Multiply).tag(loc), Box::new(multiplicand)) } /// Construct an integer division transform. @@ -485,7 +484,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::IntegerDivide.tag(loc), Box::new(divisor)) + Transform::BinOp(BinOp::Eager(EagerOp::IntegerDivide).tag(loc), Box::new(divisor)) } /// Construct a mathematical division transform. @@ -495,7 +494,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::Divide.tag(loc), Box::new(divisor)) + Transform::BinOp(BinOp::Eager(EagerOp::Divide).tag(loc), Box::new(divisor)) } /// Construct an addition transform. @@ -505,7 +504,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::Add.tag(loc), Box::new(addend)) + Transform::BinOp(BinOp::Eager(EagerOp::Add).tag(loc), Box::new(addend)) } /// Construct a subtraction transform. @@ -515,7 +514,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::Subtract.tag(loc), Box::new(subtrahend)) + Transform::BinOp(BinOp::Eager(EagerOp::Subtract).tag(loc), Box::new(subtrahend)) } /// Construct a less-than transform. @@ -525,7 +524,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::Less.tag(loc), Box::new(rhs)) + Transform::BinOp(BinOp::Eager(EagerOp::Less).tag(loc), Box::new(rhs)) } /// Construct a greater-than transform. @@ -535,7 +534,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::Greater.tag(loc), Box::new(rhs)) + Transform::BinOp(BinOp::Eager(EagerOp::Greater).tag(loc), Box::new(rhs)) } /// Construct a less-than-or-equal transform. @@ -545,7 +544,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::LessEqual.tag(loc), Box::new(rhs)) + Transform::BinOp(BinOp::Eager(EagerOp::LessEqual).tag(loc), Box::new(rhs)) } /// Construct a greater-than-or-equal transform. @@ -555,7 +554,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::GreaterEqual.tag(loc), Box::new(rhs)) + Transform::BinOp(BinOp::Eager(EagerOp::GreaterEqual).tag(loc), Box::new(rhs)) } /// Construct an equality check transform. @@ -565,7 +564,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::Equal.tag(loc), Box::new(rhs)) + Transform::BinOp(BinOp::Eager(EagerOp::Equal).tag(loc), Box::new(rhs)) } /// Construct an inequality check transform. @@ -575,7 +574,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::NotEqual.tag(loc), Box::new(rhs)) + Transform::BinOp(BinOp::Eager(EagerOp::NotEqual).tag(loc), Box::new(rhs)) } /// Construct a containment check transform. @@ -585,7 +584,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::Contains.tag(loc), Box::new(rhs)) + Transform::BinOp(BinOp::Eager(EagerOp::Contains).tag(loc), Box::new(rhs)) } /// Construct a logical conjunction transform. @@ -595,7 +594,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::And.tag(loc), Box::new(rhs)) + Transform::BinOp(BinOp::Logic(LogicOp::And).tag(loc), Box::new(rhs)) } /// Construct a logical disjunction transform. @@ -605,7 +604,7 @@ impl Transform { where Span: From, { - Transform::BinOp(BinOp::Or.tag(loc), Box::new(rhs)) + Transform::BinOp(BinOp::Logic(LogicOp::Or).tag(loc), Box::new(rhs)) } } @@ -628,39 +627,6 @@ impl Lower for Transform { } } -impl Display for UnOp { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Passthrough => f.write_str(""), - Self::ArithmeticalNegate => f.write_str("-"), - Self::LogicalNegate => f.write_str("not"), - } - } -} - -impl Display for BinOp { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Index => f.write_str("subscript"), - Self::Power => f.write_str("^"), - Self::Multiply => f.write_str("*"), - Self::IntegerDivide => f.write_str("//"), - Self::Divide => f.write_str("/"), - Self::Add => f.write_str("+"), - Self::Subtract => f.write_str("-"), - Self::Less => f.write_str("<"), - Self::Greater => f.write_str(">"), - Self::LessEqual => f.write_str("<="), - Self::GreaterEqual => f.write_str(">="), - Self::Equal => f.write_str("=="), - Self::NotEqual => f.write_str("!="), - Self::Contains => f.write_str("in"), - Self::And => f.write_str("and"), - Self::Or => f.write_str("or"), - } - } -} - // Expr // ---------------------------------------------------------------- @@ -882,7 +848,7 @@ impl Tagged { where Span: From, { - self.transform(Transform::UnOp(UnOp::ArithmeticalNegate.tag(loc))) + self.transform(Transform::UnOp(Some(UnOp::ArithmeticalNegate).tag(loc))) } /// Logically negate this expression. @@ -892,7 +858,7 @@ impl Tagged { where Span: From, { - self.transform(Transform::UnOp(UnOp::LogicalNegate.tag(loc))) + self.transform(Transform::UnOp(Some(UnOp::LogicalNegate).tag(loc))) } /// Form a function call expression from by calling this function with a diff --git a/gold/src/ast/low.rs b/gold/src/ast/low.rs index 1ad3533..d0c899f 100644 --- a/gold/src/ast/low.rs +++ b/gold/src/ast/low.rs @@ -48,7 +48,7 @@ pub struct MapBinding { #[derive(Debug, Clone)] pub enum Transform { - UnOp(Tagged), + UnOp(Tagged>), BinOp(Tagged, Box>), FunCall(Tagged>>, bool), } @@ -139,7 +139,7 @@ pub struct Function { pub keywords: Option>, pub expression: Box>, pub slots: SlotCatalog, - pub requires: Vec, + pub requires: Option>, } impl Function { @@ -190,7 +190,7 @@ impl<'a> FunctionBuilder<'a> { keywords: self.keywords, expression: Box::new(self.expression.unwrap()), slots, - requires, + requires: Some(requires), } } } diff --git a/gold/src/compile.rs b/gold/src/compile.rs index 06be0e8..49d1a92 100644 --- a/gold/src/compile.rs +++ b/gold/src/compile.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use crate::ast::low::{Binding, Expr, Function, ListBinding, ListElement, MapBinding, MapBindingElement, MapElement, StringElement, Transform, ArgElement}; use crate::error::{Action, IntervalTree, Reason, Span, Tagged, Unpack}; use crate::formatting::FormatSpec; -use crate::types::{BinOp, UnOp, Key, Res}; +use crate::types::{BinOp, Key, Res, LogicOp}; use crate::Object; use crate::ast::{BindingLoc, SlotCatalog, SlotType}; @@ -34,107 +34,271 @@ pub struct CompiledFunction { #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub enum Instruction { // Loading + // ------------------------------------------------------------------------------------------------ + + /// Load the given object from the constants array and push it on the stack. LoadConst(usize), + + /// Load the given object from the locals array and push it on the stack. LoadLocal(usize), + + /// Load the given object from the cell array and push it on the stack. LoadCell(usize), + + /// Load the given object from the enclosed array and push it on the stack. LoadEnclosed(usize), + + /// Load the given function object, wrap it into a closure and push it on the stack. LoadFunc(usize), + + /// Load the given builtin from the global BUILTINS array and push it on the stack. LoadBuiltin(usize), + + /// Process the given import path and push the result on the stack. Import(usize), // Storing + // ------------------------------------------------------------------------------------------------ + + /// Pop the stack and push the object to the local array at the given index. StoreLocal(usize), + + /// Pop the stack and push the object to the cell array at the given index. StoreCell(usize), // Destroying + // ------------------------------------------------------------------------------------------------ + + /// Reset the slot in the local array at the given index. DestroyLocal(usize), + + /// Reset the slot in the cell array at the given index. + /// This should not mutate the existing cell, just replace it with a new one. DestroyCell(usize), // Control flow + // ------------------------------------------------------------------------------------------------ + + /// Pop the stack and push the object to the lower frame's stack. Return, + + /// Pop the stack, and jump the given number of instructions if the object is truthy. CondJump(usize), + + /// Jump the given number of instructions unconditionally. Not that, since + /// the VM advances over this instruction before executing it, the delta + /// must NOT count this instruction. Jump(usize), + + /// Jump the given number of instructions in reverse, unconditionally. Note + /// that, since the VM advances over this instruction before executing it, + /// the delta must count this instruction as well. JumpBack(usize), + + /// Clone the object on the top of the stack and push it. Duplicate, + + /// Drop the object on the top of the stack. Discard, + + /// Drop several objects on the top of the stack. DiscardMany(usize), + + /// Interchange the top two objects on the stack. Interchange, + + /// Call the function at stack[2] using stack[0] as arguments (must be a + /// list) and stack[1] as keyword arguments (must be a map). Call, + + /// Do nothing. Noop, // Asserts + // ------------------------------------------------------------------------------------------------ + + /// Throw an error unless the top of the stack is a list with at least the + /// given length. AssertListMinLength(usize), + + /// Throw an error unless the top of the stack is a list with at least the + /// given minimum length and at most the given maximum length. AssertListMinMaxLength(usize, usize), + + /// Throw an error unless the top of the stack is a map. AssertMap, // Unary operators + // ------------------------------------------------------------------------------------------------ + + /// Apply the unary mathematical negation operator to the top of the stack and push the result. ArithmeticalNegate, + + /// Apply the unary logical negation operator to the top of the stack and push the result. LogicalNegate, + + /// Format the top of the stack with the given format specification and push the result. FormatWithSpec(usize), + + /// Format the top of the stack with the default format specification and push the result. FormatWithDefault, // Binary mathematical operators + // ------------------------------------------------------------------------------------------------ + + /// Pop y and x from the stack, then push `x + y`. Add, + + /// Pop y and x from the stack, then push `x - y`. Subtract, + + /// Pop y and x from the stack, then push `x * y`. Multiply, + + /// Pop y and x from the stack, then push `x // y`. IntegerDivide, + + /// Pop y and x from the stack, then push `x / y`. Divide, + + /// Pop y and x from the stack, then push `x ^ y`. Power, // Binary comparison operators + // ------------------------------------------------------------------------------------------------ + + /// Pop y and x from the stack, then push `x < y`. Less, + + /// Pop y and x from the stack, then push `x > y`. Greater, + + /// Pop y and x from the stack, then push `x <= y`. LessEqual, + + /// Pop y and x from the stack, then push `x >= y`. GreaterEqual, + + /// Pop y and x from the stack, then push `x == y`. Equal, + + /// Pop y and x from the stack, then push `x != y`. NotEqual, + + /// Pop y and x from the stack, then push `x has y`. Contains, // Other operators + // ------------------------------------------------------------------------------------------------ + + /// Pop y and x from the stack, then push `x[y]`. Index, // Constructors + // ------------------------------------------------------------------------------------------------ + + /// Push a new empty list on the stack. NewList, + + /// Push a new empty map on the stack. NewMap, + + /// Pop the stack and convert the object into an iterator, then push it on the stack. NewIterator, + + /// Push a new empty string on the stack. NewString, // Mutability + // ------------------------------------------------------------------------------------------------ + + /// Pop the stack and push the object to the list on the top of the stack. PushToList, + + /// Pop the top two stack objects (value and key) and push them to the map + /// on the top of the stack. PushToMap, + + /// Pop the stack and insert all its elements into the object on the top of + /// the stack. SplatToCollection, + + /// Remove the key from the map at the top of the stack, if it exists. DelKeyIfExists(Key), + + /// Load the cell at the given index and push it to the closure at the top + /// of the stack. Important: the cell must be pushed as-is. It should not be + /// unwrapped. PushCellToClosure(usize), + + /// Load the cell at the given enclosure index and push it to the closure at + /// the top of the stack. Important: the cell must be pushed as-is. It + /// should not be unwrapped. PushEnclosedToClosure(usize), + + /// Get the next element from the iterator at the top of the stack and push + /// it on the stack. If there are no more elements, jump the given delta. NextOrJump(usize), // Shortcuts for internal use + // ------------------------------------------------------------------------------------------------ + + /// Get the element from the list at the top of the stack at the given + /// index, and push it on the stack. IntIndexL(usize), + + /// Get the element from the list at the top of the stack at the given + /// index, push it on the stack and then jump the given delta. Do not error + /// or jump if the index does not exist. IntIndexLAndJump { index: usize, jump: usize, }, + + /// Get the element from the list at the top of the stack at the given index + /// counted from the end, and push it on the stack. IntIndexFromEnd { index: usize, root_front: usize, root_back: usize, }, + + /// Get the element from the list at the top of the stack at the giv index + /// counted from the end, push it on the stack and then jump the given + /// delta. Do not error or jump if the index does not exist. IntIndexFromEndAndJump { index: usize, root_front: usize, root_back: usize, jump: usize, }, + + /// Extract the given slice from the list at the top of the stack and push + /// it to the stack. IntSlice { start: usize, from_end: usize, }, + + /// Get the element from the map at the top of the stack and push it on the + /// stack. IntIndexM(Key), + + /// Get the element from the map at the top of the stack, push it on the + /// stack and then jump the given delta. Do not error or jump if the key + /// does not exist. IntIndexMAndJump { key: Key, jump: usize, }, + + /// Pop the stack and push the object to the kwargs map (located at + /// stack[1]) with the given key. IntPushToKwargs(Key), + + /// Pop the stack and insert the elements into either the args list (located + /// at stack[0]) or the kwargs map (located at stack[1]), depending on + /// whether it's a list or a map. IntArgSplat, } @@ -223,6 +387,119 @@ impl SlotMap { } } +trait WrapCompiler: Sized { + fn compiler(&mut self) -> &mut Compiler; + + fn instruction(mut self, instruction: Instruction) -> Self { + self.compiler().instruction(instruction); + self + } + + fn emit_expression(mut self, expr: Expr) -> Res { + self.compiler().emit_expression(expr)?; + Ok(self) + } + + fn emit_binding(mut self, binding: Tagged) -> Res { + self.compiler().emit_binding(binding)?; + Ok(self) + } + + fn emit_list_binding(mut self, binding: ListBinding) -> Res { + self.compiler().emit_list_binding(binding)?; + Ok(self) + } + + fn emit_map_binding(mut self, binding: MapBinding) -> Res { + self.compiler().emit_map_binding(binding)?; + Ok(self) + } + + fn emit_list_element(mut self, element: ListElement) -> Res { + self.compiler().emit_list_element(element)?; + Ok(self) + } + + fn emit_map_element(mut self, element: MapElement) -> Res { + self.compiler().emit_map_element(element)?; + Ok(self) + } + + fn store_instruction(mut self, index: usize) -> Option { + self.compiler().store_instruction(index)?; + Some(self) + } +} + +struct TraceWrapper<'a> { + compiler: &'a mut Compiler, + span: Span, + action: Action, + reason: Option, + + start: usize, +} + +impl<'a> WrapCompiler for TraceWrapper<'a> { + fn compiler(&mut self) -> &mut Compiler { + self.compiler + } +} + +impl<'a> TraceWrapper<'a> { + fn new(compiler: &'a mut Compiler, span: Span, action: Action) -> Self { + let start = compiler.code.len(); + Self { compiler, span, action, start, reason: None } + } + + fn reason(mut self, reason: T) -> Self + where + Reason: From, + { + self.reason = Some(Reason::from(reason)); + self + } + + fn finalize(self) -> usize { + self.compiler.actions.push((self.start, self.compiler.code.len(), self.span, self.action)); + if let Some(reason) = self.reason { + self.compiler.reasons.push((self.start, self.compiler.code.len(), reason)); + } + self.compiler.code.len() - self.start + } +} + +struct JumpWrapper<'a> { + compiler: &'a mut Compiler, + start: usize, +} + +impl<'a> WrapCompiler for JumpWrapper<'a> { + fn compiler(&mut self) -> &mut Compiler { + self.compiler + } +} + +impl<'a> JumpWrapper<'a> { + fn new(compiler: &'a mut Compiler) -> Self { + let start = compiler.code.len(); + compiler.code.push(Instruction::Noop); + Self { compiler, start} + } + + fn to_start Instruction>(mut self, to_start: F) -> Self { + let len = self.compiler().code.len() + 1 - self.start; + self.compiler.instruction(to_start(len)); + self + } + + fn finalize Instruction>(self, finalizer: F) -> usize { + let len = self.compiler.code.len() - self.start; + self.compiler.code[self.start] = finalizer(len - 1); + len + } +} + pub struct Compiler { constants: Vec, funcs: Vec, @@ -263,7 +540,7 @@ impl Compiler { compiler.instruction(Instruction::Discard); if let Some(kwargs) = keywords { - compiler.emit_map_binding(kwargs)?; + compiler.emit_map_binding(kwargs.unwrap())?; } compiler.instruction(Instruction::Discard); @@ -274,36 +551,18 @@ impl Compiler { fn emit_expression(&mut self, expr: Expr) -> Res { match expr { - Expr::Constant(index) => { - self.instruction(Instruction::LoadConst(index)); - Ok(1) - } + Expr::Constant(index) => Ok(self.instruction(Instruction::LoadConst(index))), + Expr::Slot(loc) => Ok(self.load_instruction(loc).unwrap()), + Expr::Builtin(index) => Ok(self.instruction(Instruction::LoadBuiltin(index))), Expr::String(elements) => { - self.instruction(Instruction::NewString); - - let mut len = 0; + let mut len = self.instruction(Instruction::NewString); for element in elements { len += self.emit_string_element(element)?; } Ok(len + 1) } - Expr::Slot(loc) => { - match self.load_instruction(loc) { - Some(instruction) => { - self.instruction(instruction); - Ok(1) - } - None => { panic!("my god"); } - } - } - - Expr::Builtin(index) => { - self.instruction(Instruction::LoadBuiltin(index)); - Ok(1) - } - Expr::Transformed { operand, transform } => { let mut len = self.emit_expression(operand.unwrap())?; len += self.emit_transform(transform)?; @@ -315,34 +574,30 @@ impl Compiler { true_branch, false_branch, } => { - let cond_len = self.emit_expression(condition.unwrap())?; - let cjump_index = self.instruction(Instruction::Noop); - let false_len = self.emit_expression(false_branch.unwrap())?; - let jump_index = self.instruction(Instruction::Noop); - let true_len = self.emit_expression(true_branch.unwrap())?; - - self.code[cjump_index] = Instruction::CondJump(false_len + 1); - self.code[jump_index] = Instruction::Jump(true_len); - - Ok(cond_len + false_len + true_len + 2) + let mut len = self.emit_expression(condition.unwrap())?; + len += self.with_jump() + .emit_expression(false_branch.unwrap())? + .finalize(|l| Instruction::CondJump(l + 1)); + len += self.with_jump() + .emit_expression(true_branch.unwrap())? + .finalize(|l| Instruction::Jump(l)); + Ok(len) } Expr::List(elements) => { - self.instruction(Instruction::NewList); - let mut len = 0; + let mut len = self.instruction(Instruction::NewList); for element in elements { len += self.emit_list_element(element.unwrap())?; } - Ok(len + 1) + Ok(len) } Expr::Map(elements) => { - self.instruction(Instruction::NewMap); - let mut len = 0; + let mut len = self.instruction(Instruction::NewMap); for element in elements { len += self.emit_map_element(element.unwrap())?; } - Ok(len + 1) + Ok(len) } Expr::Let { bindings, expression, slots } => { @@ -362,137 +617,106 @@ impl Compiler { let mut len = 0; for (binding, path) in imports { let index = self.import_path(path.as_ref().clone()); - let loc = self.code.len(); - self.instruction(Instruction::Import(index)); - self.actions.push((loc, loc + 1, path.span(), Action::Import)); - len += self.emit_binding(binding)? + 1; + len += self.with_trace(path.span(), Action::Import) + .instruction(Instruction::Import(index)) + .finalize(); + len += self.emit_binding(binding)?; } len += self.emit_expression(expression.unwrap())?; len += self.pop_slots(); Ok(len) } - Expr::Func(function) => { - let load_index = self.instruction(Instruction::Noop); + Expr::Func(mut function) => { + let requires = function.requires.take(); - let mut len = 0; - for loc in function.requires.iter() { - match self.push_cell_instruction(*loc) { - Some(instruction) => { - self.instruction(instruction); - len += 1; - } - None => { panic!("the stars"); } + let compiled = Compiler::compile(function)?; + let index = self.function(compiled); + let mut len = self.instruction(Instruction::LoadFunc(index)); + + if let Some(requires) = requires { + for loc in requires { + len += self.push_cell_instruction(loc).unwrap(); } } - let compiled = Compiler::compile(function.clone())?; - let index = self.function(compiled); - self.code[load_index] = Instruction::LoadFunc(index); - Ok(len + 1) + Ok(len) } } } fn emit_binding(&mut self, binding: Tagged) -> Res { - let loc = self.code.len(); let (binding, span) = binding.decompose(); - - let retval = match binding { - Binding::Slot(slot) => { - match self.store_instruction(slot) { - Some(instruction) => { - self.instruction(instruction); - Ok(1) - } - None => { panic!("oh shit"); } - } - } - - Binding::List(binding) => { - let len = self.emit_list_binding(binding.unwrap())?; - self.instruction(Instruction::Discard); - Ok(len + 1) - } - - Binding::Map(binding) => { - let len = self.emit_map_binding(binding)?; - self.instruction(Instruction::Discard); - Ok(len + 1) - } - }; - - let end = self.code.len(); - self.actions.push((loc, end, span, Action::Bind)); - - retval + match binding { + Binding::Slot(slot) => Ok( + self.with_trace(span, Action::Bind) + .store_instruction(slot).unwrap() + .finalize() + ), + Binding::List(binding) => Ok( + self.with_trace(span, Action::Bind) + .emit_list_binding(binding.unwrap())? + .instruction(Instruction::Discard) + .finalize() + ), + Binding::Map(binding) => Ok( + self.with_trace(span, Action::Bind) + .emit_map_binding(binding.unwrap())? + .instruction(Instruction::Discard) + .finalize() + ), + } } fn emit_list_binding(&mut self, binding: ListBinding) -> Res { let mut len = 0; if binding.slurp.is_some() { - self.instruction(Instruction::AssertListMinLength( + len += self.instruction(Instruction::AssertListMinLength( binding.num_front + binding.num_back, )); - len += 1; } else { - self.instruction(Instruction::AssertListMinMaxLength( + len += self.instruction(Instruction::AssertListMinMaxLength( binding.num_front, binding.num_front + binding.def_front, )); - len += 1; } if let Some(Some(slot)) = binding.slurp { - self.instruction(Instruction::IntSlice { + len += self.instruction(Instruction::IntSlice { start: binding.num_front + binding.def_front, from_end: binding.num_back + binding.def_back, }); - match self.store_instruction(slot) { - Some(instruction) => { - self.instruction(instruction); - len += 1; - } - None => { panic!("oh shit"); } - } - len += 2; + len += self.store_instruction(slot).unwrap(); } for (i, (sub_binding, default)) in binding.front.into_iter().enumerate() { if let Some(d) = default { - let index = self.instruction(Instruction::Noop); - let default_len = self.emit_expression(d.unwrap())?; - self.code[index] = Instruction::IntIndexLAndJump { - index: i, - jump: default_len, - }; - len += default_len + 1; + len += self.with_jump() + .emit_expression(d.unwrap())? + .finalize(|l| Instruction::IntIndexLAndJump { index: i, jump: l }); } else { - self.instruction(Instruction::IntIndexL(i)); - len += 1; + len += self.instruction(Instruction::IntIndexL(i)); } len += self.emit_binding(sub_binding)?; } for (i, (sub_binding, default)) in binding.back.into_iter().enumerate() { if let Some(d) = default { - let index = self.instruction(Instruction::Noop); - let default_len = self.emit_expression(d.unwrap())?; - self.code[index] = Instruction::IntIndexFromEndAndJump { - index: i, - root_front: binding.num_front + binding.def_front, - root_back: binding.num_back + binding.def_back, - jump: default_len, - }; - len += default_len + 1; + len += self.with_jump() + .emit_expression(d.unwrap())? + .finalize(|l| Instruction::IntIndexFromEndAndJump { + index: i, + root_front: binding.num_front + binding.def_front, + root_back: binding.num_back + binding.def_back, + jump: l, + }); } else { - self.instruction(Instruction::IntIndexFromEnd { + len += self.instruction(Instruction::IntIndexFromEnd { index: i, root_front: binding.num_front + binding.def_front, root_back: binding.num_back + binding.def_back, }); - len += 1; } len += self.emit_binding(sub_binding)?; } @@ -500,48 +724,31 @@ impl Compiler { Ok(len) } - fn emit_map_binding(&mut self, binding: Tagged) -> Res { - let mut len = 0; - - self.instruction(Instruction::AssertMap); - len += 1; - - let binding = binding.unwrap(); + fn emit_map_binding(&mut self, binding: MapBinding) -> Res { + let mut len = self.instruction(Instruction::AssertMap); if let Some(slot) = binding.slurp { - self.instruction(Instruction::Duplicate); - len += 1; - + len += self.instruction(Instruction::Duplicate); for MapBindingElement { key, .. } in binding.elements.iter() { - self.instruction(Instruction::DelKeyIfExists(*key.as_ref())); - len += 1; + len += self.instruction(Instruction::DelKeyIfExists(*key.as_ref())); } - - match self.store_instruction(slot) { - Some(instruction) => { - self.instruction(instruction); - len += 1; - } - None => { panic!("oh shit"); } - } - - len += 1; + len += self.store_instruction(slot).unwrap(); } for MapBindingElement { key, binding, default} in binding.elements.into_iter() { if let Some(d) = default { - let index = self.instruction(Instruction::Noop); - let default_len = self.emit_expression(d.unwrap())?; - self.code[index] = Instruction::IntIndexMAndJump { - key: *key.as_ref(), - jump: default_len, - }; - len += default_len + 1; + len += self.with_jump() + .emit_expression(d.unwrap())? + .finalize(|l| Instruction::IntIndexMAndJump { + key: key.unwrap(), + jump: l, + }); } else { - let loc = self.code.len(); - self.instruction(Instruction::IntIndexM(*key.as_ref())); - self.actions.push((loc, loc + 1, key.span(), Action::Bind)); - self.reasons.push((loc, loc + 1, Reason::from(Unpack::KeyMissing(*key)))); + let (key, span) = key.decompose(); + len += self.with_trace(span, Action::Bind) + .reason(Unpack::KeyMissing(key)) + .instruction(Instruction::IntIndexM(key)) + .finalize(); } len += self.emit_binding(binding)?; } @@ -552,23 +759,23 @@ impl Compiler { fn emit_string_element(&mut self, element: StringElement) -> Res { match element { StringElement::Raw(index) => { - self.instruction(Instruction::LoadConst(index)); - self.instruction(Instruction::Add); - Ok(2) + let mut len = self.instruction(Instruction::LoadConst(index)); + len += self.instruction(Instruction::Add); + Ok(len) } StringElement::Interpolate(expr, spec) => { let (expr, span) = expr.decompose(); - let len = self.emit_expression(expr)?; + let mut len = self.emit_expression(expr)?; if let Some(index) = spec { - self.instruction(Instruction::FormatWithSpec(index)); + len += self.instruction(Instruction::FormatWithSpec(index)); } else { - self.instruction(Instruction::FormatWithDefault); + len += self.instruction(Instruction::FormatWithDefault); } - let loc = self.code.len(); - self.actions.push((loc, loc + 1, span, Action::Format)); - self.instruction(Instruction::Add); - Ok(len + 2) + len += self.with_trace(span, Action::Format) + .instruction(Instruction::Add) + .finalize(); + Ok(len) } } } @@ -576,49 +783,50 @@ impl Compiler { fn emit_list_element(&mut self, element: ListElement) -> Res { match element { ListElement::Singleton(expr) => { - let len = self.emit_expression(expr.unwrap())?; - self.instruction(Instruction::PushToList); - Ok(len + 1) + let mut len = self.emit_expression(expr.unwrap())?; + len += self.instruction(Instruction::PushToList); + Ok(len) } ListElement::Splat(expr) => { let (expr, span) = expr.decompose(); - let len = self.emit_expression(expr)?; - let loc = self.code.len(); - self.instruction(Instruction::SplatToCollection); - self.actions.push((loc, loc + 1, span, Action::Splat)); - Ok(len + 1) + let mut len = self.emit_expression(expr)?; + len += self.with_trace(span, Action::Splat) + .instruction(Instruction::SplatToCollection) + .finalize(); + Ok(len) } ListElement::Cond { condition, element } => { - let condition_len = self.emit_expression(condition.unwrap())?; - self.instruction(Instruction::LogicalNegate); - let index = self.instruction(Instruction::Noop); - let element_len = self.emit_list_element(element.unwrap())?; - self.code[index] = Instruction::CondJump(element_len); - Ok(condition_len + element_len + 2) + let mut len = self.emit_expression(condition.unwrap())?; + len += self.instruction(Instruction::LogicalNegate); + len += self.with_jump() + .emit_list_element(element.unwrap())? + .finalize(Instruction::CondJump); + Ok(len) } ListElement::Loop { binding, iterable, element, slots } => { let (iterable, span) = iterable.decompose(); - let iterable_len = self.emit_expression(iterable)?; - let loc = self.code.len(); - self.instruction(Instruction::NewIterator); - self.actions.push((loc, loc + 1, span, Action::Iterate)); + let mut len = self.emit_expression(iterable)?; + + len += self.with_trace(span, Action::Iterate) + .instruction(Instruction::NewIterator) + .finalize(); self.push_slots(slots); - let index = self.instruction(Instruction::Noop); - let mut jump_len = self.emit_binding(binding)?; - self.instruction(Instruction::Interchange); - jump_len += self.emit_list_element(element.unwrap())?; - self.instruction(Instruction::Interchange); - self.instruction(Instruction::JumpBack(jump_len + 4)); - self.code[index] = Instruction::NextOrJump(jump_len + 3); - self.instruction(Instruction::Discard); + len += self.with_jump() + .emit_binding(binding)? + .instruction(Instruction::Interchange) + .emit_list_element(element.unwrap())? + .instruction(Instruction::Interchange) + .to_start(Instruction::JumpBack) + .finalize(Instruction::NextOrJump); - let pop_len = self.pop_slots(); - Ok(iterable_len + jump_len + pop_len + 6) + len += self.instruction(Instruction::Discard); + len += self.pop_slots(); + Ok(len) } } } @@ -626,53 +834,54 @@ impl Compiler { fn emit_map_element(&mut self, element: MapElement) -> Res { match element { MapElement::Singleton { key, value } => { - let loc = self.code.len(); let (key, span) = key.decompose(); - let mut len = self.emit_expression(key)?; - self.actions.push((loc, self.code.len(), span, Action::Assign)); + let mut len = self.with_trace(span, Action::Assign) + .emit_expression(key)? + .finalize(); len += self.emit_expression(value.unwrap())?; - self.instruction(Instruction::PushToMap); - Ok(len + 1) + len += self.instruction(Instruction::PushToMap); + Ok(len) } MapElement::Splat(expr) => { let (expr, span) = expr.decompose(); - let len = self.emit_expression(expr)?; - let loc = self.code.len(); - self.instruction(Instruction::SplatToCollection); - self.actions.push((loc, loc + 1, span, Action::Splat)); - Ok(len + 1) + let mut len = self.emit_expression(expr)?; + len += self.with_trace(span, Action::Splat) + .instruction(Instruction::SplatToCollection) + .finalize(); + Ok(len) } MapElement::Cond { condition, element } => { - let condition_len = self.emit_expression(condition.unwrap())?; - self.instruction(Instruction::LogicalNegate); - let index = self.instruction(Instruction::Noop); - let element_len = self.emit_map_element(element.unwrap())?; - self.code[index] = Instruction::CondJump(element_len); - Ok(condition_len + element_len + 2) + let mut len = self.emit_expression(condition.unwrap())?; + len += self.instruction(Instruction::LogicalNegate); + len += self.with_jump() + .emit_map_element(element.unwrap())? + .finalize(Instruction::CondJump); + Ok(len) } MapElement::Loop { binding, iterable, element, slots } => { let (iterable, span) = iterable.decompose(); - let iterable_len = self.emit_expression(iterable)?; - let loc = self.code.len(); - self.instruction(Instruction::NewIterator); - self.actions.push((loc, loc + 1, span, Action::Iterate)); + let mut len = self.emit_expression(iterable)?; + + len += self.with_trace(span, Action::Iterate) + .instruction(Instruction::NewIterator) + .finalize(); self.push_slots(slots); - let index = self.instruction(Instruction::Noop); - let mut jump_len = self.emit_binding(binding)?; - self.instruction(Instruction::Interchange); - jump_len += self.emit_map_element(element.unwrap())?; - self.instruction(Instruction::Interchange); - self.instruction(Instruction::JumpBack(jump_len + 4)); - self.code[index] = Instruction::NextOrJump(jump_len + 3); - self.instruction(Instruction::Discard); + len += self.with_jump() + .emit_binding(binding)? + .instruction(Instruction::Interchange) + .emit_map_element(element.unwrap())? + .instruction(Instruction::Interchange) + .to_start(Instruction::JumpBack) + .finalize(Instruction::NextOrJump); - let pop_len = self.pop_slots(); - Ok(iterable_len + jump_len + pop_len + 6) + len += self.instruction(Instruction::Discard); + len += self.pop_slots(); + Ok(len) } } } @@ -680,120 +889,65 @@ impl Compiler { fn emit_transform(&mut self, transform: Transform) -> Res { match transform { Transform::UnOp(op) => { - let loc = self.code.len(); - let len = match op.as_ref() { - UnOp::Passthrough => 0, - UnOp::ArithmeticalNegate => { - self.code.push(Instruction::ArithmeticalNegate); - 1 - } - UnOp::LogicalNegate => { - self.code.push(Instruction::LogicalNegate); - 1 - } - }; - - if len > 0 { - self.actions - .push((loc, loc + len, op.span(), Action::Evaluate)); + let (op, span) = op.decompose(); + if let Some(op) = op { + let len = self.with_trace(span, Action::Evaluate) + .instruction(op.instruction()) + .finalize(); + Ok(len) + } else { + Ok(0) } - Ok(len) } Transform::BinOp(operator, operand) => { let (operator, span) = operator.decompose(); - - match operator { - BinOp::Or => { - self.instruction(Instruction::Duplicate); - let cjump_index = self.instruction(Instruction::Noop); - self.instruction(Instruction::Discard); - let false_len = self.emit_expression(operand.unwrap())?; - self.code[cjump_index] = Instruction::CondJump(false_len + 1); - return Ok(false_len + 3); - } - BinOp::And => { - self.instruction(Instruction::Duplicate); - self.instruction(Instruction::CondJump(1)); - let jump_index = self.instruction(Instruction::Noop); - self.instruction(Instruction::Discard); - let true_len = self.emit_expression(operand.unwrap())?; - self.code[jump_index] = Instruction::Jump(true_len + 1); - return Ok(true_len + 4); - } - _ => {} - } - - let len = self.emit_expression(operand.unwrap())?; - let loc = self.code.len(); - match operator { - BinOp::Power => { - self.code.push(Instruction::Power); - } - BinOp::Multiply => { - self.code.push(Instruction::Multiply); - } - BinOp::IntegerDivide => { - self.code.push(Instruction::IntegerDivide); - } - BinOp::Divide => { - self.code.push(Instruction::Divide); - } - BinOp::Add => { - self.code.push(Instruction::Add); - } - BinOp::Subtract => { - self.code.push(Instruction::Subtract); + BinOp::Logic(LogicOp::Or) => { + let mut len = self.instruction(Instruction::Duplicate); + len += self.with_jump() + .instruction(Instruction::Discard) + .emit_expression(operand.unwrap())? + .finalize(Instruction::CondJump); + Ok(len) } - BinOp::Less => { - self.code.push(Instruction::Less); + BinOp::Logic(LogicOp::And) => { + let mut len = self.instruction(Instruction::Duplicate); + len += self.instruction(Instruction::CondJump(1)); + len += self.with_jump() + .instruction(Instruction::Discard) + .emit_expression(operand.unwrap())? + .finalize(Instruction::Jump); + Ok(len) } - BinOp::Greater => { - self.code.push(Instruction::Greater); + BinOp::Eager(op) => { + let mut len = self.emit_expression(operand.unwrap())?; + len += self.with_trace(span, Action::Evaluate) + .instruction(op.instruction()) + .finalize(); + Ok(len) } - BinOp::LessEqual => { - self.code.push(Instruction::LessEqual); - } - BinOp::GreaterEqual => { - self.code.push(Instruction::GreaterEqual); - } - BinOp::Equal => { - self.code.push(Instruction::Equal); - } - BinOp::NotEqual => { - self.code.push(Instruction::NotEqual); - } - BinOp::Contains => { - self.code.push(Instruction::Contains); - } - BinOp::Index => { - self.code.push(Instruction::Index); - } - BinOp::Or | BinOp::And => {} - }; - - self.actions.push((loc, loc + 1, span, Action::Evaluate)); - Ok(len + 1) + } } Transform::FunCall(args, add_action) => { - self.instruction(Instruction::NewMap); - self.instruction(Instruction::NewList); + let mut len = self.instruction(Instruction::NewMap); + len += self.instruction(Instruction::NewList); - let mut len = 0; let (args, span) = args.decompose(); for arg in args.into_iter() { len += self.emit_arg_element(arg.unwrap())?; } - let loc = self.code.len(); - self.instruction(Instruction::Call); if add_action { - self.actions.push((loc, loc + 1, span, Action::Evaluate)); + len += self.with_trace(span, Action::Evaluate) + .instruction(Instruction::Call) + .finalize(); + } else { + len += self.instruction(Instruction::Call); } - Ok(len + 3) + Ok(len) } } } @@ -801,30 +955,29 @@ impl Compiler { fn emit_arg_element(&mut self, arg: ArgElement) -> Res { match arg { ArgElement::Singleton(expr) => { - let len = self.emit_expression(expr.unwrap())?; - self.instruction(Instruction::PushToList); - Ok(len + 1) + let mut len = self.emit_expression(expr.unwrap())?; + len += self.instruction(Instruction::PushToList); + Ok(len) } ArgElement::Keyword(key, expr) => { - let len = self.emit_expression(expr.unwrap())?; - self.instruction(Instruction::IntPushToKwargs(*key)); - Ok(len + 1) + let mut len = self.emit_expression(expr.unwrap())?; + len += self.instruction(Instruction::IntPushToKwargs(*key)); + Ok(len) } ArgElement::Splat(expr) => { let (expr, span) = expr.decompose(); - let len = self.emit_expression(expr)?; - let loc = self.code.len(); - self.instruction(Instruction::IntArgSplat); - self.actions.push((loc, loc + 1, span, Action::Splat)); - Ok(len + 1) + let mut len = self.emit_expression(expr)?; + len += self.with_trace(span, Action::Splat) + .instruction(Instruction::IntArgSplat) + .finalize(); + Ok(len) } } } fn instruction(&mut self, instruction: Instruction) -> usize { - let r = self.code.len(); self.code.push(instruction); match instruction { @@ -843,20 +996,24 @@ impl Compiler { _ => {} } - r + 1 } - fn push_cell_instruction(&mut self, loc: BindingLoc) -> Option { + fn push_cell_instruction(&mut self, loc: BindingLoc) -> Option { match loc { - BindingLoc::Enclosed(i) => Some(Instruction::PushEnclosedToClosure(i)), - _ => match self.load_instruction(loc) { - Some(Instruction::LoadCell(i)) => Some(Instruction::PushCellToClosure(i)), + BindingLoc::Enclosed(i) => { + Some(self.instruction(Instruction::PushEnclosedToClosure(i))) + }, + _ => match self.load_instruction_impl(loc) { + Some(Instruction::LoadCell(i)) => { + Some(self.instruction(Instruction::PushCellToClosure(i))) + }, _ => None, } } } - fn load_instruction(&mut self, loc: BindingLoc) -> Option { + fn load_instruction_impl(&mut self, loc: BindingLoc) -> Option { match loc { BindingLoc::Enclosed(i) => { Some(Instruction::LoadEnclosed(i)) } BindingLoc::Slot(index) => { @@ -871,11 +1028,18 @@ impl Compiler { } } - fn store_instruction(&mut self, index: usize) -> Option { + fn load_instruction(&mut self, loc: BindingLoc) -> Option { + match self.load_instruction_impl(loc) { + Some(instruction) => Some(self.instruction(instruction)), + None => None, + } + } + + fn store_instruction(&mut self, index: usize) -> Option { for map in self.slots.iter_mut().rev() { let result = map.store_instruction(index); - if result.is_some() { - return result; + if let Some(instruction) = result { + return Some(self.instruction(instruction)); } } None @@ -903,6 +1067,14 @@ impl Compiler { r } + fn with_trace<'a>(&'a mut self, span: Span, action: Action) -> TraceWrapper<'a> { + TraceWrapper::new(self, span, action) + } + + fn with_jump<'a>(&'a mut self) -> JumpWrapper<'a> { + JumpWrapper::new(self) + } + fn build_trace(&mut self) -> IntervalTree { let mut endpoints: HashSet = HashSet::new(); for (left, right, ..) in &self.actions { diff --git a/gold/src/eval.rs b/gold/src/eval.rs index 3280d54..8216b0f 100644 --- a/gold/src/eval.rs +++ b/gold/src/eval.rs @@ -15,7 +15,7 @@ use crate::builtins::BUILTINS; use crate::compile::{CompiledFunction, Instruction}; use crate::error::{BindingType, Error, Internal, Reason, TypeMismatch, Unpack}; use crate::formatting::FormatSpec; -use crate::types::{BinOp, Cell, GcCell, Res}; +use crate::types::{BinOp, Cell, GcCell, Res, EagerOp}; use crate::{eval_file, eval_raw as eval_str}; use crate::{List, Map, Object, Type}; @@ -506,7 +506,7 @@ impl<'a> Vm<'a> { self.err().with_reason(TypeMismatch::BinOp( lhs.type_of(), rhs.type_of(), - BinOp::Less, + BinOp::Eager(EagerOp::Less), )) }) .map(Object::from)?; @@ -522,7 +522,7 @@ impl<'a> Vm<'a> { self.err().with_reason(TypeMismatch::BinOp( lhs.type_of(), rhs.type_of(), - BinOp::Greater, + BinOp::Eager(EagerOp::Greater), )) }) .map(Object::from)?; @@ -538,7 +538,7 @@ impl<'a> Vm<'a> { self.err().with_reason(TypeMismatch::BinOp( lhs.type_of(), rhs.type_of(), - BinOp::LessEqual, + BinOp::Eager(EagerOp::LessEqual), )) }) .map(|x| Object::from(!x))?; @@ -554,7 +554,7 @@ impl<'a> Vm<'a> { self.err().with_reason(TypeMismatch::BinOp( lhs.type_of(), rhs.type_of(), - BinOp::GreaterEqual, + BinOp::Eager(EagerOp::GreaterEqual), )) }) .map(|x| Object::from(!x))?; @@ -793,7 +793,7 @@ impl<'a> Vm<'a> { #[cfg(test)] mod tests { - use crate::types::{BinOp, UnOp, Key, Res}; + use crate::types::{BinOp, UnOp, Key, Res, EagerOp}; use crate::error::{Action, BindingType, Error, Reason, Span, TypeMismatch, Types, Unpack}; use crate::{eval_raw, Object, Type}; @@ -1701,70 +1701,70 @@ mod tests { assert_eq!( eval("1 + true"), err!( - TypeMismatch::BinOp(Type::Integer, Type::Boolean, BinOp::Add), + TypeMismatch::BinOp(Type::Integer, Type::Boolean, BinOp::Eager(EagerOp::Add)), loc!(2, Evaluate) ) ); assert_eq!( eval("\"t\" - 9"), err!( - TypeMismatch::BinOp(Type::String, Type::Integer, BinOp::Subtract), + TypeMismatch::BinOp(Type::String, Type::Integer, BinOp::Eager(EagerOp::Subtract)), loc!(4, Evaluate) ) ); assert_eq!( eval("[] * 9"), err!( - TypeMismatch::BinOp(Type::List, Type::Integer, BinOp::Multiply), + TypeMismatch::BinOp(Type::List, Type::Integer, BinOp::Eager(EagerOp::Multiply)), loc!(3, Evaluate) ) ); assert_eq!( eval("9 / {}"), err!( - TypeMismatch::BinOp(Type::Integer, Type::Map, BinOp::Divide), + TypeMismatch::BinOp(Type::Integer, Type::Map, BinOp::Eager(EagerOp::Divide)), loc!(2, Evaluate) ) ); assert_eq!( eval("null // {}"), err!( - TypeMismatch::BinOp(Type::Null, Type::Map, BinOp::IntegerDivide), + TypeMismatch::BinOp(Type::Null, Type::Map, BinOp::Eager(EagerOp::IntegerDivide)), loc!(5..7, Evaluate) ) ); assert_eq!( eval("null < true"), err!( - TypeMismatch::BinOp(Type::Null, Type::Boolean, BinOp::Less), + TypeMismatch::BinOp(Type::Null, Type::Boolean, BinOp::Eager(EagerOp::Less)), loc!(5, Evaluate) ) ); assert_eq!( eval("1 > \"\""), err!( - TypeMismatch::BinOp(Type::Integer, Type::String, BinOp::Greater), + TypeMismatch::BinOp(Type::Integer, Type::String, BinOp::Eager(EagerOp::Greater)), loc!(2, Evaluate) ) ); assert_eq!( eval("[] <= 2.1"), err!( - TypeMismatch::BinOp(Type::List, Type::Float, BinOp::LessEqual), + TypeMismatch::BinOp(Type::List, Type::Float, BinOp::Eager(EagerOp::LessEqual)), loc!(3..5, Evaluate) ) ); assert_eq!( eval("{} >= false"), err!( - TypeMismatch::BinOp(Type::Map, Type::Boolean, BinOp::GreaterEqual), + TypeMismatch::BinOp(Type::Map, Type::Boolean, BinOp::Eager(EagerOp::GreaterEqual)), loc!(3..5, Evaluate) ) ); assert_eq!( eval("1 has 2"), err!( - TypeMismatch::BinOp(Type::Integer, Type::Integer, BinOp::Contains), + TypeMismatch::BinOp(Type::Integer, Type::Integer, BinOp::Eager(EagerOp::Contains)), loc!(2..5, Evaluate) ) ); @@ -1786,21 +1786,21 @@ mod tests { assert_eq!( eval("null[2]"), err!( - TypeMismatch::BinOp(Type::Null, Type::Integer, BinOp::Index), + TypeMismatch::BinOp(Type::Null, Type::Integer, BinOp::Eager(EagerOp::Index)), loc!(4..7, Evaluate) ) ); assert_eq!( eval("2[null]"), err!( - TypeMismatch::BinOp(Type::Integer, Type::Null, BinOp::Index), + TypeMismatch::BinOp(Type::Integer, Type::Null, BinOp::Eager(EagerOp::Index)), loc!(1..7, Evaluate) ) ); assert_eq!( eval("(2).x"), err!( - TypeMismatch::BinOp(Type::Integer, Type::String, BinOp::Index), + TypeMismatch::BinOp(Type::Integer, Type::String, BinOp::Eager(EagerOp::Index)), loc!(3, Evaluate) ) ); diff --git a/gold/src/object/mod.rs b/gold/src/object/mod.rs index f82046c..9377d57 100644 --- a/gold/src/object/mod.rs +++ b/gold/src/object/mod.rs @@ -36,7 +36,7 @@ use symbol_table::GlobalSymbol; use crate::compile::CompiledFunction; use crate::error::{Error, Internal, Reason, TypeMismatch, Value}; use crate::formatting::FormatSpec; -use crate::types::{Cell, Gc, GcCell, Res, UnOp, BinOp, Key, List, Map, Type}; +use crate::types::{Cell, Gc, GcCell, Res, UnOp, BinOp, Key, List, Map, Type, EagerOp}; #[cfg(feature = "python")] use crate::types::NativeClosure; @@ -512,23 +512,23 @@ impl Object { Ok(result) } (ObjV::Str(x), ObjV::Str(y)) => Ok(Self(ObjV::Str(x.add(y)))), - _ => self.operate(other, Int::add, |x, y| x + y, BinOp::Add), + _ => self.operate(other, Int::add, |x, y| x + y, BinOp::Eager(EagerOp::Add)), } } /// The minus operator: mathematical subtraction. pub fn sub(&self, other: &Self) -> Res { - self.operate(other, Int::sub, |x, y| x - y, BinOp::Subtract) + self.operate(other, Int::sub, |x, y| x - y, BinOp::Eager(EagerOp::Subtract)) } /// The asterisk operator: mathematical multiplication. pub fn mul(&self, other: &Self) -> Res { - self.operate(other, Int::mul, |x, y| x * y, BinOp::Multiply) + self.operate(other, Int::mul, |x, y| x * y, BinOp::Eager(EagerOp::Multiply)) } /// The slash operator: mathematical division. pub fn div(&self, other: &Self) -> Res { - self.operate(other, Int::div, |x, y| x / y, BinOp::Divide) + self.operate(other, Int::div, |x, y| x / y, BinOp::Eager(EagerOp::Divide)) } /// The double slash operator: integer division. @@ -537,7 +537,7 @@ impl Object { other, Int::idiv, |x, y| (x / y).floor() as f64, - BinOp::IntegerDivide, + BinOp::Eager(EagerOp::IntegerDivide), ) } @@ -561,7 +561,7 @@ impl Object { Error::new(TypeMismatch::BinOp( self.type_of(), other.type_of(), - BinOp::Power, + BinOp::Eager(EagerOp::Power), )) })?; Ok(Self::from(xx.powf(yy))) @@ -869,7 +869,7 @@ impl Object { _ => Err(Error::new(TypeMismatch::BinOp( self.type_of(), other.type_of(), - BinOp::Index, + BinOp::Eager(EagerOp::Index), ))), } } @@ -890,7 +890,7 @@ impl Object { Err(Error::new(TypeMismatch::BinOp( self.type_of(), other.type_of(), - BinOp::Contains, + BinOp::Eager(EagerOp::Contains), ))) } diff --git a/gold/src/parsing.rs b/gold/src/parsing.rs index 2b49b20..23bbd4e 100644 --- a/gold/src/parsing.rs +++ b/gold/src/parsing.rs @@ -20,7 +20,7 @@ use crate::formatting::{ StringAlignSpec, UppercaseSpec, }; use crate::lexing::{CachedLexResult, CachedLexer, Lexer, TokenType}; -use crate::types::{UnOp, BinOp, Key, Res}; +use crate::types::{UnOp, BinOp, EagerOp, Key, Res}; use crate::Object; trait ExplainError { @@ -1072,7 +1072,7 @@ fn object_access<'a>(input: In<'a>) -> Out<'a, Tagged> { tuple((dot, fail(identifier, SyntaxElement::Identifier))), |(dot, out)| { Transform::BinOp( - BinOp::Index.tag(&dot), + BinOp::Eager(EagerOp::Index).tag(&dot), Box::new(out.map(Object::from).map(Expr::Literal)), ) .tag(dot.span()..out.span()) @@ -1092,7 +1092,7 @@ fn object_index<'a>(input: In<'a>) -> Out<'a, Tagged> { )), |(a, expr, b)| { let span = Span::from(a.span()..b.span()); - Transform::BinOp(BinOp::Index.tag(span), Box::new(expr.inner())).tag(span) + Transform::BinOp(BinOp::Eager(EagerOp::Index).tag(span), Box::new(expr.inner())).tag(span) }, )(input) } @@ -1189,9 +1189,9 @@ fn prefixed<'a>(input: In<'a>) -> Out<'a, PExpr> { map( tuple(( many1(alt(( - map(plus, |x| x.map(|_| UnOp::Passthrough)), - map(minus, |x| x.map(|_| UnOp::ArithmeticalNegate)), - map(keyword("not"), |x| x.map(|_| UnOp::LogicalNegate)), + map(plus, |x| x.map(|_| None)), + map(minus, |x| x.map(|_| Some(UnOp::ArithmeticalNegate))), + map(keyword("not"), |x| x.map(|_| Some(UnOp::LogicalNegate))), ))), fail(power, SyntaxElement::Operand), )), diff --git a/gold/src/types.rs b/gold/src/types.rs index d5edd11..be895f9 100644 --- a/gold/src/types.rs +++ b/gold/src/types.rs @@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize, Serializer, Deserializer}; use serde::de::Visitor; use symbol_table::GlobalSymbol; +use crate::compile::Instruction; use crate::{Error, Object}; use crate::builtins::BUILTINS; @@ -254,9 +255,6 @@ impl FromIterator<(K, V)> for OrderedMap { /// Enumerates all the unary operators in the Gold language. #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] pub enum UnOp { - /// Passthrough (do-nothing) operator, e.g. the unary plus - Passthrough, - /// Arithmetical negation (unary minus) ArithmeticalNegate, @@ -264,9 +262,27 @@ pub enum UnOp { LogicalNegate, } -/// Enumerates all the binary operators in the Gold language. +impl UnOp { + pub fn instruction(&self) -> Instruction { + match self { + Self::ArithmeticalNegate => Instruction::ArithmeticalNegate, + Self::LogicalNegate => Instruction::LogicalNegate, + } + } +} + +impl Display for UnOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::ArithmeticalNegate => f.write_str("-"), + Self::LogicalNegate => f.write_str("not"), + } + } +} + +/// Enumerates all eager (non-short-circuiting) binary operators in the Gold language. #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] -pub enum BinOp { +pub enum EagerOp { /// Index or subscripting operator Index, @@ -308,10 +324,81 @@ pub enum BinOp { /// Containment Contains, +} + +impl EagerOp { + pub fn instruction(&self) -> Instruction { + match self { + Self::Index => Instruction::Index, + Self::Power => Instruction::Power, + Self::Multiply => Instruction::Multiply, + Self::IntegerDivide => Instruction::IntegerDivide, + Self::Divide => Instruction::Divide, + Self::Add => Instruction::Add, + Self::Subtract => Instruction::Subtract, + Self::Less => Instruction::Less, + Self::Greater => Instruction::Greater, + Self::LessEqual => Instruction::LessEqual, + Self::GreaterEqual => Instruction::GreaterEqual, + Self::Equal => Instruction::Equal, + Self::NotEqual => Instruction::NotEqual, + Self::Contains => Instruction::Contains, + } + } +} + +impl Display for EagerOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Index => f.write_str("subscript"), + Self::Power => f.write_str("^"), + Self::Multiply => f.write_str("*"), + Self::IntegerDivide => f.write_str("//"), + Self::Divide => f.write_str("/"), + Self::Add => f.write_str("+"), + Self::Subtract => f.write_str("-"), + Self::Less => f.write_str("<"), + Self::Greater => f.write_str(">"), + Self::LessEqual => f.write_str("<="), + Self::GreaterEqual => f.write_str(">="), + Self::Equal => f.write_str("=="), + Self::NotEqual => f.write_str("!="), + Self::Contains => f.write_str("in"), + } + } +} +/// Enumerates all the short-circuiting (non-eager) binary operators in the Gold language. +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] +pub enum LogicOp { /// Logical conjunction And, /// Logical disjunction Or, } + +impl Display for LogicOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::And => f.write_str("and"), + Self::Or => f.write_str("or"), + } + } +} + +/// Enumerates all the binary operators in the Gold language. +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] +pub enum BinOp { + Eager(EagerOp), + Logic(LogicOp), +} + +impl Display for BinOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Eager(x) => Display::fmt(x, f), + Self::Logic(x) => Display::fmt(x, f), + } + } +}