mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-24 18:39:50 -04:00
151 lines
4.5 KiB
Rust
151 lines
4.5 KiB
Rust
// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
|
|
//
|
|
// 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.
|
|
|
|
//! The format string logic for ucg format expressions.
|
|
use std::error::Error;
|
|
use std::str::Chars;
|
|
|
|
use abortable_parser::iter::SliceIter;
|
|
use abortable_parser::Result as ParseResult;
|
|
|
|
use crate::ast::*;
|
|
use crate::iter;
|
|
use crate::parse;
|
|
use crate::tokenizer;
|
|
|
|
pub trait FormatRenderer {
|
|
fn render(&self, pos: &Position) -> Result<String, Box<dyn Error>>;
|
|
}
|
|
|
|
pub type TemplateResult = Result<Vec<TemplatePart>, Box<dyn Error>>;
|
|
|
|
pub trait TemplateParser {
|
|
fn parse(&self, input: &str) -> TemplateResult;
|
|
}
|
|
|
|
pub struct SimpleTemplate();
|
|
|
|
impl SimpleTemplate {
|
|
pub fn new() -> Self {
|
|
Self()
|
|
}
|
|
}
|
|
|
|
impl TemplateParser for SimpleTemplate {
|
|
fn parse(&self, input: &str) -> TemplateResult {
|
|
let mut result = Vec::new();
|
|
let mut count = 0;
|
|
let mut should_escape = false;
|
|
let mut buf: Vec<char> = Vec::new();
|
|
for c in 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 {
|
|
should_escape = true;
|
|
continue;
|
|
} else {
|
|
buf.push(c);
|
|
}
|
|
should_escape = false;
|
|
}
|
|
if buf.len() != 0 {
|
|
result.push(TemplatePart::Str(buf));
|
|
}
|
|
Ok(result)
|
|
}
|
|
}
|
|
|
|
pub struct ExpressionTemplate();
|
|
|
|
impl ExpressionTemplate {
|
|
pub fn new() -> Self {
|
|
ExpressionTemplate()
|
|
}
|
|
|
|
fn consume_expr(&self, iter: &mut Chars) -> Result<Expression, Box<dyn Error>> {
|
|
let mut result = String::new();
|
|
let mut brace_count = 0;
|
|
loop {
|
|
let c = match iter.next() {
|
|
Some(c) => c,
|
|
None => break,
|
|
};
|
|
if c == '{' {
|
|
brace_count += 1;
|
|
// We ignore the starting brace
|
|
if brace_count == 1 {
|
|
continue;
|
|
}
|
|
}
|
|
if c == '}' {
|
|
brace_count -= 1;
|
|
}
|
|
// We ignore the closing brace
|
|
if brace_count == 0 {
|
|
break;
|
|
}
|
|
result.push(c);
|
|
}
|
|
let str_iter = iter::OffsetStrIter::new(&result);
|
|
let toks = match tokenizer::tokenize(str_iter, None) {
|
|
Ok(toks) => toks,
|
|
Err(_e) => panic!("TODO(jwall): make this not a thing"),
|
|
};
|
|
|
|
let i = SliceIter::new(&toks);
|
|
match parse::expression(i) {
|
|
ParseResult::Complete(_, expr) => Ok(expr),
|
|
ParseResult::Abort(_e) | ParseResult::Fail(_e) => {
|
|
panic!("TODO(jwall): make this not a thing")
|
|
}
|
|
ParseResult::Incomplete(_ei) => panic!("TODO(jwall): make this not a thing"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TemplateParser for ExpressionTemplate {
|
|
fn parse(&self, input: &str) -> TemplateResult {
|
|
let mut parts = Vec::new();
|
|
let mut should_escape = false;
|
|
let mut iter = input.chars();
|
|
let mut buf: Vec<char> = Vec::new();
|
|
loop {
|
|
let c = match iter.next() {
|
|
Some(c) => c,
|
|
None => break,
|
|
};
|
|
if c == '@' && !should_escape {
|
|
parts.push(TemplatePart::Str(buf));
|
|
buf = Vec::new();
|
|
// consume our expression here
|
|
parts.push(TemplatePart::Expression(self.consume_expr(&mut iter)?));
|
|
} else if c == '\\' && !should_escape {
|
|
should_escape = true;
|
|
continue;
|
|
} else {
|
|
buf.push(c);
|
|
}
|
|
should_escape = false;
|
|
}
|
|
if buf.len() != 0 {
|
|
parts.push(TemplatePart::Str(buf));
|
|
}
|
|
Ok(parts)
|
|
}
|
|
}
|