diff --git a/src/build/bytecode/mod.rs b/src/build/bytecode/mod.rs deleted file mode 100644 index af2a693..0000000 --- a/src/build/bytecode/mod.rs +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright 2019 Jeremy Wall -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -use std::collections::hash_map::Entry; -use std::collections::{BTreeMap, HashMap}; -use std::rc::Rc; - -use crate::build::ir::Val; - -#[derive(Debug, PartialEq, Clone)] -pub enum Primitive { - // Primitive Types - Int(i64), - Float(f64), - Str(String), - Bool(bool), - Empty, -} - -#[derive(Debug, PartialEq, Clone)] -pub enum Composite { - List(Vec), - Tuple(Vec<(String, Value)>), - //Thunk(Frame), -} - -#[derive(Debug, PartialEq, Clone)] -pub enum Value { - // Binding names. - S(String), - // Primitive Types - P(Primitive), - // Composite Types. - C(Composite), -} - -#[derive(Debug, PartialEq, Clone)] -pub enum Op { - // Stack and Name manipulation. - Bind, // Bind a Val to a name in the heap - Pop, // Pop a Value off the value stack and discard it. - // Math ops - Add, - Sub, - Div, - Mul, - // Primitive Types ops - Val(Primitive), - // A bareword for use in bindings or lookups - Sym(String), - // Complex Type ops - InitTuple, - FIELD, - InitList, - Element, - // Operations - Cp, - // Push a new frame on the FrameStack - InitFunc, - InitMod, - EndFrame, - // - Call - // Functional operations - // - Map - // - Filter - // - Reduce -} - -pub struct Heap {} - -#[derive(Debug)] -pub struct Error {} - -/// The type of Frame environment this is -#[derive(Debug, PartialEq, Clone)] -pub enum FrameType { - Lib, - Func, - Module, -} - -#[derive(Debug, PartialEq)] -pub struct Frame { - names: BTreeMap, - stack: Vec, - typ: FrameType, -} - -/// Frames represent a functional computation environment on the stack. -/// Frames in the UCG interpreter are hermetic. They can't see their parent. -impl Frame { - fn pop(&mut self) -> Result { - match self.stack.pop() { - Some(p) => Ok(p), - None => Err(Error {}), - } - } - - fn push(&mut self, val: Value) { - self.stack.push(val); - } - - fn push_primitive(&mut self, p: Primitive) { - self.stack.push(Value::P(p)); - } - - fn push_composite(&mut self, c: Composite) { - self.stack.push(Value::C(c)); - } - - fn push_binding(&mut self, name: String, val: Value) { - self.names.insert(name, val); - } - - fn get_binding(&mut self, name: &str) -> Result<&Value, Error> { - self.names.get(name).ok_or(Error {}) - } -} - -pub struct VM { - stack: Vec, -} - -impl VM { - pub fn new() -> Self { - Self { stack: Vec::new() } - } - - pub fn run(&mut self, op_stream: I) -> Result<(), Error> - where - I: Iterator, - { - // Init our first stack frame - self.push_stack(FrameType::Lib); - for op in op_stream { - match op { - Op::Val(p) => { - self.primitive_push(p)?; - } - Op::Sym(s) => { - self.push(Value::S(s))?; - } - Op::Add => { - // Adds the previous two items in the stack. - let left = self.pop()?; - let right = self.pop()?; - // Then pushes the result onto the stack. - self.primitive_push(self.add(left, right)?)?; - } - Op::Sub => { - // Subtracts the previous two items in the stack. - let left = self.pop()?; - let right = self.pop()?; - // Then pushes the result onto the stack. - self.primitive_push(self.sub(left, right)?)?; - } - Op::Mul => { - // Multiplies the previous two items in the stack. - let left = self.pop()?; - let right = self.pop()?; - // Then pushes the result onto the stack. - self.primitive_push(self.mul(left, right)?)?; - } - Op::Div => { - // Divides the previous two items in the stack. - let left = self.pop()?; - let right = self.pop()?; - // Then pushes the result onto the stack. - self.primitive_push(self.div(left, right)?)?; - } - Op::Bind => { - // pop val off stack. - let val = self.pop()?; - // pop name off stack. - let name = self.pop()?; - if let Value::S(name) = name { - self.binding_push(name, val)?; - } else { - return Err(Error {}); - } - } - Op::InitList => { - // Add a Composite list value to the stack - self.composite_push(Composite::List(Vec::new()))?; - } - Op::InitTuple => { - // Add a composite tuple value to the stack - self.composite_push(Composite::Tuple(Vec::new()))?; - } - Op::FIELD => { - // Add a Composite field value to a tuple on the stack - // get value from stack - let val = self.pop()?; - // get name from stack. - let name = if let Value::S(s) | Value::P(Primitive::Str(s)) = self.pop()? { - s - } else { - return Err(Error {}); - }; - // get composite tuple from stack - let tpl = self.pop()?; - if let Value::C(Composite::Tuple(mut flds)) = tpl { - // add name and value to tuple - self.merge_field_into_tuple(&mut flds, name, val)?; - // place composite tuple back on stack - self.composite_push(Composite::Tuple(flds))?; - } else { - return Err(Error {}); - }; - } - Op::Element => { - // get element from stack. - let val = self.pop()?; - // get next value. It should be a Composite list. - let tpl = self.pop()?; - if let Value::C(Composite::List(mut elems)) = tpl { - // add value to list - elems.push(val); - // Add that value to the list and put list back on stack. - self.composite_push(Composite::List(elems))?; - } else { - return Err(Error {}); - }; - } - Op::Cp => { - // TODO Use Cow pointers for this? - // get next value. It should be a Composite Tuple. - if let Value::C(Composite::Tuple(flds)) = self.pop()? { - // Make a copy of the original - let original = Composite::Tuple(flds.clone()); - let copy = Composite::Tuple(flds); - // Put the original on the Stack as well as the copy - self.composite_push(original)?; - self.composite_push(copy)?; - } else { - return Err(Error {}); - }; - } - Op::InitFunc => { - self.push_stack(FrameType::Func); - } - Op::InitMod => { - self.push_stack(FrameType::Module); - } - Op::EndFrame => { - // TODO(jwall): We probably want to push this frame onto the stack - // somehow. - self.pop_stack(); - } - Op::Pop => { - self.pop()?; - } - } - } - Ok(()) - } - - fn merge_field_into_tuple( - &self, - src_fields: &mut Vec<(String, Value)>, - name: String, - value: Value, - ) -> Result<(), Error> { - for fld in src_fields.iter_mut() { - if fld.0 == name { - fld.1 = value; - return Ok(()); - } - } - src_fields.push((name, value)); - Ok(()) - } - - fn push_stack(&mut self, typ: FrameType) { - self.stack.push(Frame { - names: BTreeMap::new(), - stack: Vec::new(), - typ: typ, - }); - } - - fn pop_stack(&mut self) -> Option { - self.stack.pop() - } - - fn to_val(p: Value) -> Result { - Ok(match p { - Value::P(Primitive::Int(i)) => Val::Int(i), - Value::P(Primitive::Float(f)) => Val::Float(f), - Value::P(Primitive::Str(s)) => Val::Str(s), - Value::P(Primitive::Bool(b)) => Val::Boolean(b), - Value::P(Primitive::Empty) => Val::Empty, - Value::C(Composite::List(mut elems)) => { - let mut mapped = Vec::with_capacity(elems.len()); - for val in elems.drain(0..) { - mapped.push(Rc::new(Self::to_val(val)?)); - } - Val::List(mapped) - } - Value::C(Composite::Tuple(mut flds)) => { - let mut mapped = Vec::with_capacity(flds.len()); - for (name, val) in flds.drain(0..) { - mapped.push((name, Rc::new(Self::to_val(val)?))); - } - Val::Tuple(mapped) - } - Value::S(_) => return Err(Error {}), - //Value::C(Composite::Thunk(_)) => { - // // TODO(jwall): This is either a function or a Module - // Val::Empty - //} - }) - } - - fn push(&mut self, p: Value) -> Result<(), Error> { - match self.stack.first_mut() { - Some(f) => return Ok(f.push(p)), - None => return Err(Error {}), - }; - } - - fn primitive_push(&mut self, p: Primitive) -> Result<(), Error> { - match self.stack.first_mut() { - Some(f) => return Ok(f.push_primitive(p)), - None => return Err(Error {}), - }; - } - - fn composite_push(&mut self, c: Composite) -> Result<(), Error> { - match self.stack.first_mut() { - Some(f) => return Ok(f.push_composite(c)), - None => return Err(Error {}), - }; - } - - fn binding_push(&mut self, name: String, val: Value) -> Result<(), Error> { - match self.stack.first_mut() { - Some(f) => return Ok(f.push_binding(name, val)), - None => return Err(Error {}), - } - } - - pub fn get_binding(&mut self, name: &str) -> Result<&Value, Error> { - match self.stack.first_mut() { - Some(f) => return Ok(f.get_binding(name)?), - None => return Err(Error {}), - } - } - - fn pop(&mut self) -> Result { - match self.stack.first_mut() { - Some(f) => f.pop(), - None => Err(Error {}), - } - } - - fn mul(&self, left: Value, right: Value) -> Result { - Ok(match (left, right) { - (Value::P(Primitive::Int(i)), Value::P(Primitive::Int(ii))) => Primitive::Int(i * ii), - (Value::P(Primitive::Float(f)), Value::P(Primitive::Float(ff))) => { - Primitive::Float(f * ff) - } - _ => return Err(Error {}), - }) - } - - fn div(&self, left: Value, right: Value) -> Result { - Ok(match (left, right) { - (Value::P(Primitive::Int(i)), Value::P(Primitive::Int(ii))) => Primitive::Int(i / ii), - (Value::P(Primitive::Float(f)), Value::P(Primitive::Float(ff))) => { - Primitive::Float(f / ff) - } - _ => return Err(Error {}), - }) - } - - fn sub(&self, left: Value, right: Value) -> Result { - Ok(match (left, right) { - (Value::P(Primitive::Int(i)), Value::P(Primitive::Int(ii))) => Primitive::Int(i - ii), - (Value::P(Primitive::Float(f)), Value::P(Primitive::Float(ff))) => { - Primitive::Float(f - ff) - } - _ => return Err(Error {}), - }) - } - - fn add(&self, left: Value, right: Value) -> Result { - Ok(match (left, right) { - (Value::P(Primitive::Int(i)), Value::P(Primitive::Int(ii))) => Primitive::Int(i + ii), - (Value::P(Primitive::Float(f)), Value::P(Primitive::Float(ff))) => { - Primitive::Float(f + ff) - } - (Value::P(Primitive::Str(s)), Value::P(Primitive::Str(ss))) => { - let mut ns = String::new(); - ns.push_str(&s); - ns.push_str(&ss); - Primitive::Str(ns) - } - _ => return Err(Error {}), - }) - } -} - -#[cfg(test)] -mod test; diff --git a/src/build/mod.rs b/src/build/mod.rs index 83c4f46..20e2df5 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -39,9 +39,9 @@ use crate::iter::OffsetStrIter; use crate::parse::parse; pub mod assets; -pub mod bytecode; pub mod format; pub mod ir; +pub mod opcode; pub mod scope; mod stdlib; diff --git a/src/build/opcode/mod.rs b/src/build/opcode/mod.rs new file mode 100644 index 0000000..7d009ab --- /dev/null +++ b/src/build/opcode/mod.rs @@ -0,0 +1,342 @@ +// Copyright 2019 Jeremy Wall +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use std::collections::BTreeMap; + +pub mod pointer; +use pointer::OpPointer; + +#[derive(Debug, PartialEq, Clone)] +pub enum Primitive { + // Primitive Types + Int(i64), + Float(f64), + Str(String), + Bool(bool), + Empty, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum Composite { + List(Vec), + Tuple(Vec<(String, Value)>), +} + +#[derive(Debug, PartialEq, Clone)] +pub enum Value { + // Binding names. + S(String), + // Primitive Types + P(Primitive), + // Composite Types. + C(Composite), + // Program Pointer + T(usize), +} + +#[derive(Debug, PartialEq, Clone)] +pub enum Op { + // Stack and Name manipulation. + Bind, // Bind a Val to a name in the heap + Pop, // Pop a Value off the value stack and discard it. + // Math ops + Add, + Sub, + Div, + Mul, + // Primitive Types ops + Val(Primitive), + // A bareword for use in bindings or lookups + Sym(String), + // Complex Type ops + InitTuple, + FIELD, + InitList, + Element, + // Operations + Cp, + // record + InitThunk, + Return, + // - Call + // Runtime hooks + // - Map + // - Filter + // - Reduce + // - Import + // - Out + // - Assert + // - Print +} + +#[derive(Debug)] +pub struct Error {} + +pub struct VM { + stack: Vec, + symbols: BTreeMap, + ops: OpPointer, +} + +impl VM { + pub fn new(ops: Vec) -> Self { + Self { + stack: Vec::new(), + symbols: BTreeMap::new(), + ops: OpPointer::new(ops), + } + } + + pub fn run(&mut self) -> Result<(), Error> { + while self.ops.next().is_some() { + let idx = self.ops.ptr.unwrap(); + match self.ops.op().unwrap() { + Op::Val(p) => self.primitive_push(p.clone())?, + Op::Sym(s) => self.push(Value::S(s.clone()))?, + Op::Add => self.op_add()?, + Op::Sub => self.op_sub()?, + Op::Mul => self.op_mul()?, + Op::Div => self.op_div()?, + Op::Bind => self.op_bind()?, + // Add a Composite list value to the stack + Op::InitList => self.composite_push(Composite::List(Vec::new()))?, + // Add a composite tuple value to the stack + Op::InitTuple => self.composite_push(Composite::Tuple(Vec::new()))?, + Op::FIELD => self.op_field()?, + Op::Element => self.op_element()?, + Op::Cp => self.op_copy()?, + Op::InitThunk => self.op_thunk(idx)?, + Op::Return => { + // TODO(jwall): This means we return back to the start of the frame. + } + Op::Pop => { + self.pop()?; + } + }; + } + Ok(()) + } + + fn op_thunk(&mut self, idx: usize) -> Result<(), Error> { + // TODO(jwall): Record the position in the op codes. + self.push(Value::T(idx))?; + Ok(()) + } + + fn op_add(&mut self) -> Result<(), Error> { + // Adds the previous two items in the stack. + let left = self.pop()?; + let right = self.pop()?; + // Then pushes the result onto the stack. + self.primitive_push(self.add(left, right)?)?; + Ok(()) + } + + fn op_sub(&mut self) -> Result<(), Error> { + // Subtracts the previous two items in the stack. + let left = self.pop()?; + let right = self.pop()?; + // Then pushes the result onto the stack. + self.primitive_push(self.sub(left, right)?)?; + Ok(()) + } + + fn op_mul(&mut self) -> Result<(), Error> { + // Multiplies the previous two items in the stack. + let left = self.pop()?; + let right = self.pop()?; + // Then pushes the result onto the stack. + self.primitive_push(self.mul(left, right)?)?; + Ok(()) + } + + fn op_div(&mut self) -> Result<(), Error> { + // Divides the previous two items in the stack. + let left = self.pop()?; + let right = self.pop()?; + // Then pushes the result onto the stack. + self.primitive_push(self.div(left, right)?)?; + Ok(()) + } + + fn op_bind(&mut self) -> Result<(), Error> { + // TODO(jwall): Okay this should actually + // point to a location in the library op code + // instead of storing in the heap. + // pop val off stack. + let val = self.pop()?; + // pop name off stack. + let name = self.pop()?; + if let Value::S(name) = name { + self.binding_push(name, val)?; + } else { + return Err(Error {}); + } + Ok(()) + } + + fn op_field(&mut self) -> Result<(), Error> { + // Add a Composite field value to a tuple on the stack + // get value from stack + let val = self.pop()?; + // get name from stack. + let name = if let Value::S(s) | Value::P(Primitive::Str(s)) = self.pop()? { + s + } else { + return Err(Error {}); + }; + // get composite tuple from stack + let tpl = self.pop()?; + if let Value::C(Composite::Tuple(mut flds)) = tpl { + // add name and value to tuple + self.merge_field_into_tuple(&mut flds, name, val)?; + // place composite tuple back on stack + self.composite_push(Composite::Tuple(flds))?; + } else { + return Err(Error {}); + }; + Ok(()) + } + + fn op_element(&mut self) -> Result<(), Error> { + // get element from stack. + let val = self.pop()?; + // get next value. It should be a Composite list. + let tpl = self.pop()?; + if let Value::C(Composite::List(mut elems)) = tpl { + // add value to list + elems.push(val); + // Add that value to the list and put list back on stack. + self.composite_push(Composite::List(elems))?; + } else { + return Err(Error {}); + }; + Ok(()) + } + + fn op_copy(&mut self) -> Result<(), Error> { + // TODO Use Cow pointers for this? + // get next value. It should be a Composite Tuple. + if let Value::C(Composite::Tuple(flds)) = self.pop()? { + // Make a copy of the original + let original = Composite::Tuple(flds.clone()); + let copy = Composite::Tuple(flds); + // Put the original on the Stack as well as the copy + self.composite_push(original)?; + self.composite_push(copy)?; + } else { + return Err(Error {}); + }; + Ok(()) + } + + fn merge_field_into_tuple( + &self, + src_fields: &mut Vec<(String, Value)>, + name: String, + value: Value, + ) -> Result<(), Error> { + for fld in src_fields.iter_mut() { + if fld.0 == name { + fld.1 = value; + return Ok(()); + } + } + src_fields.push((name, value)); + Ok(()) + } + + fn push(&mut self, p: Value) -> Result<(), Error> { + self.stack.push(p); + Ok(()) + } + + fn primitive_push(&mut self, p: Primitive) -> Result<(), Error> { + self.stack.push(Value::P(p)); + Ok(()) + } + + fn composite_push(&mut self, c: Composite) -> Result<(), Error> { + self.stack.push(Value::C(c)); + Ok(()) + } + + fn binding_push(&mut self, name: String, val: Value) -> Result<(), Error> { + // FIXME(jwall): Error if the symbol already exists. + self.symbols.insert(name, val); + Ok(()) + } + + pub fn get_binding(&mut self, name: &str) -> Result<&Value, Error> { + match self.symbols.get(name) { + Some(v) => Ok(v), + None => Err(Error {}), + } + } + + fn pop(&mut self) -> Result { + match self.stack.pop() { + Some(v) => Ok(v), + None => Err(Error {}), + } + } + + fn mul(&self, left: Value, right: Value) -> Result { + Ok(match (left, right) { + (Value::P(Primitive::Int(i)), Value::P(Primitive::Int(ii))) => Primitive::Int(i * ii), + (Value::P(Primitive::Float(f)), Value::P(Primitive::Float(ff))) => { + Primitive::Float(f * ff) + } + _ => return Err(Error {}), + }) + } + + fn div(&self, left: Value, right: Value) -> Result { + Ok(match (left, right) { + (Value::P(Primitive::Int(i)), Value::P(Primitive::Int(ii))) => Primitive::Int(i / ii), + (Value::P(Primitive::Float(f)), Value::P(Primitive::Float(ff))) => { + Primitive::Float(f / ff) + } + _ => return Err(Error {}), + }) + } + + fn sub(&self, left: Value, right: Value) -> Result { + Ok(match (left, right) { + (Value::P(Primitive::Int(i)), Value::P(Primitive::Int(ii))) => Primitive::Int(i - ii), + (Value::P(Primitive::Float(f)), Value::P(Primitive::Float(ff))) => { + Primitive::Float(f - ff) + } + _ => return Err(Error {}), + }) + } + + fn add(&self, left: Value, right: Value) -> Result { + Ok(match (left, right) { + (Value::P(Primitive::Int(i)), Value::P(Primitive::Int(ii))) => Primitive::Int(i + ii), + (Value::P(Primitive::Float(f)), Value::P(Primitive::Float(ff))) => { + Primitive::Float(f + ff) + } + (Value::P(Primitive::Str(s)), Value::P(Primitive::Str(ss))) => { + let mut ns = String::new(); + ns.push_str(&s); + ns.push_str(&ss); + Primitive::Str(ns) + } + _ => return Err(Error {}), + }) + } +} + +#[cfg(test)] +mod test; diff --git a/src/build/opcode/pointer.rs b/src/build/opcode/pointer.rs new file mode 100644 index 0000000..fe2f72d --- /dev/null +++ b/src/build/opcode/pointer.rs @@ -0,0 +1,59 @@ +// Copyright 2019 Jeremy Wall +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{Error, Op}; + +pub struct OpPointer { + ops: Vec, + pub ptr: Option, +} + +impl OpPointer { + pub fn new(ops: Vec) -> Self { + // If we load an empty program what happens? + Self { + ops: ops, + ptr: None, + } + } + + pub fn next(&mut self) -> Option { + if let Some(i) = self.ptr { + let nxt = i + 1; + if nxt < self.ops.len() { + self.ptr = Some(nxt); + } else { + return None; + } + } else if self.ops.len() != 0 { + self.ptr = Some(0); + } + self.ptr + } + + pub fn jump(&mut self, ptr: usize) -> Result<(), Error> { + if ptr < self.ops.len() { + self.ptr = Some(ptr); + return Ok(()); + } + Err(Error {}) + } + + pub fn op(&self) -> Option<&Op> { + if let Some(i) = self.ptr { + return self.ops.get(i); + } + None + } +} diff --git a/src/build/bytecode/test.rs b/src/build/opcode/test.rs similarity index 89% rename from src/build/bytecode/test.rs rename to src/build/opcode/test.rs index dad0112..c71f9b4 100644 --- a/src/build/bytecode/test.rs +++ b/src/build/opcode/test.rs @@ -56,10 +56,9 @@ fn test_math_ops() { P(Int(4)), ), ]; - let mut vm = VM::new(); - - for mut case in cases.drain(0..) { - vm.run(case.0.drain(0..)).unwrap(); + for case in cases.drain(0..) { + let mut vm = VM::new(case.0); + vm.run().unwrap(); assert_eq!(vm.pop().unwrap(), case.1); } } @@ -73,11 +72,11 @@ fn test_bind_op() { ("foo", P(Int(2))), )]; - let mut vm = VM::new(); - for mut case in cases.drain(0..) { - vm.run(case.0.drain(0..)).unwrap(); + for case in cases.drain(0..) { + let mut vm = VM::new(case.0); + vm.run().unwrap(); let (name, result) = case.1; - let v = vm.get_binding(&name).unwrap(); + let v = vm.get_binding(name).unwrap(); assert_eq!(&result, v); } } @@ -95,9 +94,9 @@ fn test_list_ops() { C(List(vec![P(Int(2)), P(Int(1))])), ), ]; - let mut vm = VM::new(); - for mut case in cases.drain(0..) { - vm.run(case.0.drain(0..)).unwrap(); + for case in cases.drain(0..) { + let mut vm = VM::new(case.0); + vm.run().unwrap(); assert_eq!(vm.pop().unwrap(), case.1); } } @@ -145,9 +144,9 @@ fn test_tuple_ops() { ])), ), ]; - let mut vm = VM::new(); - for mut case in cases.drain(0..) { - vm.run(case.0.drain(0..)).unwrap(); + for case in cases.drain(0..) { + let mut vm = VM::new(case.0); + vm.run().unwrap(); assert_eq!(vm.pop().unwrap(), case.1); } }