DEV: Ranges and Select expressions.

This commit is contained in:
Jeremy Wall 2019-08-16 20:34:04 -05:00
parent 44178b601c
commit 2985d4258f
5 changed files with 184 additions and 30 deletions

View File

@ -145,6 +145,7 @@ pub enum Hook {
Assert,
Convert,
Regex,
Range,
}
#[derive(Debug, PartialEq, Clone)]

View File

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

View File

@ -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)),
];
}

View File

@ -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");
}
}
}

View File

@ -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)?;
}
self.ops.ptr.unwrap();
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))?;
}
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(())
}