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:
Андреев Григорий 2025-04-09 02:38:29 +03:00
parent 495e0f660d
commit c5f71afa5c
3 changed files with 295 additions and 28 deletions

View File

@ -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))),

View File

@ -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
}
}
}
}

View File

@ -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())
}