Compare commits
2 commits
7a2c19af66
...
08ae603106
Author | SHA1 | Date | |
---|---|---|---|
08ae603106 | |||
b814c57355 |
28 changed files with 515 additions and 320 deletions
32
docs/external/graphviz.nml
vendored
Normal file
32
docs/external/graphviz.nml
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
@import ../template.nml
|
||||||
|
%<make_doc({"External Tools"}, "Graphviz", "Graphvis")>%
|
||||||
|
|
||||||
|
# Graphs from graphviz
|
||||||
|
|
||||||
|
[graph]
|
||||||
|
digraph {
|
||||||
|
bgcolor=transparent;
|
||||||
|
|
||||||
|
filelist [color=green, label="File List"];
|
||||||
|
doclist [color=green, label="Document List"];
|
||||||
|
iscached [shape=diamond, color=red, label="Cached?"];
|
||||||
|
parse [color=white, label=Parse];
|
||||||
|
compile [color=white, label=Compile];
|
||||||
|
cache [shape=box, color=blue, label=Cache];
|
||||||
|
|
||||||
|
edge [color=gray]
|
||||||
|
filelist -> iscached;
|
||||||
|
iscached -> cache[dir=both];
|
||||||
|
iscached -> doclist[label="+"];
|
||||||
|
|
||||||
|
iscached -> parse[label="No"];
|
||||||
|
parse -> compile;
|
||||||
|
compile -> cache[label="+"];
|
||||||
|
compile -> doclist[label="+"];
|
||||||
|
|
||||||
|
buildnav [color=white, label="Build Navigation"];
|
||||||
|
doclist -> buildnav;
|
||||||
|
output [color=white, label="Output"];
|
||||||
|
buildnav -> output;
|
||||||
|
}
|
||||||
|
[/graph]
|
5
docs/external/latex.nml
vendored
5
docs/external/latex.nml
vendored
|
@ -1,8 +1,5 @@
|
||||||
@import ../template.nml
|
@import ../template.nml
|
||||||
@compiler.output = latex.html
|
%<make_doc({"External Tools"}, "LaTeX", "LaTeX")>%
|
||||||
@nav.title = LaTeX
|
|
||||||
@nav.category = External Tools
|
|
||||||
@html.page_title = Documentation | LaTeX
|
|
||||||
|
|
||||||
@LaTeX = $|[kind=inline, caption=LaTeX]\LaTeX|$
|
@LaTeX = $|[kind=inline, caption=LaTeX]\LaTeX|$
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
@import template.nml
|
@import template.nml
|
||||||
@compiler.output = index.html
|
%<make_doc({}, "Index", "Index")>%
|
||||||
@nav.title = Documentation
|
|
||||||
@html.page_title = Documentation | Index
|
|
||||||
|
|
||||||
# Welcome to the NML documentation!
|
# Welcome to the NML documentation!
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
@import ../template.nml
|
@import ../template.nml
|
||||||
@compiler.output = lua.html
|
%<make_doc({"Lua"}, "Lua", "Lua Basics")>%
|
||||||
@nav.title = Lua
|
|
||||||
@nav.category = Lua
|
|
||||||
@html.page_title = Documentation | Lua
|
|
||||||
|
|
||||||
# Running lua code
|
# Running lua code
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
@import ../template.nml
|
@import ../template.nml
|
||||||
@compiler.output = basic.html
|
%<make_doc({"Styles"}, "Basic", "Basic Styles")>%
|
||||||
@nav.title = Basic
|
|
||||||
@nav.category = Styles
|
|
||||||
@html.page_title = Documentation | Basic Styles
|
|
||||||
|
|
||||||
# Basic styles
|
# Basic styles
|
||||||
## Bold
|
## Bold
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
@import ../template.nml
|
@import ../template.nml
|
||||||
@compiler.output = user-defined.html
|
%<make_doc({"Styles"}, "User-Defined", "User-Defined Styles")>%
|
||||||
@nav.title = User-Defined
|
|
||||||
@nav.category = Styles
|
|
||||||
@html.page_title = Documentation | User-Defined Styles
|
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
|
|
|
@ -6,3 +6,22 @@
|
||||||
\definecolor{__color1}{HTML}{d5d5d5} \\
|
\definecolor{__color1}{HTML}{d5d5d5} \\
|
||||||
\everymath{\color{__color1}\displaystyle}
|
\everymath{\color{__color1}\displaystyle}
|
||||||
@tex.main.block_prepend = \color{__color1}
|
@tex.main.block_prepend = \color{__color1}
|
||||||
|
|
||||||
|
@<
|
||||||
|
function make_doc(categories, title, page_title)
|
||||||
|
-- Navigation
|
||||||
|
nml.variable.insert("nav.title", title)
|
||||||
|
if categories[1] ~= nil
|
||||||
|
then
|
||||||
|
nml.variable.insert("nav.category", categories[1])
|
||||||
|
if categories[2] ~= nil
|
||||||
|
then
|
||||||
|
nml.variable.insert("nav.subcategory", categories[2])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- HTML
|
||||||
|
nml.variable.insert("html.page_title", "NML | " .. page_title)
|
||||||
|
nml.variable.insert("compiler.output", page_title .. ".html")
|
||||||
|
end
|
||||||
|
>@
|
||||||
|
|
|
@ -38,6 +38,24 @@ impl Compiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sanitizes text for a [`Target`]
|
||||||
|
pub fn sanitize<S: AsRef<str>>(target: Target, str: S) -> String {
|
||||||
|
match target {
|
||||||
|
Target::HTML => str
|
||||||
|
.as_ref()
|
||||||
|
.replace("&", "&")
|
||||||
|
.replace("<", "<")
|
||||||
|
.replace(">", ">")
|
||||||
|
.replace("\"", """),
|
||||||
|
_ => todo!("Sanitize not implemented"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a reference name
|
||||||
|
pub fn refname<S: AsRef<str>>(target: Target, str: S) -> String {
|
||||||
|
Self::sanitize(target, str).replace(' ', "_")
|
||||||
|
}
|
||||||
|
|
||||||
/// Inserts or get a reference id for the compiled document
|
/// Inserts or get a reference id for the compiled document
|
||||||
///
|
///
|
||||||
/// # Parameters
|
/// # Parameters
|
||||||
|
@ -74,18 +92,6 @@ impl Compiler {
|
||||||
self.cache.as_ref().map(RefCell::borrow_mut)
|
self.cache.as_ref().map(RefCell::borrow_mut)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sanitize<S: AsRef<str>>(target: Target, str: S) -> String {
|
|
||||||
match target {
|
|
||||||
Target::HTML => str
|
|
||||||
.as_ref()
|
|
||||||
.replace("&", "&")
|
|
||||||
.replace("<", "<")
|
|
||||||
.replace(">", ">")
|
|
||||||
.replace("\"", """),
|
|
||||||
_ => todo!("Sanitize not implemented"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn header(&self, document: &dyn Document) -> String {
|
pub fn header(&self, document: &dyn Document) -> String {
|
||||||
pub fn get_variable_or_error(
|
pub fn get_variable_or_error(
|
||||||
document: &dyn Document,
|
document: &dyn Document,
|
||||||
|
@ -109,7 +115,10 @@ impl Compiler {
|
||||||
result += "<!DOCTYPE HTML><html><head>";
|
result += "<!DOCTYPE HTML><html><head>";
|
||||||
result += "<meta charset=\"UTF-8\">";
|
result += "<meta charset=\"UTF-8\">";
|
||||||
if let Some(page_title) = get_variable_or_error(document, "html.page_title") {
|
if let Some(page_title) = get_variable_or_error(document, "html.page_title") {
|
||||||
result += format!("<title>{}</title>", Compiler::sanitize(self.target(), page_title.to_string()))
|
result += format!(
|
||||||
|
"<title>{}</title>",
|
||||||
|
Compiler::sanitize(self.target(), page_title.to_string())
|
||||||
|
)
|
||||||
.as_str();
|
.as_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +129,7 @@ impl Compiler {
|
||||||
)
|
)
|
||||||
.as_str();
|
.as_str();
|
||||||
}
|
}
|
||||||
result += r#"</head><body><div id="layout">"#;
|
result += r#"</head><body><div class="layout">"#;
|
||||||
|
|
||||||
// TODO: TOC
|
// TODO: TOC
|
||||||
// TODO: Author, Date, Title, Div
|
// TODO: Author, Date, Title, Div
|
||||||
|
@ -148,7 +157,7 @@ impl Compiler {
|
||||||
let header = self.header(document);
|
let header = self.header(document);
|
||||||
|
|
||||||
// Body
|
// Body
|
||||||
let mut body = r#"<div id="content">"#.to_string();
|
let mut body = r#"<div class="content">"#.to_string();
|
||||||
for i in 0..borrow.len() {
|
for i in 0..borrow.len() {
|
||||||
let elem = &borrow[i];
|
let elem = &borrow[i];
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ impl NavEntry {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
match target {
|
match target {
|
||||||
Target::HTML => {
|
Target::HTML => {
|
||||||
result += r#"<div id="navbar"><ul>"#;
|
result += r#"<div class="navbar"><ul>"#;
|
||||||
|
|
||||||
fn process(
|
fn process(
|
||||||
target: Target,
|
target: Target,
|
||||||
|
|
|
@ -544,8 +544,7 @@ impl RegexRule for CodeRule {
|
||||||
reports
|
reports
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
|
||||||
let mut bindings = vec![];
|
let mut bindings = vec![];
|
||||||
bindings.push((
|
bindings.push((
|
||||||
"push_inline".to_string(),
|
"push_inline".to_string(),
|
||||||
|
@ -647,7 +646,7 @@ impl RegexRule for CodeRule {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
));
|
));
|
||||||
|
|
||||||
bindings
|
Some(bindings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,5 +80,5 @@ impl RegexRule for CommentRule {
|
||||||
return reports;
|
return reports;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
}
|
}
|
||||||
|
|
|
@ -371,5 +371,5 @@ impl RegexRule for GraphRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,5 +178,5 @@ impl RegexRule for ImportRule {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,5 +171,5 @@ impl RegexRule for LinkRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
}
|
}
|
||||||
|
|
|
@ -337,5 +337,5 @@ impl Rule for ListRule
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ use ariadne::Fmt;
|
||||||
use ariadne::Label;
|
use ariadne::Label;
|
||||||
use ariadne::Report;
|
use ariadne::Report;
|
||||||
use ariadne::ReportKind;
|
use ariadne::ReportKind;
|
||||||
|
use mlua::Function;
|
||||||
|
use mlua::Lua;
|
||||||
use regex::Captures;
|
use regex::Captures;
|
||||||
use regex::Match;
|
use regex::Match;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
@ -518,9 +520,7 @@ impl RegexRule for MediaRule {
|
||||||
reports
|
reports
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua mlua::Lua) -> Vec<(String, mlua::Function<'lua>)> {
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -150,5 +150,5 @@ impl Rule for ParagraphRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,7 +231,7 @@ impl RegexRule for RawRule {
|
||||||
reports
|
reports
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||||
let mut bindings = vec![];
|
let mut bindings = vec![];
|
||||||
|
|
||||||
bindings.push((
|
bindings.push((
|
||||||
|
@ -270,7 +270,7 @@ impl RegexRule for RawRule {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
));
|
));
|
||||||
|
|
||||||
bindings
|
Some(bindings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ use ariadne::Fmt;
|
||||||
use ariadne::Label;
|
use ariadne::Label;
|
||||||
use ariadne::Report;
|
use ariadne::Report;
|
||||||
use ariadne::ReportKind;
|
use ariadne::ReportKind;
|
||||||
|
use mlua::Function;
|
||||||
|
use mlua::Lua;
|
||||||
use regex::Captures;
|
use regex::Captures;
|
||||||
use regex::Match;
|
use regex::Match;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
@ -205,7 +207,5 @@ impl RegexRule for ReferenceRule {
|
||||||
reports
|
reports
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua mlua::Lua) -> Vec<(String, mlua::Function<'lua>)> {
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -275,5 +275,5 @@ impl RegexRule for ScriptRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,11 +38,14 @@ impl Element for Section {
|
||||||
fn as_referenceable(&self) -> Option<&dyn ReferenceableElement> { Some(self) }
|
fn as_referenceable(&self) -> Option<&dyn ReferenceableElement> { Some(self) }
|
||||||
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
||||||
match compiler.target() {
|
match compiler.target() {
|
||||||
Target::HTML => Ok(format!(
|
Target::HTML => {
|
||||||
"<h{0}>{1}</h{0}>",
|
Ok(format!(
|
||||||
|
r#"<h{0} id="{1}">{2}</h{0}>"#,
|
||||||
self.depth,
|
self.depth,
|
||||||
|
Compiler::refname(compiler.target(), self.title.as_str()),
|
||||||
Compiler::sanitize(compiler.target(), self.title.as_str())
|
Compiler::sanitize(compiler.target(), self.title.as_str())
|
||||||
)),
|
))
|
||||||
|
},
|
||||||
Target::LATEX => Err("Unimplemented compiler".to_string()),
|
Target::LATEX => Err("Unimplemented compiler".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,11 +59,27 @@ impl ReferenceableElement for Section {
|
||||||
fn compile_reference(
|
fn compile_reference(
|
||||||
&self,
|
&self,
|
||||||
compiler: &Compiler,
|
compiler: &Compiler,
|
||||||
document: &dyn Document,
|
_document: &dyn Document,
|
||||||
reference: &super::reference::Reference,
|
reference: &super::reference::Reference,
|
||||||
refid: usize,
|
_refid: usize,
|
||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
todo!()
|
match compiler.target() {
|
||||||
|
Target::HTML => {
|
||||||
|
let caption = reference.caption().map_or(
|
||||||
|
format!(
|
||||||
|
"({})",
|
||||||
|
Compiler::sanitize(compiler.target(), self.title.as_str())
|
||||||
|
),
|
||||||
|
|cap| cap.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(format!(
|
||||||
|
"<a class=\"section-ref\" href=\"#{}\">{caption}</a>",
|
||||||
|
Compiler::refname(compiler.target(), self.title.as_str())
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => todo!(""),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +254,7 @@ impl RegexRule for SectionRule {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||||
let mut bindings = vec![];
|
let mut bindings = vec![];
|
||||||
|
|
||||||
bindings.push((
|
bindings.push((
|
||||||
|
@ -280,6 +299,6 @@ impl RegexRule for SectionRule {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
));
|
));
|
||||||
|
|
||||||
bindings
|
Some(bindings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,5 +183,5 @@ impl RegexRule for StyleRule
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
}
|
}
|
||||||
|
|
|
@ -430,7 +430,7 @@ impl RegexRule for TexRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -61,7 +61,7 @@ impl Rule for TextRule {
|
||||||
panic!("Text cannot match");
|
panic!("Text cannot match");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||||
let mut bindings = vec![];
|
let mut bindings = vec![];
|
||||||
bindings.push((
|
bindings.push((
|
||||||
"push".to_string(),
|
"push".to_string(),
|
||||||
|
@ -83,6 +83,6 @@ impl Rule for TextRule {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
));
|
));
|
||||||
|
|
||||||
bindings
|
Some(bindings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,41 @@
|
||||||
use mlua::{Function, Lua};
|
use crate::document::document::Document;
|
||||||
|
use crate::document::variable::BaseVariable;
|
||||||
|
use crate::document::variable::PathVariable;
|
||||||
|
use crate::document::variable::Variable;
|
||||||
|
use crate::lua::kernel::CTX;
|
||||||
|
use crate::parser::parser::Parser;
|
||||||
|
use crate::parser::parser::ReportColors;
|
||||||
|
use crate::parser::rule::RegexRule;
|
||||||
|
use crate::parser::source::Source;
|
||||||
|
use crate::parser::source::Token;
|
||||||
|
use ariadne::Fmt;
|
||||||
|
use ariadne::Label;
|
||||||
|
use ariadne::Report;
|
||||||
|
use ariadne::ReportKind;
|
||||||
|
use mlua::Function;
|
||||||
|
use mlua::Lua;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use crate::{document::document::Document, parser::{parser::{Parser, ReportColors}, rule::RegexRule, source::{Source, Token}}};
|
use std::ops::Range;
|
||||||
use ariadne::{Report, Fmt, Label, ReportKind};
|
use std::rc::Rc;
|
||||||
use crate::document::variable::{BaseVariable, PathVariable, Variable};
|
use std::str::FromStr;
|
||||||
use std::{ops::Range, rc::Rc};
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum VariableKind {
|
||||||
|
Regular,
|
||||||
|
Path,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for VariableKind {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"regular" | "" => Ok(VariableKind::Regular),
|
||||||
|
"path" | "'" => Ok(VariableKind::Path),
|
||||||
|
_ => Err(format!("Uknnown variable kind: `{s}`")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct VariableRule {
|
pub struct VariableRule {
|
||||||
re: [Regex; 1],
|
re: [Regex; 1],
|
||||||
|
@ -14,20 +46,20 @@ impl VariableRule {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
re: [Regex::new(r"(?:^|\n)@([^[:alpha:]])?(.*?)=((?:\\\n|.)*)").unwrap()],
|
re: [Regex::new(r"(?:^|\n)@([^[:alpha:]])?(.*?)=((?:\\\n|.)*)").unwrap()],
|
||||||
kinds: vec![
|
kinds: vec![("".into(), "Regular".into()), ("'".into(), "Path".into())],
|
||||||
("".into(), "Regular".into()),
|
|
||||||
("'".into(), "Path".into())
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_variable(&self, colors: &ReportColors, location: Token, kind: usize, name: String, value: String) -> Result<Rc<dyn Variable>, String>
|
pub fn make_variable(
|
||||||
{
|
&self,
|
||||||
match self.kinds[kind].0.as_str()
|
colors: &ReportColors,
|
||||||
{
|
location: Token,
|
||||||
"" => {
|
kind: usize,
|
||||||
Ok(Rc::new(BaseVariable::new(location, name, value)))
|
name: String,
|
||||||
}
|
value: String,
|
||||||
|
) -> Result<Rc<dyn Variable>, String> {
|
||||||
|
match self.kinds[kind].0.as_str() {
|
||||||
|
"" => Ok(Rc::new(BaseVariable::new(location, name, value))),
|
||||||
"'" => {
|
"'" => {
|
||||||
match std::fs::canonicalize(value.as_str()) // TODO: not canonicalize
|
match std::fs::canonicalize(value.as_str()) // TODO: not canonicalize
|
||||||
{
|
{
|
||||||
|
@ -37,42 +69,41 @@ impl VariableRule {
|
||||||
e.to_string()))
|
e.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => panic!("Unhandled variable kind")
|
_ => panic!("Unhandled variable kind"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trim and check variable name for validity
|
// Trim and check variable name for validity
|
||||||
pub fn validate_name<'a>(colors: &ReportColors, original_name: &'a str) -> Result<&'a str, String>
|
pub fn validate_name<'a>(
|
||||||
{
|
colors: &ReportColors,
|
||||||
|
original_name: &'a str,
|
||||||
|
) -> Result<&'a str, String> {
|
||||||
let name = original_name.trim_start().trim_end();
|
let name = original_name.trim_start().trim_end();
|
||||||
if name.contains("%")
|
if name.contains("%") {
|
||||||
{
|
return Err(format!("Name cannot contain '{}'", "%".fg(colors.info)));
|
||||||
return Err(format!("Name cannot contain '{}'",
|
|
||||||
"%".fg(colors.info)));
|
|
||||||
}
|
}
|
||||||
return Ok(name);
|
return Ok(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_value(_colors: &ReportColors, original_value: &str) -> Result<String, String>
|
pub fn validate_value(_colors: &ReportColors, original_value: &str) -> Result<String, String> {
|
||||||
{
|
|
||||||
let mut escaped = 0usize;
|
let mut escaped = 0usize;
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
for c in original_value.trim_start().trim_end().chars() {
|
for c in original_value.trim_start().trim_end().chars() {
|
||||||
if c == '\\' { escaped += 1 }
|
if c == '\\' {
|
||||||
else if c == '\n' {
|
escaped += 1
|
||||||
|
} else if c == '\n' {
|
||||||
match escaped {
|
match escaped {
|
||||||
0 => return Err("Unknown error wile capturing variable".to_string()),
|
0 => return Err("Unknown error wile capturing variable".to_string()),
|
||||||
// Remove '\n'
|
// Remove '\n'
|
||||||
1 => {},
|
1 => {}
|
||||||
// Insert '\n'
|
// Insert '\n'
|
||||||
_ => {
|
_ => {
|
||||||
result.push(c);
|
result.push(c);
|
||||||
(0..escaped-2).for_each(|_| result.push('\\'));
|
(0..escaped - 2).for_each(|_| result.push('\\'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
escaped = 0;
|
escaped = 0;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
(0..escaped).for_each(|_| result.push('\\'));
|
(0..escaped).for_each(|_| result.push('\\'));
|
||||||
escaped = 0;
|
escaped = 0;
|
||||||
result.push(c);
|
result.push(c);
|
||||||
|
@ -89,35 +120,54 @@ impl RegexRule for VariableRule {
|
||||||
|
|
||||||
fn regexes(&self) -> &[Regex] { &self.re }
|
fn regexes(&self) -> &[Regex] { &self.re }
|
||||||
|
|
||||||
fn on_regex_match<'a>(&self, _: usize, parser: &dyn Parser, document: &'a dyn Document, token: Token, matches: regex::Captures) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>
|
fn on_regex_match<'a>(
|
||||||
{
|
&self,
|
||||||
|
_: usize,
|
||||||
|
parser: &dyn Parser,
|
||||||
|
document: &'a dyn Document,
|
||||||
|
token: Token,
|
||||||
|
matches: regex::Captures,
|
||||||
|
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
|
||||||
let mut result = vec![];
|
let mut result = vec![];
|
||||||
// [Optional] variable kind
|
// [Optional] variable kind
|
||||||
let var_kind = match matches.get(1)
|
let var_kind = match matches.get(1) {
|
||||||
{
|
|
||||||
Some(kind) => {
|
Some(kind) => {
|
||||||
// Find kind
|
// Find kind
|
||||||
let r = self.kinds.iter().enumerate().find(|(_i, (ref char, ref _name))| {
|
let r = self
|
||||||
char == kind.as_str() });
|
.kinds
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_i, (ref char, ref _name))| char == kind.as_str());
|
||||||
|
|
||||||
// Unknown kind specified
|
// Unknown kind specified
|
||||||
if r.is_none()
|
if r.is_none() {
|
||||||
{
|
|
||||||
result.push(
|
result.push(
|
||||||
Report::build(ReportKind::Error, token.source(), kind.start())
|
Report::build(ReportKind::Error, token.source(), kind.start())
|
||||||
.with_message("Unknown variable kind")
|
.with_message("Unknown variable kind")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source(), kind.range()))
|
Label::new((token.source(), kind.range()))
|
||||||
.with_message(format!("Variable kind `{}` is unknown",
|
.with_message(format!(
|
||||||
kind.as_str().fg(parser.colors().highlight)))
|
"Variable kind `{}` is unknown",
|
||||||
.with_color(parser.colors().error))
|
kind.as_str().fg(parser.colors().highlight)
|
||||||
.with_help(format!("Leave empty for regular variables. Available variable kinds:{}",
|
))
|
||||||
self.kinds.iter().skip(1).fold("".to_string(), |acc, (char, name)| {
|
.with_color(parser.colors().error),
|
||||||
acc + format!("\n - `{}` : {}",
|
)
|
||||||
|
.with_help(format!(
|
||||||
|
"Leave empty for regular variables. Available variable kinds:{}",
|
||||||
|
self.kinds.iter().skip(1).fold(
|
||||||
|
"".to_string(),
|
||||||
|
|acc, (char, name)| {
|
||||||
|
acc + format!(
|
||||||
|
"\n - `{}` : {}",
|
||||||
char.fg(parser.colors().highlight),
|
char.fg(parser.colors().highlight),
|
||||||
name.fg(parser.colors().info)).as_str()
|
name.fg(parser.colors().info)
|
||||||
})))
|
)
|
||||||
.finish());
|
.as_str()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
))
|
||||||
|
.finish(),
|
||||||
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -127,11 +177,8 @@ impl RegexRule for VariableRule {
|
||||||
None => 0,
|
None => 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let var_name = match matches.get(2)
|
let var_name = match matches.get(2) {
|
||||||
{
|
Some(name) => match VariableRule::validate_name(&parser.colors(), name.as_str()) {
|
||||||
Some(name) => {
|
|
||||||
match VariableRule::validate_name(&parser.colors(), name.as_str())
|
|
||||||
{
|
|
||||||
Ok(var_name) => var_name,
|
Ok(var_name) => var_name,
|
||||||
Err(msg) => {
|
Err(msg) => {
|
||||||
result.push(
|
result.push(
|
||||||
|
@ -139,44 +186,52 @@ impl RegexRule for VariableRule {
|
||||||
.with_message("Invalid variable name")
|
.with_message("Invalid variable name")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source(), name.range()))
|
Label::new((token.source(), name.range()))
|
||||||
.with_message(format!("Variable name `{}` is not allowed. {msg}",
|
.with_message(format!(
|
||||||
name.as_str().fg(parser.colors().highlight)))
|
"Variable name `{}` is not allowed. {msg}",
|
||||||
.with_color(parser.colors().error))
|
name.as_str().fg(parser.colors().highlight)
|
||||||
.finish());
|
))
|
||||||
|
.with_color(parser.colors().error),
|
||||||
|
)
|
||||||
|
.finish(),
|
||||||
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => panic!("Unknown variable name")
|
_ => panic!("Unknown variable name"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let var_value = match matches.get(3)
|
let var_value = match matches.get(3) {
|
||||||
{
|
Some(value) => match VariableRule::validate_value(&parser.colors(), value.as_str()) {
|
||||||
Some(value) => {
|
|
||||||
match VariableRule::validate_value(&parser.colors(), value.as_str())
|
|
||||||
{
|
|
||||||
Ok(var_value) => var_value,
|
Ok(var_value) => var_value,
|
||||||
Err(msg ) => {
|
Err(msg) => {
|
||||||
result.push(
|
result.push(
|
||||||
Report::build(ReportKind::Error, token.source(), value.start())
|
Report::build(ReportKind::Error, token.source(), value.start())
|
||||||
.with_message("Invalid variable value")
|
.with_message("Invalid variable value")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source(), value.range()))
|
Label::new((token.source(), value.range()))
|
||||||
.with_message(format!("Variable value `{}` is not allowed. {msg}",
|
.with_message(format!(
|
||||||
value.as_str().fg(parser.colors().highlight)))
|
"Variable value `{}` is not allowed. {msg}",
|
||||||
.with_color(parser.colors().error))
|
value.as_str().fg(parser.colors().highlight)
|
||||||
.finish());
|
))
|
||||||
|
.with_color(parser.colors().error),
|
||||||
|
)
|
||||||
|
.finish(),
|
||||||
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
_ => panic!("Invalid variable value"),
|
||||||
_ => panic!("Invalid variable value")
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.make_variable(&parser.colors(), token.clone(), var_kind, var_name.to_string(), var_value)
|
match self.make_variable(
|
||||||
{
|
&parser.colors(),
|
||||||
|
token.clone(),
|
||||||
|
var_kind,
|
||||||
|
var_name.to_string(),
|
||||||
|
var_value,
|
||||||
|
) {
|
||||||
Ok(variable) => document.add_variable(variable),
|
Ok(variable) => document.add_variable(variable),
|
||||||
Err(msg) => {
|
Err(msg) => {
|
||||||
let m = matches.get(0).unwrap();
|
let m = matches.get(0).unwrap();
|
||||||
|
@ -184,12 +239,16 @@ impl RegexRule for VariableRule {
|
||||||
Report::build(ReportKind::Error, token.source(), m.start())
|
Report::build(ReportKind::Error, token.source(), m.start())
|
||||||
.with_message("Unable to create variable")
|
.with_message("Unable to create variable")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source(), m.start()+1 .. m.end() ))
|
Label::new((token.source(), m.start() + 1..m.end()))
|
||||||
.with_message(format!("Unable to create variable `{}`. {}",
|
.with_message(format!(
|
||||||
|
"Unable to create variable `{}`. {}",
|
||||||
var_name.fg(parser.colors().highlight),
|
var_name.fg(parser.colors().highlight),
|
||||||
msg))
|
msg
|
||||||
.with_color(parser.colors().error))
|
))
|
||||||
.finish());
|
.with_color(parser.colors().error),
|
||||||
|
)
|
||||||
|
.finish(),
|
||||||
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -198,12 +257,45 @@ impl RegexRule for VariableRule {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
|
let mut bindings = vec![];
|
||||||
|
bindings.push((
|
||||||
|
"insert".to_string(),
|
||||||
|
lua.create_function(|_, (name, value): (String, String)| {
|
||||||
|
CTX.with_borrow(|ctx| {
|
||||||
|
ctx.as_ref().map(|ctx| {
|
||||||
|
let var = Rc::new(BaseVariable::new(ctx.location.clone(), name, value));
|
||||||
|
ctx.document.add_variable(var);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.unwrap(),
|
||||||
|
));
|
||||||
|
bindings.push((
|
||||||
|
"get".to_string(),
|
||||||
|
lua.create_function(|_, name: String| {
|
||||||
|
let mut value: Option<String> = None;
|
||||||
|
CTX.with_borrow(|ctx| {
|
||||||
|
ctx.as_ref().map(|ctx| {
|
||||||
|
if let Some(var) = ctx.document.get_variable(name.as_str())
|
||||||
|
{
|
||||||
|
value = Some(var.to_string());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
|
})
|
||||||
|
.unwrap(),
|
||||||
|
));
|
||||||
|
|
||||||
|
Some(bindings)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VariableSubstitutionRule
|
pub struct VariableSubstitutionRule {
|
||||||
{
|
|
||||||
re: [Regex; 1],
|
re: [Regex; 1],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,100 +307,113 @@ impl VariableSubstitutionRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegexRule for VariableSubstitutionRule
|
impl RegexRule for VariableSubstitutionRule {
|
||||||
{
|
|
||||||
fn name(&self) -> &'static str { "Variable Substitution" }
|
fn name(&self) -> &'static str { "Variable Substitution" }
|
||||||
|
|
||||||
fn regexes(&self) -> &[regex::Regex] { &self.re }
|
fn regexes(&self) -> &[regex::Regex] { &self.re }
|
||||||
|
|
||||||
fn on_regex_match<'a>(&self, _index: usize, parser: &dyn Parser, document: &'a dyn Document<'a>, token: Token, matches: regex::Captures) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
|
fn on_regex_match<'a>(
|
||||||
|
&self,
|
||||||
|
_index: usize,
|
||||||
|
parser: &dyn Parser,
|
||||||
|
document: &'a dyn Document<'a>,
|
||||||
|
token: Token,
|
||||||
|
matches: regex::Captures,
|
||||||
|
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
|
||||||
let mut result = vec![];
|
let mut result = vec![];
|
||||||
|
|
||||||
let variable = match matches.get(1)
|
let variable = match matches.get(1) {
|
||||||
{
|
|
||||||
Some(name) => {
|
Some(name) => {
|
||||||
// Empty name
|
// Empty name
|
||||||
if name.as_str().is_empty()
|
if name.as_str().is_empty() {
|
||||||
{
|
|
||||||
result.push(
|
result.push(
|
||||||
Report::build(ReportKind::Error, token.source(), name.start())
|
Report::build(ReportKind::Error, token.source(), name.start())
|
||||||
.with_message("Empty variable name")
|
.with_message("Empty variable name")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source(), matches.get(0).unwrap().range()))
|
Label::new((token.source(), matches.get(0).unwrap().range()))
|
||||||
.with_message(format!("Missing variable name for substitution"))
|
.with_message(format!("Missing variable name for substitution"))
|
||||||
.with_color(parser.colors().error))
|
.with_color(parser.colors().error),
|
||||||
.finish());
|
)
|
||||||
|
.finish(),
|
||||||
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
// Leading spaces
|
// Leading spaces
|
||||||
else if name.as_str().trim_start() != name.as_str()
|
else if name.as_str().trim_start() != name.as_str() {
|
||||||
{
|
|
||||||
result.push(
|
result.push(
|
||||||
Report::build(ReportKind::Error, token.source(), name.start())
|
Report::build(ReportKind::Error, token.source(), name.start())
|
||||||
.with_message("Invalid variable name")
|
.with_message("Invalid variable name")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source(), name.range()))
|
Label::new((token.source(), name.range()))
|
||||||
.with_message(format!("Variable names contains leading spaces"))
|
.with_message(format!("Variable names contains leading spaces"))
|
||||||
.with_color(parser.colors().error))
|
.with_color(parser.colors().error),
|
||||||
|
)
|
||||||
.with_help("Remove leading spaces")
|
.with_help("Remove leading spaces")
|
||||||
.finish());
|
.finish(),
|
||||||
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
// Trailing spaces
|
// Trailing spaces
|
||||||
else if name.as_str().trim_end() != name.as_str()
|
else if name.as_str().trim_end() != name.as_str() {
|
||||||
{
|
|
||||||
result.push(
|
result.push(
|
||||||
Report::build(ReportKind::Error, token.source(), name.start())
|
Report::build(ReportKind::Error, token.source(), name.start())
|
||||||
.with_message("Invalid variable name")
|
.with_message("Invalid variable name")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source(), name.range()))
|
Label::new((token.source(), name.range()))
|
||||||
.with_message(format!("Variable names contains trailing spaces"))
|
.with_message(format!(
|
||||||
.with_color(parser.colors().error))
|
"Variable names contains trailing spaces"
|
||||||
|
))
|
||||||
|
.with_color(parser.colors().error),
|
||||||
|
)
|
||||||
.with_help("Remove trailing spaces")
|
.with_help("Remove trailing spaces")
|
||||||
.finish());
|
.finish(),
|
||||||
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
// Invalid name
|
// Invalid name
|
||||||
match VariableRule::validate_name(&parser.colors(), name.as_str())
|
match VariableRule::validate_name(&parser.colors(), name.as_str()) {
|
||||||
{
|
Err(msg) => {
|
||||||
Err(msg) =>
|
|
||||||
{
|
|
||||||
result.push(
|
result.push(
|
||||||
Report::build(ReportKind::Error, token.source(), name.start())
|
Report::build(ReportKind::Error, token.source(), name.start())
|
||||||
.with_message("Invalid variable name")
|
.with_message("Invalid variable name")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source(), name.range()))
|
Label::new((token.source(), name.range()))
|
||||||
.with_message(msg)
|
.with_message(msg)
|
||||||
.with_color(parser.colors().error))
|
.with_color(parser.colors().error),
|
||||||
.finish());
|
)
|
||||||
|
.finish(),
|
||||||
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
_ => {},
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get variable
|
// Get variable
|
||||||
match document.get_variable(name.as_str())
|
match document.get_variable(name.as_str()) {
|
||||||
{
|
|
||||||
None => {
|
None => {
|
||||||
result.push(
|
result.push(
|
||||||
Report::build(ReportKind::Error, token.source(), name.start())
|
Report::build(ReportKind::Error, token.source(), name.start())
|
||||||
.with_message("Unknown variable name")
|
.with_message("Unknown variable name")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source(), name.range()))
|
Label::new((token.source(), name.range()))
|
||||||
.with_message(format!("Unable to find variable with name: `{}`",
|
.with_message(format!(
|
||||||
name.as_str().fg(parser.colors().highlight)))
|
"Unable to find variable with name: `{}`",
|
||||||
.with_color(parser.colors().error))
|
name.as_str().fg(parser.colors().highlight)
|
||||||
.finish());
|
))
|
||||||
|
.with_color(parser.colors().error),
|
||||||
|
)
|
||||||
|
.finish(),
|
||||||
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
Some(var) => var,
|
Some(var) => var,
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => panic!("Unknown error")
|
_ => panic!("Unknown error"),
|
||||||
};
|
};
|
||||||
|
|
||||||
variable.parse(token, parser, document);
|
variable.parse(token, parser, document);
|
||||||
|
@ -316,6 +421,5 @@ impl RegexRule for VariableSubstitutionRule
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,17 +33,20 @@ impl Kernel {
|
||||||
let nml_table = lua.create_table().unwrap();
|
let nml_table = lua.create_table().unwrap();
|
||||||
|
|
||||||
for rule in parser.rules()
|
for rule in parser.rules()
|
||||||
|
{
|
||||||
|
if let Some(bindings) = rule.lua_bindings(&lua)
|
||||||
{
|
{
|
||||||
let table = lua.create_table().unwrap();
|
let table = lua.create_table().unwrap();
|
||||||
let name = rule.name().to_lowercase();
|
let name = rule.name().to_lowercase();
|
||||||
|
|
||||||
for (fun_name, fun) in rule.lua_bindings(&lua)
|
for (fun_name, fun) in bindings
|
||||||
{
|
{
|
||||||
table.set(fun_name, fun).unwrap();
|
table.set(fun_name, fun).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
nml_table.set(name, table).unwrap();
|
nml_table.set(name, table).unwrap();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
lua.globals().set("nml", nml_table).unwrap();
|
lua.globals().set("nml", nml_table).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ pub trait Rule {
|
||||||
match_data: Option<Box<dyn Any>>,
|
match_data: Option<Box<dyn Any>>,
|
||||||
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>);
|
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>);
|
||||||
/// Export bindings to lua
|
/// Export bindings to lua
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)>;
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::fmt::Debug for dyn Rule {
|
impl core::fmt::Debug for dyn Rule {
|
||||||
|
@ -89,7 +89,7 @@ pub trait RegexRule {
|
||||||
matches: regex::Captures,
|
matches: regex::Captures,
|
||||||
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>;
|
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>;
|
||||||
|
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)>;
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: RegexRule> Rule for T {
|
impl<T: RegexRule> Rule for T {
|
||||||
|
@ -144,7 +144,7 @@ impl<T: RegexRule> Rule for T {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
|
||||||
self.lua_bindings(lua)
|
self.lua_bindings(lua)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
52
style.css
52
style.css
|
@ -2,9 +2,19 @@ body {
|
||||||
background-color: #1b1b1d;
|
background-color: #1b1b1d;
|
||||||
color: #c5c5c5;
|
color: #c5c5c5;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
max-width: 90ch;
|
.layout {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
max-width: 99ch;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Styles */
|
/* Styles */
|
||||||
|
@ -29,13 +39,18 @@ a.inline-code {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Navbar */
|
/* Navbar */
|
||||||
#navbar {
|
.navbar {
|
||||||
|
display: none;
|
||||||
|
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: max(16vw, 20ch);
|
width: max(calc((100vw - 99ch) / 2 - 15vw), 24ch);
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
margin-right: 1em;
|
||||||
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
position: absolute;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overscroll-behavior-y: contain;
|
overscroll-behavior-y: contain;
|
||||||
|
|
||||||
|
@ -46,44 +61,53 @@ a.inline-code {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
#navbar a {
|
@media (min-width: 130ch) {
|
||||||
|
.navbar {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar a {
|
||||||
color: #ffb454;
|
color: #ffb454;
|
||||||
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
#navbar li {
|
.navbar li {
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-left: 1em;
|
padding-left: 1em;
|
||||||
margin-left: 0em;
|
margin-left: 0em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#navbar ul {
|
.navbar ul {
|
||||||
margin-left: 0em;
|
margin-left: 0em;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#navbar summary{
|
.navbar summary{
|
||||||
display: block;
|
display: block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#navbar summary::marker,
|
.navbar summary::marker,
|
||||||
#navbar summary::-webkit-details-marker{
|
.navbar summary::-webkit-details-marker{
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#navbar summary:focus{
|
.navbar summary:focus{
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#navbar summary:focus-visible{
|
.navbar summary:focus-visible{
|
||||||
outline: 1px dotted #000;
|
outline: 1px dotted #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
#navbar summary:before {
|
.navbar summary:before {
|
||||||
content: "+";
|
content: "+";
|
||||||
color: #ffb454;
|
color: #ffb454;
|
||||||
float: left;
|
float: left;
|
||||||
|
@ -91,7 +115,7 @@ a.inline-code {
|
||||||
width: 1em;
|
width: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#navbar details[open] > summary:before {
|
.navbar details[open] > summary:before {
|
||||||
content: "–";
|
content: "–";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue