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 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)
} }
} }

View file

@ -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 }
} }

View file

@ -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 }
} }

View file

@ -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 }
} }

View file

@ -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 }
} }

View file

@ -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 }
} }

View file

@ -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)]

View file

@ -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 }
} }

View file

@ -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)
} }
} }

View file

@ -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![]
}
} }

View file

@ -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 }
} }

View file

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

View file

@ -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 }
} }

View file

@ -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)]

View file

@ -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)
} }
} }

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 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![] }
} }

View file

@ -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();
} }

View file

@ -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)
} }
} }