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>>;
|
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 {
|
||||||
|
@ -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 {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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())),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -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))),
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user