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,78 +1,109 @@
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],
kinds: Vec<(String, String)>, kinds: Vec<(String, String)>,
} }
impl VariableRule { 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 std::fs::canonicalize(value.as_str()) // TODO: not canonicalize 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
{ {
Ok(path) => Ok(Rc::new(PathVariable::new(location, name, path))), Ok(path) => Ok(Rc::new(PathVariable::new(location, name, path))),
Err(e) => Err(format!("Unable to canonicalize path `{}`: {}", Err(e) => Err(format!("Unable to canonicalize path `{}`: {}",
value.fg(colors.highlight), value.fg(colors.highlight),
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,
let name = original_name.trim_start().trim_end(); original_name: &'a str,
if name.contains("%") ) -> Result<&'a str, String> {
{ let name = original_name.trim_start().trim_end();
return Err(format!("Name cannot contain '{}'", if name.contains("%") {
"%".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);
@ -81,7 +112,7 @@ impl VariableRule {
(0..escaped).for_each(|_| result.push('\\')); (0..escaped).for_each(|_| result.push('\\'));
Ok(result) Ok(result)
} }
} }
impl RegexRule for VariableRule { impl RegexRule for VariableRule {
@ -89,233 +120,306 @@ 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!(
.with_message(format!("Variable kind `{}` is unknown", "Variable kind `{}` is unknown",
kind.as_str().fg(parser.colors().highlight))) kind.as_str().fg(parser.colors().highlight)
.with_color(parser.colors().error)) ))
.with_help(format!("Leave empty for regular variables. Available variable kinds:{}", .with_color(parser.colors().error),
self.kinds.iter().skip(1).fold("".to_string(), |acc, (char, name)| { )
acc + format!("\n - `{}` : {}", .with_help(format!(
char.fg(parser.colors().highlight), "Leave empty for regular variables. Available variable kinds:{}",
name.fg(parser.colors().info)).as_str() self.kinds.iter().skip(1).fold(
}))) "".to_string(),
.finish()); |acc, (char, name)| {
acc + format!(
"\n - `{}` : {}",
char.fg(parser.colors().highlight),
name.fg(parser.colors().info)
)
.as_str()
}
)
))
.finish(),
);
return result; return result;
} }
r.unwrap().0 r.unwrap().0
} }
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) => { Ok(var_name) => var_name,
match VariableRule::validate_name(&parser.colors(), name.as_str()) Err(msg) => {
{ result.push(
Ok(var_name) => var_name, Report::build(ReportKind::Error, token.source(), name.start())
Err(msg) => { .with_message("Invalid variable name")
result.push( .with_label(
Report::build(ReportKind::Error, token.source(), name.start()) Label::new((token.source(), name.range()))
.with_message("Invalid variable name") .with_message(format!(
.with_label( "Variable name `{}` is not allowed. {msg}",
Label::new((token.source(), name.range())) name.as_str().fg(parser.colors().highlight)
.with_message(format!("Variable name `{}` is not allowed. {msg}", ))
name.as_str().fg(parser.colors().highlight))) .with_color(parser.colors().error),
.with_color(parser.colors().error)) )
.finish()); .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) => { Ok(var_value) => var_value,
match VariableRule::validate_value(&parser.colors(), value.as_str()) Err(msg) => {
{ result.push(
Ok(var_value) => var_value, Report::build(ReportKind::Error, token.source(), value.start())
Err(msg ) => { .with_message("Invalid variable value")
result.push( .with_label(
Report::build(ReportKind::Error, token.source(), value.start()) Label::new((token.source(), value.range()))
.with_message("Invalid variable value") .with_message(format!(
.with_label( "Variable value `{}` is not allowed. {msg}",
Label::new((token.source(), value.range())) value.as_str().fg(parser.colors().highlight)
.with_message(format!("Variable value `{}` is not allowed. {msg}", ))
value.as_str().fg(parser.colors().highlight))) .with_color(parser.colors().error),
.with_color(parser.colors().error)) )
.finish()); .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(),
Ok(variable) => document.add_variable(variable), token.clone(),
Err(msg) => { var_kind,
let m = matches.get(0).unwrap(); var_name.to_string(),
result.push( var_value,
Report::build(ReportKind::Error, token.source(), m.start()) ) {
.with_message("Unable to create variable") Ok(variable) => document.add_variable(variable),
.with_label( Err(msg) => {
Label::new((token.source(), m.start()+1 .. m.end() )) let m = matches.get(0).unwrap();
.with_message(format!("Unable to create variable `{}`. {}", result.push(
var_name.fg(parser.colors().highlight), Report::build(ReportKind::Error, token.source(), m.start())
msg)) .with_message("Unable to create variable")
.with_color(parser.colors().error)) .with_label(
.finish()); 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(),
);
return result; return result;
} }
} }
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],
} }
impl VariableSubstitutionRule { impl VariableSubstitutionRule {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
re: [Regex::new(r"%(.*?)%").unwrap()], re: [Regex::new(r"%(.*?)%").unwrap()],
} }
} }
} }
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(
{ Report::build(ReportKind::Error, token.source(), name.start())
result.push( .with_message("Empty variable name")
Report::build(ReportKind::Error, token.source(), name.start()) .with_label(
.with_message("Empty variable name") Label::new((token.source(), matches.get(0).unwrap().range()))
.with_label( .with_message(format!("Missing variable name for substitution"))
Label::new((token.source(), matches.get(0).unwrap().range())) .with_color(parser.colors().error),
.with_message(format!("Missing variable name for substitution")) )
.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_label(
Label::new((token.source(), name.range()))
.with_message(format!("Variable names contains leading spaces"))
.with_color(parser.colors().error))
.with_help("Remove leading spaces")
.finish());
return result;
}
// Trailing spaces
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_help("Remove trailing spaces")
.finish());
return result;
}
// Invalid name
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_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(format!("Variable names contains leading spaces"))
.with_color(parser.colors().error)) .with_color(parser.colors().error),
.finish()); )
.with_help("Remove leading spaces")
.finish(),
);
return result;
}
// Trailing spaces
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_help("Remove trailing spaces")
.finish(),
);
return result;
}
// Invalid name
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(),
);
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!(
.with_message(format!("Unable to find variable with name: `{}`", "Unable to find variable with name: `{}`",
name.as_str().fg(parser.colors().highlight))) name.as_str().fg(parser.colors().highlight)
.with_color(parser.colors().error)) ))
.finish()); .with_color(parser.colors().error),
return result; )
} .finish(),
Some(var) => var, );
} return result;
}, }
_ => panic!("Unknown error") Some(var) => var,
}; }
}
_ => panic!("Unknown error"),
};
variable.parse(token, parser, document); variable.parse(token, parser, document);
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

@ -34,15 +34,18 @@ impl Kernel {
for rule in parser.rules() for rule in parser.rules()
{ {
let table = lua.create_table().unwrap(); if let Some(bindings) = rule.lua_bindings(&lua)
let name = rule.name().to_lowercase();
for (fun_name, fun) in rule.lua_bindings(&lua)
{ {
table.set(fun_name, fun).unwrap(); let table = lua.create_table().unwrap();
} let name = rule.name().to_lowercase();
nml_table.set(name, table).unwrap(); 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(); 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)
} }
} }