Знал ли ты, Марк, что в одном стакане Фрэнзи-Кофе 44 чайных ложки Гора?
This commit is contained in:
parent
a9023c6a8c
commit
9ce0e602b3
@ -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}")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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)),
|
||||||
|
|||||||
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user