Знал ли ты, Марк, что в одном стакане Фрэнзи-Кофе 44 чайных ложки Гора?

This commit is contained in:
Андреев Григорий 2025-04-23 15:14:05 +03:00
parent a9023c6a8c
commit 9ce0e602b3
5 changed files with 548 additions and 377 deletions

View File

@ -2,11 +2,11 @@ extern crate mtgott;
use std::env; use std::env;
use std::process; use std::process;
use crate::mtgott::charclasses::is_bad_name; use crate::mtgott::charclasses::is_special_name;
use crate::mtgott::dirsearch::get_root_html; use crate::mtgott::dirsearch::get_root_html;
use crate::mtgott::runtime::{Value, MTGOTT}; use crate::mtgott::runtime::{Value, MTGOTT};
use std::collections::HashMap; use std::collections::HashMap;
use mtgott::dirsearch::add_path_with_dots_to_root; use crate::mtgott::runtime::*;
use std::rc::Rc; use std::rc::Rc;
fn usage() -> ! { fn usage() -> ! {
@ -14,6 +14,20 @@ fn usage() -> ! {
process::exit(1); process::exit(1);
} }
fn arg_dict_to_value<'t>(x: SharedValue) -> Value<'t> {
match x {
SharedValue::Str(s) => Value::Str(Rc::new(s)),
SharedValue::Dict(d) => {
let mut map: HashMap<String, Value> = HashMap::new();
for (key, value) in d {
map.insert(key, arg_dict_to_value(value));
}
Value::Dict(Rc::new(map))
}
_ => panic!()
}
}
fn main() { fn main() {
let mut args = env::args(); let mut args = env::args();
args.next().unwrap(); args.next().unwrap();
@ -31,7 +45,7 @@ fn main() {
None => usage() None => usage()
}; };
for s in name.split('.') { for s in name.split('.') {
if is_bad_name(s) { if is_special_name(s) {
eprintln!("Bad name: {}", s); eprintln!("Bad name: {}", s);
process::exit(1); process::exit(1);
} }
@ -41,11 +55,12 @@ fn main() {
usage(); usage();
} }
} }
let mut guest_root = Value::Dict(Rc::new(HashMap::new())); let mut guest_root = SharedValue::Dict(HashMap::new());
for (name_path, val) in defines { for (name_path, val) in defines {
add_path_with_dots_to_root(&mut guest_root, &name_path, Value::Str(Rc::new(val))).unwrap(); add_path_with_dots_to_root(&mut guest_root, &name_path, SharedValue::Str(val)).unwrap();
} }
let dir_root = get_root_html(&path_arg).unwrap(); let guest_root = arg_dict_to_value(guest_root);
let dir_root: MTGOTT = get_root_html(&path_arg).unwrap();
let res = dir_root.render(guest_root, &template_name, 500).unwrap(); let res = dir_root.render(guest_root, &template_name, 500).unwrap();
print!("{res}") print!("{res}")
} }

View File

@ -3,13 +3,13 @@ use std::fs::{read_dir, metadata, canonicalize};
use std::path::PathBuf; use std::path::PathBuf;
use std::error::Error; use std::error::Error;
use super::charclasses::{escape_for_html, is_special_name}; use super::charclasses::{escape_for_html, is_special_name};
use super::runtime::{Value, RuntimeEnv, MTGOTT}; use super::runtime::*;
use super::parser::{parse_one_file_simplified, parse_one_file_packed}; use super::parser::{parse_one_file_simplified, parse_one_file_packed};
use super::lambda_compilation::{plemege_to_value}; use super::lambda_compilation::{plemege_to_value};
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
pub fn search_dir_rec_helper<F: Fn(&str) -> bool>( pub fn search_dir_rec_helper(
res: &mut Vec<Vec<String>>, res: &mut Vec<Vec<String>>,
fs_path: PathBuf, virtual_path: String, fs_path: PathBuf, virtual_path: String,
allowed_extensions: &[&str], is_valid_name: fn(&str) -> bool, recc: u32 allowed_extensions: &[&str], is_valid_name: fn(&str) -> bool, recc: u32
@ -71,38 +71,9 @@ pub fn search_mtgott_dir(p: &str, plain_ext: &str) -> Result<MtgottDirContent, B
}) })
} }
/* Panics if somebody else borrowed root. splitter is usually / or . */
fn add_path_to_root(root: &mut Value, unsplit_path: &str, splitter: char, obj: Value) -> Result<(), Box<dyn Error>> {
let parts: Vec<&str> = unsplit_path.split(splitter).collect();
let mut cur: &mut Value = root;
assert!(parts.len() > 0);
for i in 0usize..parts.len() {
match cur {
Value::Dict(hashmap) => {
cur = hashmap.entry(String::from(parts[i])).or_insert(Default::default());
}
_ => return Err(format!("Overlapping root elements {}", parts[..i].join(&String::from(splitter))).into())
}
}
match cur {
Value::Int(x) if *x == 0 => {},
_ => return Err(format!("Overlapping root elements {}", unsplit_path).into()),
}
*cur = obj;
Ok(())
}
pub fn add_path_with_slashes_to_root(root: &mut Value, unsplit_path: &str, obj: Value) -> Result<(), Box<dyn Error>> {
add_path_to_root(root, unsplit_path, '/', obj)
}
pub fn add_path_with_dots_to_root(root: &mut Value, unsplit_path: &str, obj: Value) -> Result<(), Box<dyn Error>> {
add_path_to_root(root, unsplit_path, '.', obj)
}
pub fn get_all_templates(p: &str, plain_ext: &str) -> Result<MTGOTT, Box<dyn Error>> { pub fn get_all_templates(p: &str, plain_ext: &str) -> Result<MTGOTT, Box<dyn Error>> {
let source = search_mtgott_dir(p, plain_ext)?; let source = search_mtgott_dir(p, plain_ext)?;
let mut res = MTGOTT{root: Value::Dict(Rc::new(HashMap::new())), source_expressions: Vec::new(), source_elements: Vec::new()}; let mut res = MTGOTT{root: SharedValue::Dict(HashMap::new()), source_expressions: Vec::new(), source_elements: Vec::new()};
for cut_path in source.mtgott { for cut_path in source.mtgott {
let path = format!("{cut_path}.mtgott{plain_ext}"); let path = format!("{cut_path}.mtgott{plain_ext}");
let text_bytes = fs::read(PathBuf::from(p).join(&path))?; let text_bytes = fs::read(PathBuf::from(p).join(&path))?;
@ -123,7 +94,7 @@ pub fn get_all_templates(p: &str, plain_ext: &str) -> Result<MTGOTT, Box<dyn Er
let path = format!("{cut_path}{plain_ext}"); let path = format!("{cut_path}{plain_ext}");
let text_bytes = fs::read(PathBuf::from(p).join(&path))?; let text_bytes = fs::read(PathBuf::from(p).join(&path))?;
let text = String::from_utf8(text_bytes)?; let text = String::from_utf8(text_bytes)?;
add_path_to_root(&mut res.root, &cut_path, '/', Value::Str(Rc::new(text)))? add_path_to_root(&mut res.root, &cut_path, '/', SharedValue::Str(text))?
} }
Ok(res) Ok(res)
} }
@ -133,22 +104,23 @@ pub fn get_all_templates_plus_builtins(
sanitize: fn(&str) -> String, sanitize: fn(&str) -> String,
) -> Result<MTGOTT, Box<dyn Error>>{ ) -> Result<MTGOTT, Box<dyn Error>>{
let mut res = get_all_templates(p, plain_ext)?; let mut res = get_all_templates(p, plain_ext)?;
add_path_with_slashes_to_root(&mut res.root, "sanitize", Value::Fn(Rc::new( add_path_with_slashes_to_root(&mut res.root, "sanitize", SharedValue::Fn(Box::new(
/* One obvious problem with this sanitizer is that it makes copy even when it /* One obvious problem with this sanitizer is that it makes copy even when it
* takes full ownership of some string and does not have to replace any characters*/ * takes full ownership of some string and does not have to replace any characters*/
|d_state: &RuntimeEnv, _: &MTGOTT, arg: Value, _: u32| -> Result<Value, String> { move |d_state: &RuntimeEnv, arg: Value, _: u32| -> Result<Value, String> {
let s: &Value = if let Value::Ref(nr) = arg { nr } else { &arg }; if let Some(s) = arg.if_str_get_ref() {
match s { Ok(Value::Str(Rc::new(sanitize(s))))
Value::Str(s) => Ok(Value::Str(Rc::new(sanitize(s.as_ref())))), } else if let Some(n) = arg.if_int_get() {
Value::Int(num) => Ok(Value::Str(Rc::new(num.to_string()))), Ok(Value::Str(Rc::new(n.to_string())))
_ => Ok(Value::Str(Rc::new(sanitize(&format!("{:?}", arg))))) } else {
Ok(Value::Str(Rc::new(sanitize(&format!("{:?}", arg)))))
} }
})))?; })))?;
add_path_with_slashes_to_root(&mut res.root, "cmd_tag_start", Value::Str("{%".into()))?; add_path_with_slashes_to_root(&mut res.root, "cmd_tag_start", SharedValue::Str("{%".into()))?;
add_path_with_slashes_to_root(&mut res.root, "write_tag_start", Value::Str("{{".into()))?; add_path_with_slashes_to_root(&mut res.root, "write_tag_start", SharedValue::Str("{{".into()))?;
add_path_with_slashes_to_root(&mut res.root, "roughinsert_tag_start", Value::Str("{[".into()))?; add_path_with_slashes_to_root(&mut res.root, "roughinsert_tag_start", SharedValue::Str("{[".into()))?;
add_path_with_slashes_to_root(&mut res.root, "magic_block_ending_tag", Value::Str("{%}".into()))?; add_path_with_slashes_to_root(&mut res.root, "magic_block_ending_tag", SharedValue::Str("{%}".into()))?;
add_path_with_slashes_to_root(&mut res.root, "element_ending_tag", Value::Str("{@}".into()))?; add_path_with_slashes_to_root(&mut res.root, "element_ending_tag", SharedValue::Str("{@}".into()))?;
Ok(res) Ok(res)
} }

View File

@ -1,8 +1,7 @@
use super::runtime::*; use runtime::*;
use super::parser::*; use parser::*;
use std::error::Error;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Deref; use std::rc::Rc;
struct ElementPartsCollector { struct ElementPartsCollector {
res: String, cur_col: usize, res: String, cur_col: usize,
@ -33,25 +32,14 @@ impl ElementPartsCollector {
} }
} }
impl RuntimeEnv { impl<'m> RuntimeEnv<'m> {
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>( fn get_needed_if_block<'i>(
&self, if_sub_el: &'i IfSubElement, mtgott: &MTGOTT, arg_stack: &[&Value], recc: u32 &self, if_sub_el: &'i IfSubElement, local: &[Value<'m>], recc: u32
) -> Result<Option<&'i Element>, String> { ) -> Result<Option<&'i Element>, String> {
let n = if_sub_el.conditions.len(); let n = if_sub_el.conditions.len();
for i in 0..n { for i in 0..n {
let expr_res = self.execute_expression(&if_sub_el.conditions[i], mtgott, arg_stack, recc - 1)?; let expr_res = self.execute_expression(&if_sub_el.conditions[i], local, recc - 1)?;
if self.value_to_bool(expr_res.deref())? { if expr_res.to_bool()? {
return Ok(Some(&if_sub_el.branches[i])); return Ok(Some(&if_sub_el.branches[i]));
} }
} }
@ -62,8 +50,9 @@ impl RuntimeEnv {
} }
} }
/* if Ok, local remains unchanged after return, but if Err, don't use local anymore */
fn execute_element_block( fn execute_element_block(
&self, el: &[SubElement], mtgott: &MTGOTT, arg_stack: &[& Value], recc: u32 &self, el: &[SubElement], local: &mut Vec<Value<'m>>, recc: u32
) -> Result<String, String> { ) -> Result<String, String> {
if recc == 0 { return Err(self.make_error("Recursion limit exceeded")) } if recc == 0 { return Err(self.make_error("Recursion limit exceeded")) }
let mut res = ElementPartsCollector{res: String::new(), cur_col: 0}; let mut res = ElementPartsCollector{res: String::new(), cur_col: 0};
@ -72,63 +61,66 @@ impl RuntimeEnv {
SubElement::Static(ps) => { SubElement::Static(ps) => {
res.without_pad(ps); res.without_pad(ps);
}, },
SubElement::If(if_sub_el) => SubElement::If(if_sub_el) => if let Some(branch_el) =
if let Some(branch_el) = self.get_needed_if_block(if_sub_el, mtgott, arg_stack, recc - 1)? { self.get_needed_if_block(if_sub_el, local, recc - 1)? {
res.add_single_padded_element_piece( res.add_single_padded_element_piece(
&self.execute_element_block( &self.execute_element_block(branch_el, local, recc - 1)?)
&branch_el.sub_elements, mtgott, arg_stack, recc - 1)?) },
SubElement::InsertExpr(expr) => {
let val = self.execute_expression(expr, local, recc - 1)?;
match val.if_str_get_ref() {
Some(s) => res.add_single_padded_element_piece(s),
None => return Err(self.make_error(&format!(
"Can't insert {} into template. Consider using {{{{}}}}", val.type_name()
)))
} }
, },
SubElement::InsertExpr(expr) => res.add_single_padded_element_piece( SubElement::For(ForSubElement{iterable, core, separator}) => {
match self.execute_expression(expr, mtgott, 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 _g = self.register("In {%for%}".into());
let expr_val = self.execute_expression(iterable, mtgott, arg_stack, recc - 1)?; let r = self.execute_expression(iterable, local, recc - 1)?;
let an = arg_stack.len();
let saved_offset = res.cur_col; let saved_offset = res.cur_col;
match expr_val.deref() { let k = local.len();
Value::Dict(dict) => { local.push(Value::Int(0)); local.push(Value::Int(0));
let mut it = dict.iter(); let collected: Vec<String> = match r {
for i in 0usize.. { Value::Dict(dict) => dict.as_ref().iter()
if let Some((key, value)) = it.next() { .map(|(key ,value)| -> Result<String, String> {
let itv = Value::Str(key.clone()); local[k] = Value::Str(Rc::new(key.clone()));
let mut arg_stack_updated: Vec<&Value> = Vec::with_capacity(an + 2); local[k + 1] = value.clone();
arg_stack_updated.extend_from_slice(arg_stack); self.execute_element_block(core, local, recc - 1)
arg_stack_updated.push(&itv); }).collect::<Result<Vec<String>, String>>()?,
arg_stack_updated.push(value); Value::Ref(SharedValue::Dict(dict)) => dict.iter()
if i > 0 {res.add_padded_element_piece(saved_offset, join); } .map(|(key, sh_val)| -> Result<String, String> {
res.add_padded_element_piece( local[k] = Value::Str(Rc::new(key.clone()));
saved_offset, &self.execute_element_block( local[k + 1] = Value::Ref(sh_val);
&core.sub_elements, mtgott, arg_stack, recc - 1)?); self.execute_element_block(core, local, recc - 1)
} else { break } }).collect::<Result<Vec<String>, String>>()?,
Value::Arr(arr) => arr.as_ref().iter().enumerate()
.map(|(ind ,value)| -> Result<String, String> {
local[k] = Value::Int(ind as u64);
local[k + 1] = value.clone();
self.execute_element_block(core, local, recc - 1)
}).collect::<Result<Vec<String>, String>>()?,
Value::Ref(SharedValue::Arr(arr)) => arr.iter().enumerate()
.map(|(ind, sh_val)| -> Result<String, String> {
local[k] = Value::Int(ind as u64);
local[k + 1] = Value::Ref(sh_val);
self.execute_element_block(core, local, recc - 1)
}).collect::<Result<Vec<String>, String>>()?,
_ => return Err(self.make_error(&format!("Can't iterate over {}", r.type_name())))
};
if !collected.is_empty() {
res.add_padded_element_piece(saved_offset, &collected[0]);
for s in &collected[1..]{
res.add_padded_element_piece(saved_offset, separator);
res.add_padded_element_piece(saved_offset, s);
} }
} }
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, mtgott, arg_stack, recc - 1)?);
}
}
_ => return Err(self.make_error(&format!("Can't iterate over {}", expr_val.deref().stringify_type())))
}
} }
SubElement::Let(expr, core) => { SubElement::Let(expr, core) => {
let expr_val = self.execute_expression(expr, mtgott, arg_stack, recc - 1)?; let r = self.execute_expression(expr, local, recc - 1)?;
let mut arg_stack_updated = Vec::with_capacity(arg_stack.len() + 1); local.push(r);
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( res.add_single_padded_element_piece(&self.execute_element_block(
&core.sub_elements, mtgott, arg_stack, recc - 1)?); &core, local, recc - 1)?);
} }
}; };
} }
@ -136,157 +128,88 @@ impl RuntimeEnv {
} }
} }
enum ExpressionResult<'l, 't> { /* We construct intermediate closures for elements at runtime
LocalRef(&'l Value<'l>), * argc >= 1 */
Owned(Value<'t>), impl<'m> RuntimeEnv<'m> {
} fn construct_result_of_element_lambda(
&self, mut prev: Vec<Value<'m>>, argc: usize, el_source_pos: usize, recc: u32,
) -> Result<Value<'m>, String> {
impl<'l, 't> Deref for ExpressionResult<'l> { if prev.len() == argc {
type Target = Value; self.execute_element_block(&self.mtgott.source_elements[el_source_pos], &mut prev, recc)
.map(|s| Value::Str(Rc::new(s)))
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 { } else {
Err(self.make_error(&format!("Dictionary doesn't have key {}", key))) Ok(Value::Fn(Rc::new(move |re, arg, counter| -> Result<Value<'m>, String> {
} re.check_recc(counter)?;
}, let mut prev = prev.clone();
ExpressionResult::Owned(Value::Dict(mut dictionary)) => { prev.push(arg);
/* I can't think of a single use case for this */ re.construct_result_of_element_lambda(prev, argc, el_source_pos, counter - 1)
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, mtgott: &'l MTGOTT, 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(&mtgott.root),
Expression::Argument(id) => ExpressionResult::LocalRef(arg_stack[*id as usize]),
Expression::Get(e1, e2) => {
let mut r1: ExpressionResult<'l> = self.execute_expression(&e1, mtgott, arg_stack, recc - 1)?;
let r2: ExpressionResult = self.execute_expression(&e2, mtgott, 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, mtgott, arg_stack, recc - 1)?, key)?
}
Expression::Call(e1, e2) => {
let r1: ExpressionResult = self.execute_expression(e1, mtgott, arg_stack, recc - 1)?;
let r2: ExpressionResult = self.execute_expression(e2, mtgott, arg_stack, recc - 1)?;
match r1.deref() {
Value::Fn(func) => ExpressionResult::Owned(func(self, mtgott, 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, mtgott, recc - 1)?)
),
_ => f1
})
}
}
/* We construct intermediate closures for elements at runtime */
fn construct_element_closure<'a>(
prev: Vec<&'a Value>, argc: usize, el_source_pos: usize, cr_recc: u32
) -> Result<Box<dyn Fn(&RuntimeEnv, &MTGOTT, &'a Value, u32) -> Result<Value, String> + 'a>, String> {
if cr_recc == 0 { return Err("Recursion limit exceeded".into()) }
Ok(if prev.len() + 1 == argc {
Box::new(move |re: &RuntimeEnv, mtgott: &MTGOTT, 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(&mtgott.source[el_source_pos].sub_elements, mtgott, &new, ecc - 1).map(Value::Str)
})
} else {
Box::new(move |re: &RuntimeEnv, mtgott: &MTGOTT, 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, argc, el_source_pos, cr_recc - 1).map(Value::Fn)
})
})
} }
/* This function is supposed to compile all elements in one particular file. /* This function is supposed to compile all elements in one particular file.
Due to how bad mtgott type system is, Some elements require me to save source tree into Due to how bad mtgott type system is, some elements require me to save source tree into
a storage */ a storage */
pub fn plemege_to_value( pub fn plemege_to_value(
x: Plemege, file_path: &str, x: Plemege, file_path: &str,
source_elements: &mut Vec<Element>, source_expr: &mut Vec<Expression>, recc: u32 source_elements: &mut Vec<Element>, source_expr: &mut Vec<Expression>, recc: u32
) -> Result<Value, String> { ) -> Result<SharedValue, String> {
if recc == 0 { return Err("Recursion limit exceeded".into()) } if recc == 0 { return Err("Recursion limit exceeded".into()) }
match x { match x {
Plemege::Package(map) => { Plemege::Package(map) => {
let mut new_dict: HashMap<String, Value> = HashMap::new(); let mut new_dict: HashMap<String, SharedValue> = HashMap::new();
for (key, thing) in map { for (key, thing) in map {
new_dict.insert(key, plemege_to_value(thing, file_path, source, recc - 1)?); new_dict.insert(key, plemege_to_value(thing, file_path, source_elements, source_expr, recc - 1)?);
} }
Ok(Value::Dict(new_dict)) Ok(SharedValue::Dict(new_dict))
}, },
Plemege::Element(el) => { Plemege::Element(argc, el) => {
if argc == 0 && el.iter().all(|se| matches!(se, SubElement::Static(_))) {
/* The only optimization we ever make (we could do more, but sorry, my life is finite) */ /* 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(_))) { Ok(SharedValue::Str(el.into_iter()
return Ok(Value::Str(el.sub_elements.into_iter()
.fold(String::new(), |mut acc, p| { .fold(String::new(), |mut acc, p| {
let SubElement::Static(s) = p else { panic!() }; let SubElement::Static(s) = p else { panic!() };
acc.push_str(&s); acc acc.push_str(&s); acc
}))) })))
} } else if argc == 0 {
if el.argc == 0 { Ok(SharedValue::RuntimeConst(Box::new(
Ok(Value::RuntimeStr(Box::new( move |re: &RuntimeEnv, counter: u32| -> Result<Value, String> {
move |re: &RuntimeEnv, mtgott: &MTGOTT, recc: u32| -> Result<String, String> { re.check_recc(counter)?;
re.execute_element_block(&el.sub_elements, mtgott, &[], recc) /* No need to save element into source vector. We move it here */
re.execute_element_block(&el, &mut Vec::new(), counter).map(|s| Value::Str(Rc::new(s)))
}))) })))
} else { } else {
let argc = el.argc; let body_element_id = source_elements.len();
source.push(el); source_elements.push(el);
construct_element_closure(Vec::new(), argc, source.len() - 1, recc).map(Value::Fn) Ok(SharedValue::Fn(Box::new(
} move |re, x, counter| -> Result<Value, String> {
re.check_recc(counter)?;
re.construct_result_of_element_lambda(vec![x], argc, body_element_id, counter - 1)
}
)))
}
}
Plemege::Expression(expr) => match expr {
Expression::Int(n) => Ok(SharedValue::Int(n)),
Expression::Str(s) => Ok(SharedValue::Str(s)),
Expression::Lambda(NewLambdaExpression{local_var_array, expr_id}) => {
assert_eq!(local_var_array, vec![usize::MAX]);
Ok(SharedValue::Fn(Box::new(
move |re, arg, counter| -> Result<Value, String> {
re.check_recc(counter)?;
re.execute_expression(
&re.mtgott.source_expressions[expr_id],
&vec![arg], counter - 1)
}))
)
},
_ => Ok(SharedValue::RuntimeConst(Box::new(
move |re, counter| -> Result<Value, String> {
re.check_recc(counter)?;
/* No need to save expression into source vector. We move it here */
re.execute_expression(&expr, &Vec::new(), counter - 1)
})))
} }
} }
} }

View File

@ -6,19 +6,19 @@ use std::error::Error;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct NewLambdaExpression { pub struct NewLambdaExpression {
local_var_array: Vec<usize>, pub local_var_array: Vec<usize>,
/* We store all expression trees in giant vector in MTGOTT object. /* We store all expression trees in giant vector in MTGOTT object.
* We fill this global (to all the files) vector at compile time, and it can be used * We fill this global (to all the files) vector at compile time, and it can be used
* to access expressions (parsed source code) from any dynamically created lambda */ * to access expressions (parsed source code) from any dynamically created lambda */
expr_id: usize, pub expr_id: usize,
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum Expression { pub enum Expression {
Root, Root,
Local(usize), Local(usize),
Get(Box<Expression>, Box<Expression>),
Attribute(Box<Expression>, String), Attribute(Box<Expression>, String),
Get(Box<Expression>, Box<Expression>),
Call(Box<Expression>, Box<Expression>), Call(Box<Expression>, Box<Expression>),
Lambda(NewLambdaExpression), Lambda(NewLambdaExpression),
Int(u64), Int(u64),
@ -37,7 +37,7 @@ pub struct ForSubElement {
pub iterable: Expression, pub iterable: Expression,
pub core: Element, pub core: Element,
// Either "\n", " " or "" // Either "\n", " " or ""
pub join: String, pub separator: String,
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -222,7 +222,7 @@ impl<'a> Parser<'a> {
} }
fn parse_pack_plus_ending( fn parse_pack_plus_ending(
&mut self, top: bool, source_expr: &mut Vec<Expression>, recc: u32 &mut self, top: bool, recc: u32
) -> Result<Plemege, FileParsingError> { ) -> Result<Plemege, FileParsingError> {
self.check_recursion_limit(recc)?; self.check_recursion_limit(recc)?;
let mut res: HashMap<String, Plemege> = HashMap::new(); let mut res: HashMap<String, Plemege> = HashMap::new();
@ -261,7 +261,7 @@ impl<'a> Parser<'a> {
if self.is_ahead("$}"){ if self.is_ahead("$}"){
self.p += 2; self.p += 2;
res.insert(String::from(child_name), self.parse_pack_plus_ending(false, source_expr, recc - 1)?); res.insert(String::from(child_name), self.parse_pack_plus_ending(false, recc - 1)?);
} else if self.is_char_ahead('=') { } else if self.is_char_ahead('=') {
self.p += 1; self.p += 1;
self.skip_whitespace(); self.skip_whitespace();
@ -324,7 +324,7 @@ impl<'a> Parser<'a> {
arg_names.push(arg_name); arg_names.push(arg_name);
} }
let (child_el, end_cmd): (Element, ReasonOfElementEnd) = self.parse_element_plus_ending_tag( let (child_el, end_cmd): (Element, ReasonOfElementEnd) = self.parse_element_plus_ending_tag(
&arg_names, source_expr, recc - 1)?; &arg_names, recc - 1)?;
if !matches!(end_cmd.cmd, BlockEndingTag::END_ELEMENT) { if !matches!(end_cmd.cmd, BlockEndingTag::END_ELEMENT) {
return Err(FileParsingError::new(incorrect_block_ending_tag_expected_end_element, end_cmd.p1, self.p)) return Err(FileParsingError::new(incorrect_block_ending_tag_expected_end_element, end_cmd.p1, self.p))
} }
@ -446,7 +446,7 @@ impl<'a> Parser<'a> {
/* If BlockEndingCmdTag::ELSE_IF is returned, the ending tag won't be read completely, /* If BlockEndingCmdTag::ELSE_IF is returned, the ending tag won't be read completely,
* But in other case it would be read to the end */ * But in other case it would be read to the end */
fn parse_element_plus_ending_tag ( fn parse_element_plus_ending_tag (
&mut self, arg_names: &Vec<&str>, source_expr: &mut Vec<Expression>, recc: u32 &mut self, arg_names: &Vec<&str>, recc: u32
) -> Result<(Element, ReasonOfElementEnd), FileParsingError> { ) -> Result<(Element, ReasonOfElementEnd), FileParsingError> {
self.check_recursion_limit(recc)?; self.check_recursion_limit(recc)?;
let mut res: Vec<SubElement> = Vec::new(); let mut res: Vec<SubElement> = Vec::new();
@ -547,9 +547,9 @@ impl<'a> Parser<'a> {
} }
"endif" => return just_one_thing(self, BlockEndingTag::ENDIF, res), "endif" => return just_one_thing(self, BlockEndingTag::ENDIF, res),
"endloop" => return just_one_thing(self, BlockEndingTag::ENDLOOP, res), "endloop" => return just_one_thing(self, BlockEndingTag::ENDLOOP, res),
"for" => res.push(self.parse_let(arg_names, source_expr, recc - 1)?), "for" => res.push(self.parse_let(arg_names, recc - 1)?),
"if" => res.push(self.parse_if(arg_names, source_expr, recc - 1)?), "if" => res.push(self.parse_if(arg_names, recc - 1)?),
"let" => res.push(self.parse_let(arg_names, source_expr, recc - 1)?), "let" => res.push(self.parse_let(arg_names, recc - 1)?),
_ => return Err(FileParsingError::new(illegal_command_name, pb, self.p)), _ => return Err(FileParsingError::new(illegal_command_name, pb, self.p)),
} }
tp1 = self.p; tp1 = self.p;
@ -574,14 +574,14 @@ impl<'a> Parser<'a> {
/* /*
* It parses expr %} block {% else if expr %} block {% else %} block {%} */ * It parses expr %} block {% else if expr %} block {% else %} block {%} */
fn parse_if( fn parse_if(
&mut self, arg_names: &Vec<&str>, source_expr: &mut Vec<Expression>, recc: u32 &mut self, arg_names: &Vec<&str>, recc: u32
) -> Result<SubElement, FileParsingError> { ) -> Result<SubElement, FileParsingError> {
self.check_recursion_limit(recc)?; self.check_recursion_limit(recc)?;
let mut conditions: Vec<Expression> = Vec::new(); let mut conditions: Vec<Expression> = Vec::new();
let mut blocks: Vec<Element> = Vec::new(); let mut blocks: Vec<Element> = Vec::new();
loop { loop {
let expr = self.parse_expression_at_cmd_tag_end(arg_names, recc - 1)?; let expr = self.parse_expression_at_cmd_tag_end(arg_names, recc - 1)?;
let (inner_block, ending_tag) = self.parse_element_plus_ending_tag(arg_names, source_expr, recc - 1)?; let (inner_block, ending_tag) = self.parse_element_plus_ending_tag(arg_names, recc - 1)?;
conditions.push(expr); conditions.push(expr);
match ending_tag.cmd { match ending_tag.cmd {
BlockEndingTag::ELSE | BlockEndingTag::NORMAL | BlockEndingTag::ENDIF | BlockEndingTag::ELSE | BlockEndingTag::NORMAL | BlockEndingTag::ENDIF |
@ -590,7 +590,7 @@ impl<'a> Parser<'a> {
incorrect_block_ending_tag_expected_normal_or_endif_or_else_or_else_if, ending_tag.p1, self.p)), incorrect_block_ending_tag_expected_normal_or_endif_or_else_or_else_if, ending_tag.p1, self.p)),
} }
if matches!(ending_tag.cmd, BlockEndingTag::ELSE) { if matches!(ending_tag.cmd, BlockEndingTag::ELSE) {
let (else_block, the_end) = self.parse_element_plus_ending_tag(arg_names, source_expr, recc - 1)?; let (else_block, the_end) = self.parse_element_plus_ending_tag(arg_names, recc - 1)?;
if !matches!(the_end.cmd, BlockEndingTag::NORMAL | BlockEndingTag::ENDIF){ if !matches!(the_end.cmd, BlockEndingTag::NORMAL | BlockEndingTag::ENDIF){
return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal_or_endif, the_end.p1, self.p)); return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal_or_endif, the_end.p1, self.p));
} }
@ -603,7 +603,7 @@ impl<'a> Parser<'a> {
Ok(SubElement::If(IfSubElement{branches: blocks, conditions})) Ok(SubElement::If(IfSubElement{branches: blocks, conditions}))
} }
fn parse_let(&mut self, arg_names: &Vec<&str>, source_expr: &mut Vec<Expression>, recc: u32) -> Result<SubElement, FileParsingError> { fn parse_let(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<SubElement, FileParsingError> {
self.check_recursion_limit(recc)?; self.check_recursion_limit(recc)?;
self.skip_whitespace(); self.skip_whitespace();
let p1 = self.p; let p1 = self.p;
@ -624,7 +624,7 @@ impl<'a> Parser<'a> {
let expr = self.parse_expression_at_cmd_tag_end(arg_names, recc - 1)?; let expr = self.parse_expression_at_cmd_tag_end(arg_names, recc - 1)?;
let mut arg_names_extended = arg_names.clone(); let mut arg_names_extended = arg_names.clone();
arg_names_extended.push(new_variable_name); arg_names_extended.push(new_variable_name);
let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended, source_expr, recc - 1)?; let (inner_block, ending) = self.parse_element_plus_ending_tag(&arg_names_extended, recc - 1)?;
if !matches!(ending.cmd, BlockEndingTag::NORMAL) { if !matches!(ending.cmd, BlockEndingTag::NORMAL) {
return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal, ending.p1, self.p)); return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal, ending.p1, self.p));
} }
@ -645,7 +645,7 @@ impl<'a> Parser<'a> {
Ok((name, t1)) Ok((name, t1))
} }
fn parse_for(&mut self, arg_names: &Vec<&str>, source_expr: &mut Vec<Expression>, recc: u32) -> Result<SubElement, FileParsingError> { fn parse_for(&mut self, arg_names: &Vec<&str>, recc: u32) -> Result<SubElement, FileParsingError> {
self.check_recursion_limit(recc)?; self.check_recursion_limit(recc)?;
let mut arg_names_extended = arg_names.clone(); let mut arg_names_extended = arg_names.clone();
@ -672,7 +672,7 @@ impl<'a> Parser<'a> {
let expr = self.parse_expression_at_cmd_tag_end(arg_names, recc - 1)?; let expr = self.parse_expression_at_cmd_tag_end(arg_names, recc - 1)?;
let (inner_block, ending) = self.parse_element_plus_ending_tag( let (inner_block, ending) = self.parse_element_plus_ending_tag(
&arg_names_extended, source_expr, recc - 1)?; &arg_names_extended, recc - 1)?;
let separator: String = String::from(match ending.cmd { let separator: String = String::from(match ending.cmd {
BlockEndingTag::NOGAP => "", BlockEndingTag::NOGAP => "",
BlockEndingTag::GAP => " ", BlockEndingTag::GAP => " ",
@ -680,7 +680,7 @@ impl<'a> Parser<'a> {
_ => return Err(FileParsingError::new( _ => return Err(FileParsingError::new(
incorrect_block_ending_tag_expected_normal_or_lf_gap_nogap_or_forloop, ending.p1, self.p)), incorrect_block_ending_tag_expected_normal_or_lf_gap_nogap_or_forloop, ending.p1, self.p)),
}); });
Ok(SubElement::For(ForSubElement{iterable: expr, core: inner_block, join: separator})) Ok(SubElement::For(ForSubElement{iterable: expr, core: inner_block, separator}))
} }
/* Checks for ]} }} and %}. May actually return NoneOfThose */ /* Checks for ]} }} and %}. May actually return NoneOfThose */
@ -726,7 +726,7 @@ impl<'a> Parser<'a> {
/* Suppose we use some toplevel name in expression. There is a chance it is a local constant. /* Suppose we use some toplevel name in expression. There is a chance it is a local constant.
* If so, we search for its index I in arg_names and if name_in_expr belongs to arg_names * If so, we search for its index I in arg_names and if name_in_expr belongs to arg_names
* we return the index of number I in used_local_consts (pushing I to the right if needed) * we return the index of number I in used_local_consts (pushing I to the right if needed).
* If name_in_expr is not a local constant, we return None */ * If name_in_expr is not a local constant, we return None */
fn expression_parsing_include_local_const( fn expression_parsing_include_local_const(
arg_names: &Vec<&str>, used_local_consts: &mut Vec<usize>, name_in_expr: &str arg_names: &Vec<&str>, used_local_consts: &mut Vec<usize>, name_in_expr: &str
@ -784,19 +784,26 @@ impl<'a> Parser<'a> {
return Err(FileParsingError::new(illegal_lambda_argument_name, p1, p2)) return Err(FileParsingError::new(illegal_lambda_argument_name, p1, p2))
} }
self.p += 1; self.p += 1;
let argument_iig = arg_names.len();
let mut arg_names_extended = arg_names.clone(); let mut arg_names_extended = arg_names.clone();
arg_names_extended.push(toplevel_name); arg_names_extended.push(toplevel_name);
let mut used_by_lambda: Vec<usize> = Vec::new(); let mut used_by_lambda: Vec<usize> = Vec::new();
let lambda_body = self.parse_expression( let lambda_body = self.parse_expression(
&arg_names_extended, &mut used_by_lambda, recc - 1)?; &arg_names_extended, &mut used_by_lambda, recc - 1)?;
for iig in &used_by_lambda { for iig in &used_by_lambda {
if !used_local_consts.contains(iig) { used_local_consts.push(*iig); } if !used_local_consts.contains(iig) && *iig != argument_iig {
used_local_consts.push(*iig);
}
} }
let expr_id = self.source_expr.len(); let expr_id = self.source_expr.len();
self.source_expr.push(lambda_body); self.source_expr.push(lambda_body);
Ok(Some(Expression::Lambda(NewLambdaExpression{ Ok(Some(Expression::Lambda(NewLambdaExpression{
local_var_array: used_by_lambda.iter().map( |iig| -> usize { local_var_array: used_by_lambda.iter().map( |iig| -> usize {
if *iig == argument_iig {
usize::MAX
} else {
used_local_consts.iter().position(|iig_of_this| iig_of_this == iig).unwrap() used_local_consts.iter().position(|iig_of_this| iig_of_this == iig).unwrap()
}
}).collect(), expr_id}))) }).collect(), expr_id})))
} else if toplevel_name == "this" { } else if toplevel_name == "this" {
Ok(Some(self.fn_parse_expression_l2_trail( Ok(Some(self.fn_parse_expression_l2_trail(
@ -900,14 +907,14 @@ pub fn parse_one_file_packed(
text: &str, file_path: &str, source_expr: &mut Vec<Expression>, text: &str, file_path: &str, source_expr: &mut Vec<Expression>,
) -> Result<Plemege, FileParsingError> { ) -> Result<Plemege, FileParsingError> {
let mut parser: Parser = Parser{text, p: 0, this_meaning: make_expression_for_this(file_path), source_expr}; let mut parser: Parser = Parser{text, p: 0, this_meaning: make_expression_for_this(file_path), source_expr};
parser.parse_pack_plus_ending(true, source_expr, 150) parser.parse_pack_plus_ending(true, 150)
} }
pub fn parse_one_file_simplified( pub fn parse_one_file_simplified(
text: &str, file_path: &str, source_expr: &mut Vec<Expression>, text: &str, file_path: &str, source_expr: &mut Vec<Expression>,
) -> Result<Plemege, FileParsingError> { ) -> Result<Plemege, FileParsingError> {
let mut parser: Parser = Parser{text, p: 0, this_meaning: make_expression_for_this(file_path), source_expr}; let mut parser: Parser = Parser{text, p: 0, this_meaning: make_expression_for_this(file_path), source_expr};
let (el, tt): (Element, ReasonOfElementEnd) = parser.parse_element_plus_ending_tag(&vec!["$"], source_expr, 150)?; let (el, tt): (Element, ReasonOfElementEnd) = parser.parse_element_plus_ending_tag(&vec!["$"], 150)?;
match tt.cmd { match tt.cmd {
BlockEndingTag::EOF => Ok(Plemege::Element(1, el)), BlockEndingTag::EOF => Ok(Plemege::Element(1, el)),
BlockEndingTag::END_ELEMENT => Err(FileParsingError::new(unmatched_element_ending_tag, tt.p1, parser.p)), BlockEndingTag::END_ELEMENT => Err(FileParsingError::new(unmatched_element_ending_tag, tt.p1, parser.p)),

View File

@ -1,10 +1,9 @@
use std::collections::HashMap;
use std::fmt;
use std::fmt::Debug;
use std::error::Error;
use std::cell::RefCell;
use parser::{Element, Expression};
use std::rc::Rc; use std::rc::Rc;
use std::collections::HashMap;
use std::error::Error;
use std::fmt::{Debug, Formatter};
use std::cell::RefCell;
use parser::{Element, Expression, NewLambdaExpression};
pub struct DebugStateGuard<'a> ( pub struct DebugStateGuard<'a> (
&'a RefCell<Vec<String>> &'a RefCell<Vec<String>>
@ -16,129 +15,384 @@ impl<'a> Drop for DebugStateGuard<'a> {
} }
} }
pub struct RuntimeEnv( pub enum SharedValue {
RefCell<Vec<String>> Int(u64),
); Str(String),
Arr(Vec<SharedValue>),
impl<'a> RuntimeEnv { Dict(HashMap<String, SharedValue>),
pub fn register(&'a self, msg: String) -> DebugStateGuard<'a> { Fn(Box<dyn for<'m> Fn(& RuntimeEnv<'m>, Value<'m>, u32) -> Result<Value<'m>, String> + Send + Sync>),
self.0.borrow_mut().push(msg); RuntimeConst(Box<dyn for<'m> Fn(& RuntimeEnv<'m>, u32) -> Result<Value<'m>, String> + Send + Sync>),
DebugStateGuard(&self.0)
}
}
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
}
} }
pub struct MTGOTT { pub struct MTGOTT {
pub root: Value<'static>, pub root: SharedValue,
pub source_elements: Vec<Element>, pub source_elements: Vec<Element>,
pub source_expressions: Vec<Expression> pub source_expressions: Vec<Expression>
} }
pub enum Value<'t> { pub struct RuntimeEnv<'m> {
Str(Rc<String>), pub stack: RefCell<Vec<String>>,
Int(u64), pub mtgott: &'m MTGOTT,
Arr(Rc<Vec<Value<'t>>>),
Dict(Rc<HashMap<String, Value<'t>>>),
Fn(Rc<dyn Fn(&'t RuntimeEnv, &'t MTGOTT, Value<'t>, u32) -> Result<Value<'t>, String>>),
Ref(&'t Value<'t>),
/* Some may call it a crutch (and I am definitely agree with them): my current mtgott
compiler does not perform precomputational optimization for elements with no arguments.
They cannot be turned into Fn, as it would be a type error, so they get compiled into
Value::RuntimeConst. At runtime, this compilation artifact is the same thing as
Value\{::RuntimeConst}.
Expression evaluator automatically converts them as soon as possible. */
RuntimeConst(Box<dyn Fn(&'t RuntimeEnv, &'t MTGOTT, u32) -> Result<Value<'t>, String>>),
} }
impl<'t> Value<'t> { impl<'a> RuntimeEnv<'a> {
pub fn stringify_type(&self) -> String { pub fn new(mtgott: &'a MTGOTT) -> Self {
RuntimeEnv{stack: RefCell::new(Vec::new()), mtgott}
}
pub fn register(&'a self, mut msg: String) -> DebugStateGuard<'a> {
msg.push('\n');
self.stack.borrow_mut().push(msg);
DebugStateGuard(&self.stack)
}
}
impl<'m> RuntimeEnv<'m> {
pub fn make_error(&self, err: &str) -> String {
let total_len: usize = self.stack.borrow().iter().map(|s| { s.len() }).sum();
let mut res = self.stack.borrow().iter().fold(String::with_capacity(total_len), |mut acc, p| { acc.push_str(p.as_str()); acc });
res + err
}
}
#[derive(Clone)]
pub enum Value<'m> {
Int(u64),
Str(Rc<String>),
Arr(Rc<Vec<Value<'m>>>),
Dict(Rc<HashMap<String, Value<'m>>>),
Fn(Rc<dyn Fn(& RuntimeEnv<'m>, Value<'m>, u32) -> Result<Value<'m>, String> + 'm>),
Ref(&'m SharedValue),
}
impl Debug for SharedValue {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
SharedValue::Int(n) => write!(f, "Int({n})"),
SharedValue::Str(s) => write!(f, "Str({})", s),
SharedValue::Arr(arr) => write!(f, "Arr({:?})", arr),
SharedValue::Dict(dict) => write!(f, "Dict({:?})", dict),
SharedValue::Fn(_) => write!(f, "Fn(<function>)"),
SharedValue::RuntimeConst(_) => write!(f, "RuntimeConst(<function>)")
}
}
}
impl SharedValue {
pub fn type_name(&self) -> String {
match self {
SharedValue::Int(_) => "Integer".into(),
SharedValue::Str(_) => "String".into(),
SharedValue::Arr(_) => "Array".into(),
SharedValue::Dict(_) => "Dictionary".into(),
SharedValue::Fn(_) => "Function".into(),
SharedValue::RuntimeConst(_) => "Runtime Const".into(),
}
}
}
impl<'m> Debug for Value<'m> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Value::Int(n) => write!(f, "Int({n})"),
Value::Str(s) => write!(f, "Str({})", s.as_ref()),
Value::Arr(arr) => write!(f, "Arr({:?})", arr.as_ref()),
Value::Dict(dict) => write!(f, "Dict({:?})", dict.as_ref()),
Value::Fn(_) => write!(f, "Fn(<function>)"),
Value::Ref(shv) => write!(f, "Ref({shv:?})"),
}
}
}
impl SharedValue {
pub fn is_empty_dictionary(&self) -> bool {
if let SharedValue::Dict(d) = self { d.is_empty() } else { false }
}
}
impl<'m> Value<'m> {
pub fn if_int_get(&self) -> Option<u64> {
match self {
Value::Int(n) => Some(*n),
Value::Ref(SharedValue::Int(n)) => Some(*n),
_ => None,
}
}
pub fn if_str_get_ref(&self) -> Option<& String> {
match self {
Value::Str(s) => Some(s.as_ref()),
Value::Ref(SharedValue::Str(s)) => Some(s),
_ => None,
}
}
pub fn is_str(&self) -> bool {
match self {
Value::Str(_) => true, Value::Ref(SharedValue::Str(_)) => true,
_ => false,
}
}
pub fn get_str(self) -> String {
match self {
Value::Str(s) => Rc::try_unwrap(s).unwrap_or_else(|s| s.as_ref().clone()),
Value::Ref(SharedValue::Str(s)) => s.clone(),
_ => panic!("Not a string"),
}
}
pub fn is_arr(&self) -> bool {
match self {Value::Arr(_) => true, Value::Ref(SharedValue::Arr(_)) => true, _ => false}
}
pub fn is_dict(&self) -> bool {
match self {Value::Dict(_) => true, Value::Ref(SharedValue::Dict(_)) => true, _ => false}
}
pub fn access_arr(&self, ind: usize) -> Option<Value<'m>> {
match self {
Value::Arr(array) => array.get(ind).map(|r| r.clone()),
Value::Ref(SharedValue::Arr(array)) => array.get(ind)
.map(|rshv| Value::Ref(rshv)),
_ => panic!("Not an array")
}
}
pub fn arr_len(&self) -> usize {
match self {
Value::Arr(array) => array.len(),
Value::Ref(SharedValue::Arr(array)) => array.len(),
_ => panic!("Not an array")
}
}
pub fn access_dict(&self, key: &String) -> Option<Value<'m>> {
match self {
Value::Dict(map) => map.get(key).map(|r| r.clone()),
Value::Ref(SharedValue::Dict(map)) => map.get(key)
.map(|rshv| Value::Ref(rshv)),
_ => panic!("Not a dictionary")
}
}
pub fn dict_is_empty(&self) -> bool {
match self {
Value::Dict(dict) => dict.as_ref().is_empty(),
Value::Ref(SharedValue::Dict(dict)) => dict.is_empty(),
_ => panic!("Not a dictionary")
}
}
pub fn type_name(&self) -> String {
match self {
Value::Int(_) => "Integer".into(),
Value::Str(_) => "String".into(), Value::Str(_) => "String".into(),
Value::Int(u64) => "Int".into(),
Value::Arr(_) => "Array".into(), Value::Arr(_) => "Array".into(),
Value::Dict(_) => "Dictionary".into(), Value::Dict(_) => "Dictionary".into(),
Value::Fn(_) => "Function".into(), Value::Fn(_) => "Function".into(),
Value::Ref(i) => format!("Reference to {}", i.stringify_type()), Value::Ref(r) => format!("Reference to {}", r.type_name())
Value::RuntimeConst(_) => "Compilation Artifact".into(), }
}
pub fn to_bool(&self) -> Result<bool, String> {
if let Some(n) = self.if_int_get() {
Ok(n != 0)
} else if let Some(s) = self.if_str_get_ref() {
Ok(!s.is_empty())
} else if self.is_arr() {
Ok(self.arr_len() > 0)
} else if self.is_dict() {
Ok(self.dict_is_empty())
} else {
Err("Can't convert Function to bool".into())
} }
} }
} }
impl Default for Value { impl<'a, 'm> Value<'m> {
fn default() -> Self { pub fn if_fn_get(&'a self) -> Option<&'a (dyn Fn(&RuntimeEnv<'m>, Value<'m>, u32) -> Result<Value<'m>, String> + 'm) > {
Value::Int(0)
}
}
impl Debug for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Value::Str(s) => write!(f, "Str({})", s), Value::Fn(func) => Some(func.as_ref()),
Value::Int(i) => write!(f, "Int({})", i), Value::Ref(SharedValue::Fn(func)) => Some(func),
Value::Arr(a) => write!(f, "Arr({:?})", a), _ => None
Value::Dict(d) => {
let dict_debug: Vec<String> = d.iter()
.map(|(k, v)| format!("{}: {:?}", k, v))
.collect();
write!(f, "Dict({{{}}})", dict_debug.join(", "))
}
Value::Fn(_) => write!(f, "Fn(<function>)"),
Value::Ref(&b) => write!(f, "Ref({b:?})"),
Value::RuntimeConst(_) => write!(f, "C-comp-pilat_onArrrrr%%#^#}}&_)"),
} }
} }
} }
impl MTGOTT { impl<'m> RuntimeEnv<'m> {
fn get_sub_value<'a>(&'a self, name: &str) -> Result<&'a Value, String> { pub fn call_fn(&'m self, f: Value<'m>, x: Value<'m>, recc: u32) -> Result<Value<'m>, String> {
let mut cur: &Value = &self.root; match f {
let parts: Vec<String> = name.split(".").map(String::from).collect(); Value::Fn(func) => func.as_ref()(self, x, recc),
for i in 0usize..parts.len() { Value::Ref(SharedValue::Fn(func)) => func.as_ref()(self, x, recc),
_ => panic!(),
}
}
}
impl<'m> RuntimeEnv<'m> {
/* Might change it into a macros one day */
pub fn check_recc(&self, recc: u32) -> Result<(), String> {
if recc == 0 { Err("Recursion limit exceeded".into()) } else { Ok(()) }
}
pub fn clean_rrc(& self, x: Value<'m>, recc: u32) -> Result<Value<'m>, String> {
self.check_recc(recc)?;
match x {
Value::Ref(SharedValue::RuntimeConst(zero_arg_fn)) =>
Ok(zero_arg_fn(self, recc - 1)?),
_ => Ok(x)
}
}
pub fn execute_expression(&self, expr: &Expression, local: &[Value<'m>], recc: u32) -> Result<Value<'m>, String> {
self.check_recc(recc)?;
match expr {
Expression::Root => Ok(Value::Ref(&self.mtgott.root)),
Expression::Local(i) => Ok(local[*i].clone()),
Expression::Int(n) => Ok(Value::Int(*n)),
/* Yes, I could have avoided cloning. But who cares anyway, consider using
* Element syntax for long text */
Expression::Str(s) => Ok(Value::Str(Rc::new(s.clone()))),
Expression::Arr(arr) => Ok(Value::Arr(Rc::new(arr.iter()
.map(|e| -> Result<Value, String> {
self.execute_expression(e, local, recc - 1)
}).collect::<Result<Vec<Value>, String>>()?))),
Expression::Attribute(e1, key) => {
let r: Value = self.execute_expression(e1, local, recc - 1)?;
if !r.is_dict() { return Err(format!("Taking attribute of {}", r.type_name())); }
match r.access_dict(key) {
Some(v) => self.clean_rrc(v, recc - 1),
None => Err(format!("Dictionary has no attribute {}", key))
}
},
Expression::Get(e1, e2) => {
let r1: Value = self.execute_expression(e1, local, recc - 1)?;
let r2: Value = self.execute_expression(e2, local, recc - 1)?;
if r1.is_arr(){
match r2.if_int_get() {
Some(ind) => match r1.access_arr(ind as usize) {
Some(v) => self.clean_rrc(v, recc - 1),
None => Err(format!(
"Out of bounds: accessing array of size {} with index {}",
r1.arr_len(), ind))
},
None => Err(format!("Trying to access array using {}", r2.type_name()))
}
} else if r1.is_dict() {
match r2.if_str_get_ref() {
Some(key) => match r1.access_dict(key) {
Some(v) => self.clean_rrc(v, recc - 1),
None => Err(format!("Dictionary doesn't have key {}", key))
},
None => Err(format!("Trying to access dictionary using {}", r2.type_name()))
}
} else {
Err(format!("Operator [] does not work on {}", r1.type_name()))
}
}
Expression::Call(e1, e2) => {
let r1: Value = self.execute_expression(e1, local, recc - 1)?;
let r2: Value = self.execute_expression(e2, local, recc - 1)?;
match r1.if_fn_get() {
/* All functions must return Value-RRC */
Some(f) => f(self, r2, recc - 1),
None => Err(format!("Can't call {}", r1.type_name()))
}
}
Expression::Lambda(NewLambdaExpression{ local_var_array: captured, expr_id: body_expr_id}) => {
// let mut where_arg_goes = usize::MAX;
let local_var_array: Vec<Value> = captured.iter().map(|oid| {
if *oid == usize::MAX { Value::Int(0) } else { local[*oid].clone() }
}).collect();
let where_arg_goes = captured.iter().position(|oid| *oid == usize::MAX).unwrap();
let body_expr_id: usize = *body_expr_id;
Ok(Value::Fn(Rc::new(move |re, x: Value, counter| -> Result<Value, String> {
let mut local_var_array: Vec<Value> = local_var_array.clone();
local_var_array[where_arg_goes] = x;
re.execute_expression(&re.mtgott.source_expressions[body_expr_id], &local_var_array, counter - 1)
})))
}
}
}
}
/* If we have to add two paths and one is a prefix of another, like here:
* aaa/bbb > Dict{...}
* aaa > String("...")
* we should add objects with short path first, because they would otherwise override
* nested dictionaries containing objects with long paths */
pub fn add_path_to_root(root: &mut SharedValue, path: &str, splitter: char, x: SharedValue) -> Result<(), Box<dyn Error>>{
let path: Vec<String> = path.split(splitter).map(String::from).collect();
let mut cur: &mut SharedValue = root;
assert!(path.len() > 0);
for i in 0..path.len() {
match cur { match cur {
Value::Dict(hashmap) => { SharedValue::Dict(dict) => {
match hashmap.get(&parts[i]) { cur = dict.entry(path[i].clone()).or_insert(SharedValue::Dict(HashMap::new()));
Some(nxt ) => { cur = nxt; } }
None => return Err(format!("No such root element: {}", parts[..=i].join("/"))) _ => return Err(format!("Overlapping root paths: {}", path[0..i].join(&splitter.to_string())).into())
} }
} }
_ => return Err(format!("Not a dictionary: {}", parts[..i].join("/"))) if !cur.is_empty_dictionary() {
return Err(format!("Overlapping root paths: {}", path.join(&splitter.to_string())).into())
}
*cur = x;
Ok(())
}
pub fn add_path_with_slashes_to_root(root: &mut SharedValue, path: &str, x: SharedValue) -> Result<(), Box<dyn Error>> {
add_path_to_root(root, path, '/', x)
}
pub fn add_path_with_dots_to_root(root: &mut SharedValue, path: &str, x: SharedValue) -> Result<(), Box<dyn Error>> {
add_path_to_root(root, path, '.', x)
}
pub fn access_root_by_path<'m>(root: &'m SharedValue, path: &str, splitter: char) -> Result<&'m SharedValue, Box<dyn Error>> {
let path: Vec<String> = path.split(splitter).map(String::from).collect();
let mut cur = root;
for i in 0..path.len() {
cur = match cur {
SharedValue::Dict(d) => match d.get(&path[i]) {
Some(nxt) => nxt,
None => return Err(
format!("No such path in root: {}", path[0..i+1].join(&splitter.to_string())).into())
}
_ => return Err(
format!("Can't access {}, {} is not a dictionary",
path[0..i+1].join(&splitter.to_string()), path[0..i].join(&splitter.to_string())).into())
} }
} }
Ok(cur) Ok(cur)
} }
pub fn render(&self, guest_root: Value, name: &str, recursion_limit: u32) -> Result<String, Box<dyn Error>> { pub fn access_root_by_path_with_slashes<'m>(root: &'m SharedValue, path: &str) -> Result<&'m SharedValue, Box<dyn Error>> {
let main = self.get_sub_value(name)?; access_root_by_path(root, path, '/')
let res = match main {
Value::Fn(main_func) => {
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())
};
main_func(&d_stack, self, guest_root, recursion_limit)?
} }
Value::Str(me) => Value::Ref(main),
Value::RuntimeConst(zero_arg_fnc) => { pub fn access_root_by_path_with_dots<'m>(root: &'m SharedValue, path: &str) -> Result<&'m SharedValue, Box<dyn Error>> {
let mut d_stack = RuntimeEnv(RefCell::new(Vec::new())); access_root_by_path(root, path, '.')
zero_arg_fnc(&mut d_stack, self, recursion_limit)? }
},
_ => return Err(format!("Called {name} template that is not a function / string, but {}", main.stringify_type()).into())
}; impl MTGOTT {
match res { /* name is a path with dots (and obviously without extension) */
Value::Ref(Value::Str(s1)) => Ok(s1.as_ref().clone()), pub fn render<'t>(&'t self, guest_root: Value<'t>, name: &str, recursion_limit: u32) -> Result<String, Box<dyn Error>> {
Value::Str(answer) => if Rc::strong_count(&answer) == 1 { let re = RuntimeEnv::new(self);
Ok(Rc::try_unwrap(answer).unwrap()) let main: Value = re.clean_rrc(Value::Ref(access_root_by_path_with_dots(&self.root, name)?), recursion_limit)?;
if let Some(func_main) = main.if_fn_get() {
let result = func_main(&re, guest_root, recursion_limit)?;
if result.is_str(){
Ok(result.get_str())
} else { } else {
Ok(answer.as_ref().clone()) Err(format!("Function {name} returned {}", result.type_name()).into())
} }
_ => Err(format!("Template {name} returned {} instead of string", res.stringify_type()).into()) } else if main.is_str() {
Ok(main.get_str())
} else {
Err(format!("Path {name} leads to {}", main.type_name()).into())
} }
} }
} }