Знал ли ты, Марк, что в одном стакане Фрэнзи-Кофе 44 чайных ложки Гора?
This commit is contained in:
parent
a9023c6a8c
commit
9ce0e602b3
@ -2,11 +2,11 @@ extern crate mtgott;
|
||||
|
||||
use std::env;
|
||||
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::runtime::{Value, MTGOTT};
|
||||
use std::collections::HashMap;
|
||||
use mtgott::dirsearch::add_path_with_dots_to_root;
|
||||
use crate::mtgott::runtime::*;
|
||||
use std::rc::Rc;
|
||||
|
||||
fn usage() -> ! {
|
||||
@ -14,6 +14,20 @@ fn usage() -> ! {
|
||||
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() {
|
||||
let mut args = env::args();
|
||||
args.next().unwrap();
|
||||
@ -31,7 +45,7 @@ fn main() {
|
||||
None => usage()
|
||||
};
|
||||
for s in name.split('.') {
|
||||
if is_bad_name(s) {
|
||||
if is_special_name(s) {
|
||||
eprintln!("Bad name: {}", s);
|
||||
process::exit(1);
|
||||
}
|
||||
@ -41,11 +55,12 @@ fn main() {
|
||||
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 {
|
||||
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();
|
||||
print!("{res}")
|
||||
}
|
||||
|
||||
@ -3,13 +3,13 @@ use std::fs::{read_dir, metadata, canonicalize};
|
||||
use std::path::PathBuf;
|
||||
use std::error::Error;
|
||||
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::lambda_compilation::{plemege_to_value};
|
||||
use std::collections::HashMap;
|
||||
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>>,
|
||||
fs_path: PathBuf, virtual_path: String,
|
||||
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>> {
|
||||
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 {
|
||||
let path = format!("{cut_path}.mtgott{plain_ext}");
|
||||
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 text_bytes = fs::read(PathBuf::from(p).join(&path))?;
|
||||
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)
|
||||
}
|
||||
@ -133,22 +104,23 @@ pub fn get_all_templates_plus_builtins(
|
||||
sanitize: fn(&str) -> String,
|
||||
) -> Result<MTGOTT, Box<dyn Error>>{
|
||||
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
|
||||
* takes full ownership of some string and does not have to replace any characters*/
|
||||
|d_state: &RuntimeEnv, _: &MTGOTT, arg: Value, _: u32| -> Result<Value, String> {
|
||||
let s: &Value = if let Value::Ref(nr) = arg { nr } else { &arg };
|
||||
match s {
|
||||
Value::Str(s) => Ok(Value::Str(Rc::new(sanitize(s.as_ref())))),
|
||||
Value::Int(num) => Ok(Value::Str(Rc::new(num.to_string()))),
|
||||
_ => Ok(Value::Str(Rc::new(sanitize(&format!("{:?}", arg)))))
|
||||
move |d_state: &RuntimeEnv, arg: Value, _: u32| -> Result<Value, String> {
|
||||
if let Some(s) = arg.if_str_get_ref() {
|
||||
Ok(Value::Str(Rc::new(sanitize(s))))
|
||||
} else if let Some(n) = arg.if_int_get() {
|
||||
Ok(Value::Str(Rc::new(n.to_string())))
|
||||
} 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, "write_tag_start", Value::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, "magic_block_ending_tag", Value::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, "cmd_tag_start", SharedValue::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", SharedValue::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", SharedValue::Str("{@}".into()))?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
use super::runtime::*;
|
||||
use super::parser::*;
|
||||
use std::error::Error;
|
||||
use runtime::*;
|
||||
use parser::*;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
struct ElementPartsCollector {
|
||||
res: String, cur_col: usize,
|
||||
@ -33,25 +32,14 @@ impl ElementPartsCollector {
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'m> RuntimeEnv<'m> {
|
||||
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> {
|
||||
let n = if_sub_el.conditions.len();
|
||||
for i in 0..n {
|
||||
let expr_res = self.execute_expression(&if_sub_el.conditions[i], mtgott, arg_stack, recc - 1)?;
|
||||
if self.value_to_bool(expr_res.deref())? {
|
||||
let expr_res = self.execute_expression(&if_sub_el.conditions[i], local, recc - 1)?;
|
||||
if expr_res.to_bool()? {
|
||||
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(
|
||||
&self, el: &[SubElement], mtgott: &MTGOTT, arg_stack: &[& Value], recc: u32
|
||||
&self, el: &[SubElement], local: &mut Vec<Value<'m>>, 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};
|
||||
@ -72,63 +61,66 @@ impl RuntimeEnv {
|
||||
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, mtgott, arg_stack, recc - 1)? {
|
||||
SubElement::If(if_sub_el) => if let Some(branch_el) =
|
||||
self.get_needed_if_block(if_sub_el, local, recc - 1)? {
|
||||
res.add_single_padded_element_piece(
|
||||
&self.execute_element_block(
|
||||
&branch_el.sub_elements, mtgott, arg_stack, recc - 1)?)
|
||||
&self.execute_element_block(branch_el, local, 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(
|
||||
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}) => {
|
||||
},
|
||||
SubElement::For(ForSubElement{iterable, core, separator}) => {
|
||||
let _g = self.register("In {%for%}".into());
|
||||
let expr_val = self.execute_expression(iterable, mtgott, arg_stack, recc - 1)?;
|
||||
let an = arg_stack.len();
|
||||
let r = self.execute_expression(iterable, local, recc - 1)?;
|
||||
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, mtgott, arg_stack, recc - 1)?);
|
||||
} else { break }
|
||||
let k = local.len();
|
||||
local.push(Value::Int(0)); local.push(Value::Int(0));
|
||||
let collected: Vec<String> = match r {
|
||||
Value::Dict(dict) => dict.as_ref().iter()
|
||||
.map(|(key ,value)| -> Result<String, String> {
|
||||
local[k] = Value::Str(Rc::new(key.clone()));
|
||||
local[k + 1] = value.clone();
|
||||
self.execute_element_block(core, local, recc - 1)
|
||||
}).collect::<Result<Vec<String>, String>>()?,
|
||||
Value::Ref(SharedValue::Dict(dict)) => dict.iter()
|
||||
.map(|(key, sh_val)| -> Result<String, String> {
|
||||
local[k] = Value::Str(Rc::new(key.clone()));
|
||||
local[k + 1] = Value::Ref(sh_val);
|
||||
self.execute_element_block(core, local, recc - 1)
|
||||
}).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) => {
|
||||
let expr_val = self.execute_expression(expr, mtgott, 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());
|
||||
let r = self.execute_expression(expr, local, recc - 1)?;
|
||||
local.push(r);
|
||||
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> {
|
||||
LocalRef(&'l Value<'l>),
|
||||
Owned(Value<'t>),
|
||||
}
|
||||
|
||||
|
||||
impl<'l, 't> 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))
|
||||
/* We construct intermediate closures for elements at runtime
|
||||
* argc >= 1 */
|
||||
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> {
|
||||
if prev.len() == argc {
|
||||
self.execute_element_block(&self.mtgott.source_elements[el_source_pos], &mut prev, recc)
|
||||
.map(|s| Value::Str(Rc::new(s)))
|
||||
} 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)))
|
||||
Ok(Value::Fn(Rc::new(move |re, arg, counter| -> Result<Value<'m>, String> {
|
||||
re.check_recc(counter)?;
|
||||
let mut prev = prev.clone();
|
||||
prev.push(arg);
|
||||
re.construct_result_of_element_lambda(prev, argc, el_source_pos, counter - 1)
|
||||
} )))
|
||||
}
|
||||
}
|
||||
_ => 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.
|
||||
Due to how bad mtgott type system is, Some elements require me to save source tree into
|
||||
a storage*/
|
||||
Due to how bad mtgott type system is, some elements require me to save source tree into
|
||||
a storage */
|
||||
pub fn plemege_to_value(
|
||||
x: Plemege, file_path: &str,
|
||||
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()) }
|
||||
match x {
|
||||
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 {
|
||||
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) */
|
||||
if el.sub_elements.iter().all(|se| matches!(se, SubElement::Static(_))) {
|
||||
return Ok(Value::Str(el.sub_elements.into_iter()
|
||||
Ok(SharedValue::Str(el.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, mtgott: &MTGOTT, recc: u32| -> Result<String, String> {
|
||||
re.execute_element_block(&el.sub_elements, mtgott, &[], recc)
|
||||
} else if argc == 0 {
|
||||
Ok(SharedValue::RuntimeConst(Box::new(
|
||||
move |re: &RuntimeEnv, counter: u32| -> Result<Value, String> {
|
||||
re.check_recc(counter)?;
|
||||
/* 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 {
|
||||
let argc = el.argc;
|
||||
source.push(el);
|
||||
construct_element_closure(Vec::new(), argc, source.len() - 1, recc).map(Value::Fn)
|
||||
let body_element_id = source_elements.len();
|
||||
source_elements.push(el);
|
||||
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)
|
||||
})))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,19 +6,19 @@ use std::error::Error;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
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 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 */
|
||||
expr_id: usize,
|
||||
pub expr_id: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Expression {
|
||||
Root,
|
||||
Local(usize),
|
||||
Get(Box<Expression>, Box<Expression>),
|
||||
Attribute(Box<Expression>, String),
|
||||
Get(Box<Expression>, Box<Expression>),
|
||||
Call(Box<Expression>, Box<Expression>),
|
||||
Lambda(NewLambdaExpression),
|
||||
Int(u64),
|
||||
@ -37,7 +37,7 @@ pub struct ForSubElement {
|
||||
pub iterable: Expression,
|
||||
pub core: Element,
|
||||
// Either "\n", " " or ""
|
||||
pub join: String,
|
||||
pub separator: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@ -222,7 +222,7 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
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> {
|
||||
self.check_recursion_limit(recc)?;
|
||||
let mut res: HashMap<String, Plemege> = HashMap::new();
|
||||
@ -261,7 +261,7 @@ impl<'a> Parser<'a> {
|
||||
|
||||
if self.is_ahead("$}"){
|
||||
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('=') {
|
||||
self.p += 1;
|
||||
self.skip_whitespace();
|
||||
@ -324,7 +324,7 @@ impl<'a> Parser<'a> {
|
||||
arg_names.push(arg_name);
|
||||
}
|
||||
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) {
|
||||
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,
|
||||
* But in other case it would be read to the end */
|
||||
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> {
|
||||
self.check_recursion_limit(recc)?;
|
||||
let mut res: Vec<SubElement> = Vec::new();
|
||||
@ -547,9 +547,9 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
"endif" => return just_one_thing(self, BlockEndingTag::ENDIF, res),
|
||||
"endloop" => return just_one_thing(self, BlockEndingTag::ENDLOOP, res),
|
||||
"for" => res.push(self.parse_let(arg_names, source_expr, recc - 1)?),
|
||||
"if" => res.push(self.parse_if(arg_names, source_expr, recc - 1)?),
|
||||
"let" => 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, recc - 1)?),
|
||||
"let" => res.push(self.parse_let(arg_names, recc - 1)?),
|
||||
_ => return Err(FileParsingError::new(illegal_command_name, pb, self.p)),
|
||||
}
|
||||
tp1 = self.p;
|
||||
@ -574,14 +574,14 @@ impl<'a> Parser<'a> {
|
||||
/*
|
||||
* It parses expr %} block {% else if expr %} block {% else %} block {%} */
|
||||
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> {
|
||||
self.check_recursion_limit(recc)?;
|
||||
let mut conditions: Vec<Expression> = Vec::new();
|
||||
let mut blocks: Vec<Element> = Vec::new();
|
||||
loop {
|
||||
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);
|
||||
match ending_tag.cmd {
|
||||
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)),
|
||||
}
|
||||
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){
|
||||
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}))
|
||||
}
|
||||
|
||||
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.skip_whitespace();
|
||||
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 mut arg_names_extended = arg_names.clone();
|
||||
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) {
|
||||
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))
|
||||
}
|
||||
|
||||
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)?;
|
||||
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 (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 {
|
||||
BlockEndingTag::NOGAP => "",
|
||||
BlockEndingTag::GAP => " ",
|
||||
@ -680,7 +680,7 @@ impl<'a> Parser<'a> {
|
||||
_ => return Err(FileParsingError::new(
|
||||
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 */
|
||||
@ -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.
|
||||
* 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 */
|
||||
fn expression_parsing_include_local_const(
|
||||
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))
|
||||
}
|
||||
self.p += 1;
|
||||
let argument_iig = arg_names.len();
|
||||
let mut arg_names_extended = arg_names.clone();
|
||||
arg_names_extended.push(toplevel_name);
|
||||
let mut used_by_lambda: Vec<usize> = Vec::new();
|
||||
let lambda_body = self.parse_expression(
|
||||
&arg_names_extended, &mut used_by_lambda, recc - 1)?;
|
||||
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();
|
||||
self.source_expr.push(lambda_body);
|
||||
Ok(Some(Expression::Lambda(NewLambdaExpression{
|
||||
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()
|
||||
}
|
||||
}).collect(), expr_id})))
|
||||
} else if toplevel_name == "this" {
|
||||
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>,
|
||||
) -> Result<Plemege, FileParsingError> {
|
||||
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(
|
||||
text: &str, file_path: &str, source_expr: &mut Vec<Expression>,
|
||||
) -> Result<Plemege, FileParsingError> {
|
||||
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 {
|
||||
BlockEndingTag::EOF => Ok(Plemege::Element(1, el)),
|
||||
BlockEndingTag::END_ELEMENT => Err(FileParsingError::new(unmatched_element_ending_tag, tt.p1, parser.p)),
|
||||
|
||||
@ -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::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::cell::RefCell;
|
||||
use parser::{Element, Expression, NewLambdaExpression};
|
||||
|
||||
pub struct DebugStateGuard<'a> (
|
||||
&'a RefCell<Vec<String>>
|
||||
@ -16,129 +15,384 @@ impl<'a> Drop for DebugStateGuard<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RuntimeEnv(
|
||||
RefCell<Vec<String>>
|
||||
);
|
||||
|
||||
impl<'a> RuntimeEnv {
|
||||
pub fn register(&'a self, msg: String) -> DebugStateGuard<'a> {
|
||||
self.0.borrow_mut().push(msg);
|
||||
DebugStateGuard(&self.0)
|
||||
}
|
||||
pub enum SharedValue {
|
||||
Int(u64),
|
||||
Str(String),
|
||||
Arr(Vec<SharedValue>),
|
||||
Dict(HashMap<String, SharedValue>),
|
||||
Fn(Box<dyn for<'m> Fn(& RuntimeEnv<'m>, Value<'m>, u32) -> Result<Value<'m>, String> + Send + Sync>),
|
||||
RuntimeConst(Box<dyn for<'m> Fn(& RuntimeEnv<'m>, u32) -> Result<Value<'m>, String> + Send + Sync>),
|
||||
}
|
||||
|
||||
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 root: Value<'static>,
|
||||
pub struct MTGOTT {
|
||||
pub root: SharedValue,
|
||||
pub source_elements: Vec<Element>,
|
||||
pub source_expressions: Vec<Expression>
|
||||
}
|
||||
|
||||
pub enum Value<'t> {
|
||||
Str(Rc<String>),
|
||||
Int(u64),
|
||||
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>>),
|
||||
pub struct RuntimeEnv<'m> {
|
||||
pub stack: RefCell<Vec<String>>,
|
||||
pub mtgott: &'m MTGOTT,
|
||||
}
|
||||
|
||||
impl<'t> Value<'t> {
|
||||
pub fn stringify_type(&self) -> String {
|
||||
impl<'a> RuntimeEnv<'a> {
|
||||
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 {
|
||||
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::Int(u64) => "Int".into(),
|
||||
Value::Arr(_) => "Array".into(),
|
||||
Value::Dict(_) => "Dictionary".into(),
|
||||
Value::Fn(_) => "Function".into(),
|
||||
Value::Ref(i) => format!("Reference to {}", i.stringify_type()),
|
||||
Value::RuntimeConst(_) => "Compilation Artifact".into(),
|
||||
Value::Ref(r) => format!("Reference to {}", r.type_name())
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
fn default() -> Self {
|
||||
Value::Int(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Value {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
impl<'a, 'm> Value<'m> {
|
||||
pub fn if_fn_get(&'a self) -> Option<&'a (dyn Fn(&RuntimeEnv<'m>, Value<'m>, u32) -> Result<Value<'m>, String> + 'm) > {
|
||||
match self {
|
||||
Value::Str(s) => write!(f, "Str({})", s),
|
||||
Value::Int(i) => write!(f, "Int({})", i),
|
||||
Value::Arr(a) => write!(f, "Arr({:?})", a),
|
||||
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%%#^#}}&_)"),
|
||||
Value::Fn(func) => Some(func.as_ref()),
|
||||
Value::Ref(SharedValue::Fn(func)) => Some(func),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MTGOTT {
|
||||
fn get_sub_value<'a>(&'a self, name: &str) -> Result<&'a Value, String> {
|
||||
let mut cur: &Value = &self.root;
|
||||
let parts: Vec<String> = name.split(".").map(String::from).collect();
|
||||
for i in 0usize..parts.len() {
|
||||
impl<'m> RuntimeEnv<'m> {
|
||||
pub fn call_fn(&'m self, f: Value<'m>, x: Value<'m>, recc: u32) -> Result<Value<'m>, String> {
|
||||
match f {
|
||||
Value::Fn(func) => func.as_ref()(self, x, recc),
|
||||
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 {
|
||||
Value::Dict(hashmap) => {
|
||||
match hashmap.get(&parts[i]) {
|
||||
Some(nxt ) => { cur = nxt; }
|
||||
None => return Err(format!("No such root element: {}", parts[..=i].join("/")))
|
||||
SharedValue::Dict(dict) => {
|
||||
cur = dict.entry(path[i].clone()).or_insert(SharedValue::Dict(HashMap::new()));
|
||||
}
|
||||
_ => 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)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&self, guest_root: Value, name: &str, recursion_limit: u32) -> Result<String, Box<dyn Error>> {
|
||||
let main = self.get_sub_value(name)?;
|
||||
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) => {
|
||||
let mut d_stack = RuntimeEnv(RefCell::new(Vec::new()));
|
||||
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())
|
||||
};
|
||||
match res {
|
||||
Value::Ref(Value::Str(s1)) => Ok(s1.as_ref().clone()),
|
||||
Value::Str(answer) => if Rc::strong_count(&answer) == 1 {
|
||||
Ok(Rc::try_unwrap(answer).unwrap())
|
||||
pub fn access_root_by_path_with_slashes<'m>(root: &'m SharedValue, path: &str) -> Result<&'m SharedValue, Box<dyn Error>> {
|
||||
access_root_by_path(root, path, '/')
|
||||
}
|
||||
|
||||
pub fn access_root_by_path_with_dots<'m>(root: &'m SharedValue, path: &str) -> Result<&'m SharedValue, Box<dyn Error>> {
|
||||
access_root_by_path(root, path, '.')
|
||||
}
|
||||
|
||||
|
||||
impl MTGOTT {
|
||||
/* name is a path with dots (and obviously without extension) */
|
||||
pub fn render<'t>(&'t self, guest_root: Value<'t>, name: &str, recursion_limit: u32) -> Result<String, Box<dyn Error>> {
|
||||
let re = RuntimeEnv::new(self);
|
||||
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 {
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user