2019-07-29 17:51:08 -05:00
|
|
|
// 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.
|
2020-05-25 10:58:45 -04:00
|
|
|
use std::collections::BTreeMap;
|
2019-08-17 16:57:45 -05:00
|
|
|
use std::path::Path;
|
|
|
|
|
2020-06-04 14:03:27 -05:00
|
|
|
use crate::ast::rewrite::Rewriter;
|
2020-05-25 11:56:41 -04:00
|
|
|
use crate::ast::walk::Walker;
|
2019-09-02 10:10:24 -05:00
|
|
|
use crate::ast::{
|
2020-05-25 11:56:41 -04:00
|
|
|
BinaryExprType, BinaryOpDef, Expression, FormatArgs, FuncOpDef, Position, PositionedItem,
|
2020-06-04 14:03:27 -05:00
|
|
|
SelectDef, Statement, TemplatePart, Token, TokenType, Value,
|
2019-09-02 10:10:24 -05:00
|
|
|
};
|
2019-08-12 22:07:26 -05:00
|
|
|
use crate::build::format::{ExpressionTemplate, SimpleTemplate, TemplateParser};
|
2019-07-29 17:51:08 -05:00
|
|
|
use crate::build::opcode::Primitive;
|
2019-08-17 12:57:40 -05:00
|
|
|
use crate::build::opcode::{Hook, Op};
|
2019-07-29 17:51:08 -05:00
|
|
|
|
|
|
|
pub struct AST();
|
|
|
|
|
2019-08-18 08:40:30 -05:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-05-25 10:58:45 -04:00
|
|
|
pub struct OpsMap {
|
2019-08-18 08:40:30 -05:00
|
|
|
pub ops: Vec<Op>,
|
|
|
|
pub pos: Vec<Position>,
|
2020-05-25 13:26:57 -04:00
|
|
|
pub links: BTreeMap<String, Position>,
|
2019-08-18 08:40:30 -05:00
|
|
|
}
|
|
|
|
|
2020-05-25 10:58:45 -04:00
|
|
|
impl OpsMap {
|
2019-09-03 18:38:06 -05:00
|
|
|
pub fn new() -> Self {
|
2020-05-25 10:58:45 -04:00
|
|
|
OpsMap {
|
2019-09-03 18:38:06 -05:00
|
|
|
ops: Vec::new(),
|
|
|
|
pos: Vec::new(),
|
2020-05-25 10:58:45 -04:00
|
|
|
links: BTreeMap::new(),
|
2019-09-03 18:38:06 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-25 10:58:45 -04:00
|
|
|
pub fn with_ops(mut self, ops: Vec<Op>, pos: Vec<Position>) -> Self {
|
|
|
|
self.ops = ops;
|
|
|
|
self.pos = pos;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-05-25 13:26:57 -04:00
|
|
|
pub fn add_link(&mut self, path: String, pos: Position) {
|
|
|
|
self.links.insert(path, pos);
|
2020-05-25 10:58:45 -04:00
|
|
|
}
|
|
|
|
|
2019-08-18 08:40:30 -05:00
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.ops.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn push(&mut self, op: Op, pos: Position) {
|
|
|
|
self.ops.push(op);
|
|
|
|
self.pos.push(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn replace(&mut self, idx: usize, op: Op) {
|
|
|
|
self.ops[idx] = op;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-29 17:51:08 -05:00
|
|
|
impl AST {
|
2020-05-25 11:56:41 -04:00
|
|
|
pub fn translate<P: AsRef<Path>>(mut stmts: Vec<Statement>, root: &P) -> OpsMap {
|
|
|
|
let mut rewriter = Rewriter::new(root.as_ref());
|
|
|
|
let mut_stmts = stmts.iter_mut().collect();
|
|
|
|
rewriter.walk_statement_list(mut_stmts);
|
2020-05-25 10:58:45 -04:00
|
|
|
let mut ops = OpsMap::new();
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_stmts(stmts, &mut ops, root.as_ref());
|
2019-08-15 18:11:54 -05:00
|
|
|
return ops;
|
|
|
|
}
|
|
|
|
|
2020-05-25 11:56:41 -04:00
|
|
|
fn translate_stmt(stmt: Statement, mut ops: &mut OpsMap, root: &Path) {
|
2019-08-26 21:24:17 -05:00
|
|
|
match stmt {
|
|
|
|
Statement::Expression(expr) => {
|
|
|
|
let expr_pos = expr.pos().clone();
|
|
|
|
Self::translate_expr(expr, &mut ops, root);
|
|
|
|
ops.push(Op::Pop, expr_pos);
|
|
|
|
}
|
|
|
|
Statement::Assert(pos, expr) => {
|
|
|
|
Self::translate_expr(expr, &mut ops, root);
|
|
|
|
ops.push(Op::Runtime(Hook::Assert), pos);
|
|
|
|
}
|
|
|
|
Statement::Let(def) => {
|
|
|
|
let binding = def.name.fragment;
|
|
|
|
ops.push(Op::Sym(binding), def.name.pos);
|
|
|
|
Self::translate_expr(def.value, &mut ops, root);
|
|
|
|
ops.push(Op::Bind, def.pos);
|
|
|
|
}
|
|
|
|
Statement::Output(pos, tok, expr) => {
|
|
|
|
ops.push(Op::Val(Primitive::Str(tok.fragment)), tok.pos);
|
|
|
|
Self::translate_expr(expr, &mut ops, root);
|
|
|
|
ops.push(Op::Runtime(Hook::Out), pos);
|
|
|
|
}
|
|
|
|
Statement::Print(pos, tok, expr) => {
|
|
|
|
ops.push(Op::Val(Primitive::Str(tok.fragment)), tok.pos);
|
|
|
|
Self::translate_expr(expr, &mut ops, root);
|
|
|
|
ops.push(Op::Runtime(Hook::Convert), pos.clone());
|
|
|
|
ops.push(Op::Pop, pos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-25 10:58:45 -04:00
|
|
|
fn translate_stmts(stmts: Vec<Statement>, mut ops: &mut OpsMap, root: &Path) {
|
2019-07-29 17:51:08 -05:00
|
|
|
for stmt in stmts {
|
2019-08-26 21:24:17 -05:00
|
|
|
Self::translate_stmt(stmt, &mut ops, root);
|
2019-07-29 17:51:08 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-25 10:58:45 -04:00
|
|
|
fn translate_expr(expr: Expression, mut ops: &mut OpsMap, root: &Path) {
|
2019-08-01 19:04:58 -05:00
|
|
|
match expr {
|
2019-07-29 17:51:08 -05:00
|
|
|
Expression::Simple(v) => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_value(v, &mut ops, root);
|
2019-07-29 17:51:08 -05:00
|
|
|
}
|
2019-07-29 17:53:02 -05:00
|
|
|
Expression::Binary(def) => {
|
|
|
|
match def.kind {
|
|
|
|
BinaryExprType::Add => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Add, def.pos);
|
2019-07-29 17:53:02 -05:00
|
|
|
}
|
|
|
|
BinaryExprType::Sub => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Sub, def.pos);
|
2019-07-29 17:53:02 -05:00
|
|
|
}
|
|
|
|
BinaryExprType::Div => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Div, def.pos);
|
2019-07-29 17:53:02 -05:00
|
|
|
}
|
|
|
|
BinaryExprType::Mul => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Mul, def.pos);
|
2019-07-29 17:53:02 -05:00
|
|
|
}
|
|
|
|
BinaryExprType::Equal => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Equal, def.pos);
|
2019-07-29 17:53:02 -05:00
|
|
|
}
|
|
|
|
BinaryExprType::GT => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Gt, def.pos);
|
2019-07-29 17:53:02 -05:00
|
|
|
}
|
|
|
|
BinaryExprType::LT => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Lt, def.pos);
|
2019-07-29 17:53:02 -05:00
|
|
|
}
|
|
|
|
BinaryExprType::GTEqual => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::GtEq, def.pos);
|
2019-07-29 17:53:02 -05:00
|
|
|
}
|
|
|
|
BinaryExprType::LTEqual => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::LtEq, def.pos);
|
2019-07-29 17:53:02 -05:00
|
|
|
}
|
|
|
|
BinaryExprType::NotEqual => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Equal, def.pos.clone());
|
|
|
|
ops.push(Op::Not, def.pos);
|
2019-07-29 17:53:02 -05:00
|
|
|
}
|
2019-07-31 18:12:35 -05:00
|
|
|
BinaryExprType::REMatch => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Runtime(Hook::Regex), def.pos);
|
2019-07-31 18:12:35 -05:00
|
|
|
}
|
|
|
|
BinaryExprType::NotREMatch => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Runtime(Hook::Regex), def.pos.clone());
|
|
|
|
ops.push(Op::Not, def.pos);
|
2019-07-31 18:12:35 -05:00
|
|
|
}
|
2019-07-31 20:08:05 -05:00
|
|
|
BinaryExprType::IS => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Typ, def.pos.clone());
|
|
|
|
ops.push(Op::Equal, def.pos);
|
2019-07-31 20:08:05 -05:00
|
|
|
}
|
2019-08-01 19:04:58 -05:00
|
|
|
BinaryExprType::AND => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Noop, def.pos);
|
2019-08-01 19:04:58 -05:00
|
|
|
let idx = ops.len() - 1;
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
2019-08-01 19:04:58 -05:00
|
|
|
let jptr = (ops.len() - 1 - idx) as i32;
|
2019-08-26 21:24:17 -05:00
|
|
|
ops.replace(idx, Op::And(jptr));
|
2019-08-01 19:04:58 -05:00
|
|
|
}
|
|
|
|
BinaryExprType::OR => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Noop, def.pos); // Placeholder
|
2019-08-01 19:04:58 -05:00
|
|
|
let idx = ops.len() - 1;
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
2019-08-01 19:04:58 -05:00
|
|
|
let jptr = (ops.len() - 1 - idx) as i32;
|
2019-08-26 21:24:17 -05:00
|
|
|
ops.replace(idx, Op::Or(jptr));
|
2019-08-01 19:04:58 -05:00
|
|
|
}
|
2019-08-02 18:02:45 -05:00
|
|
|
BinaryExprType::Mod => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.right, &mut ops, root);
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Mod, def.pos);
|
2019-08-02 18:02:45 -05:00
|
|
|
}
|
2019-08-02 18:55:18 -05:00
|
|
|
BinaryExprType::IN => {
|
2019-09-02 10:10:24 -05:00
|
|
|
// Dot expressions expect the right side to be pushed first
|
|
|
|
Self::translate_expr(*def.right.clone(), &mut ops, root);
|
2019-09-01 10:21:05 -05:00
|
|
|
// Symbols on the left side should be converted to strings to satisfy
|
2019-08-13 18:16:00 -05:00
|
|
|
// the Index operation contract.
|
2019-09-02 10:10:24 -05:00
|
|
|
match *def.left.clone() {
|
2019-08-13 18:16:00 -05:00
|
|
|
Expression::Simple(Value::Symbol(name)) => {
|
2019-09-02 10:10:24 -05:00
|
|
|
// We really just want an expression that turns a symbol
|
|
|
|
// into a name if the subject is a tuple and doesn't
|
|
|
|
// otherwise
|
|
|
|
let new_expr = Expression::Select(SelectDef {
|
|
|
|
val: Box::new(Expression::Binary(BinaryOpDef {
|
|
|
|
kind: BinaryExprType::IS,
|
|
|
|
right: Box::new(Expression::Simple(Value::Str(
|
|
|
|
PositionedItem::new(
|
|
|
|
"tuple".to_owned(),
|
|
|
|
def.left.pos().clone(),
|
|
|
|
),
|
|
|
|
))),
|
|
|
|
left: def.right.clone(),
|
|
|
|
pos: def.left.pos().clone(),
|
|
|
|
})),
|
|
|
|
default: Some(Box::new(Expression::Simple(Value::Symbol(
|
|
|
|
name.clone(),
|
|
|
|
)))),
|
|
|
|
tuple: vec![(
|
|
|
|
Token::new("true", TokenType::BAREWORD, def.right.pos()),
|
|
|
|
Expression::Simple(Value::Str(name)),
|
|
|
|
)],
|
|
|
|
pos: def.left.pos().clone(),
|
|
|
|
});
|
|
|
|
Self::translate_expr(new_expr, &mut ops, root);
|
2019-08-13 18:16:00 -05:00
|
|
|
}
|
|
|
|
expr => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(expr, &mut ops, root);
|
2019-08-13 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
2019-09-01 10:21:05 -05:00
|
|
|
ops.push(Op::Exist, def.pos.clone());
|
2019-08-02 18:55:18 -05:00
|
|
|
}
|
|
|
|
BinaryExprType::DOT => {
|
|
|
|
// Symbols on the right side should be converted to strings to satisfy
|
|
|
|
// the Index operation contract.
|
2019-08-26 21:24:17 -05:00
|
|
|
match *def.right {
|
2019-08-20 20:51:46 -05:00
|
|
|
Expression::Copy(copy_def) => {
|
2019-09-01 19:21:02 -05:00
|
|
|
// Dot expressions expect the left side to be pushed first
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-20 20:51:46 -05:00
|
|
|
// first handle the selector
|
|
|
|
match copy_def.selector {
|
|
|
|
Value::Str(sym) | Value::Symbol(sym) => {
|
|
|
|
ops.push(Op::Val(Primitive::Str(sym.val)), sym.pos);
|
|
|
|
}
|
|
|
|
Value::Int(sym) => {
|
|
|
|
ops.push(Op::Val(Primitive::Int(sym.val)), sym.pos);
|
|
|
|
}
|
2019-08-20 21:30:17 -05:00
|
|
|
_ => unreachable!(),
|
2019-08-20 20:51:46 -05:00
|
|
|
}
|
|
|
|
ops.push(Op::Index, copy_def.pos.clone());
|
|
|
|
Self::translate_copy(ops, copy_def.fields, copy_def.pos, root);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Expression::Call(call_def) => {
|
|
|
|
// first push our arguments.
|
2019-09-01 19:21:02 -05:00
|
|
|
let count = call_def.arglist.len() as i64;
|
2019-08-20 20:51:46 -05:00
|
|
|
for e in call_def.arglist {
|
|
|
|
Self::translate_expr(e, &mut ops, root);
|
|
|
|
}
|
2019-09-01 19:21:02 -05:00
|
|
|
ops.push(Op::Val(Primitive::Int(count)), call_def.pos.clone());
|
|
|
|
// Dot expressions expect the left side to be pushed first
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-20 20:51:46 -05:00
|
|
|
// then handle the selector
|
|
|
|
let func_pos = call_def.funcref.pos().clone();
|
|
|
|
match call_def.funcref {
|
|
|
|
Value::Str(sym) | Value::Symbol(sym) => {
|
|
|
|
ops.push(Op::Val(Primitive::Str(sym.val)), sym.pos);
|
|
|
|
}
|
|
|
|
Value::Int(sym) => {
|
|
|
|
ops.push(Op::Val(Primitive::Int(sym.val)), sym.pos);
|
|
|
|
}
|
2019-08-20 21:30:17 -05:00
|
|
|
_ => unreachable!(),
|
2019-08-20 20:51:46 -05:00
|
|
|
}
|
2019-09-01 10:21:05 -05:00
|
|
|
ops.push(Op::Index, def.pos);
|
2019-08-20 20:51:46 -05:00
|
|
|
ops.push(Op::FCall, func_pos);
|
|
|
|
return;
|
|
|
|
}
|
2019-08-02 18:55:18 -05:00
|
|
|
Expression::Simple(Value::Symbol(name)) => {
|
2019-09-01 19:21:02 -05:00
|
|
|
// Dot expressions expect the left side to be pushed first
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-02 18:55:18 -05:00
|
|
|
Self::translate_expr(
|
|
|
|
Expression::Simple(Value::Str(name)),
|
|
|
|
&mut ops,
|
2019-08-17 16:57:45 -05:00
|
|
|
root,
|
2019-08-02 18:55:18 -05:00
|
|
|
);
|
|
|
|
}
|
|
|
|
expr => {
|
2019-09-01 19:21:02 -05:00
|
|
|
// Dot expressions expect the left side to be pushed first
|
|
|
|
Self::translate_expr(*def.left, &mut ops, root);
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(expr, &mut ops, root);
|
2019-08-02 18:55:18 -05:00
|
|
|
}
|
|
|
|
}
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Index, def.pos);
|
2019-07-29 17:53:02 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2019-07-29 18:23:57 -05:00
|
|
|
Expression::Grouped(expr, _) => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*expr, &mut ops, root);
|
2019-07-29 18:23:57 -05:00
|
|
|
}
|
2019-08-13 18:23:02 -05:00
|
|
|
Expression::Fail(def) => {
|
2019-08-28 19:11:09 -05:00
|
|
|
let msg_pos = def.message.pos().clone();
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.message, &mut ops, root);
|
2019-09-01 10:21:05 -05:00
|
|
|
ops.push(Op::Val(Primitive::Str("UserDefined: ".to_owned())), msg_pos);
|
2019-08-28 19:11:09 -05:00
|
|
|
ops.push(Op::Add, def.pos.clone());
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Bang, def.pos);
|
2019-08-13 18:23:02 -05:00
|
|
|
}
|
2019-08-03 16:53:43 -05:00
|
|
|
Expression::Format(def) => {
|
|
|
|
match def.args {
|
2019-08-05 19:04:38 -05:00
|
|
|
FormatArgs::List(mut elems) => {
|
|
|
|
let formatter = SimpleTemplate::new();
|
2019-08-12 22:07:26 -05:00
|
|
|
// TODO(jwall): This really belongs in a preprocess step
|
|
|
|
// before here.
|
|
|
|
let mut parts = formatter.parse(&def.template).unwrap();
|
2019-08-05 19:04:38 -05:00
|
|
|
// We need to push process these in reverse order for the
|
|
|
|
// vm to process things correctly;
|
|
|
|
elems.reverse();
|
|
|
|
parts.reverse();
|
|
|
|
let mut elems_iter = elems.drain(0..);
|
|
|
|
let mut parts_iter = parts.drain(0..);
|
|
|
|
Self::translate_template_part(
|
2019-08-18 08:40:30 -05:00
|
|
|
def.pos.clone(),
|
2019-08-05 19:04:38 -05:00
|
|
|
parts_iter.next().unwrap(),
|
|
|
|
&mut elems_iter,
|
|
|
|
&mut ops,
|
|
|
|
true,
|
2019-08-17 16:57:45 -05:00
|
|
|
root,
|
2019-08-05 19:04:38 -05:00
|
|
|
);
|
|
|
|
for p in parts_iter {
|
2019-08-18 08:40:30 -05:00
|
|
|
Self::translate_template_part(
|
|
|
|
def.pos.clone(),
|
|
|
|
p,
|
|
|
|
&mut elems_iter,
|
|
|
|
&mut ops,
|
|
|
|
true,
|
|
|
|
root,
|
|
|
|
);
|
|
|
|
// TODO(jwall): We could get a little more helpful about where
|
|
|
|
// these positions are.
|
|
|
|
ops.push(Op::Add, def.pos.clone());
|
2019-08-03 16:53:43 -05:00
|
|
|
}
|
|
|
|
}
|
2019-08-12 22:07:26 -05:00
|
|
|
FormatArgs::Single(expr) => {
|
|
|
|
let formatter = ExpressionTemplate::new();
|
|
|
|
// TODO(jwall): This really belongs in a preprocess step
|
|
|
|
// before here.
|
|
|
|
let mut parts = formatter.parse(&def.template).unwrap();
|
|
|
|
parts.reverse();
|
|
|
|
let mut parts_iter = parts.drain(0..);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Noop, expr.pos().clone());
|
2019-08-12 22:07:26 -05:00
|
|
|
let scope_idx = ops.len() - 1;
|
|
|
|
|
|
|
|
// Add our item binding shadowing any binding that already
|
|
|
|
// existed.
|
2019-08-18 08:40:30 -05:00
|
|
|
let expr_pos = expr.pos().clone();
|
|
|
|
ops.push(Op::Sym("item".to_owned()), expr.pos().clone());
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*expr, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::BindOver, expr_pos.clone());
|
2019-08-12 22:07:26 -05:00
|
|
|
let mut elems = Vec::new();
|
|
|
|
let mut elems_iter = elems.drain(0..);
|
|
|
|
Self::translate_template_part(
|
2019-08-18 08:40:30 -05:00
|
|
|
def.pos.clone(),
|
2019-08-12 22:07:26 -05:00
|
|
|
parts_iter.next().unwrap(),
|
|
|
|
&mut elems_iter,
|
|
|
|
&mut ops,
|
|
|
|
false,
|
2019-08-17 16:57:45 -05:00
|
|
|
root,
|
2019-08-12 22:07:26 -05:00
|
|
|
);
|
|
|
|
for p in parts_iter {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_template_part(
|
2019-08-18 08:40:30 -05:00
|
|
|
def.pos.clone(),
|
2019-08-17 16:57:45 -05:00
|
|
|
p,
|
|
|
|
&mut elems_iter,
|
|
|
|
&mut ops,
|
|
|
|
false,
|
|
|
|
root,
|
|
|
|
);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Add, expr_pos.clone());
|
2019-08-12 22:07:26 -05:00
|
|
|
}
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Return, expr_pos);
|
2019-08-12 22:07:26 -05:00
|
|
|
let jump_idx = (ops.len() - 1 - scope_idx) as i32;
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.replace(scope_idx, Op::NewScope(jump_idx));
|
2019-08-03 16:53:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-14 19:52:41 -05:00
|
|
|
Expression::Func(def) => {
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::InitList, def.pos.clone());
|
2019-08-14 19:52:41 -05:00
|
|
|
for b in def.argdefs {
|
2020-04-26 12:16:20 -04:00
|
|
|
// FIXME(jwall): if there is a projection
|
|
|
|
// then add the projection check here?
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Sym(b.val), b.pos.clone());
|
|
|
|
ops.push(Op::Element, b.pos);
|
2019-08-14 19:52:41 -05:00
|
|
|
}
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Noop, def.pos.clone());
|
2019-08-14 19:52:41 -05:00
|
|
|
let idx = ops.len() - 1;
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.fields, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Return, def.pos);
|
2019-08-14 19:52:41 -05:00
|
|
|
let jptr = ops.len() - 1 - idx;
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.replace(idx, Op::Func(jptr as i32));
|
2019-08-14 19:52:41 -05:00
|
|
|
}
|
2019-08-16 20:34:04 -05:00
|
|
|
Expression::FuncOp(def) => {
|
|
|
|
match def {
|
|
|
|
FuncOpDef::Map(def) => {
|
|
|
|
// push the function on the stack first.
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.func, &mut ops, root);
|
2019-08-16 20:34:04 -05:00
|
|
|
// push the target on the stack third
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.target, &mut ops, root);
|
2019-08-16 20:34:04 -05:00
|
|
|
// finally push the Hook::Map opcode
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Runtime(Hook::Map), def.pos);
|
2019-08-16 20:34:04 -05:00
|
|
|
}
|
|
|
|
FuncOpDef::Filter(def) => {
|
|
|
|
// push the function on the stack first.
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.func, &mut ops, root);
|
2019-08-16 20:34:04 -05:00
|
|
|
// push the target on the stack third
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.target, &mut ops, root);
|
2019-08-16 20:34:04 -05:00
|
|
|
// finally push the Hook::Map opcode
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Runtime(Hook::Filter), def.pos);
|
2019-08-16 20:34:04 -05:00
|
|
|
}
|
|
|
|
FuncOpDef::Reduce(def) => {
|
|
|
|
// push the function on the stack first.
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.func, &mut ops, root);
|
2019-08-16 20:34:04 -05:00
|
|
|
// push the accumulator on the stack third
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.acc, &mut ops, root);
|
2019-08-16 20:34:04 -05:00
|
|
|
// push the target on the stack third
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.target, &mut ops, root);
|
2019-08-16 20:34:04 -05:00
|
|
|
// finally push the Hook::Map opcode
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Runtime(Hook::Reduce), def.pos);
|
2019-08-16 20:34:04 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-15 20:22:47 -05:00
|
|
|
Expression::Import(def) => {
|
2020-05-25 10:58:45 -04:00
|
|
|
let link_path = def.path.fragment.clone();
|
2020-05-25 13:26:57 -04:00
|
|
|
ops.add_link(link_path, def.path.pos.clone());
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Val(Primitive::Str(def.path.fragment)), def.path.pos);
|
|
|
|
ops.push(Op::Runtime(Hook::Import), def.pos);
|
2019-08-15 20:22:47 -05:00
|
|
|
}
|
|
|
|
Expression::Include(def) => {
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Val(Primitive::Str(def.typ.fragment)), def.typ.pos);
|
|
|
|
ops.push(Op::Val(Primitive::Str(def.path.fragment)), def.path.pos);
|
|
|
|
ops.push(Op::Runtime(Hook::Include), def.pos);
|
2019-08-15 20:22:47 -05:00
|
|
|
}
|
2020-05-25 11:56:41 -04:00
|
|
|
Expression::Module(def) => {
|
2019-08-15 18:11:54 -05:00
|
|
|
let argset = def.arg_set;
|
|
|
|
let out_expr = def.out_expr;
|
|
|
|
let stmts = def.statements;
|
|
|
|
// Init our module tuple bindings
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::InitTuple, def.pos.clone());
|
2019-08-15 18:11:54 -05:00
|
|
|
for (t, e) in argset {
|
2020-04-26 12:16:20 -04:00
|
|
|
// FIXME(jwall): if there is a projection
|
|
|
|
// then add the projection check here?
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Sym(t.fragment), t.pos.clone());
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(e, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Field, t.pos);
|
2019-08-15 18:11:54 -05:00
|
|
|
}
|
|
|
|
// If there is one then emit our return expression
|
|
|
|
if let Some(expr) = out_expr {
|
|
|
|
// Insert placeholder until we know jptr for this thunk
|
2019-08-18 08:40:30 -05:00
|
|
|
let expr_pos = expr.pos().clone();
|
|
|
|
ops.push(Op::Noop, expr.pos().clone());
|
2019-08-15 18:11:54 -05:00
|
|
|
let idx = ops.len() - 1;
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*expr, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Return, expr_pos.clone());
|
2019-08-15 18:11:54 -05:00
|
|
|
let jptr = ops.len() - idx - 1;
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.replace(idx, Op::InitThunk(jptr as i32));
|
2019-08-15 18:11:54 -05:00
|
|
|
}
|
|
|
|
// Insert a placeholder Opcode until we know jptr for the
|
|
|
|
// module.
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Noop, def.pos.clone());
|
2019-08-15 18:11:54 -05:00
|
|
|
let idx = ops.len() - 1;
|
|
|
|
// Bind our mod tuple.
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Bind, def.pos.clone());
|
2019-08-15 18:11:54 -05:00
|
|
|
// emit all of our statements;
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_stmts(stmts, &mut ops, root);
|
2019-08-15 18:11:54 -05:00
|
|
|
// Return from the module
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Return, def.pos);
|
2019-08-15 18:11:54 -05:00
|
|
|
let jptr = ops.len() - idx - 1;
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.replace(idx, Op::Module(jptr as i32));
|
2019-08-15 18:11:54 -05:00
|
|
|
}
|
2019-07-29 18:37:24 -05:00
|
|
|
Expression::Not(def) => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.expr, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Not, def.pos);
|
2019-07-29 18:37:24 -05:00
|
|
|
}
|
2019-08-16 20:34:04 -05:00
|
|
|
Expression::Range(def) => {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.end, &mut ops, root);
|
2019-08-16 20:34:04 -05:00
|
|
|
if let Some(expr) = def.step {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*expr, &mut ops, root);
|
2019-08-16 20:34:04 -05:00
|
|
|
} else {
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Val(Primitive::Empty), def.pos.clone());
|
2019-08-16 20:34:04 -05:00
|
|
|
}
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.start, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Runtime(Hook::Range), def.pos);
|
2019-08-16 20:34:04 -05:00
|
|
|
}
|
|
|
|
Expression::Select(def) => {
|
2019-08-28 19:11:09 -05:00
|
|
|
let default_pos = def.val.pos().clone();
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.val, &mut ops, root);
|
2019-08-16 20:34:04 -05:00
|
|
|
let mut jumps = Vec::new();
|
|
|
|
for (key, val) in def.tuple {
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Sym(key.fragment), key.pos.clone());
|
|
|
|
ops.push(Op::Noop, key.pos);
|
2019-08-16 20:34:04 -05:00
|
|
|
let idx = ops.len() - 1;
|
2019-08-18 08:40:30 -05:00
|
|
|
let expr_pos = val.pos().clone();
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(val, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Noop, expr_pos);
|
2019-08-16 20:34:04 -05:00
|
|
|
jumps.push(ops.len() - 1);
|
|
|
|
let jptr = ops.len() - idx - 1;
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.replace(idx, Op::SelectJump(jptr as i32));
|
2019-08-16 20:34:04 -05:00
|
|
|
}
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Pop, def.pos.clone());
|
2019-08-16 20:34:04 -05:00
|
|
|
if let Some(default) = def.default {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*default, &mut ops, root);
|
2019-08-16 20:34:04 -05:00
|
|
|
} else {
|
2019-08-28 19:11:09 -05:00
|
|
|
ops.push(
|
|
|
|
Op::Val(Primitive::Str(
|
|
|
|
"Unhandled select case with no default".to_owned(),
|
|
|
|
)),
|
|
|
|
default_pos,
|
|
|
|
);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Bang, def.pos);
|
2019-08-16 20:34:04 -05:00
|
|
|
}
|
2019-08-26 21:24:17 -05:00
|
|
|
let end = ops.len() - 1;
|
|
|
|
for i in jumps {
|
|
|
|
let idx = end - i;
|
|
|
|
ops.replace(i, Op::Jump(idx as i32));
|
|
|
|
}
|
2019-08-16 20:34:04 -05:00
|
|
|
}
|
2019-09-01 19:21:02 -05:00
|
|
|
Expression::Call(call_def) => {
|
|
|
|
let count = call_def.arglist.len() as i64;
|
|
|
|
for e in call_def.arglist {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(e, &mut ops, root);
|
2019-08-14 19:52:41 -05:00
|
|
|
}
|
2019-09-01 19:21:02 -05:00
|
|
|
ops.push(Op::Val(Primitive::Int(count)), call_def.pos.clone());
|
2019-08-14 19:52:41 -05:00
|
|
|
// then push the func reference
|
2019-09-01 19:21:02 -05:00
|
|
|
let func_pos = call_def.funcref.pos().clone();
|
|
|
|
Self::translate_value(call_def.funcref, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::FCall, func_pos);
|
2019-08-14 19:52:41 -05:00
|
|
|
}
|
2019-11-11 20:06:17 -06:00
|
|
|
Expression::Cast(cast_def) => {
|
|
|
|
Self::translate_expr(*cast_def.target, &mut ops, root);
|
|
|
|
ops.push(Op::Cast(cast_def.cast_type), cast_def.pos);
|
|
|
|
}
|
2019-08-15 18:11:54 -05:00
|
|
|
Expression::Copy(def) => {
|
2019-08-20 19:36:07 -05:00
|
|
|
Self::translate_value(def.selector, &mut ops, root);
|
2019-08-20 20:51:46 -05:00
|
|
|
Self::translate_copy(ops, def.fields, def.pos, root);
|
2019-08-15 18:11:54 -05:00
|
|
|
}
|
2019-08-16 20:34:04 -05:00
|
|
|
Expression::Debug(def) => {
|
2019-08-17 11:48:56 -05:00
|
|
|
let mut buffer: Vec<u8> = Vec::new();
|
|
|
|
{
|
|
|
|
let mut printer = crate::ast::printer::AstPrinter::new(2, &mut buffer);
|
|
|
|
let _ = printer.render_expr(&def.expr);
|
|
|
|
}
|
|
|
|
let expr_pretty = String::from_utf8(buffer).unwrap();
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Val(Primitive::Str(expr_pretty)), def.pos.clone());
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(*def.expr, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Runtime(Hook::Trace(def.pos.clone())), def.pos);
|
2019-08-16 20:34:04 -05:00
|
|
|
}
|
2019-07-29 17:51:08 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-05 19:04:38 -05:00
|
|
|
fn translate_template_part<EI: Iterator<Item = Expression>>(
|
2019-08-18 08:40:30 -05:00
|
|
|
pos: Position,
|
2019-08-05 19:04:38 -05:00
|
|
|
part: TemplatePart,
|
|
|
|
elems: &mut EI,
|
2020-05-25 10:58:45 -04:00
|
|
|
mut ops: &mut OpsMap,
|
2019-08-05 19:04:38 -05:00
|
|
|
place_holder: bool,
|
2019-08-17 16:57:45 -05:00
|
|
|
root: &Path,
|
2019-08-05 19:04:38 -05:00
|
|
|
) {
|
|
|
|
match part {
|
|
|
|
TemplatePart::Str(s) => {
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Val(Primitive::Str(s.into_iter().collect())), pos);
|
2019-08-05 19:04:38 -05:00
|
|
|
}
|
|
|
|
TemplatePart::PlaceHolder(_idx) => {
|
|
|
|
if !place_holder {
|
|
|
|
// In theory this should never be reachable
|
|
|
|
unreachable!();
|
|
|
|
} else {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(elems.next().unwrap(), &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Render, pos);
|
2019-08-05 19:04:38 -05:00
|
|
|
}
|
|
|
|
}
|
2019-08-12 22:07:26 -05:00
|
|
|
TemplatePart::Expression(expr) => {
|
2019-08-05 19:04:38 -05:00
|
|
|
if place_holder {
|
|
|
|
unreachable!();
|
|
|
|
} else {
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(expr, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Render, pos);
|
2019-08-05 19:04:38 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-20 21:30:17 -05:00
|
|
|
fn translate_copy(
|
2020-05-25 10:58:45 -04:00
|
|
|
mut ops: &mut OpsMap,
|
2019-08-20 21:30:17 -05:00
|
|
|
flds: Vec<(Token, Expression)>,
|
|
|
|
pos: Position,
|
|
|
|
root: &Path,
|
|
|
|
) {
|
2019-08-20 20:51:46 -05:00
|
|
|
ops.push(Op::PushSelf, pos.clone());
|
|
|
|
ops.push(Op::InitTuple, pos.clone());
|
|
|
|
for (t, e) in flds {
|
|
|
|
ops.push(Op::Sym(t.fragment), t.pos.clone());
|
|
|
|
Self::translate_expr(e, &mut ops, root);
|
|
|
|
ops.push(Op::Field, t.pos.clone());
|
|
|
|
}
|
|
|
|
ops.push(Op::Cp, pos.clone());
|
|
|
|
ops.push(Op::PopSelf, pos);
|
|
|
|
}
|
|
|
|
|
2020-05-25 10:58:45 -04:00
|
|
|
fn translate_value(value: Value, mut ops: &mut OpsMap, root: &Path) {
|
2019-08-01 19:04:58 -05:00
|
|
|
match value {
|
2019-08-18 08:40:30 -05:00
|
|
|
Value::Int(i) => ops.push(Op::Val(Primitive::Int(i.val)), i.pos),
|
|
|
|
Value::Float(f) => ops.push(Op::Val(Primitive::Float(f.val)), f.pos),
|
|
|
|
Value::Str(s) => ops.push(Op::Val(Primitive::Str(s.val)), s.pos),
|
|
|
|
Value::Empty(pos) => ops.push(Op::Val(Primitive::Empty), pos),
|
|
|
|
Value::Boolean(b) => ops.push(Op::Val(Primitive::Bool(b.val)), b.pos),
|
2019-08-02 18:55:18 -05:00
|
|
|
Value::Symbol(s) => {
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::DeRef(s.val), s.pos);
|
2019-08-02 18:55:18 -05:00
|
|
|
}
|
2019-08-02 19:26:11 -05:00
|
|
|
Value::Tuple(flds) => {
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::InitTuple, flds.pos);
|
2019-08-02 19:26:11 -05:00
|
|
|
for (k, v) in flds.val {
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Sym(k.fragment), k.pos.clone());
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(v, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Field, k.pos.clone());
|
2019-08-02 19:26:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Value::List(els) => {
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::InitList, els.pos);
|
2019-08-02 19:26:11 -05:00
|
|
|
for el in els.elems {
|
2019-08-18 08:40:30 -05:00
|
|
|
let el_pos = el.pos().clone();
|
2019-08-17 16:57:45 -05:00
|
|
|
Self::translate_expr(el, &mut ops, root);
|
2019-08-18 08:40:30 -05:00
|
|
|
ops.push(Op::Element, el_pos);
|
2019-08-02 19:26:11 -05:00
|
|
|
}
|
|
|
|
}
|
2019-07-29 17:51:08 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|