mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
DEV: Ranges and Select expressions.
This commit is contained in:
parent
44178b601c
commit
2985d4258f
@ -145,6 +145,7 @@ pub enum Hook {
|
|||||||
Assert,
|
Assert,
|
||||||
Convert,
|
Convert,
|
||||||
Regex,
|
Regex,
|
||||||
|
Range,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
@ -27,7 +27,7 @@ use super::{Composite, Error, Hook, Primitive, Value};
|
|||||||
use crate::build::AssertCollector;
|
use crate::build::AssertCollector;
|
||||||
use crate::convert::{ConverterRegistry, ImporterRegistry};
|
use crate::convert::{ConverterRegistry, ImporterRegistry};
|
||||||
use Composite::{List, Tuple};
|
use Composite::{List, Tuple};
|
||||||
use Primitive::{Bool, Empty, Str};
|
use Primitive::{Bool, Empty, Str, Int};
|
||||||
|
|
||||||
pub struct Builtins {
|
pub struct Builtins {
|
||||||
op_cache: cache::Ops,
|
op_cache: cache::Ops,
|
||||||
@ -75,6 +75,7 @@ impl Builtins {
|
|||||||
Hook::Filter => self.filter(path, stack),
|
Hook::Filter => self.filter(path, stack),
|
||||||
Hook::Reduce => self.reduce(path, stack),
|
Hook::Reduce => self.reduce(path, stack),
|
||||||
Hook::Regex => self.regex(stack),
|
Hook::Regex => self.regex(stack),
|
||||||
|
Hook::Range => self.range(stack),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,10 +280,16 @@ impl Builtins {
|
|||||||
} else {
|
} else {
|
||||||
return Err(dbg!(Error {}));
|
return Err(dbg!(Error {}));
|
||||||
};
|
};
|
||||||
let elems = if let &C(List(ref elems)) = list.as_ref() {
|
// TODO(jwall): This can also be tuples or strings.
|
||||||
elems
|
let elems = match list.as_ref() {
|
||||||
} else {
|
&C(List(ref elems)) => elems,
|
||||||
return Err(dbg!(Error {}));
|
&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
|
// get the func ptr from the stack
|
||||||
@ -316,10 +323,16 @@ impl Builtins {
|
|||||||
} else {
|
} else {
|
||||||
return Err(dbg!(Error {}));
|
return Err(dbg!(Error {}));
|
||||||
};
|
};
|
||||||
let elems = if let &C(List(ref elems)) = list.as_ref() {
|
// TODO(jwall): This can also be tuples or strings.
|
||||||
elems
|
let elems = match list.as_ref() {
|
||||||
} else {
|
&C(List(ref elems)) => elems,
|
||||||
return Err(dbg!(Error {}));
|
&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
|
// get the func ptr from the stack
|
||||||
@ -390,10 +403,16 @@ impl Builtins {
|
|||||||
} else {
|
} else {
|
||||||
return dbg!(Err(Error {}));
|
return dbg!(Err(Error {}));
|
||||||
};
|
};
|
||||||
let elems = if let &C(List(ref elems)) = list.as_ref() {
|
// TODO(jwall): This can also be tuples or strings.
|
||||||
elems
|
let elems = match list.as_ref() {
|
||||||
} else {
|
&C(List(ref elems)) => elems,
|
||||||
return dbg!(Err(Error {}));
|
&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
|
// Get the accumulator from the stack
|
||||||
@ -428,4 +447,45 @@ impl Builtins {
|
|||||||
stack.push(acc);
|
stack.push(acc);
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -442,7 +442,7 @@ fn module_call() {
|
|||||||
fn select_short_circuit() {
|
fn select_short_circuit() {
|
||||||
assert_cases![
|
assert_cases![
|
||||||
vec![
|
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
|
Sym("not_field".to_owned()), // 1 // first field to compare
|
||||||
SelectJump(2), // 2
|
SelectJump(2), // 2
|
||||||
Val(Str("not our value".to_owned())), // 3 // expression for first field
|
Val(Str("not our value".to_owned())), // 3 // expression for first field
|
||||||
@ -455,7 +455,7 @@ fn select_short_circuit() {
|
|||||||
Bang, // 10 // default case
|
Bang, // 10 // default case
|
||||||
] => P(Int(1)),
|
] => P(Int(1)),
|
||||||
vec![
|
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
|
Sym("not_field".to_owned()), // 1 // first field to compare
|
||||||
SelectJump(2), // 2
|
SelectJump(2), // 2
|
||||||
Val(Str("not our value".to_owned())), // 3 // expression for first field
|
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)),
|
"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)),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use crate::ast::{BinaryExprType, Expression, FormatArgs, Statement, Value};
|
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::format::{ExpressionTemplate, SimpleTemplate, TemplateParser};
|
||||||
use crate::build::opcode::Primitive;
|
use crate::build::opcode::Primitive;
|
||||||
use crate::build::opcode::{Func, Hook, Op};
|
use crate::build::opcode::{Func, Hook, Op};
|
||||||
@ -268,7 +268,36 @@ impl AST {
|
|||||||
let jptr = ops.len() - 1 - idx;
|
let jptr = ops.len() - 1 - idx;
|
||||||
ops[idx] = Op::Func(jptr as i32);
|
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) => {
|
Expression::Import(def) => {
|
||||||
ops.push(Op::Val(Primitive::Str(def.path.fragment)));
|
ops.push(Op::Val(Primitive::Str(def.path.fragment)));
|
||||||
ops.push(Op::Runtime(Hook::Import));
|
ops.push(Op::Runtime(Hook::Import));
|
||||||
@ -316,8 +345,42 @@ impl AST {
|
|||||||
Self::translate_expr(*def.expr, &mut ops);
|
Self::translate_expr(*def.expr, &mut ops);
|
||||||
ops.push(Op::Not);
|
ops.push(Op::Not);
|
||||||
}
|
}
|
||||||
Expression::Range(_) => unimplemented!("Range expressions are not implmented yet"),
|
Expression::Range(def) => {
|
||||||
Expression::Select(_) => unimplemented!("Select expressions are not implmented yet"),
|
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) => {
|
Expression::Call(def) => {
|
||||||
// first push our arguments.
|
// first push our arguments.
|
||||||
for e in def.arglist {
|
for e in def.arglist {
|
||||||
@ -338,7 +401,9 @@ impl AST {
|
|||||||
Self::translate_value(def.selector, &mut ops);
|
Self::translate_value(def.selector, &mut ops);
|
||||||
ops.push(Op::Cp);
|
ops.push(Op::Cp);
|
||||||
}
|
}
|
||||||
Expression::Debug(_) => unimplemented!("Debug expressions are not implmented yet"),
|
Expression::Debug(def) => {
|
||||||
|
unimplemented!("Debug expressions are not implmented yet");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +167,6 @@ impl<'a> VM {
|
|||||||
.map(|v| (v as i32 + jp) as usize)
|
.map(|v| (v as i32 + jp) as usize)
|
||||||
.unwrap_or(jp as usize),
|
.unwrap_or(jp as usize),
|
||||||
)?;
|
)?;
|
||||||
dbg!(&self.stack);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,16 +220,19 @@ impl<'a> VM {
|
|||||||
|
|
||||||
fn op_select_jump(&'a mut self, jp: i32) -> Result<(), Error> {
|
fn op_select_jump(&'a mut self, jp: i32) -> Result<(), Error> {
|
||||||
// pop field value off
|
// pop field value off
|
||||||
let field_name = self.pop()?;
|
let field_name = dbg!(self.pop())?;
|
||||||
// pop search value off
|
// pop search value off
|
||||||
let search = self.pop()?;
|
let search = dbg!(self.pop())?;
|
||||||
// compare them.
|
// compare them.
|
||||||
if field_name != search {
|
let matched = match (field_name.as_ref(), search.as_ref()) {
|
||||||
self.op_jump(jp)?;
|
(&S(ref fname), &P(Str(ref sname))) | (&S(ref fname), &S(ref sname)) => fname == sname,
|
||||||
self.push(search)?;
|
_ => 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -560,8 +562,6 @@ impl<'a> VM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn op_bang(&mut self) -> Result<(), Error> {
|
fn op_bang(&mut self) -> Result<(), Error> {
|
||||||
let msg = self.pop()?;
|
|
||||||
// TODO(jwall): record an error here.
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user