DEV: SimpleFormat expressions work.

This commit is contained in:
Jeremy Wall 2019-08-05 19:04:38 -05:00
parent 405ad2880e
commit e3f9b685c6
6 changed files with 174 additions and 71 deletions

View File

@ -27,6 +27,25 @@ pub trait FormatRenderer {
fn render(&self, pos: &Position) -> Result<String, Box<dyn Error>>;
}
#[derive(Debug)]
pub enum TemplatePart<'a> {
Str(Vec<char>),
PlaceHolder(usize),
Expression(&'a str),
}
pub trait TemplateParser {
fn parse<'a>(&self, input: &'a str) -> Vec<TemplatePart<'a>>;
}
pub struct SimpleTemplate();
impl SimpleTemplate {
pub fn new() -> Self {
Self()
}
}
/// Implements the logic for format strings in UCG format expressions.
pub struct SimpleFormatter<V: Into<String> + Clone> {
tmpl: String,
@ -43,6 +62,36 @@ impl<V: Into<String> + Clone> SimpleFormatter<V> {
}
}
impl TemplateParser for SimpleTemplate {
fn parse<'a>(&self, input: &'a str) -> Vec<TemplatePart<'a>> {
let mut result = Vec::new();
let mut count = 0;
let mut should_escape = false;
let mut buf: Vec<char> = Vec::new();
for c in dbg!(input).chars() {
if c == '@' && !should_escape {
result.push(TemplatePart::Str(buf));
buf = Vec::new();
// This is a placeholder in our template.
result.push(TemplatePart::PlaceHolder(count));
count += 1;
} else if c == '\\' && !should_escape {
eprintln!("escaping next character");
should_escape = true;
continue;
} else {
buf.push(c);
dbg!(&buf);
}
should_escape = false;
}
if buf.len() != 0 {
result.push(TemplatePart::Str(buf));
}
result
}
}
impl<V: Into<String> + Clone> FormatRenderer for SimpleFormatter<V> {
/// Renders a formatter to a string or returns an error.
///
@ -50,30 +99,33 @@ impl<V: Into<String> + Clone> FormatRenderer for SimpleFormatter<V> {
/// it will return an error. Otherwise it will return the formatted string.
fn render(&self, pos: &Position) -> Result<String, Box<dyn Error>> {
let mut buf = String::new();
let mut should_escape = false;
let mut count = 0;
for c in self.tmpl.chars() {
if c == '@' && !should_escape {
if count == self.args.len() {
return Err(error::BuildError::with_pos(
"Too few arguments to string \
formatter.",
error::ErrorType::FormatError,
pos.clone(),
)
.to_boxed());
let parser = SimpleTemplate::new();
let parts = parser.parse(&self.tmpl);
for p in parts {
match p {
TemplatePart::PlaceHolder(idx) => {
if idx == self.args.len() {
return Err(error::BuildError::with_pos(
"Too few arguments to string \
formatter.",
error::ErrorType::FormatError,
pos.clone(),
)
.to_boxed());
}
let arg = self.args[count].clone();
let strval = arg.into();
buf.push_str(&strval);
count += 1;
}
let arg = self.args[count].clone();
let strval = arg.into();
buf.push_str(&strval);
count += 1;
should_escape = false;
} else if c == '\\' && !should_escape {
eprintln!("found an escape char {}", self.tmpl);
should_escape = true;
} else {
buf.push(c);
should_escape = false;
TemplatePart::Str(cs) => {
buf.reserve(cs.len());
for c in cs {
buf.push(c);
}
}
TemplatePart::Expression(_) => unreachable!(),
}
}
if self.args.len() != count {

View File

@ -24,4 +24,4 @@ where
// FIXME(jwall): This should really have more information for debugging
Error {}
}
}
}

View File

@ -202,8 +202,7 @@ pub enum Op {
// Runtime hooks
Runtime(Hook),
// TODO(jwall): TRACE instruction
// TODO(jwall): Format instruction
Format,
Render,
}
use super::ir::Val;
@ -230,7 +229,7 @@ impl TryFrom<&Value> for Val {
let mut flds = Vec::new();
for &(ref k, ref v) in fs.iter() {
let v = v.clone();
// TODO(jwall): The RC for a Val should no longer be required.
// TODO(jwall): The Rc for a Val should no longer be required.
flds.push((k.clone(), Rc::new(v.try_into()?)));
}
Val::Tuple(flds)

View File

@ -16,8 +16,8 @@ use std::rc::Rc;
use super::scope::Stack;
use super::Composite::{List, Tuple};
use super::Op::{
Add, Bang, Bind, Cp, DeRef, Div, Element, Equal, FCall, Field, Format, Func, Index, InitList,
InitThunk, InitTuple, Jump, JumpIfFalse, JumpIfTrue, Module, Mul, Noop, Pop, Return,
Add, Bang, Bind, Cp, DeRef, Div, Element, Equal, FCall, Field, Func, Index, InitList,
InitThunk, InitTuple, Jump, JumpIfFalse, JumpIfTrue, Module, Mul, Noop, Pop, Render, Return,
SelectJump, Sub, Sym, Typ, Val,
};
use super::Primitive::{Bool, Empty, Float, Int, Str};
@ -634,16 +634,43 @@ fn simple_not_expr() {
}
#[test]
fn simple_format_expr() {
fn simple_render_operation() {
assert_cases![
vec![
InitList,
Val(Int(1)),
Element,
Val(Int(2)),
Element,
Val(Str("@@".to_owned())),
Format,
] => P(Str("12".to_owned())),
Render,
] => P(Str("1".to_owned())),
vec![
Val(Float(1.1)),
Render,
] => P(Str("1.1".to_owned())),
vec![
Val(Bool(true)),
Render,
] => P(Str("true".to_owned())),
vec![
Val(Bool(false)),
Render,
] => P(Str("false".to_owned())),
vec![
Val(Empty),
Render,
] => P(Str("NULL".to_owned())),
vec![
Val(Str("foo".to_owned())),
Render,
] => P(Str("foo".to_owned())),
];
}
#[test]
fn simple_format_expressions() {
assert_parse_cases![
"\"@\" % (1);" => P(Str("1".to_owned())),
"\"@\" % (1.1);" => P(Str("1.1".to_owned())),
"\"@\" % (\"foo\");" => P(Str("foo".to_owned())),
"\"@\" % (NULL);" => P(Str("NULL".to_owned())),
"\"@ @ @\" % (1, 2, 3);" => P(Str("1 2 3".to_owned())),
"\"@ \\\\@\" % (1);" => P(Str("1 @".to_owned())),
];
}

View File

@ -11,7 +11,11 @@
// 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 crate::ast::{BinaryExprType, Expression, Statement, Value, FormatArgs};
use crate::ast::Position;
use crate::ast::{BinaryExprType, Expression, FormatArgs, Statement, Value};
use crate::build::format::{
ExpressionFormatter, FormatRenderer, SimpleTemplate, TemplateParser, TemplatePart,
};
use crate::build::opcode::Primitive;
use crate::build::opcode::Value::{C, F, M, P, T};
use crate::build::opcode::{Hook, Op};
@ -169,22 +173,33 @@ impl AST {
}
Expression::Fail(_) => unimplemented!("Fail expressions are not implmented yet"),
Expression::Format(def) => {
// TODO(jwall): It would actually be safer if this was happening
// when we create the format def instead of here.
match def.args {
FormatArgs::List(elems) => {
ops.push(Op::InitList);
for e in elems {
Self::translate_expr(e, &mut ops);
ops.push(Op::Element);
FormatArgs::List(mut elems) => {
let formatter = SimpleTemplate::new();
let mut parts = dbg!(formatter.parse(&def.template));
// 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(
parts_iter.next().unwrap(),
&mut elems_iter,
&mut ops,
true,
);
for p in parts_iter {
Self::translate_template_part(p, &mut elems_iter, &mut ops, true);
ops.push(Op::Add);
}
}
FormatArgs::Single(e) => {
ops.push(Op::InitList);
Self::translate_expr(*e, &mut ops);
ops.push(Op::Element);
// TODO(jwall): Use expression formatter here.
}
}
ops.push(Op::Val(Primitive::Str(def.template)));
ops.push(Op::Format);
}
Expression::Func(_) => unimplemented!("Func expressions are not implmented yet"),
Expression::FuncOp(_) => unimplemented!("FuncOp expressions are not implmented yet"),
@ -203,6 +218,36 @@ impl AST {
}
}
fn translate_template_part<EI: Iterator<Item = Expression>>(
part: TemplatePart,
elems: &mut EI,
mut ops: &mut Vec<Op>,
place_holder: bool,
) {
match part {
TemplatePart::Str(s) => {
ops.push(Op::Val(Primitive::Str(s.into_iter().collect())));
}
TemplatePart::PlaceHolder(_idx) => {
if !place_holder {
// In theory this should never be reachable
unreachable!();
} else {
Self::translate_expr(dbg!(elems.next().unwrap()), &mut ops);
ops.push(Op::Render);
}
}
TemplatePart::Expression(_expr) => {
// TODO(jwall): We need to parse this.
if place_holder {
unreachable!();
} else {
unimplemented!("Expression Formatters are unimmplemented");
}
}
}
}
fn translate_value(value: Value, mut ops: &mut Vec<Op>) {
match value {
Value::Int(i) => ops.push(Op::Val(Primitive::Int(i.val))),

View File

@ -24,8 +24,6 @@ use super::Primitive::{Bool, Empty, Float, Int, Str};
use super::Value::{C, F, M, P, S, T};
use super::{Error, Op, Primitive, Value};
use super::{Func, Module};
use crate::ast::Position;
use crate::build::format::{ExpressionFormatter, FormatRenderer, SimpleFormatter};
pub struct VM {
stack: Vec<Rc<Value>>,
@ -124,7 +122,7 @@ impl<'a> VM {
}
Op::Typ => self.op_typ()?,
Op::Runtime(h) => self.op_runtime(h)?,
Op::Format => self.op_format()?,
Op::Render => self.op_render()?,
};
}
Ok(())
@ -722,27 +720,9 @@ impl<'a> VM {
.handle(&self.path, h, &mut self.stack)
}
fn op_format(&mut self) -> Result<(), Error> {
let template = self.pop()?;
let template = match template.as_ref() {
&P(Str(ref s)) => s.clone(),
_ => return Err(dbg!(Error {})),
};
let args = self.pop()?;
let args: Vec<&Value> = match args.as_ref() {
&C(List(ref elems)) => elems.iter().map(|v| v.as_ref()).collect(),
_ => return Err(dbg!(Error {})),
};
let f = if args.len() == 1 {
unimplemented!("Expression formatters are not implemented yet");
} else {
SimpleFormatter::new(template, args)
};
// TODO(jwall): Position should be valid here.
match dbg!(f.render(&Position::new(0, 0, 0))) {
Err(_e) => return Err(dbg!(Error {})),
Ok(s) => self.push(Rc::new(P(Str(s))))?,
}
fn op_render(&mut self) -> Result<(), Error> {
let val = self.pop()?;
self.push(Rc::new(P(Str(val.as_ref().into()))))?;
Ok(())
}
}