2025-04-07 15:40:38 +03:00
|
|
|
use super::runtime::*;
|
|
|
|
|
use super::parser::*;
|
|
|
|
|
use std::error::Error;
|
|
|
|
|
use std::collections::HashMap;
|
2025-04-09 02:38:29 +03:00
|
|
|
use std::ops::Deref;
|
2025-04-07 15:40:38 +03:00
|
|
|
|
2025-04-09 02:38:29 +03:00
|
|
|
struct ElementPartsCollector {
|
|
|
|
|
res: String, cur_col: usize,
|
2025-04-07 15:40:38 +03:00
|
|
|
}
|
|
|
|
|
|
2025-04-09 02:38:29 +03:00
|
|
|
impl ElementPartsCollector {
|
|
|
|
|
fn without_pad(&mut self, thp: &str) {
|
|
|
|
|
self.res += thp;
|
|
|
|
|
for ch in thp.chars() {
|
|
|
|
|
if ch == '\n' { self.cur_col = 0; } else { self.cur_col += 1; }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn add_padded_element_piece(&mut self, offset: usize, thp: &str){
|
|
|
|
|
for ch in thp.chars() {
|
|
|
|
|
self.res.push(ch);
|
|
|
|
|
if ch == '\n' {
|
|
|
|
|
self.res.push_str(&" ".repeat(offset));
|
|
|
|
|
self.cur_col = offset;
|
|
|
|
|
} else {
|
|
|
|
|
self.cur_col += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn add_single_padded_element_piece(&mut self, thp: &str) {
|
|
|
|
|
self.add_padded_element_piece(self.cur_col, thp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RuntimeEnv {
|
|
|
|
|
fn value_to_bool(&self, val: &Value) -> Result<bool, String> {
|
|
|
|
|
match val {
|
|
|
|
|
Value::Int(num) => Ok(*num != 0),
|
|
|
|
|
Value::Str(str) => Ok(str.len() > 0),
|
|
|
|
|
Value::Arr(arr) => Ok(arr.len() > 0),
|
|
|
|
|
Value::Dict(dict) => Ok(dict.len() > 0),
|
|
|
|
|
Value::Fn(_) => Err(self.make_error("Can't convert function to bool")),
|
|
|
|
|
_ => panic!("Get rid of runtime strings in expressions")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_needed_if_block<'i>(
|
2025-04-20 00:04:01 +03:00
|
|
|
&self, if_sub_el: &'i IfSubElement, mtgott: &MTGOTT, arg_stack: &[&Value], recc: u32
|
2025-04-09 02:38:29 +03:00
|
|
|
) -> Result<Option<&'i Element>, String> {
|
|
|
|
|
let n = if_sub_el.conditions.len();
|
|
|
|
|
for i in 0..n {
|
2025-04-20 00:04:01 +03:00
|
|
|
let expr_res = self.execute_expression(&if_sub_el.conditions[i], mtgott, arg_stack, recc - 1)?;
|
2025-04-09 02:38:29 +03:00
|
|
|
if self.value_to_bool(expr_res.deref())? {
|
|
|
|
|
return Ok(Some(&if_sub_el.branches[i]));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if if_sub_el.branches.len() > n {
|
|
|
|
|
Ok(Some(&if_sub_el.branches[n]))
|
|
|
|
|
} else {
|
|
|
|
|
Ok(None)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-20 00:04:01 +03:00
|
|
|
fn execute_element_block(
|
|
|
|
|
&self, el: &[SubElement], mtgott: &MTGOTT, arg_stack: &[& Value], recc: u32
|
2025-04-09 02:38:29 +03:00
|
|
|
) -> Result<String, String> {
|
|
|
|
|
if recc == 0 { return Err(self.make_error("Recursion limit exceeded")) }
|
|
|
|
|
let mut res = ElementPartsCollector{res: String::new(), cur_col: 0};
|
|
|
|
|
for se in el {
|
|
|
|
|
match se {
|
|
|
|
|
SubElement::Static(ps) => {
|
|
|
|
|
res.without_pad(ps);
|
|
|
|
|
},
|
|
|
|
|
SubElement::If(if_sub_el) =>
|
2025-04-20 00:04:01 +03:00
|
|
|
if let Some(branch_el) = self.get_needed_if_block(if_sub_el, mtgott, arg_stack, recc - 1)? {
|
2025-04-09 02:38:29 +03:00
|
|
|
res.add_single_padded_element_piece(
|
|
|
|
|
&self.execute_element_block(
|
2025-04-20 00:04:01 +03:00
|
|
|
&branch_el.sub_elements, mtgott, arg_stack, recc - 1)?)
|
2025-04-09 02:38:29 +03:00
|
|
|
}
|
|
|
|
|
,
|
|
|
|
|
SubElement::InsertExpr(expr) => res.add_single_padded_element_piece(
|
2025-04-20 00:04:01 +03:00
|
|
|
match self.execute_expression(expr, mtgott, arg_stack, recc - 1)?.deref() {
|
2025-04-09 02:38:29 +03:00
|
|
|
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());
|
2025-04-20 00:04:01 +03:00
|
|
|
let expr_val = self.execute_expression(iterable, mtgott, arg_stack, recc - 1)?;
|
2025-04-09 02:38:29 +03:00
|
|
|
let an = arg_stack.len();
|
|
|
|
|
let saved_offset = res.cur_col;
|
|
|
|
|
match expr_val.deref() {
|
|
|
|
|
Value::Dict(dict) => {
|
|
|
|
|
let mut it = dict.iter();
|
|
|
|
|
for i in 0usize.. {
|
|
|
|
|
if let Some((key, value)) = it.next() {
|
|
|
|
|
let itv = Value::Str(key.clone());
|
|
|
|
|
let mut arg_stack_updated: Vec<&Value> = Vec::with_capacity(an + 2);
|
|
|
|
|
arg_stack_updated.extend_from_slice(arg_stack);
|
|
|
|
|
arg_stack_updated.push(&itv);
|
|
|
|
|
arg_stack_updated.push(value);
|
|
|
|
|
if i > 0 {res.add_padded_element_piece(saved_offset, join); }
|
|
|
|
|
res.add_padded_element_piece(
|
|
|
|
|
saved_offset, &self.execute_element_block(
|
2025-04-20 00:04:01 +03:00
|
|
|
&core.sub_elements, mtgott, arg_stack, recc - 1)?);
|
2025-04-09 02:38:29 +03:00
|
|
|
} else { break }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Value::Arr(array) => {
|
|
|
|
|
for i in 0..array.len() {
|
|
|
|
|
let itv = Value::Int(i as u64);
|
|
|
|
|
let mut arg_stack_updated: Vec<&Value> = Vec::with_capacity(an + 2);
|
|
|
|
|
arg_stack_updated.extend_from_slice(arg_stack);
|
|
|
|
|
arg_stack_updated.push(&itv);
|
|
|
|
|
arg_stack_updated.push(&array[i]);
|
|
|
|
|
if i > 0 { res.add_padded_element_piece(saved_offset, join); }
|
|
|
|
|
res.add_padded_element_piece(
|
|
|
|
|
saved_offset, &self.execute_element_block(
|
2025-04-20 00:04:01 +03:00
|
|
|
&core.sub_elements, mtgott, arg_stack, recc - 1)?);
|
2025-04-09 02:38:29 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => return Err(self.make_error(&format!("Can't iterate over {}", expr_val.deref().stringify_type())))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
SubElement::Let(expr, core) => {
|
2025-04-20 00:04:01 +03:00
|
|
|
let expr_val = self.execute_expression(expr, mtgott, arg_stack, recc - 1)?;
|
2025-04-09 02:38:29 +03:00
|
|
|
let mut arg_stack_updated = Vec::with_capacity(arg_stack.len() + 1);
|
|
|
|
|
arg_stack_updated.extend_from_slice(arg_stack);
|
|
|
|
|
arg_stack_updated.push(expr_val.deref());
|
|
|
|
|
res.add_single_padded_element_piece(&self.execute_element_block(
|
2025-04-20 00:04:01 +03:00
|
|
|
&core.sub_elements, mtgott, arg_stack, recc - 1)?);
|
2025-04-09 02:38:29 +03:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
Ok(res.res)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-20 00:04:01 +03:00
|
|
|
enum ExpressionResult<'l, 't> {
|
|
|
|
|
LocalRef(&'l Value<'l>),
|
|
|
|
|
Owned(Value<'t>),
|
2025-04-09 02:38:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-04-20 00:04:01 +03:00
|
|
|
impl<'l, 't> Deref for ExpressionResult<'l> {
|
2025-04-09 02:38:29 +03:00
|
|
|
type Target = Value;
|
|
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
|
match self {
|
|
|
|
|
ExpressionResult::LocalRef(val) => val,
|
|
|
|
|
ExpressionResult::Owned(val) => val,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RuntimeEnv {
|
|
|
|
|
fn in_expression_index_by_key<'l>(&self, mut base: ExpressionResult<'l>, key: &String) -> Result<ExpressionResult<'l>, String> {
|
|
|
|
|
match base {
|
|
|
|
|
ExpressionResult::LocalRef(Value::Dict(dictionary )) => {
|
|
|
|
|
if let Some(el) = dictionary.get(key) {
|
|
|
|
|
Ok(ExpressionResult::LocalRef(el))
|
|
|
|
|
} else {
|
|
|
|
|
Err(self.make_error(&format!("Dictionary doesn't have key {}", key)))
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
ExpressionResult::Owned(Value::Dict(mut dictionary)) => {
|
|
|
|
|
/* I can't think of a single use case for this */
|
|
|
|
|
if let Some(el) = dictionary.get_mut(key) {
|
|
|
|
|
Ok(ExpressionResult::Owned(std::mem::take(el)))
|
|
|
|
|
} else {
|
|
|
|
|
Err(self.make_error(&format!("Locally created dictionary doesn't have key {}", key)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => Err(self.make_error(&format!("Can't take attribute {} (or really any attribute) from {}", key, base.deref().stringify_type())))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn execute_expression<'l>(
|
2025-04-20 00:04:01 +03:00
|
|
|
&self, expr: &Expression, mtgott: &'l MTGOTT, arg_stack: &[&'l Value], recc: u32
|
2025-04-09 02:38:29 +03:00
|
|
|
) -> Result<ExpressionResult<'l>, String> {
|
|
|
|
|
if recc == 0 { return Err(self.make_error("Recursion limit exceeded".into())) }
|
|
|
|
|
let f1: ExpressionResult<'l> = match expr {
|
2025-04-20 00:04:01 +03:00
|
|
|
Expression::Root => ExpressionResult::LocalRef(&mtgott.root),
|
2025-04-09 02:38:29 +03:00
|
|
|
Expression::Argument(id) => ExpressionResult::LocalRef(arg_stack[*id as usize]),
|
|
|
|
|
Expression::Get(e1, e2) => {
|
2025-04-20 00:04:01 +03:00
|
|
|
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)?;
|
2025-04-09 02:38:29 +03:00
|
|
|
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(
|
2025-04-20 00:04:01 +03:00
|
|
|
self.execute_expression(&e1, mtgott, arg_stack, recc - 1)?, key)?
|
2025-04-09 02:38:29 +03:00
|
|
|
}
|
|
|
|
|
Expression::Call(e1, e2) => {
|
2025-04-20 00:04:01 +03:00
|
|
|
let r1: ExpressionResult = self.execute_expression(e1, mtgott, arg_stack, recc - 1)?;
|
|
|
|
|
let r2: ExpressionResult = self.execute_expression(e2, mtgott, arg_stack, recc - 1)?;
|
2025-04-09 02:38:29 +03:00
|
|
|
match r1.deref() {
|
2025-04-20 00:04:01 +03:00
|
|
|
Value::Fn(func) => ExpressionResult::Owned(func(self, mtgott, r2.deref(), recc - 1)?),
|
2025-04-09 02:38:29 +03:00
|
|
|
_ => 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(
|
2025-04-20 00:04:01 +03:00
|
|
|
Value::Str(fnc(self, mtgott, recc - 1)?)
|
2025-04-09 02:38:29 +03:00
|
|
|
),
|
|
|
|
|
_ => f1
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We construct intermediate closures for elements at runtime */
|
2025-04-20 00:04:01 +03:00
|
|
|
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> {
|
2025-04-09 02:38:29 +03:00
|
|
|
if cr_recc == 0 { return Err("Recursion limit exceeded".into()) }
|
2025-04-20 00:04:01 +03:00
|
|
|
Ok(if prev.len() + 1 == argc {
|
|
|
|
|
Box::new(move |re: &RuntimeEnv, mtgott: &MTGOTT, arg: &Value, ecc: u32| -> Result<Value, String> {
|
2025-04-09 02:38:29 +03:00
|
|
|
if ecc == 0 { return Err(re.make_error("Recursion limit exceeded".into())) }
|
|
|
|
|
let mut new = prev.clone(); new.push(arg);
|
2025-04-20 00:04:01 +03:00
|
|
|
re.execute_element_block(&mtgott.source[el_source_pos].sub_elements, mtgott, &new, ecc - 1).map(Value::Str)
|
2025-04-09 02:38:29 +03:00
|
|
|
})
|
|
|
|
|
} else {
|
2025-04-20 00:04:01 +03:00
|
|
|
Box::new(move |re: &RuntimeEnv, mtgott: &MTGOTT, arg: &Value, ecc: u32| -> Result<Value, String> {
|
2025-04-09 02:38:29 +03:00
|
|
|
if ecc == 0 { return Err(re.make_error("Recursion limit exceeded".into())) }
|
|
|
|
|
let mut new = prev.clone(); new.push(arg);
|
2025-04-20 00:04:01 +03:00
|
|
|
construct_element_closure(new, argc, el_source_pos, cr_recc - 1).map(Value::Fn)
|
2025-04-09 02:38:29 +03:00
|
|
|
})
|
|
|
|
|
})
|
2025-04-08 13:26:26 +03:00
|
|
|
}
|
|
|
|
|
|
2025-04-20 00:04:01 +03:00
|
|
|
/* 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*/
|
|
|
|
|
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> {
|
2025-04-09 02:38:29 +03:00
|
|
|
if recc == 0 { return Err("Recursion limit exceeded".into()) }
|
2025-04-07 15:40:38 +03:00
|
|
|
match x {
|
|
|
|
|
Plemege::Package(map) => {
|
|
|
|
|
let mut new_dict: HashMap<String, Value> = HashMap::new();
|
|
|
|
|
for (key, thing) in map {
|
2025-04-20 00:04:01 +03:00
|
|
|
new_dict.insert(key, plemege_to_value(thing, file_path, source, recc - 1)?);
|
2025-04-07 15:40:38 +03:00
|
|
|
}
|
2025-04-08 01:46:36 +03:00
|
|
|
Ok(Value::Dict(new_dict))
|
2025-04-07 15:40:38 +03:00
|
|
|
},
|
|
|
|
|
Plemege::Element(el) => {
|
2025-04-09 02:38:29 +03:00
|
|
|
/* The only optimization we ever make (we could do more, but sorry, my life is finite) */
|
|
|
|
|
if el.sub_elements.iter().all(|se| matches!(se, SubElement::Static(_))) {
|
|
|
|
|
return Ok(Value::Str(el.sub_elements.into_iter()
|
|
|
|
|
.fold(String::new(), |mut acc, p| {
|
|
|
|
|
let SubElement::Static(s) = p else { panic!() };
|
|
|
|
|
acc.push_str(&s); acc
|
|
|
|
|
})))
|
|
|
|
|
}
|
|
|
|
|
if el.argc == 0 {
|
|
|
|
|
Ok(Value::RuntimeStr(Box::new(
|
2025-04-20 00:04:01 +03:00
|
|
|
move |re: &RuntimeEnv, mtgott: &MTGOTT, recc: u32| -> Result<String, String> {
|
|
|
|
|
re.execute_element_block(&el.sub_elements, mtgott, &[], recc)
|
2025-04-09 02:38:29 +03:00
|
|
|
})))
|
|
|
|
|
} else {
|
2025-04-20 00:04:01 +03:00
|
|
|
let argc = el.argc;
|
|
|
|
|
source.push(el);
|
|
|
|
|
construct_element_closure(Vec::new(), argc, source.len() - 1, recc).map(Value::Fn)
|
2025-04-09 02:38:29 +03:00
|
|
|
}
|
2025-04-07 15:40:38 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|