Added bindings

This commit is contained in:
ef3d0c3e 2024-07-30 11:01:22 +02:00
parent 7a2c19af66
commit b814c57355
18 changed files with 370 additions and 264 deletions

View file

@ -544,8 +544,7 @@ impl RegexRule for CodeRule {
reports
}
// TODO
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![];
bindings.push((
"push_inline".to_string(),
@ -647,7 +646,7 @@ impl RegexRule for CodeRule {
.unwrap(),
));
bindings
Some(bindings)
}
}

View file

@ -80,5 +80,5 @@ impl RegexRule for CommentRule {
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 }
}

View file

@ -371,5 +371,5 @@ impl RegexRule for GraphRule {
}
// 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 }
}

View file

@ -178,5 +178,5 @@ impl RegexRule for ImportRule {
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 }
}

View file

@ -171,5 +171,5 @@ impl RegexRule for LinkRule {
}
// 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 }
}

View file

@ -337,5 +337,5 @@ impl Rule for ListRule
}
// 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 }
}

View file

@ -7,6 +7,8 @@ use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Function;
use mlua::Lua;
use regex::Captures;
use regex::Match;
use regex::Regex;
@ -518,9 +520,7 @@ impl RegexRule for MediaRule {
reports
}
fn lua_bindings<'lua>(&self, _lua: &'lua mlua::Lua) -> Vec<(String, mlua::Function<'lua>)> {
vec![]
}
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
}
#[cfg(test)]

View file

@ -150,5 +150,5 @@ impl Rule for ParagraphRule {
}
// 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 }
}

View file

@ -231,7 +231,7 @@ impl RegexRule for RawRule {
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![];
bindings.push((
@ -270,7 +270,7 @@ impl RegexRule for RawRule {
.unwrap(),
));
bindings
Some(bindings)
}
}

View file

@ -6,6 +6,8 @@ use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Function;
use mlua::Lua;
use regex::Captures;
use regex::Match;
use regex::Regex;
@ -205,7 +207,5 @@ impl RegexRule for ReferenceRule {
reports
}
fn lua_bindings<'lua>(&self, _lua: &'lua mlua::Lua) -> Vec<(String, mlua::Function<'lua>)> {
vec![]
}
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
}

View file

@ -275,5 +275,5 @@ impl RegexRule for ScriptRule {
}
// 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 }
}

View file

@ -235,7 +235,7 @@ impl RegexRule for SectionRule {
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![];
bindings.push((
@ -280,6 +280,6 @@ impl RegexRule for SectionRule {
.unwrap(),
));
bindings
Some(bindings)
}
}

View file

@ -183,5 +183,5 @@ impl RegexRule for StyleRule
}
// 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 }
}

View file

@ -430,7 +430,7 @@ impl RegexRule for TexRule {
}
// 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)]

View file

@ -61,7 +61,7 @@ impl Rule for TextRule {
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![];
bindings.push((
"push".to_string(),
@ -83,6 +83,6 @@ impl Rule for TextRule {
.unwrap(),
));
bindings
Some(bindings)
}
}

View file

@ -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 crate::{document::document::Document, parser::{parser::{Parser, ReportColors}, rule::RegexRule, source::{Source, Token}}};
use ariadne::{Report, Fmt, Label, ReportKind};
use crate::document::variable::{BaseVariable, PathVariable, Variable};
use std::{ops::Range, rc::Rc};
use std::ops::Range;
use std::rc::Rc;
use std::str::FromStr;
#[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 {
re: [Regex; 1],
@ -14,20 +46,20 @@ impl VariableRule {
pub fn new() -> Self {
Self {
re: [Regex::new(r"(?:^|\n)@([^[:alpha:]])?(.*?)=((?:\\\n|.)*)").unwrap()],
kinds: vec![
("".into(), "Regular".into()),
("'".into(), "Path".into())
]
kinds: vec![("".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>
{
match self.kinds[kind].0.as_str()
{
"" => {
Ok(Rc::new(BaseVariable::new(location, name, value)))
}
pub fn make_variable(
&self,
colors: &ReportColors,
location: Token,
kind: usize,
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
{
@ -37,42 +69,41 @@ impl VariableRule {
e.to_string()))
}
}
_ => panic!("Unhandled variable kind")
_ => panic!("Unhandled variable kind"),
}
}
// 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();
if name.contains("%")
{
return Err(format!("Name cannot contain '{}'",
"%".fg(colors.info)));
if name.contains("%") {
return Err(format!("Name cannot contain '{}'", "%".fg(colors.info)));
}
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 result = String::new();
for c in original_value.trim_start().trim_end().chars() {
if c == '\\' { escaped += 1 }
else if c == '\n' {
if c == '\\' {
escaped += 1
} else if c == '\n' {
match escaped {
0 => return Err("Unknown error wile capturing variable".to_string()),
// Remove '\n'
1 => {},
1 => {}
// Insert '\n'
_ => {
result.push(c);
(0..escaped-2).for_each(|_| result.push('\\'));
(0..escaped - 2).for_each(|_| result.push('\\'));
}
}
escaped = 0;
}
else {
} else {
(0..escaped).for_each(|_| result.push('\\'));
escaped = 0;
result.push(c);
@ -89,35 +120,54 @@ impl RegexRule for VariableRule {
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![];
// [Optional] variable kind
let var_kind = match matches.get(1)
{
let var_kind = match matches.get(1) {
Some(kind) => {
// Find kind
let r = self.kinds.iter().enumerate().find(|(_i, (ref char, ref _name))| {
char == kind.as_str() });
let r = self
.kinds
.iter()
.enumerate()
.find(|(_i, (ref char, ref _name))| char == kind.as_str());
// Unknown kind specified
if r.is_none()
{
if r.is_none() {
result.push(
Report::build(ReportKind::Error, token.source(), kind.start())
.with_message("Unknown variable kind")
.with_label(
Label::new((token.source(), kind.range()))
.with_message(format!("Variable kind `{}` is unknown",
kind.as_str().fg(parser.colors().highlight)))
.with_color(parser.colors().error))
.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 - `{}` : {}",
.with_message(format!(
"Variable kind `{}` is unknown",
kind.as_str().fg(parser.colors().highlight)
))
.with_color(parser.colors().error),
)
.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),
name.fg(parser.colors().info)).as_str()
})))
.finish());
name.fg(parser.colors().info)
)
.as_str()
}
)
))
.finish(),
);
return result;
}
@ -127,11 +177,8 @@ impl RegexRule for VariableRule {
None => 0,
};
let var_name = match matches.get(2)
{
Some(name) => {
match VariableRule::validate_name(&parser.colors(), name.as_str())
{
let var_name = match matches.get(2) {
Some(name) => match VariableRule::validate_name(&parser.colors(), name.as_str()) {
Ok(var_name) => var_name,
Err(msg) => {
result.push(
@ -139,44 +186,52 @@ impl RegexRule for VariableRule {
.with_message("Invalid variable name")
.with_label(
Label::new((token.source(), name.range()))
.with_message(format!("Variable name `{}` is not allowed. {msg}",
name.as_str().fg(parser.colors().highlight)))
.with_color(parser.colors().error))
.finish());
.with_message(format!(
"Variable name `{}` is not allowed. {msg}",
name.as_str().fg(parser.colors().highlight)
))
.with_color(parser.colors().error),
)
.finish(),
);
return result;
},
}
},
_ => panic!("Unknown variable name")
_ => panic!("Unknown variable name"),
};
let var_value = match matches.get(3)
{
Some(value) => {
match VariableRule::validate_value(&parser.colors(), value.as_str())
{
let var_value = match matches.get(3) {
Some(value) => match VariableRule::validate_value(&parser.colors(), value.as_str()) {
Ok(var_value) => var_value,
Err(msg ) => {
Err(msg) => {
result.push(
Report::build(ReportKind::Error, token.source(), value.start())
.with_message("Invalid variable value")
.with_label(
Label::new((token.source(), value.range()))
.with_message(format!("Variable value `{}` is not allowed. {msg}",
value.as_str().fg(parser.colors().highlight)))
.with_color(parser.colors().error))
.finish());
.with_message(format!(
"Variable value `{}` is not allowed. {msg}",
value.as_str().fg(parser.colors().highlight)
))
.with_color(parser.colors().error),
)
.finish(),
);
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),
Err(msg) => {
let m = matches.get(0).unwrap();
@ -184,12 +239,16 @@ impl RegexRule for VariableRule {
Report::build(ReportKind::Error, token.source(), m.start())
.with_message("Unable to create variable")
.with_label(
Label::new((token.source(), m.start()+1 .. m.end() ))
.with_message(format!("Unable to create variable `{}`. {}",
Label::new((token.source(), m.start() + 1..m.end()))
.with_message(format!(
"Unable to create variable `{}`. {}",
var_name.fg(parser.colors().highlight),
msg))
.with_color(parser.colors().error))
.finish());
msg
))
.with_color(parser.colors().error),
)
.finish(),
);
return result;
}
@ -198,12 +257,45 @@ impl RegexRule for VariableRule {
return result;
}
// 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>)>> {
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],
}
@ -215,100 +307,113 @@ impl VariableSubstitutionRule {
}
}
impl RegexRule for VariableSubstitutionRule
{
impl RegexRule for VariableSubstitutionRule {
fn name(&self) -> &'static str { "Variable Substitution" }
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 variable = match matches.get(1)
{
let variable = match matches.get(1) {
Some(name) => {
// Empty name
if name.as_str().is_empty()
{
if name.as_str().is_empty() {
result.push(
Report::build(ReportKind::Error, token.source(), name.start())
.with_message("Empty variable name")
.with_label(
Label::new((token.source(), matches.get(0).unwrap().range()))
.with_message(format!("Missing variable name for substitution"))
.with_color(parser.colors().error))
.finish());
.with_color(parser.colors().error),
)
.finish(),
);
return result;
}
// Leading spaces
else if name.as_str().trim_start() != name.as_str()
{
else if name.as_str().trim_start() != name.as_str() {
result.push(
Report::build(ReportKind::Error, token.source(), name.start())
.with_message("Invalid variable name")
.with_label(
Label::new((token.source(), name.range()))
.with_message(format!("Variable names contains leading spaces"))
.with_color(parser.colors().error))
.with_color(parser.colors().error),
)
.with_help("Remove leading spaces")
.finish());
.finish(),
);
return result;
}
// Trailing spaces
else if name.as_str().trim_end() != name.as_str()
{
else if name.as_str().trim_end() != name.as_str() {
result.push(
Report::build(ReportKind::Error, token.source(), name.start())
.with_message("Invalid variable name")
.with_label(
Label::new((token.source(), name.range()))
.with_message(format!("Variable names contains trailing spaces"))
.with_color(parser.colors().error))
.with_message(format!(
"Variable names contains trailing spaces"
))
.with_color(parser.colors().error),
)
.with_help("Remove trailing spaces")
.finish());
.finish(),
);
return result;
}
// Invalid name
match VariableRule::validate_name(&parser.colors(), name.as_str())
{
Err(msg) =>
{
match VariableRule::validate_name(&parser.colors(), name.as_str()) {
Err(msg) => {
result.push(
Report::build(ReportKind::Error, token.source(), name.start())
.with_message("Invalid variable name")
.with_label(
Label::new((token.source(), name.range()))
.with_message(msg)
.with_color(parser.colors().error))
.finish());
.with_color(parser.colors().error),
)
.finish(),
);
return result;
}
_ => {},
_ => {}
}
// Get variable
match document.get_variable(name.as_str())
{
match document.get_variable(name.as_str()) {
None => {
result.push(
Report::build(ReportKind::Error, token.source(), name.start())
.with_message("Unknown variable name")
.with_label(
Label::new((token.source(), name.range()))
.with_message(format!("Unable to find variable with name: `{}`",
name.as_str().fg(parser.colors().highlight)))
.with_color(parser.colors().error))
.finish());
.with_message(format!(
"Unable to find variable with name: `{}`",
name.as_str().fg(parser.colors().highlight)
))
.with_color(parser.colors().error),
)
.finish(),
);
return result;
}
Some(var) => var,
}
},
_ => panic!("Unknown error")
}
_ => panic!("Unknown error"),
};
variable.parse(token, parser, document);
@ -316,6 +421,5 @@ impl RegexRule for VariableSubstitutionRule
return result;
}
// 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 }
}

View file

@ -33,17 +33,20 @@ impl Kernel {
let nml_table = lua.create_table().unwrap();
for rule in parser.rules()
{
if let Some(bindings) = rule.lua_bindings(&lua)
{
let table = lua.create_table().unwrap();
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();
}
nml_table.set(name, table).unwrap();
}
}
lua.globals().set("nml", nml_table).unwrap();
}

View file

@ -25,7 +25,7 @@ pub trait Rule {
match_data: Option<Box<dyn Any>>,
) -> (Cursor, Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>>);
/// 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 {
@ -89,7 +89,7 @@ pub trait RegexRule {
matches: regex::Captures,
) -> 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 {
@ -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)
}
}