diff --git a/src/build/opcode/mod.rs b/src/build/opcode/mod.rs index 3215448..83f382c 100644 --- a/src/build/opcode/mod.rs +++ b/src/build/opcode/mod.rs @@ -145,6 +145,7 @@ pub enum Hook { Assert, Convert, Regex, + Range, } #[derive(Debug, PartialEq, Clone)] diff --git a/src/build/opcode/runtime.rs b/src/build/opcode/runtime.rs index 42b8507..1a41c4a 100644 --- a/src/build/opcode/runtime.rs +++ b/src/build/opcode/runtime.rs @@ -27,7 +27,7 @@ use super::{Composite, Error, Hook, Primitive, Value}; use crate::build::AssertCollector; use crate::convert::{ConverterRegistry, ImporterRegistry}; use Composite::{List, Tuple}; -use Primitive::{Bool, Empty, Str}; +use Primitive::{Bool, Empty, Str, Int}; pub struct Builtins { op_cache: cache::Ops, @@ -75,6 +75,7 @@ impl Builtins { Hook::Filter => self.filter(path, stack), Hook::Reduce => self.reduce(path, stack), Hook::Regex => self.regex(stack), + Hook::Range => self.range(stack), } } @@ -279,10 +280,16 @@ impl Builtins { } else { return Err(dbg!(Error {})); }; - let elems = if let &C(List(ref elems)) = list.as_ref() { - elems - } else { - return Err(dbg!(Error {})); + // TODO(jwall): This can also be tuples or strings. + let elems = match list.as_ref() { + &C(List(ref elems)) => elems, + &C(Tuple(ref flds)) => { + unimplemented!("Tuple functional operations"); + } + &P(Str(ref s)) => { + unimplemented!("string functional operations"); + } + _ => return Err(dbg!(Error {})), }; // get the func ptr from the stack @@ -316,10 +323,16 @@ impl Builtins { } else { return Err(dbg!(Error {})); }; - let elems = if let &C(List(ref elems)) = list.as_ref() { - elems - } else { - return Err(dbg!(Error {})); + // TODO(jwall): This can also be tuples or strings. + let elems = match list.as_ref() { + &C(List(ref elems)) => elems, + &C(Tuple(ref flds)) => { + unimplemented!("Tuple functional operations"); + } + &P(Str(ref s)) => { + unimplemented!("string functional operations"); + } + _ => return Err(dbg!(Error {})), }; // get the func ptr from the stack @@ -390,10 +403,16 @@ impl Builtins { } else { return dbg!(Err(Error {})); }; - let elems = if let &C(List(ref elems)) = list.as_ref() { - elems - } else { - return dbg!(Err(Error {})); + // TODO(jwall): This can also be tuples or strings. + let elems = match list.as_ref() { + &C(List(ref elems)) => elems, + &C(Tuple(ref flds)) => { + unimplemented!("Tuple functional operations"); + } + &P(Str(ref s)) => { + unimplemented!("string functional operations"); + } + _ => return Err(dbg!(Error {})), }; // Get the accumulator from the stack @@ -428,4 +447,45 @@ impl Builtins { stack.push(acc); Ok(()) } + + fn range(&self, stack: &mut Vec>) -> Result<(), Error> { + let start = if let Some(start) = stack.pop() { + start + } else { + return dbg!(Err(Error {})); + }; + let step = if let Some(step) = stack.pop() { + if let &P(Empty) = step.as_ref() { + Rc::new(P(Int(1))) + } else { + step + } + } else { + return dbg!(Err(Error {})); + }; + let end = if let Some(end) = stack.pop() { + end + } else { + return dbg!(Err(Error {})); + }; + + let mut elems = Vec::new(); + match (start.as_ref(), step.as_ref(), end.as_ref()) { + (&P(Int(start)), &P(Int(step)), &P(Int(end))) => { + let mut num = start; + loop { + if num > end { + break; + } + elems.push(Rc::new(P(Int(num)))); + num += step; + } + } + _ => { + return dbg!(Err(Error {})); + } + } + stack.push(Rc::new(C(List(elems)))); + Ok(()) + } } diff --git a/src/build/opcode/test.rs b/src/build/opcode/test.rs index 64e1fcd..182347d 100644 --- a/src/build/opcode/test.rs +++ b/src/build/opcode/test.rs @@ -442,7 +442,7 @@ fn module_call() { fn select_short_circuit() { assert_cases![ vec![ - Sym("field".to_owned()), // 0 // search field + Val(Str("field".to_owned())), // 0 // search field Sym("not_field".to_owned()), // 1 // first field to compare SelectJump(2), // 2 Val(Str("not our value".to_owned())), // 3 // expression for first field @@ -455,7 +455,7 @@ fn select_short_circuit() { Bang, // 10 // default case ] => P(Int(1)), vec![ - Sym("field".to_owned()), // 0 // search field + Val(Str("field".to_owned())), // 0 // search field Sym("not_field".to_owned()), // 1 // first field to compare SelectJump(2), // 2 Val(Str("not our value".to_owned())), // 3 // expression for first field @@ -726,3 +726,31 @@ fn simple_modules() { "let m = module{val=NULL} => (v) { let v = mod.val + 1; }; m{val=1};" => P(Int(2)), ]; } + +#[test] +fn simple_range() { + assert_parse_cases![ + "1:2;" => C(List(vec![ + Rc::new(P(Int(1))), + Rc::new(P(Int(2))), + ])), + "0:2:4;" => C(List(vec![ + Rc::new(P(Int(0))), + Rc::new(P(Int(2))), + Rc::new(P(Int(4))), + ])), + "0:3:4;" => C(List(vec![ + Rc::new(P(Int(0))), + Rc::new(P(Int(3))), + ])), + ]; +} + +#[test] +fn simple_selects() { + assert_parse_cases![ + "select \"foo\", { foo = 1, bar = 2, };" => P(Int(1)), + "select \"bar\", { foo = 1, bar = 2, };" => P(Int(2)), + "select \"quux\", 3, { foo = 1, bar = 2, };" => P(Int(3)), + ]; +} diff --git a/src/build/opcode/translate.rs b/src/build/opcode/translate.rs index c40f50a..658eacd 100644 --- a/src/build/opcode/translate.rs +++ b/src/build/opcode/translate.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. use crate::ast::{BinaryExprType, Expression, FormatArgs, Statement, Value}; -use crate::ast::{Position, TemplatePart}; +use crate::ast::{FuncOpDef, Position, TemplatePart}; use crate::build::format::{ExpressionTemplate, SimpleTemplate, TemplateParser}; use crate::build::opcode::Primitive; use crate::build::opcode::{Func, Hook, Op}; @@ -268,7 +268,36 @@ impl AST { let jptr = ops.len() - 1 - idx; ops[idx] = Op::Func(jptr as i32); } - Expression::FuncOp(_) => unimplemented!("FuncOp expressions are not implmented yet"), + Expression::FuncOp(def) => { + match def { + FuncOpDef::Map(def) => { + // push the function on the stack first. + Self::translate_expr(*def.func, &mut ops); + // push the target on the stack third + Self::translate_expr(*def.target, &mut ops); + // finally push the Hook::Map opcode + ops.push(Op::Runtime(Hook::Map)); + } + FuncOpDef::Filter(def) => { + // push the function on the stack first. + Self::translate_expr(*def.func, &mut ops); + // push the target on the stack third + Self::translate_expr(*def.target, &mut ops); + // finally push the Hook::Map opcode + ops.push(Op::Runtime(Hook::Filter)); + } + FuncOpDef::Reduce(def) => { + // push the function on the stack first. + Self::translate_expr(*def.func, &mut ops); + // push the accumulator on the stack third + Self::translate_expr(*def.acc, &mut ops); + // push the target on the stack third + Self::translate_expr(*def.target, &mut ops); + // finally push the Hook::Map opcode + ops.push(Op::Runtime(Hook::Reduce)); + } + } + } Expression::Import(def) => { ops.push(Op::Val(Primitive::Str(def.path.fragment))); ops.push(Op::Runtime(Hook::Import)); @@ -316,8 +345,42 @@ impl AST { Self::translate_expr(*def.expr, &mut ops); ops.push(Op::Not); } - Expression::Range(_) => unimplemented!("Range expressions are not implmented yet"), - Expression::Select(_) => unimplemented!("Select expressions are not implmented yet"), + Expression::Range(def) => { + Self::translate_expr(*def.end, &mut ops); + if let Some(expr) = def.step { + Self::translate_expr(*expr, &mut ops); + } else { + ops.push(Op::Val(Primitive::Empty)); + } + Self::translate_expr(*def.start, &mut ops); + ops.push(Op::Runtime(Hook::Range)); + } + Expression::Select(def) => { + Self::translate_expr(*def.val, &mut ops); + let mut jumps = Vec::new(); + for (key, val) in def.tuple { + ops.push(Op::Sym(key.fragment)); + ops.push(Op::Noop); + let idx = ops.len() - 1; + Self::translate_expr(val, &mut ops); + ops.push(Op::Noop); + jumps.push(ops.len() - 1); + let jptr = ops.len() - idx - 1; + ops[idx] = Op::SelectJump(jptr as i32); + } + ops.push(Op::Pop); + let end = ops.len() - 1; + for i in jumps { + let idx = end - i; + ops[i] = Op::Jump(idx as i32); + } + if let Some(default) = def.default { + Self::translate_expr(*default, &mut ops); + } else { + ops.push(Op::Bang); + } + dbg!(&ops); + } Expression::Call(def) => { // first push our arguments. for e in def.arglist { @@ -338,7 +401,9 @@ impl AST { Self::translate_value(def.selector, &mut ops); ops.push(Op::Cp); } - Expression::Debug(_) => unimplemented!("Debug expressions are not implmented yet"), + Expression::Debug(def) => { + unimplemented!("Debug expressions are not implmented yet"); + } } } diff --git a/src/build/opcode/vm.rs b/src/build/opcode/vm.rs index ad1bee5..e57a1c1 100644 --- a/src/build/opcode/vm.rs +++ b/src/build/opcode/vm.rs @@ -167,7 +167,6 @@ impl<'a> VM { .map(|v| (v as i32 + jp) as usize) .unwrap_or(jp as usize), )?; - dbg!(&self.stack); Ok(()) } @@ -221,16 +220,19 @@ impl<'a> VM { fn op_select_jump(&'a mut self, jp: i32) -> Result<(), Error> { // pop field value off - let field_name = self.pop()?; + let field_name = dbg!(self.pop())?; // pop search value off - let search = self.pop()?; + let search = dbg!(self.pop())?; // compare them. - if field_name != search { - self.op_jump(jp)?; - self.push(search)?; + let matched = match (field_name.as_ref(), search.as_ref()) { + (&S(ref fname), &P(Str(ref sname))) | (&S(ref fname), &S(ref sname)) => fname == sname, + _ => false, + }; + if !matched { + // if they aren't equal then push search value back on and jump + self.push(dbg!(search))?; + self.op_jump(dbg!(jp))?; } - self.ops.ptr.unwrap(); - // if they aren't equal then push search value back on and jump Ok(()) } @@ -560,8 +562,6 @@ impl<'a> VM { } fn op_bang(&mut self) -> Result<(), Error> { - let msg = self.pop()?; - // TODO(jwall): record an error here. Ok(()) }