Fixed some bugs, added stlib to mtgott, displaying my age at index page

This commit is contained in:
Андреев Григорий 2025-04-28 01:10:14 +03:00
parent 3d5af95212
commit 4656288cf1
20 changed files with 810 additions and 99 deletions

305
Cargo.lock generated
View File

@ -17,6 +17,27 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]] [[package]]
name = "axum" name = "axum"
version = "0.8.3" version = "0.8.3"
@ -86,18 +107,59 @@ dependencies = [
"windows-targets", "windows-targets",
] ]
[[package]]
name = "bumpalo"
version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.10.1" version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cc"
version = "1.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a"
dependencies = [
"shlex",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-link",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@ -152,6 +214,12 @@ version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "hashbrown"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]] [[package]]
name = "http" name = "http"
version = "1.3.1" version = "1.3.1"
@ -233,12 +301,56 @@ dependencies = [
"tower-service", "tower-service",
] ]
[[package]]
name = "iana-time-zone"
version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "indexmap"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.15" version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "js-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.171" version = "0.2.171"
@ -293,6 +405,15 @@ dependencies = [
name = "mtgott" name = "mtgott"
version = "0.1.0" version = "0.1.0"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "object" name = "object"
version = "0.36.7" version = "0.36.7"
@ -404,6 +525,15 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_spanned"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "serde_urlencoded" name = "serde_urlencoded"
version = "0.7.1" version = "0.7.1"
@ -416,6 +546,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.15.0" version = "1.15.0"
@ -475,6 +611,47 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "toml"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900f6c86a685850b1bc9f6223b20125115ee3f31e01207d81655bbcc0aea9231"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10558ed0bd2a1562e630926a2d1f0b98c827da99fabd3fe20920a59642504485"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"toml_write",
"winnow",
]
[[package]]
name = "toml_write"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28391a4201ba7eb1984cfeb6862c0b3ea2cfe23332298967c749dddc0d6cd976"
[[package]] [[package]]
name = "tower" name = "tower"
version = "0.5.2" version = "0.5.2"
@ -535,6 +712,123 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
dependencies = [
"unicode-ident",
]
[[package]]
name = "windows-core"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-link"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
[[package]]
name = "windows-result"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
dependencies = [
"windows-link",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.52.0" version = "0.52.0"
@ -608,11 +902,22 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "yyyi_ru" name = "yyyi_ru"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"axum", "axum",
"chrono",
"mtgott", "mtgott",
"tokio", "tokio",
"toml",
] ]

View File

@ -7,3 +7,5 @@ edition = "2024"
axum = "0.8.3" axum = "0.8.3"
tokio = { version = "1.44.1", features = ["rt-multi-thread"] } tokio = { version = "1.44.1", features = ["rt-multi-thread"] }
mtgott = { path = "./mtgott" } mtgott = { path = "./mtgott" }
toml="0.8.21"
chrono = "0.4.40"

View File

@ -1,3 +0,0 @@
I am Andreev Gregory ({{age}} y.o.),
living in Moscow, learning programming, big fond of making patches to dwm, sometimes write some unhinged ravings.
Most interesting stuff about me gets published <a href="/blog">here</a>.

View File

@ -1,3 +0,0 @@
Я Андреев Григорий ({{age}}),
живу в Москве, учусь прогать, люблю курить dwm и иногда пишу разного рода дичь.
Самым интересным в своей жизни делюсь <a href="/blog">вот здесь</a>.

View File

@ -1,12 +1,14 @@
{@ main lang title body @}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="{{lang}}">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/assets/css/common.css"> <link rel="stylesheet" href="/assets/css/common.css">
<title>Блог Гриши</title> <title>{{title}}</title>
</head> </head>
<body> <body>
{#body#}
</body> </body>
</html> </html>
{@}

View File

@ -0,0 +1,6 @@
{@ body $ @}
{%let pres = missing-text[$lang].blog %}
BLOG BODY
{%}
{@}
{$ main = d: base.main d.lang missing-text[d.lang].blog.title (this.body d) $}

View File

@ -1,17 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/assets/css/common.css">
<title>{{pres.index.title}}</title>
</head>
<body>
<div class="main-container">
<div>
<h2 class="">{{pres.index.about_me_header}}</h2>
<p class="description-text">{% if aboutme_template is string %} STRING {{lang_macro::incl('aboutme/')}} {% else %} NOT STRING{%endif%}</p>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,11 @@
{@ body $ @}
{%let pres = missing-text[$lang].index %}
<div class="main-container">
<div>
<h2 class="">{{ pres.about-me-header }}</h2>
<p class="description-text">{#pres.aboutme $#}</p>
</div>
</div>
{%}
{@}
{$ main = d: base.main d.lang missing-text[d.lang].index.title (this.body d) $}

View File

@ -0,0 +1,12 @@
{$ index $}
{@ title @} Gregory title page {@}
{@ about-me-header @} About me {@}
{@ aboutme $ @}
I am Andreev Gregory ({{$age}} y.o.),
living in Moscow, learning programming, big fond of making patches to dwm, sometimes write some unhinged ravings.
Most interesting stuff about me gets published <a href="/blog">here</a>.
{@}
{$}
{$ blog $}
{@ title @} Gregory blog {@}
{$}

View File

@ -0,0 +1,12 @@
{$index$}
{@ title @} Гришина заглавная страничка {@}
{@ about-me-header @} Обо мне {@}
{@ aboutme $ @}
Я Андреев Григорий ({{$age}} {%let d = o/o $age 10%}{%if == d 1%}год{%else if && (<= 2 d) (<= d 4) %}года{%else%}лет{%}{%}),
живу в Москве, учусь прогать, люблю курить dwm и иногда пишу разного рода дичь.
Самым интересным в своей жизни делюсь <a href="/blog">вот здесь</a>.
{@}
{$}
{$blog$}
{@ title @} Гришин блог {@}
{$}

View File

@ -10,7 +10,7 @@ use crate::mtgott::runtime::*;
use std::rc::Rc; use std::rc::Rc;
fn usage() -> ! { fn usage() -> ! {
eprintln!("Usage: program <path> [-D name value]..."); eprintln!("Usage: mtgott_cli <template dir> <path> [-D name value]...");
process::exit(1); process::exit(1);
} }

View File

@ -14,7 +14,7 @@ pub fn is_digit(ch: char) -> bool {
pub fn is_normal_word_constituent(ch: char) -> bool { pub fn is_normal_word_constituent(ch: char) -> bool {
('0'..='9').contains(&ch) || ('a'..='z').contains(&ch) || ('A'..='Z').contains(&ch) ('0'..='9').contains(&ch) || ('a'..='z').contains(&ch) || ('A'..='Z').contains(&ch)
|| "-_+/|&~!^*".contains(ch) || "<>=-_+/|&~!^*".contains(ch)
} }
pub fn is_normal_word(s: &str) -> bool { pub fn is_normal_word(s: &str) -> bool {

View File

@ -8,6 +8,7 @@ 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;
use super::stdlib::add_entire_stdlib_to_root;
pub fn search_dir_rec_helper( pub fn search_dir_rec_helper(
res: &mut Vec<Vec<String>>, res: &mut Vec<Vec<String>>,
@ -121,6 +122,8 @@ pub fn get_all_templates_from_dir_text(dir: MtgottDirContent, plain_ext: &str) -
Ok(res) Ok(res)
} }
// fn operator_
pub fn get_all_templates_plus_builtins_from_dir_text( pub fn get_all_templates_plus_builtins_from_dir_text(
dir: MtgottDirContent, plain_ext: &str, dir: MtgottDirContent, plain_ext: &str,
sanitize: fn(&str) -> String, sanitize: fn(&str) -> String,
@ -137,12 +140,9 @@ pub fn get_all_templates_plus_builtins_from_dir_text(
} else { } else {
Ok(Value::Str(Rc::new(sanitize(&format!("{:?}", arg))))) Ok(Value::Str(Rc::new(sanitize(&format!("{:?}", arg)))))
} }
})))?; }))
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_entire_stdlib_to_root(&mut res.root)?;
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) Ok(res)
} }

View File

@ -38,8 +38,8 @@ impl<'m> RuntimeEnv<'m> {
) -> 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], local, recc - 1)?; let r = self.execute_expression(&if_sub_el.conditions[i], local, recc - 1)?;
if expr_res.to_bool()? { if self.value_to_bool(&r)? {
return Ok(Some(&if_sub_el.branches[i])); return Ok(Some(&if_sub_el.branches[i]));
} }
} }
@ -96,13 +96,13 @@ impl<'m> RuntimeEnv<'m> {
}).collect::<Result<Vec<String>, String>>()?, }).collect::<Result<Vec<String>, String>>()?,
Value::Arr(arr) => arr.as_ref().iter().enumerate() Value::Arr(arr) => arr.as_ref().iter().enumerate()
.map(|(ind ,value)| -> Result<String, String> { .map(|(ind ,value)| -> Result<String, String> {
local[k] = Value::Int(ind as u64); local[k] = Value::Int(ind as i64);
local[k + 1] = value.clone(); local[k + 1] = value.clone();
self.execute_element_block(core, local, recc - 1) self.execute_element_block(core, local, recc - 1)
}).collect::<Result<Vec<String>, String>>()?, }).collect::<Result<Vec<String>, String>>()?,
Value::Ref(SharedValue::Arr(arr)) => arr.iter().enumerate() Value::Ref(SharedValue::Arr(arr)) => arr.iter().enumerate()
.map(|(ind, sh_val)| -> Result<String, String> { .map(|(ind, sh_val)| -> Result<String, String> {
local[k] = Value::Int(ind as u64); local[k] = Value::Int(ind as i64);
local[k + 1] = Value::Ref(sh_val); local[k + 1] = Value::Ref(sh_val);
self.execute_element_block(core, local, recc - 1) self.execute_element_block(core, local, recc - 1)
}).collect::<Result<Vec<String>, String>>()?, }).collect::<Result<Vec<String>, String>>()?,
@ -191,7 +191,7 @@ pub fn plemege_to_value(
} }
} }
Plemege::Expression(expr) => match expr { Plemege::Expression(expr) => match expr {
Expression::Int(n) => Ok(SharedValue::Int(n)), Expression::Int(n) => Ok(SharedValue::Int(n as i64)),
Expression::Str(s) => Ok(SharedValue::Str(s)), Expression::Str(s) => Ok(SharedValue::Str(s)),
// todo: optimize arrays // todo: optimize arrays
Expression::Lambda(NewLambdaExpression{local_var_array, expr_id}) => { Expression::Lambda(NewLambdaExpression{local_var_array, expr_id}) => {

View File

@ -3,3 +3,4 @@ pub mod parser;
pub mod dirsearch; pub mod dirsearch;
pub mod runtime; pub mod runtime;
pub mod lambda_compilation; pub mod lambda_compilation;
pub mod stdlib;

View File

@ -472,13 +472,13 @@ impl<'a> Parser<'a> {
} }
self.p += 2; self.p += 2;
tp1 = self.p; tp1 = self.p;
} else if self.is_ahead("{[") { } else if self.is_ahead("{#") {
fin_static(self, tp1, &mut res); fin_static(self, tp1, &mut res);
self.p += 2; self.p += 2;
let expr: Expression = self.parse_expression( let expr: Expression = self.parse_expression(
arg_names, &mut (0..arg_names.len()).collect(), recc)?; arg_names, &mut (0..arg_names.len()).collect(), recc)?;
res.push(SubElement::InsertExpr(expr)); res.push(SubElement::InsertExpr(expr));
if !self.is_ahead("]}") { if !self.is_ahead("#}") {
return Err(FileParsingError::new(expected_write_tag_end_after_expression, self.p - 2, self.p)); return Err(FileParsingError::new(expected_write_tag_end_after_expression, self.p - 2, self.p));
} }
self.p += 2; self.p += 2;
@ -869,8 +869,10 @@ impl<'a> Parser<'a> {
used_local_consts: &mut Vec<usize>, recc: u32 used_local_consts: &mut Vec<usize>, recc: u32
) -> Result<Expression, FileParsingError> { ) -> Result<Expression, FileParsingError> {
self.check_recursion_limit(recc)?; self.check_recursion_limit(recc)?;
let mut e1: Expression = self.parse_expression_l2(arg_names, used_local_consts, recc - 1)? let mut e1o: Option<Expression> = self.parse_expression_l2(arg_names, used_local_consts, recc - 1)?;
.ok_or(self.new_unexpected_char_error(expected_round_brackets_open_or_dollar_or_word_or_int_or_string_or_square_bracket_open))?; let Some(mut e1) = e1o else { return Err(
self.new_unexpected_char_error(expected_round_brackets_open_or_dollar_or_word_or_int_or_string_or_square_bracket_open)
)};
loop { loop {
let arg_expr: Option<Expression> = self.parse_expression_l2(arg_names, used_local_consts,recc - 1)?; let arg_expr: Option<Expression> = self.parse_expression_l2(arg_names, used_local_consts,recc - 1)?;
if matches!(arg_expr, None) { break } if matches!(arg_expr, None) { break }

View File

@ -16,7 +16,7 @@ impl<'a> Drop for DebugStateGuard<'a> {
} }
pub enum SharedValue { pub enum SharedValue {
Int(u64), Int(i64),
Str(String), Str(String),
Arr(Vec<SharedValue>), Arr(Vec<SharedValue>),
Dict(HashMap<String, SharedValue>), Dict(HashMap<String, SharedValue>),
@ -58,7 +58,7 @@ impl<'m> RuntimeEnv<'m> {
#[derive(Clone)] #[derive(Clone)]
pub enum Value<'m> { pub enum Value<'m> {
Int(u64), Int(i64),
Str(Rc<String>), Str(Rc<String>),
Arr(Rc<Vec<Value<'m>>>), Arr(Rc<Vec<Value<'m>>>),
Dict(Rc<HashMap<String, Value<'m>>>), Dict(Rc<HashMap<String, Value<'m>>>),
@ -112,7 +112,7 @@ impl SharedValue {
} }
impl<'m> Value<'m> { impl<'m> Value<'m> {
pub fn if_int_get(&self) -> Option<u64> { pub fn if_int_get(&self) -> Option<i64> {
match self { match self {
Value::Int(n) => Some(*n), Value::Int(n) => Some(*n),
Value::Ref(SharedValue::Int(n)) => Some(*n), Value::Ref(SharedValue::Int(n)) => Some(*n),
@ -195,20 +195,6 @@ impl<'m> Value<'m> {
Value::Ref(r) => format!("Reference to {}", r.type_name()) 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<'a, 'm> Value<'m> { impl<'a, 'm> Value<'m> {
@ -251,7 +237,7 @@ impl<'m> RuntimeEnv<'m> {
match expr { match expr {
Expression::Root => Ok(Value::Ref(&self.mtgott.root)), Expression::Root => Ok(Value::Ref(&self.mtgott.root)),
Expression::Local(i) => Ok(local[*i].clone()), Expression::Local(i) => Ok(local[*i].clone()),
Expression::Int(n) => Ok(Value::Int(*n)), Expression::Int(n) => Ok(Value::Int(*n as i64)),
/* Yes, I could have avoided cloning. But who cares anyway, consider using /* Yes, I could have avoided cloning. But who cares anyway, consider using
* Element syntax for long text */ * Element syntax for long text */
Expression::Str(s) => Ok(Value::Str(Rc::new(s.clone()))), Expression::Str(s) => Ok(Value::Str(Rc::new(s.clone()))),
@ -318,6 +304,43 @@ impl<'m> RuntimeEnv<'m> {
} }
} }
} }
pub fn value_to_bool(&self, x: &Value<'m>) -> Result<bool, String> {
if let Some(n) = x.if_int_get() {
Ok(n != 0)
} else if let Some(s) = x.if_str_get_ref() {
Ok(!s.is_empty())
} else if x.is_arr() {
Ok(x.arr_len() > 0)
} else if x.is_dict() {
Ok(!x.dict_is_empty())
} else {
Err(self.make_error("Can't convert Function to bool".into()))
}
}
pub fn value_eq_value(&self, a: &Value<'m>, b: &Value<'m>, recc: u32) -> Result<bool, String> {
self.check_recc(recc)?;
if let (Some(na), Some(nb)) = (a.if_int_get(), b.if_int_get()) {
Ok(na == nb)
} else if let (Some(sa), Some(sb)) = (a.if_str_get_ref(), b.if_str_get_ref()) {
Ok(sa == sb)
} else if a.is_arr() && b.is_arr() {
let n = a.arr_len();
if b.arr_len() != n { return Ok(false); }
for i in 0..n {
if !self.value_eq_value(&a.access_arr(i).unwrap(), &b.access_arr(i).unwrap(), recc - 1)?{
return Ok(false);
}
}
Ok(true)
} else if a.is_dict() || b.is_dict() || a.if_fn_get().is_some() || b.if_fn_get().is_some() {
Err(self.make_error("Can't compare dictionaries and functions"))
} else {
Ok(false)
}
}
} }
/* If we have to add two paths and one is a prefix of another, like here: /* If we have to add two paths and one is a prefix of another, like here:

185
mtgott/src/stdlib.rs Normal file
View File

@ -0,0 +1,185 @@
/* I pray for rustc correctly understanding this filename */
use std::error::Error;
use std::rc::Rc;
use runtime::*;
fn add_basic_bin_operator_to_root(
root: &mut SharedValue, path: &str,
op: for<'a> fn(&RuntimeEnv<'a>, Value<'a>, Value<'a>, u32) -> Result<Value<'a>, String>
) -> Result<(), Box<dyn Error>> {
add_path_with_dots_to_root(root, path, SharedValue::Fn(Box::new(
move |re: &RuntimeEnv, a: Value, _: u32| -> Result<Value, String> {
Ok(Value::Fn(Rc::new(move |re: &RuntimeEnv, b: Value, recc: u32| -> Result<Value, String> {
re.check_recc(recc)?;
op(re, a.clone(), b, recc)
})))
}
)))
}
fn operator_plus<'a>(
re: &RuntimeEnv<'a>, a: Value<'a>, b: Value<'a>, _: u32
) -> Result<Value<'a>, String> {
if let (Some(na), Some(nb)) = (a.if_int_get(), b.if_int_get()) {
Ok(Value::Int(na + nb))
} else if let (Some(sa), Some(sb)) = (a.if_str_get_ref(), b.if_str_get_ref()) {
Ok(Value::Str(Rc::new(String::from(sa) + sb)))
} else {
Err(re.make_error(&format!("Incorrect operation {} + {}", a.type_name(), b.type_name())))
}
}
fn operator_minus<'a>(
re: &RuntimeEnv<'a>, a: Value<'a>, b: Value<'a>, _: u32
) -> Result<Value<'a>, String> {
if let (Some(na), Some(nb)) = (a.if_int_get(), b.if_int_get()) {
Ok(Value::Int(na - nb))
} else {
Err(re.make_error(&format!("Incorrect operation {} - {}", a.type_name(), b.type_name())))
}
}
fn operator_product<'a>(
re: &RuntimeEnv<'a>, a: Value<'a>, b: Value<'a>, _: u32
) -> Result<Value<'a>, String> {
if let (Some(na), Some(nb)) = (a.if_int_get(), b.if_int_get()) {
Ok(Value::Int(na + nb))
} else if let (Some(s), Some(n)) = (a.if_str_get_ref(), b.if_int_get()) {
Ok(Value::Str(Rc::new(s.repeat(n as usize))))
} else if let (Some(n), Some(s)) = (a.if_int_get(), b.if_str_get_ref()) {
Ok(Value::Str(Rc::new(s.repeat(n as usize))))
} else {
Err(re.make_error(&format!("Incorrect operation {} * {}", a.type_name(), b.type_name())))
}
}
fn operator_division<'a>(
re: &RuntimeEnv<'a>, a: Value<'a>, b: Value<'a>, _: u32
) -> Result<Value<'a>, String> {
if let (Some(na), Some(nb)) = (a.if_int_get(), b.if_int_get()) {
if nb == 0 {
Err(re.make_error("Divided by zero".into()))
} else {
Ok(Value::Int(na.div_euclid(nb)))
}
} else {
Err(re.make_error(&format!("Incorrect operation {} / {}", a.type_name(), b.type_name())))
}
}
fn operator_remainder<'a>(
re: &RuntimeEnv<'a>, a: Value<'a>, b: Value<'a>, _: u32
) -> Result<Value<'a>, String> {
if let (Some(na), Some(nb)) = (a.if_int_get(), b.if_int_get()) {
if nb == 0 {
Err(re.make_error("Divided by zero".into()))
} else {
Ok(Value::Int(na.rem_euclid(nb)))
}
} else {
Err(re.make_error(&format!("Incorrect operation {} % {}", a.type_name(), b.type_name())))
}
}
fn operator_and<'a>(
re: &RuntimeEnv<'a>, a: Value<'a>, b: Value<'a>, _: u32
) -> Result<Value<'a>, String> {
Ok(Value::Int((re.value_to_bool(&a)? && re.value_to_bool(&b)?) as i64))
}
fn operator_or<'a>(
re: &RuntimeEnv<'a>, a: Value<'a>, b: Value<'a>, _: u32
) -> Result<Value<'a>, String> {
Ok(Value::Int((re.value_to_bool(&a)? || re.value_to_bool(&b)?) as i64))
}
fn operator_xor<'a>(
re: &RuntimeEnv<'a>, a: Value<'a>, b: Value<'a>, _: u32
) -> Result<Value<'a>, String> {
Ok(Value::Int((re.value_to_bool(&a)? ^ re.value_to_bool(&b)?) as i64))
}
fn operator_equal<'a>(
re: &RuntimeEnv<'a>, a: Value<'a>, b: Value<'a>, recc: u32
) -> Result<Value<'a>, String> {
Ok(Value::Int(re.value_eq_value(&a, &b, recc)? as i64))
}
fn operator_not_equal<'a>(
re: &RuntimeEnv<'a>, a: Value<'a>, b: Value<'a>, recc: u32
) -> Result<Value<'a>, String> {
Ok(Value::Int(!re.value_eq_value(&a, &b, recc)? as i64))
}
fn value_less(a: &Value, b: &Value) -> Result<bool, ()> {
if let (Some(na), Some(nb)) = (a.if_int_get(), b.if_int_get()) {
Ok(na < nb)
} else if let (Some(sa), Some(sb)) = (a.if_str_get_ref(), b.if_str_get_ref()) {
Ok(sa < sb)
} else {
Err(())
}
}
fn operator_less<'a>(
re: &RuntimeEnv<'a>, a: Value<'a>, b: Value<'a>, _: u32
) -> Result<Value<'a>, String> {
match value_less(&a, &b) {
Ok(ans) => Ok(Value::Int(ans as i64)),
Err(_) => Err(re.make_error(&format!("Incorrect operation {} < {}", a.type_name(), b.type_name())))
}
}
fn operator_less_or_equal<'a>(
re: &RuntimeEnv<'a>, a: Value<'a>, b: Value<'a>, _: u32
) -> Result<Value<'a>, String> {
match value_less(&b, &a) {
Ok(ans) => Ok(Value::Int(!ans as i64)),
Err(_) => Err(re.make_error(&format!("Incorrect operation {} <= {}", a.type_name(), b.type_name())))
}
}
fn operator_greater<'a>(
re: &RuntimeEnv<'a>, a: Value<'a>, b: Value<'a>, _: u32
) -> Result<Value<'a>, String> {
match value_less(&b, &a) {
Ok(ans) => Ok(Value::Int(ans as i64)),
Err(_) => Err(re.make_error(&format!("Incorrect operation {} > {}", a.type_name(), b.type_name())))
}
}
fn operator_greater_or_equal<'a>(
re: &RuntimeEnv<'a>, a: Value<'a>, b: Value<'a>, _: u32
) -> Result<Value<'a>, String> {
match value_less(&a, &b) {
Ok(ans) => Ok(Value::Int(!ans as i64)),
Err(_) => Err(re.make_error(&format!("Incorrect operation {} >= {}", a.type_name(), b.type_name())))
}
}
pub fn add_entire_stdlib_to_root(root: &mut SharedValue) -> Result<(), Box<dyn Error>> {
add_path_with_slashes_to_root(root, "magic_tag_start", SharedValue::Str("{%".into()))?;
add_path_with_slashes_to_root(root, "write_tag_start", SharedValue::Str("{{".into()))?;
add_path_with_slashes_to_root(root, "roughinsert_tag_start", SharedValue::Str("{#".into()))?;
add_path_with_slashes_to_root(root, "magic_block_ending_tag", SharedValue::Str("{%}".into()))?;
add_path_with_slashes_to_root(root, "element_ending_tag", SharedValue::Str("{@}".into()))?;
add_basic_bin_operator_to_root(root, "+", operator_plus)?;
add_basic_bin_operator_to_root(root, "-", operator_minus)?;
add_basic_bin_operator_to_root(root, "*", operator_product)?;
add_basic_bin_operator_to_root(root, "/", operator_division)?;
add_basic_bin_operator_to_root(root, "o/o", operator_remainder)?;
add_basic_bin_operator_to_root(root, "&&", operator_and)?;
add_basic_bin_operator_to_root(root, "||", operator_or)?;
add_basic_bin_operator_to_root(root, "^^", operator_xor)?;
add_basic_bin_operator_to_root(root, "==", operator_equal)?;
add_basic_bin_operator_to_root(root, "!=", operator_not_equal)?;
add_basic_bin_operator_to_root(root, "<", operator_less)?;
add_basic_bin_operator_to_root(root, "<=", operator_less_or_equal)?;
add_basic_bin_operator_to_root(root, ">", operator_greater)?;
add_basic_bin_operator_to_root(root, ">=", operator_greater_or_equal)?;
Ok(())
}

View File

@ -84,7 +84,7 @@ fn t007(){
let i = MtgottDirContent{mtgott: vec![ let i = MtgottDirContent{mtgott: vec![
FileWithPath{v_path: "file".into(), text: "{$ pack $}{$c=45$}{$} {@ el @}---{{this.pack.c}}---{@} ".into()} FileWithPath{v_path: "file".into(), text: "{$ pack $}{$c=45$}{$} {@ el @}---{{this.pack.c}}---{@} ".into()}
], imtgott: vec![ ], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{[file.el]}".into()} FileWithPath{v_path: "index".into(), text: "{#file.el#}".into()}
], plain: vec![]}; ], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap(); let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "---45---"); assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "---45---");
@ -95,7 +95,7 @@ fn t008(){
let i = MtgottDirContent{mtgott: vec![ let i = MtgottDirContent{mtgott: vec![
FileWithPath{v_path: "file".into(), text: "{@ el a@}---{{a}}---{@} ".into()} FileWithPath{v_path: "file".into(), text: "{@ el a@}---{{a}}---{@} ".into()}
], imtgott: vec![ ], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{[file.el \"45\"]}".into()} FileWithPath{v_path: "index".into(), text: "{#file.el \"45\"#}".into()}
], plain: vec![]}; ], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap(); let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "---45---"); assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "---45---");
@ -106,7 +106,7 @@ fn t009(){
let i = MtgottDirContent{mtgott: vec![ let i = MtgottDirContent{mtgott: vec![
FileWithPath{v_path: "file".into(), text: "{@ el a b c d e f g @}---{{a}}---{{c}}---{{e}}---{{g}}---{@} ".into()} FileWithPath{v_path: "file".into(), text: "{@ el a b c d e f g @}---{{a}}---{{c}}---{{e}}---{{g}}---{@} ".into()}
], imtgott: vec![ ], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{[file.el 1 2 3 4 5 6 7]}".into()} FileWithPath{v_path: "index".into(), text: "{#file.el 1 2 3 4 5 6 7#}".into()}
], plain: vec![]}; ], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap(); let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "---1---3---5---7---"); assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "---1---3---5---7---");
@ -118,7 +118,7 @@ fn t010(){
let i = MtgottDirContent{mtgott: vec![ let i = MtgottDirContent{mtgott: vec![
FileWithPath{v_path: "file".into(), text: "{@el a b @} {{a}}{{b}} {@} ".into()} FileWithPath{v_path: "file".into(), text: "{@el a b @} {{a}}{{b}} {@} ".into()}
], imtgott: vec![ ], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{[file.el 1 2]}".into()} FileWithPath{v_path: "index".into(), text: "{#file.el 1 2#}".into()}
], plain: vec![]}; ], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap(); let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "12"); assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "12");
@ -127,9 +127,9 @@ fn t010(){
#[test] #[test]
fn t011(){ fn t011(){
let i = MtgottDirContent{mtgott: vec![ let i = MtgottDirContent{mtgott: vec![
FileWithPath{v_path: "file".into(), text: "{@el a @} {{a[1]}} {@} ".into()} FileWithPath{v_path: "file".into(), text: "{@el a @} {{a[1#}} {@} ".into()}
], imtgott: vec![ ], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{[file.el $]}".into()} FileWithPath{v_path: "index".into(), text: "{#file.el $#}".into()}
], plain: vec![]}; ], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap(); let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render( assert_gave!(r.render(
@ -142,9 +142,9 @@ fn t011(){
#[test] #[test]
fn t012(){ fn t012(){
let i = MtgottDirContent{mtgott: vec![ let i = MtgottDirContent{mtgott: vec![
FileWithPath{v_path: "file".into(), text: "{@el a @} {{a[1]}} {@} ".into()} FileWithPath{v_path: "file".into(), text: "{@el a @} {{a[1#}} {@} ".into()}
], imtgott: vec![ ], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{[file[\"el\"] $]}".into()} FileWithPath{v_path: "index".into(), text: "{#file[\"el\"] $#}".into()}
], plain: vec![]}; ], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap(); let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render( assert_gave!(r.render(
@ -170,7 +170,7 @@ fn t013(){
fn t014(){ fn t014(){
let i = MtgottDirContent{mtgott: vec![ let i = MtgottDirContent{mtgott: vec![
], imtgott: vec![ ], imtgott: vec![
FileWithPath{v_path: "index".into(), text: " {{ $[0 ]}} {{$ [1]}} {{$[ 2]}} ".into()} FileWithPath{v_path: "index".into(), text: " {{ $[0 #}} {{$ [1#}} {{$[ 2#}} ".into()}
], plain: vec![]}; ], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap(); let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render( assert_gave!(r.render(
@ -182,9 +182,9 @@ fn t014(){
#[test] #[test]
fn t015(){ fn t015(){
let i = MtgottDirContent{mtgott: vec![ let i = MtgottDirContent{mtgott: vec![
FileWithPath{v_path: "help".into(), text: " {@www $ a b@} {{$[a]}} {{$[b]}} {@}".into()} FileWithPath{v_path: "help".into(), text: " {@www $ a b@} {{$[a#}} {{$[b#}} {@}".into()}
], imtgott: vec![ ], imtgott: vec![
FileWithPath{v_path: "index".into(), text: " {[help.www $ 2 1 ]}".into()} FileWithPath{v_path: "index".into(), text: " {#help.www $ 2 1 #}".into()}
], plain: vec![]}; ], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap(); let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render( assert_gave!(r.render(
@ -242,7 +242,7 @@ fn t019(){
let i = MtgottDirContent{mtgott: vec![ let i = MtgottDirContent{mtgott: vec![
FileWithPath{v_path: "__".into(), text: "{$arr = [0, 10, 20, 30]$}".into()} FileWithPath{v_path: "__".into(), text: "{$arr = [0, 10, 20, 30]$}".into()}
], imtgott: vec![ ], imtgott: vec![
FileWithPath{v_path: "index".into(), text: " {{ (x:y:z:w:w x z) 2 \"Lol\" __ x:z:z.arr[x]}}".into()} FileWithPath{v_path: "index".into(), text: " {{ (x:y:z:w:w x z) 2 \"Lol\" __ x:z:z.arr[x#}}".into()}
], plain: vec![]}; ], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap(); let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render( assert_gave!(r.render(
@ -256,7 +256,7 @@ fn t020(){
let i = MtgottDirContent{mtgott: vec![ let i = MtgottDirContent{mtgott: vec![
FileWithPath{v_path: "__".into(), text: "{$arr = [0, 10, 20, 30]$}".into()} FileWithPath{v_path: "__".into(), text: "{$arr = [0, 10, 20, 30]$}".into()}
], imtgott: vec![ ], imtgott: vec![
FileWithPath{v_path: "index".into(), text: " {{ (x:y:z:w:w x z) __ \"Lol\" 2 x:z:x.arr[z]}}".into()} FileWithPath{v_path: "index".into(), text: " {{ (x:y:z:w:w x z) __ \"Lol\" 2 x:z:x.arr[z#}}".into()}
], plain: vec![]}; ], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap(); let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render( assert_gave!(r.render(
@ -268,7 +268,7 @@ fn t020(){
#[test] #[test]
fn t021(){ fn t021(){
let i = MtgottDirContent{mtgott: vec![ let i = MtgottDirContent{mtgott: vec![
FileWithPath{v_path: "__".into(), text: "{$arr = [\" zero\", 10, 20,]$} {$dict$} {$lol = \"HELLO\"$} {@ cat a b @}{[a]}{[b]}{@} {$}".into()} FileWithPath{v_path: "__".into(), text: "{$arr = [\" zero\", 10, 20,]$} {$dict$} {$lol = \"HELLO\"$} {@ cat a b @}{#a#}{#b#}{@} {$}".into()}
], imtgott: vec![ ], imtgott: vec![
FileWithPath{v_path: "index".into(), text: " {{ __.dict.cat ((a:b:c:c[b][a]) \"lol\" \"dict\" __) __.arr[0] }}".into()} FileWithPath{v_path: "index".into(), text: " {{ __.dict.cat ((a:b:c:c[b][a]) \"lol\" \"dict\" __) __.arr[0] }}".into()}
], plain: vec![]}; ], plain: vec![]};
@ -281,7 +281,7 @@ fn t021(){
#[test] #[test]
fn t022(){ fn t022(){
let i = MtgottDirContent{mtgott: vec![ let i = MtgottDirContent{mtgott: vec![
FileWithPath{v_path: "__".into(), text: "{$arr = [\" zero\", 10, 20,]$} {$dict$} {$lol = \"HELLO\"$} {@ cat a b @}{[a]}{[b]}{@} {$}".into()} FileWithPath{v_path: "__".into(), text: "{$arr = [\" zero\", 10, 20,]$} {$dict$} {$lol = \"HELLO\"$} {@ cat a b @}{#a#}{#b#}{@} {$}".into()}
], imtgott: vec![ ], imtgott: vec![
FileWithPath{v_path: "index".into(), text: " {{ __.dict.cat ((a:b:c:c[a][b]) \"dict\" \"lol\" __) __.arr[0] }}".into()} FileWithPath{v_path: "index".into(), text: " {{ __.dict.cat ((a:b:c:c[a][b]) \"dict\" \"lol\" __) __.arr[0] }}".into()}
], plain: vec![]}; ], plain: vec![]};
@ -301,7 +301,7 @@ fn t023(){
B B
{@} ".into()} {@} ".into()}
], imtgott: vec![ ], imtgott: vec![
FileWithPath{v_path: "index".into(), text: " {[ aaa.el ([1, 2, 3]) ]}".into()} FileWithPath{v_path: "index".into(), text: " {# aaa.el ([1, 2, 3]) #}".into()}
], plain: vec![]}; ], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap(); let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render( assert_gave!(r.render(
@ -414,9 +414,7 @@ fn t029(){
#[test] #[test]
fn t030(){ fn t030(){
let i = MtgottDirContent{mtgott: vec![ let i = MtgottDirContent{mtgott: vec![], imtgott: vec![
FileWithPath{v_path: "A".into(), text: "".into()}
], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{% for k, v: $ %} {{k }}->{{ v}} {% lf %} ".into()} FileWithPath{v_path: "index".into(), text: "{% for k, v: $ %} {{k }}->{{ v}} {% lf %} ".into()}
], plain: vec![]}; ], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap(); let r = get_root_html_from_dir_text_html(i).unwrap();
@ -429,9 +427,7 @@ fn t030(){
#[test] #[test]
fn t031(){ fn t031(){
let i = MtgottDirContent{mtgott: vec![ let i = MtgottDirContent{mtgott: vec![], imtgott: vec![
FileWithPath{v_path: "A".into(), text: "".into()}
], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{% for k, v: $ %} {{k }}:{{ v}} {% gap %} ".into()} FileWithPath{v_path: "index".into(), text: "{% for k, v: $ %} {{k }}:{{ v}} {% gap %} ".into()}
], plain: vec![]}; ], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap(); let r = get_root_html_from_dir_text_html(i).unwrap();
@ -444,9 +440,7 @@ fn t031(){
#[test] #[test]
fn t032(){ fn t032(){
let i = MtgottDirContent{mtgott: vec![ let i = MtgottDirContent{mtgott: vec![], imtgott: vec![
FileWithPath{v_path: "A".into(), text: "".into()}
], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{%for v: $ %} ->{{v}} {%gap%} ".into()} FileWithPath{v_path: "index".into(), text: "{%for v: $ %} ->{{v}} {%gap%} ".into()}
], plain: vec![]}; ], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap(); let r = get_root_html_from_dir_text_html(i).unwrap();
@ -456,3 +450,131 @@ fn t032(){
).unwrap(); ).unwrap();
assert!(["->1 ->2".to_string(), "->2 ->1".to_string()].contains(&a)); assert!(["->1 ->2".to_string(), "->2 ->1".to_string()].contains(&a));
} }
#[test]
fn t033(){
let i = MtgottDirContent{mtgott: vec![], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{% if 1%}Yes{%}".into()}
], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render(
Value::Int(0), "index", 50
).unwrap(), "Yes");
}
#[test]
fn t034(){
let i = MtgottDirContent{mtgott: vec![], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{% if 0%}Yes{%}".into()}
], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render(
Value::Int(0), "index", 50
).unwrap(), "");
}
#[test]
fn t035(){
let i = MtgottDirContent{mtgott: vec![], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{%if 0%}Yes{%endif%}".into()}
], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render(
Value::Int(0), "index", 50
).unwrap(), "");
}
#[test]
fn t036(){
let i = MtgottDirContent{mtgott: vec![], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{%if 0%}Yes{%else%}No{%}".into()}
], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render(
Value::Int(0), "index", 50
).unwrap(), "No");
}
#[test]
fn t037(){
let i = MtgottDirContent{mtgott: vec![], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{%if \"0\"%}Yes{%else%}No{%endif%}".into()}
], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render(
Value::Int(0), "index", 50
).unwrap(), "Yes");
}
#[test]
fn t038(){
let i = MtgottDirContent{mtgott: vec![], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{%if \"0\"%}Yes{%else%}No{%endif%}".into()}
], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render(
Value::Int(0), "index", 50
).unwrap(), "Yes");
}
#[test]
fn t039(){
let i = MtgottDirContent{mtgott: vec![], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{%if \"\"%}Yes{%else if 1%}Or Maybe{%else%}No{%endif%}".into()}
], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render(
Value::Int(0), "index", 50
).unwrap(), "Or Maybe");
}
#[test]
fn t040(){
let i = MtgottDirContent{mtgott: vec![], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{%if \"\"%}Yes{%else if 0%}Or Maybe{%}".into()}
], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render(
Value::Int(0), "index", 50
).unwrap(), "");
}
#[test]
fn t041(){
let i = MtgottDirContent{mtgott: vec![
FileWithPath{v_path: "dictionaries".into(), text: "{$ empty $}{$} {$second$}{$el = 3$} {$}".into()}
], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "{%if dictionaries.empty%}Yes {%else if dictionaries.second %}Or Maybe {%}".into()}
], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render(
Value::Int(0), "index", 50
).unwrap(), "Or Maybe");
}
#[test]
fn t042(){
let i = MtgottDirContent{mtgott: vec![], imtgott: vec![
FileWithPath{v_path: "index".into(), text: "
START
{%for x: $%}
{%if x.a%} A {% else if x.b%}B {%else if x.c%}C {%else%} D{%}
{%lf%}
END
".into()}
], plain: vec![]};
let r = get_root_html_from_dir_text_html(i).unwrap();
assert_gave!(r.render(
valarr![
valdict!["a" => Value::Int(1), "b" => Value::Int(1), "c" => Value::Int(1),],
valdict!["a" => Value::Int(0), "b" => Value::Int(1), "c" => Value::Int(1),],
valdict!["a" => Value::Int(0), "b" => Value::Int(0), "c" => Value::Int(1),],
valdict!["a" => Value::Int(0), "b" => Value::Int(0), "c" => Value::Int(0),],
],
"index", 50
).unwrap(), "START\n A\n B\n C\n D\nEND");
}

View File

@ -9,6 +9,48 @@ use axum::http;
use mtgott::dirsearch::{search_dir, get_root_html}; use mtgott::dirsearch::{search_dir, get_root_html};
use mtgott::runtime::{MTGOTT, Value}; use mtgott::runtime::{MTGOTT, Value};
use std::error::Error; use std::error::Error;
use axum::http::header::ACCEPT_LANGUAGE;
use std::rc::Rc;
use chrono::{Datelike, TimeZone, Utc};
macro_rules! valarr {
($($el:expr),* $(,)?) => {
mtgott::runtime::Value::Arr(std::rc::Rc::new(
vec![$($el),*]
))
};
}
macro_rules! valdict {
($($el:literal => $val:expr),* $(,)?) => {{
let mut hashmap: std::collections::HashMap<String, mtgott::runtime::Value> = std::collections::HashMap::new();
$(hashmap.insert($el.to_string(), $val);)*
mtgott::runtime::Value::Dict(std::rc::Rc::new(hashmap))
}}
}
fn years_between<T: chrono::Datelike>(start: T, end: T) -> i32 {
let mut years = end.year() - start.year();
if (end.month(), end.day()) < (start.month(), start.day()) {
years -= 1;
}
years
}
struct YyyiConfig {
birthday: chrono::DateTime<Utc>,
}
impl YyyiConfig {
fn for_me() -> YyyiConfig { Self {
birthday: Utc.with_ymd_and_hms(2005, 06, 21, 0, 0, 0).single().unwrap(),
} }
}
struct LanguageConfig {
available: Vec<String>,
default: String,
}
struct StaticAsset { struct StaticAsset {
content_type: HeaderValue, content_type: HeaderValue,
@ -18,6 +60,8 @@ struct StaticAsset {
struct AssetsCache { struct AssetsCache {
static_assets: HashMap<String, StaticAsset>, static_assets: HashMap<String, StaticAsset>,
pages: MTGOTT, pages: MTGOTT,
l_conf: LanguageConfig,
misc_cfg: YyyiConfig,
} }
struct ExtensionContentTypeCorr{ struct ExtensionContentTypeCorr{
@ -56,7 +100,12 @@ impl AssetsCache {
fn load_assets(assets_dir: &str) -> Result<AssetsCache, Box<dyn Error>> { fn load_assets(assets_dir: &str) -> Result<AssetsCache, Box<dyn Error>> {
Ok(AssetsCache { Ok(AssetsCache {
static_assets: load_needed_static_assets(assets_dir)?, static_assets: load_needed_static_assets(assets_dir)?,
pages: get_root_html(&format!("{assets_dir}/HypertextPages"))? pages: get_root_html(&format!("{assets_dir}/HypertextPages"))?,
l_conf: LanguageConfig{
available: vec!["ru-RU".into(), "en-US".into()],
default: "ru-RU".into(),
},
misc_cfg: YyyiConfig::for_me(),
}) })
} }
} }
@ -76,21 +125,23 @@ async fn static_assets(
async fn page_index( async fn page_index(
axum::extract::State(assets): axum::extract::State<Arc<AssetsCache>>, axum::extract::State(assets): axum::extract::State<Arc<AssetsCache>>,
// axum::extract::Extension(t): axum::extract::Extension<tera::Tera>
) -> axum::response::Html<String> { ) -> axum::response::Html<String> {
let gr = Value::Int(0); let now = chrono::Utc::now();
axum::response::Html(assets.pages.render(gr, "index", 500).unwrap()) let age = years_between(assets.misc_cfg.birthday, now);
let gr = valdict![
"age" => Value::Int(age as i64),
"lang" => Value::Str(Rc::new(assets.l_conf.default.clone()))
];
axum::response::Html(assets.pages.render(gr, "index.main", 500).unwrap())
} }
async fn page_blog( async fn page_blog(
axum::extract::State(assets): axum::extract::State<Arc<AssetsCache>>, axum::extract::State(assets): axum::extract::State<Arc<AssetsCache>>,
// axum::extract::Extension(t): axum::extract::Extension<tera::Tera>
) -> impl axum::response::IntoResponse { ) -> impl axum::response::IntoResponse {
// let mut context = tera::Context::new(); let gr = valdict![
// context.insert("lang", &assets.missing_text[0].0); "lang" => Value::Str(Rc::new(assets.l_conf.default.clone()))
// context.insert("pres", &assets.missing_text[0].1); ];
// axum::response::Html(assets.templates.render("blog.html", &context).expect("Tera failure")) axum::response::Html(assets.pages.render(gr, "blog.main", 500).unwrap())
axum::response::Html(String::new())
} }
async fn fallback_page() -> axum::http::StatusCode { async fn fallback_page() -> axum::http::StatusCode {
@ -108,8 +159,8 @@ pub async fn run_yyyi_ru() {
let app = axum::Router::new() let app = axum::Router::new()
.without_v07_checks() .without_v07_checks()
.route("/", axum::routing::MethodRouter::new().get(page_index)) .route("/", axum::routing::MethodRouter::new().get(page_index))
.route("/assets/{*path}", axum::routing::get(static_assets))
.route("/blog", axum::routing::get(page_blog)) .route("/blog", axum::routing::get(page_blog))
.route("/assets/{*path}", axum::routing::get(static_assets))
.fallback(fallback_page).with_state(assets); .fallback(fallback_page).with_state(assets);
// .layer(axum::Extension(templates)); // .layer(axum::Extension(templates));