mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
DEV: SimpleFormat expressions work.
This commit is contained in:
parent
405ad2880e
commit
e3f9b685c6
@ -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 {
|
||||
|
@ -24,4 +24,4 @@ where
|
||||
// FIXME(jwall): This should really have more information for debugging
|
||||
Error {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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())),
|
||||
];
|
||||
}
|
||||
|
@ -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))),
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user