Daily update. I was almost sure that I have a woring idea, but no, I forgot to pass reference to source code tree to runtime. How do I even do that?
This commit is contained in:
parent
495e0f660d
commit
c5f71afa5c
@ -3,7 +3,7 @@ use std::fs::{read_dir, metadata, canonicalize};
|
||||
use std::path::PathBuf;
|
||||
use std::error::Error;
|
||||
use super::charclasses::{escape_for_html, is_bad_name};
|
||||
use super::runtime::{HigherLevelFunc, Value, DebugState};
|
||||
use super::runtime::{HigherLevelFunc, Value, RuntimeEnv};
|
||||
use super::parser::{parse_one_file_simplified, parse_one_file_packed};
|
||||
use super::lambda_compilation::{plemege_to_value};
|
||||
use std::collections::HashMap;
|
||||
@ -105,7 +105,7 @@ pub fn get_all_templates(p: &str, plain_ext: &str) -> Result<Value, Box<dyn Err
|
||||
let text_bytes = fs::read(PathBuf::from(p).join(&path))?;
|
||||
let text = std::str::from_utf8(&text_bytes)?;
|
||||
let plemege = parse_one_file_packed(text)?;
|
||||
let compiled = plemege_to_value(plemege, &path)?;
|
||||
let compiled = plemege_to_value(plemege, &path, 150)?;
|
||||
add_path_to_root(&mut res, &cut_path, '/', compiled)?
|
||||
}
|
||||
for cut_path in source.imtgott {
|
||||
@ -113,7 +113,7 @@ pub fn get_all_templates(p: &str, plain_ext: &str) -> Result<Value, Box<dyn Err
|
||||
let text_bytes = fs::read(PathBuf::from(p).join(&path))?;
|
||||
let text = std::str::from_utf8(&text_bytes)?;
|
||||
let plemege = parse_one_file_simplified(text)?;
|
||||
let compiled = plemege_to_value(plemege, &path)?;
|
||||
let compiled = plemege_to_value(plemege, &path, 150)?;
|
||||
add_path_to_root(&mut res, &cut_path, '/', compiled)?
|
||||
}
|
||||
for cut_path in source.plain {
|
||||
@ -138,7 +138,7 @@ pub fn get_all_templates_plus_builtins(p: &str, plain_ext: &str, sanitize: Box<H
|
||||
|
||||
pub fn get_root_html(p: &str) -> Result<Value, Box<dyn Error>>{
|
||||
get_all_templates_plus_builtins(p, ".html", Box::new(
|
||||
|d_state: &DebugState, _: &Value, arg: &Value| -> Result<Value, Box<dyn Error>> {
|
||||
|d_state: &RuntimeEnv, _: &Value, arg: &Value, _: u32| -> Result<Value, String> {
|
||||
let _g = d_state.register("In sanitizer".into());
|
||||
match arg {
|
||||
Value::Str(s) => Ok(Value::Str(escape_for_html(&s))),
|
||||
|
||||
@ -2,30 +2,283 @@ use super::runtime::*;
|
||||
use super::parser::*;
|
||||
use std::error::Error;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
|
||||
fn cat_vec(arr: Vec<String>) -> String {
|
||||
let total_len: usize = arr.iter().map(|s| { s.len() }).sum();
|
||||
arr.into_iter().fold(String::with_capacity(total_len), |mut acc, p| { acc.push_str(p.as_str()); acc })
|
||||
struct ElementPartsCollector {
|
||||
res: String, cur_col: usize,
|
||||
}
|
||||
|
||||
fn compile(el: &[SubElement]) -> Result<Box<HigherLevelFunc>, Box<dyn Error>> {
|
||||
return Err("sd".into())
|
||||
impl ElementPartsCollector {
|
||||
fn without_pad(&mut self, thp: &str) {
|
||||
self.res += thp;
|
||||
for ch in thp.chars() {
|
||||
if ch == '\n' { self.cur_col = 0; } else { self.cur_col += 1; }
|
||||
}
|
||||
}
|
||||
|
||||
fn add_padded_element_piece(&mut self, offset: usize, thp: &str){
|
||||
for ch in thp.chars() {
|
||||
self.res.push(ch);
|
||||
if ch == '\n' {
|
||||
self.res.push_str(&" ".repeat(offset));
|
||||
self.cur_col = offset;
|
||||
} else {
|
||||
self.cur_col += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_single_padded_element_piece(&mut self, thp: &str) {
|
||||
self.add_padded_element_piece(self.cur_col, thp);
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeEnv {
|
||||
fn value_to_bool(&self, val: &Value) -> Result<bool, String> {
|
||||
match val {
|
||||
Value::Int(num) => Ok(*num != 0),
|
||||
Value::Str(str) => Ok(str.len() > 0),
|
||||
Value::Arr(arr) => Ok(arr.len() > 0),
|
||||
Value::Dict(dict) => Ok(dict.len() > 0),
|
||||
Value::Fn(_) => Err(self.make_error("Can't convert function to bool")),
|
||||
_ => panic!("Get rid of runtime strings in expressions")
|
||||
}
|
||||
}
|
||||
|
||||
fn get_needed_if_block<'i>(
|
||||
&self, if_sub_el: &'i IfSubElement, root: &Value, arg_stack: &[&Value], recc: u32
|
||||
) -> Result<Option<&'i Element>, String> {
|
||||
let n = if_sub_el.conditions.len();
|
||||
for i in 0..n {
|
||||
let expr_res = self.execute_expression(&if_sub_el.conditions[i], root, arg_stack, recc - 1)?;
|
||||
if self.value_to_bool(expr_res.deref())? {
|
||||
return Ok(Some(&if_sub_el.branches[i]));
|
||||
}
|
||||
}
|
||||
if if_sub_el.branches.len() > n {
|
||||
Ok(Some(&if_sub_el.branches[n]))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_element_block<'r, 'aa>(
|
||||
&self, el: &[SubElement], root: &'r Value, arg_stack: &[&'aa Value], recc: u32
|
||||
) -> Result<String, String> {
|
||||
if recc == 0 { return Err(self.make_error("Recursion limit exceeded")) }
|
||||
let mut res = ElementPartsCollector{res: String::new(), cur_col: 0};
|
||||
for se in el {
|
||||
match se {
|
||||
SubElement::Static(ps) => {
|
||||
res.without_pad(ps);
|
||||
},
|
||||
SubElement::If(if_sub_el) =>
|
||||
if let Some(branch_el) = self.get_needed_if_block(if_sub_el, root, arg_stack, recc - 1)? {
|
||||
res.add_single_padded_element_piece(
|
||||
&self.execute_element_block(
|
||||
&branch_el.sub_elements, root, arg_stack, recc - 1)?)
|
||||
}
|
||||
,
|
||||
SubElement::InsertExpr(expr) => res.add_single_padded_element_piece(
|
||||
match self.execute_expression(expr, root, arg_stack, recc - 1)?.deref() {
|
||||
Value::Str(str) => str,
|
||||
_ => return Err(self.make_error("Cannot insert non-string. Try using {{}}")),
|
||||
}),
|
||||
SubElement::For(ForSubElement{iterable, core, join}) => {
|
||||
let _g = self.register("In {%for%}".into());
|
||||
let expr_val = self.execute_expression(iterable, root, arg_stack, recc - 1)?;
|
||||
let an = arg_stack.len();
|
||||
let saved_offset = res.cur_col;
|
||||
match expr_val.deref() {
|
||||
Value::Dict(dict) => {
|
||||
let mut it = dict.iter();
|
||||
for i in 0usize.. {
|
||||
if let Some((key, value)) = it.next() {
|
||||
let itv = Value::Str(key.clone());
|
||||
let mut arg_stack_updated: Vec<&Value> = Vec::with_capacity(an + 2);
|
||||
arg_stack_updated.extend_from_slice(arg_stack);
|
||||
arg_stack_updated.push(&itv);
|
||||
arg_stack_updated.push(value);
|
||||
if i > 0 {res.add_padded_element_piece(saved_offset, join); }
|
||||
res.add_padded_element_piece(
|
||||
saved_offset, &self.execute_element_block(
|
||||
&core.sub_elements, root, arg_stack, recc - 1)?);
|
||||
} else { break }
|
||||
}
|
||||
}
|
||||
Value::Arr(array) => {
|
||||
for i in 0..array.len() {
|
||||
let itv = Value::Int(i as u64);
|
||||
let mut arg_stack_updated: Vec<&Value> = Vec::with_capacity(an + 2);
|
||||
arg_stack_updated.extend_from_slice(arg_stack);
|
||||
arg_stack_updated.push(&itv);
|
||||
arg_stack_updated.push(&array[i]);
|
||||
if i > 0 { res.add_padded_element_piece(saved_offset, join); }
|
||||
res.add_padded_element_piece(
|
||||
saved_offset, &self.execute_element_block(
|
||||
&core.sub_elements, root, arg_stack, recc - 1)?);
|
||||
}
|
||||
}
|
||||
_ => return Err(self.make_error(&format!("Can't iterate over {}", expr_val.deref().stringify_type())))
|
||||
}
|
||||
}
|
||||
SubElement::Let(expr, core) => {
|
||||
let expr_val = self.execute_expression(expr, root, arg_stack, recc - 1)?;
|
||||
let mut arg_stack_updated = Vec::with_capacity(arg_stack.len() + 1);
|
||||
arg_stack_updated.extend_from_slice(arg_stack);
|
||||
arg_stack_updated.push(expr_val.deref());
|
||||
res.add_single_padded_element_piece(&self.execute_element_block(
|
||||
&core.sub_elements, root, arg_stack, recc - 1)?);
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(res.res)
|
||||
}
|
||||
}
|
||||
|
||||
enum ExpressionResult<'l> {
|
||||
LocalRef(&'l Value),
|
||||
Owned(Value),
|
||||
}
|
||||
|
||||
|
||||
impl<'l> Deref for ExpressionResult<'l> {
|
||||
type Target = Value;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
ExpressionResult::LocalRef(val) => val,
|
||||
ExpressionResult::Owned(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeEnv {
|
||||
fn in_expression_index_by_key<'l>(&self, mut base: ExpressionResult<'l>, key: &String) -> Result<ExpressionResult<'l>, String> {
|
||||
match base {
|
||||
ExpressionResult::LocalRef(Value::Dict(dictionary )) => {
|
||||
if let Some(el) = dictionary.get(key) {
|
||||
Ok(ExpressionResult::LocalRef(el))
|
||||
} else {
|
||||
Err(self.make_error(&format!("Dictionary doesn't have key {}", key)))
|
||||
}
|
||||
},
|
||||
ExpressionResult::Owned(Value::Dict(mut dictionary)) => {
|
||||
/* I can't think of a single use case for this */
|
||||
if let Some(el) = dictionary.get_mut(key) {
|
||||
Ok(ExpressionResult::Owned(std::mem::take(el)))
|
||||
} else {
|
||||
Err(self.make_error(&format!("Locally created dictionary doesn't have key {}", key)))
|
||||
}
|
||||
}
|
||||
_ => Err(self.make_error(&format!("Can't take attribute {} (or really any attribute) from {}", key, base.deref().stringify_type())))
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_expression<'l>(
|
||||
&self, expr: &Expression, root: &'l Value, arg_stack: &[&'l Value], recc: u32
|
||||
) -> Result<ExpressionResult<'l>, String> {
|
||||
if recc == 0 { return Err(self.make_error("Recursion limit exceeded".into())) }
|
||||
let f1: ExpressionResult<'l> = match expr {
|
||||
Expression::Root => ExpressionResult::LocalRef(root),
|
||||
Expression::Argument(id) => ExpressionResult::LocalRef(arg_stack[*id as usize]),
|
||||
Expression::Get(e1, e2) => {
|
||||
let mut r1: ExpressionResult<'l> = self.execute_expression(&e1, root, arg_stack, recc - 1)?;
|
||||
let r2: ExpressionResult = self.execute_expression(&e2, root, arg_stack, recc - 1)?;
|
||||
match (r1, r2.deref()) {
|
||||
(ExpressionResult::LocalRef(Value::Arr(arr)), &Value::Int(index)) => {
|
||||
if index as usize > arr.len() {
|
||||
return Err(self.make_error(&format!(
|
||||
"Indexing array of size {} at position {}", arr.len(), index)))
|
||||
}
|
||||
ExpressionResult::LocalRef(&arr[index as usize])
|
||||
},
|
||||
(ExpressionResult::Owned(Value::Arr(mut arr)), &Value::Int(index)) => {
|
||||
/* I can't think of a single use case for this */
|
||||
if index as usize > arr.len() {
|
||||
return Err(self.make_error(&format!(
|
||||
"Indexing locally created array of size {} at position {}", arr.len(), index)))
|
||||
}
|
||||
ExpressionResult::Owned(std::mem::take(&mut arr[index as usize]))
|
||||
},
|
||||
(r1, Value::Str(key)) => self.in_expression_index_by_key(r1, key)?,
|
||||
(r1, r2) => return Err(self.make_error(&format!(
|
||||
"Trying to index {} with {}", r1.deref().stringify_type(), r2.stringify_type())))
|
||||
}
|
||||
}
|
||||
Expression::Attribute(e1, key) => {
|
||||
self.in_expression_index_by_key(
|
||||
self.execute_expression(&e1, root, arg_stack, recc - 1)?, key)?
|
||||
}
|
||||
Expression::Call(e1, e2) => {
|
||||
let r1: ExpressionResult = self.execute_expression(e1, root, arg_stack, recc - 1)?;
|
||||
let r2: ExpressionResult = self.execute_expression(e2, root, arg_stack, recc - 1)?;
|
||||
match r1.deref() {
|
||||
Value::Fn(func) => ExpressionResult::Owned(func(self, root, r2.deref(), recc - 1)?),
|
||||
_ => return Err(self.make_error(&format!(
|
||||
"Can't pass argument to {}", r1.deref().stringify_type())))
|
||||
}
|
||||
}
|
||||
Expression::Int(num ) => ExpressionResult::Owned(Value::Int(*num)),
|
||||
/* Absurd, and yet possible case */
|
||||
Expression::None => ExpressionResult::Owned(Value::Int(0)),
|
||||
};
|
||||
Ok(match f1.deref() {
|
||||
Value::RuntimeStr(fnc) => ExpressionResult::Owned(
|
||||
Value::Str(fnc(self, root, recc - 1)?)
|
||||
),
|
||||
_ => f1
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/* We construct intermediate closures for elements at runtime */
|
||||
fn construct_element_closure(prev: Vec<&Value>, el: Element, cr_recc: u32) -> Result<Box<HigherLevelFunc>, String> {
|
||||
if cr_recc == 0 { return Err("Recursion limit exceeded".into()) }
|
||||
Ok(if prev.len() + 1 == el.argc {
|
||||
Box::new(move |re: &RuntimeEnv, root: &Value, arg: &Value, ecc: u32| -> Result<Value, String> {
|
||||
if ecc == 0 { return Err(re.make_error("Recursion limit exceeded".into())) }
|
||||
let mut new = prev.clone(); new.push(arg);
|
||||
re.execute_element_block(&el.sub_elements, root, &new, ecc - 1).map(Value::Str)
|
||||
})
|
||||
} else {
|
||||
Box::new(move |re: &RuntimeEnv, root: &Value, arg: &Value, ecc: u32| -> Result<Value, String> {
|
||||
if ecc == 0 { return Err(re.make_error("Recursion limit exceeded".into())) }
|
||||
let mut new = prev.clone(); new.push(arg);
|
||||
construct_element_closure(new, el, cr_recc - 1).map(Value::Fn)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/* This function is supposed to compile all elements in one particular file */
|
||||
pub fn plemege_to_value(mut x: Plemege, file_path: &str) -> Result<Value, Box<dyn Error>> {
|
||||
pub fn plemege_to_value(x: Plemege, file_path: &str, recc: u32) -> Result<Value, String> {
|
||||
if recc == 0 { return Err("Recursion limit exceeded".into()) }
|
||||
match x {
|
||||
Plemege::Package(map) => {
|
||||
let mut new_dict: HashMap<String, Value> = HashMap::new();
|
||||
for (key, thing) in map {
|
||||
new_dict.insert(key, plemege_to_value(thing, file_path)?);
|
||||
new_dict.insert(key, plemege_to_value(thing, file_path, recc - 1)?);
|
||||
}
|
||||
Ok(Value::Dict(new_dict))
|
||||
},
|
||||
Plemege::Element(el) => {
|
||||
// Rc::new(|d_state: &DebugState, _: &Value, _: &Value, args: &[&Value]|)
|
||||
// todo
|
||||
Ok(Value::default())
|
||||
/* The only optimization we ever make (we could do more, but sorry, my life is finite) */
|
||||
if el.sub_elements.iter().all(|se| matches!(se, SubElement::Static(_))) {
|
||||
return Ok(Value::Str(el.sub_elements.into_iter()
|
||||
.fold(String::new(), |mut acc, p| {
|
||||
let SubElement::Static(s) = p else { panic!() };
|
||||
acc.push_str(&s); acc
|
||||
})))
|
||||
}
|
||||
if el.argc == 0 {
|
||||
Ok(Value::RuntimeStr(Box::new(
|
||||
move |re: &RuntimeEnv, root: &Value, recc: u32| -> Result<String, String> {
|
||||
re.execute_element_block(&el.sub_elements, root, &[], recc)
|
||||
})))
|
||||
} else {
|
||||
let c1 = construct_element_closure(Vec::new(), el, recc).map(Value::Fn);
|
||||
c1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,25 +10,33 @@ pub struct DebugStateGuard<'a> (
|
||||
|
||||
impl<'a> Drop for DebugStateGuard<'a> {
|
||||
fn drop(&mut self) {
|
||||
assert!(matches!(self.0.borrow_mut().pop(), Some(_)));
|
||||
self.0.borrow_mut().pop();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DebugState (
|
||||
pub struct RuntimeEnv(
|
||||
RefCell<Vec<String>>
|
||||
);
|
||||
|
||||
impl<'a> DebugState {
|
||||
impl<'a> RuntimeEnv {
|
||||
pub fn register(&'a self, msg: String) -> DebugStateGuard<'a> {
|
||||
self.0.borrow_mut().push(msg);
|
||||
DebugStateGuard(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/* Arguments are: stack trace for debug, root, actual function argument */
|
||||
pub type HigherLevelFunc = dyn Fn(&DebugState, &Value, &Value) -> Result<Value, Box<dyn Error>>;
|
||||
/* Arguments are: stack trace for debug, root */
|
||||
pub type OopsForgotToPrecompileThis = dyn Fn(&DebugState, &Value) -> Result<String, Box<dyn Error>>;
|
||||
impl RuntimeEnv {
|
||||
pub fn make_error(&self, err: &str) -> String {
|
||||
let total_len: usize = self.0.borrow().iter().map(|s| { s.len() }).sum();
|
||||
let mut res = self.0.borrow().iter().fold(String::with_capacity(total_len), |mut acc, p| { acc.push_str(p.as_str()); acc });
|
||||
res + err
|
||||
}
|
||||
}
|
||||
|
||||
/* Arguments are: stack trace for debug, root, actual function argument, recursion counter */
|
||||
pub type HigherLevelFunc = dyn Fn(&RuntimeEnv, &Value, &Value, u32) -> Result<Value, String>;
|
||||
/* Arguments are: stack trace for debug, root, recursion counter */
|
||||
pub type OopsForgotToPrecompileThis = dyn Fn(&RuntimeEnv, &Value, u32) -> Result<String, String>;
|
||||
|
||||
pub enum Value {
|
||||
Str(String),
|
||||
@ -45,9 +53,16 @@ pub enum Value {
|
||||
}
|
||||
|
||||
impl Value {
|
||||
fn as_dict(&self) -> &HashMap<String, Value> {
|
||||
pub fn as_dict(&self) -> &HashMap<String, Value> {
|
||||
match self { Value::Dict(s) => s, _ => panic!(), }
|
||||
}
|
||||
|
||||
pub fn stringify_type(&self) -> &'static str {
|
||||
match self {
|
||||
Value::Str(_) => "String", Value::Int(_) => "Int", Value::Arr(_) => "Array",
|
||||
Value::Dict(_) => "Dictionary", Value::Fn(_) => "Function", _ => panic!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Value {
|
||||
@ -69,7 +84,7 @@ impl Debug for Value {
|
||||
write!(f, "Dict({{{}}})", dict_debug.join(", "))
|
||||
}
|
||||
Value::Fn(_) => write!(f, "Fn(<function>)"),
|
||||
Value::RuntimeStr(_) => write!(f, "C-comp-pilat_onArrrr%%#^#}}&_)")
|
||||
Value::RuntimeStr(_) => write!(f, "C-comp-pilat_onArrrrr%%#^#}}&_)")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -81,7 +96,6 @@ impl<'t> PartialEq for Value {
|
||||
(Value::Int(i1), Value::Int(i2)) => i1 == i2,
|
||||
(Value::Arr(a1), Value::Arr(a2)) => a1 == a2,
|
||||
(Value::Dict(d1), Value::Dict(d2)) => d1 == d2,
|
||||
(Value::Fn(_), Value::Fn(_)) => false,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -119,11 +133,11 @@ pub fn generate_template(root: &Value, guest_root: Option<&Value>, name: &str) -
|
||||
};
|
||||
match main {
|
||||
Value::Fn(main_func) => {
|
||||
let d_stack = DebugState(RefCell::new(Vec::new()));
|
||||
let d_stack = RuntimeEnv(RefCell::new(Vec::new()));
|
||||
let Some(guest_root) = guest_root else {
|
||||
return Err(format!("Need guest argument root for template {name}").into())
|
||||
};
|
||||
let rv = main_func(&d_stack, root, guest_root)?;
|
||||
let rv = main_func(&d_stack, root, guest_root, 150)?;
|
||||
match rv {
|
||||
Value::Str(answer) => Ok(answer),
|
||||
_ => Err(format!("template {name} returned not a string").into())
|
||||
@ -131,8 +145,8 @@ pub fn generate_template(root: &Value, guest_root: Option<&Value>, name: &str) -
|
||||
}
|
||||
Value::Str(str) => Ok(str.clone()),
|
||||
Value::RuntimeStr(str_fnc) => {
|
||||
let mut d_stack = DebugState(RefCell::new(Vec::new()));
|
||||
Ok(str_fnc(&mut d_stack, root)?)
|
||||
let mut d_stack = RuntimeEnv(RefCell::new(Vec::new()));
|
||||
Ok(str_fnc(&mut d_stack, root, 150)?)
|
||||
}
|
||||
_ => Err(format!("Called {name} template that is not a function / string").into())
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user