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>>; 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. /// Implements the logic for format strings in UCG format expressions.
pub struct SimpleFormatter<V: Into<String> + Clone> { pub struct SimpleFormatter<V: Into<String> + Clone> {
tmpl: String, 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> { impl<V: Into<String> + Clone> FormatRenderer for SimpleFormatter<V> {
/// Renders a formatter to a string or returns an error. /// 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. /// it will return an error. Otherwise it will return the formatted string.
fn render(&self, pos: &Position) -> Result<String, Box<dyn Error>> { fn render(&self, pos: &Position) -> Result<String, Box<dyn Error>> {
let mut buf = String::new(); let mut buf = String::new();
let mut should_escape = false;
let mut count = 0; let mut count = 0;
for c in self.tmpl.chars() { let parser = SimpleTemplate::new();
if c == '@' && !should_escape { let parts = parser.parse(&self.tmpl);
if count == self.args.len() { for p in parts {
return Err(error::BuildError::with_pos( match p {
"Too few arguments to string \ TemplatePart::PlaceHolder(idx) => {
formatter.", if idx == self.args.len() {
error::ErrorType::FormatError, return Err(error::BuildError::with_pos(
pos.clone(), "Too few arguments to string \
) formatter.",
.to_boxed()); 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(); TemplatePart::Str(cs) => {
let strval = arg.into(); buf.reserve(cs.len());
buf.push_str(&strval); for c in cs {
count += 1; buf.push(c);
should_escape = false; }
} else if c == '\\' && !should_escape { }
eprintln!("found an escape char {}", self.tmpl); TemplatePart::Expression(_) => unreachable!(),
should_escape = true;
} else {
buf.push(c);
should_escape = false;
} }
} }
if self.args.len() != count { if self.args.len() != count {

View File

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

View File

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

View File

@ -16,8 +16,8 @@ use std::rc::Rc;
use super::scope::Stack; use super::scope::Stack;
use super::Composite::{List, Tuple}; use super::Composite::{List, Tuple};
use super::Op::{ use super::Op::{
Add, Bang, Bind, Cp, DeRef, Div, Element, Equal, FCall, Field, Format, Func, Index, InitList, Add, Bang, Bind, Cp, DeRef, Div, Element, Equal, FCall, Field, Func, Index, InitList,
InitThunk, InitTuple, Jump, JumpIfFalse, JumpIfTrue, Module, Mul, Noop, Pop, Return, InitThunk, InitTuple, Jump, JumpIfFalse, JumpIfTrue, Module, Mul, Noop, Pop, Render, Return,
SelectJump, Sub, Sym, Typ, Val, SelectJump, Sub, Sym, Typ, Val,
}; };
use super::Primitive::{Bool, Empty, Float, Int, Str}; use super::Primitive::{Bool, Empty, Float, Int, Str};
@ -634,16 +634,43 @@ fn simple_not_expr() {
} }
#[test] #[test]
fn simple_format_expr() { fn simple_render_operation() {
assert_cases![ assert_cases![
vec![ vec![
InitList,
Val(Int(1)), Val(Int(1)),
Element, Render,
Val(Int(2)), ] => P(Str("1".to_owned())),
Element, vec![
Val(Str("@@".to_owned())), Val(Float(1.1)),
Format, Render,
] => P(Str("12".to_owned())), ] => 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. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// 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, 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::Primitive;
use crate::build::opcode::Value::{C, F, M, P, T}; use crate::build::opcode::Value::{C, F, M, P, T};
use crate::build::opcode::{Hook, Op}; use crate::build::opcode::{Hook, Op};
@ -169,22 +173,33 @@ impl AST {
} }
Expression::Fail(_) => unimplemented!("Fail expressions are not implmented yet"), Expression::Fail(_) => unimplemented!("Fail expressions are not implmented yet"),
Expression::Format(def) => { 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 { match def.args {
FormatArgs::List(elems) => { FormatArgs::List(mut elems) => {
ops.push(Op::InitList); let formatter = SimpleTemplate::new();
for e in elems { let mut parts = dbg!(formatter.parse(&def.template));
Self::translate_expr(e, &mut ops); // We need to push process these in reverse order for the
ops.push(Op::Element); // 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) => { FormatArgs::Single(e) => {
ops.push(Op::InitList); // TODO(jwall): Use expression formatter here.
Self::translate_expr(*e, &mut ops);
ops.push(Op::Element);
} }
} }
ops.push(Op::Val(Primitive::Str(def.template)));
ops.push(Op::Format);
} }
Expression::Func(_) => unimplemented!("Func expressions are not implmented yet"), Expression::Func(_) => unimplemented!("Func expressions are not implmented yet"),
Expression::FuncOp(_) => unimplemented!("FuncOp 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>) { fn translate_value(value: Value, mut ops: &mut Vec<Op>) {
match value { match value {
Value::Int(i) => ops.push(Op::Val(Primitive::Int(i.val))), 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::Value::{C, F, M, P, S, T};
use super::{Error, Op, Primitive, Value}; use super::{Error, Op, Primitive, Value};
use super::{Func, Module}; use super::{Func, Module};
use crate::ast::Position;
use crate::build::format::{ExpressionFormatter, FormatRenderer, SimpleFormatter};
pub struct VM { pub struct VM {
stack: Vec<Rc<Value>>, stack: Vec<Rc<Value>>,
@ -124,7 +122,7 @@ impl<'a> VM {
} }
Op::Typ => self.op_typ()?, Op::Typ => self.op_typ()?,
Op::Runtime(h) => self.op_runtime(h)?, Op::Runtime(h) => self.op_runtime(h)?,
Op::Format => self.op_format()?, Op::Render => self.op_render()?,
}; };
} }
Ok(()) Ok(())
@ -722,27 +720,9 @@ impl<'a> VM {
.handle(&self.path, h, &mut self.stack) .handle(&self.path, h, &mut self.stack)
} }
fn op_format(&mut self) -> Result<(), Error> { fn op_render(&mut self) -> Result<(), Error> {
let template = self.pop()?; let val = self.pop()?;
let template = match template.as_ref() { self.push(Rc::new(P(Str(val.as_ref().into()))))?;
&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))))?,
}
Ok(()) Ok(())
} }
} }