Add Lua support to transpiler
This commit is contained in:
parent
9f8b31e2aa
commit
01040964af
|
@ -6,9 +6,10 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["shulkerbox"]
|
default = ["lua", "shulkerbox"]
|
||||||
shulkerbox = ["dep:shulkerbox"]
|
shulkerbox = ["dep:shulkerbox"]
|
||||||
serde = ["dep:serde"]
|
serde = ["dep:serde"]
|
||||||
|
lua = ["dep:mlua"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chksum-md5 = "0.0.0"
|
chksum-md5 = "0.0.0"
|
||||||
|
@ -16,6 +17,7 @@ colored = "2.1.0"
|
||||||
derive_more = { version = "0.99.17", default-features = false, features = ["deref", "from", "deref_mut"] }
|
derive_more = { version = "0.99.17", default-features = false, features = ["deref", "from", "deref_mut"] }
|
||||||
enum-as-inner = "0.6.0"
|
enum-as-inner = "0.6.0"
|
||||||
getset = "0.1.2"
|
getset = "0.1.2"
|
||||||
|
mlua = { version = "0.9.7", features = ["luau"], optional = true }
|
||||||
serde = { version = "1.0.197", features = ["derive", "rc"], optional = true }
|
serde = { version = "1.0.197", features = ["derive", "rc"], optional = true }
|
||||||
shulkerbox = { path = "../shulkerbox", optional = true}
|
shulkerbox = { path = "../shulkerbox", optional = true}
|
||||||
strum = { version = "0.26.2", features = ["derive"] }
|
strum = { version = "0.26.2", features = ["derive"] }
|
||||||
|
|
62
grammar.md
62
grammar.md
|
@ -59,36 +59,77 @@ Conditional:
|
||||||
;
|
;
|
||||||
```
|
```
|
||||||
|
|
||||||
### ParenthizedCondition
|
### Condition
|
||||||
|
```ebnf
|
||||||
|
Condition:
|
||||||
|
PrimaryCondition
|
||||||
|
BinaryCondition
|
||||||
|
;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### PrimaryCondition
|
||||||
|
```ebnf
|
||||||
|
PrimaryCondition:
|
||||||
|
ConditionalPrefix
|
||||||
|
| ParenthesizedCondition
|
||||||
|
| StringLiteral
|
||||||
|
;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ConditionalPrefix
|
||||||
|
```ebnf
|
||||||
|
ConditionalPrefix:
|
||||||
|
ConditionalPrefixOperator PrimaryCondition
|
||||||
|
;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ConditionalPrefixOperator
|
||||||
|
``` ebnf
|
||||||
|
ConditionalPrefixOperator: '!';
|
||||||
|
```
|
||||||
|
|
||||||
|
#### BinaryCondition
|
||||||
|
``` ebnf
|
||||||
|
BinaryCondition:
|
||||||
|
Condition ConditionalBinaryOperator Condition
|
||||||
|
;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ConditionalBinaryOperator
|
||||||
|
``` ebnf
|
||||||
|
ConditionalBinaryOperator:
|
||||||
|
'&&'
|
||||||
|
| '||'
|
||||||
|
;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ParenthizedCondition
|
||||||
```ebnf
|
```ebnf
|
||||||
ParenthizedCondition:
|
ParenthizedCondition:
|
||||||
'(' Condition ')'
|
'(' Condition ')'
|
||||||
;
|
;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Condition
|
|
||||||
```ebnf
|
|
||||||
Condition:
|
|
||||||
StringLiteral
|
|
||||||
```
|
|
||||||
|
|
||||||
### Grouping
|
### Grouping
|
||||||
``` ebnf
|
``` ebnf
|
||||||
Grouping:
|
Grouping:
|
||||||
'group' Block
|
'group' Block
|
||||||
;
|
;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Expression
|
### Expression
|
||||||
```ebnf
|
```ebnf
|
||||||
Expression:
|
Expression:
|
||||||
Primary
|
Primary
|
||||||
|
;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Primary
|
### Primary
|
||||||
``` ebnf
|
``` ebnf
|
||||||
Primary:
|
Primary:
|
||||||
FunctionCall
|
FunctionCall
|
||||||
|
;
|
||||||
```
|
```
|
||||||
|
|
||||||
### FunctionCall
|
### FunctionCall
|
||||||
|
@ -97,3 +138,10 @@ FunctionCall:
|
||||||
Identifier '(' (Expression (',' Expression)*)? ')'
|
Identifier '(' (Expression (',' Expression)*)? ')'
|
||||||
;
|
;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### LuaCode
|
||||||
|
```ebnf
|
||||||
|
LuaCode:
|
||||||
|
'lua' '(' (Expression (',' Expression)*)? ')' '{' (.*?)* '}'
|
||||||
|
;
|
||||||
|
```
|
|
@ -51,6 +51,12 @@ impl SourceFile {
|
||||||
&self.content
|
&self.content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the path of the source file.
|
||||||
|
#[must_use]
|
||||||
|
pub fn path(&self) -> &Path {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the line of the source file at the given line number.
|
/// Get the line of the source file at the given line number.
|
||||||
///
|
///
|
||||||
/// Numbering starts at 1.
|
/// Numbering starts at 1.
|
||||||
|
@ -95,8 +101,6 @@ impl SourceFile {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_location(&self, byte_index: usize) -> Option<Location> {
|
pub fn get_location(&self, byte_index: usize) -> Option<Location> {
|
||||||
if self.content.is_char_boundary(byte_index) {
|
if self.content.is_char_boundary(byte_index) {
|
||||||
None
|
|
||||||
} else {
|
|
||||||
// get the line number by binary searching the line ranges
|
// get the line number by binary searching the line ranges
|
||||||
let line = self
|
let line = self
|
||||||
.lines
|
.lines
|
||||||
|
@ -125,6 +129,8 @@ impl SourceFile {
|
||||||
line: line + 1,
|
line: line + 1,
|
||||||
column,
|
column,
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ pub enum KeywordKind {
|
||||||
Else,
|
Else,
|
||||||
Group,
|
Group,
|
||||||
Run,
|
Run,
|
||||||
|
Lua,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for KeywordKind {
|
impl ToString for KeywordKind {
|
||||||
|
@ -66,6 +67,7 @@ impl KeywordKind {
|
||||||
Self::Else => "else",
|
Self::Else => "else",
|
||||||
Self::Group => "group",
|
Self::Group => "group",
|
||||||
Self::Run => "run",
|
Self::Run => "run",
|
||||||
|
Self::Lua => "lua",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,12 @@
|
||||||
use std::{fmt::Debug, sync::Arc};
|
use std::{fmt::Debug, sync::Arc};
|
||||||
|
|
||||||
use derive_more::{Deref, From};
|
use derive_more::{Deref, From};
|
||||||
|
use enum_as_inner::EnumAsInner;
|
||||||
|
|
||||||
use crate::base::{source_file::SourceFile, Handler};
|
use crate::base::{
|
||||||
|
source_file::{SourceElement, SourceFile, Span},
|
||||||
|
Handler,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
error::{self, UndelimitedDelimiter},
|
error::{self, UndelimitedDelimiter},
|
||||||
|
@ -164,13 +168,26 @@ impl TokenStream {
|
||||||
|
|
||||||
/// Is an enumeration of either a [`Token`] or a [`Delimited`].
|
/// Is an enumeration of either a [`Token`] or a [`Delimited`].
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, From)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, From, EnumAsInner)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub enum TokenTree {
|
pub enum TokenTree {
|
||||||
Token(Token),
|
Token(Token),
|
||||||
Delimited(Delimited),
|
Delimited(Delimited),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SourceElement for TokenTree {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
match self {
|
||||||
|
Self::Token(token) => token.span().to_owned(),
|
||||||
|
Self::Delimited(delimited) => delimited
|
||||||
|
.open
|
||||||
|
.span()
|
||||||
|
.join(&delimited.close.span)
|
||||||
|
.expect("Invalid delimited span"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Is an enumeration of the different types of delimiters in the [`Delimited`].
|
/// Is an enumeration of the different types of delimiters in the [`Delimited`].
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
|
|
@ -247,6 +247,12 @@ impl<'a> Frame<'a> {
|
||||||
self.get_reading(self.token_provider.token_stream().get(self.current_index))
|
self.get_reading(self.token_provider.token_stream().get(self.current_index))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a raw [`Token`] pointing by the `current_index` of the [`Frame`].
|
||||||
|
#[must_use]
|
||||||
|
pub fn peek_raw(&self) -> Option<&TokenTree> {
|
||||||
|
self.token_provider.token_stream().get(self.current_index)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the next significant [`Token`] after the `current_index` of the [`Frame`].
|
/// Returns the next significant [`Token`] after the `current_index` of the [`Frame`].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn peek_significant(&self) -> Reading {
|
pub fn peek_significant(&self) -> Reading {
|
||||||
|
|
|
@ -23,9 +23,9 @@ use crate::{
|
||||||
/// Syntax Synopsis:
|
/// Syntax Synopsis:
|
||||||
///
|
///
|
||||||
/// ``` ebnf
|
/// ``` ebnf
|
||||||
/// Expression:
|
/// PrimaryCondition:
|
||||||
/// Prefix
|
/// ConditionalPrefix
|
||||||
/// | Parenthesized
|
/// | ParenthesizedCondition
|
||||||
/// | StringLiteral
|
/// | StringLiteral
|
||||||
/// ```
|
/// ```
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
@ -88,7 +88,7 @@ impl BinaryCondition {
|
||||||
/// Syntax Synopsis:
|
/// Syntax Synopsis:
|
||||||
///
|
///
|
||||||
/// ``` ebnf
|
/// ``` ebnf
|
||||||
/// BinaryOperator:
|
/// ConditionalBinaryOperator:
|
||||||
/// '&&'
|
/// '&&'
|
||||||
/// | '||'
|
/// | '||'
|
||||||
/// ;
|
/// ;
|
||||||
|
@ -165,7 +165,7 @@ impl SourceElement for ParenthesizedCondition {
|
||||||
/// Syntax Synopsis:
|
/// Syntax Synopsis:
|
||||||
///
|
///
|
||||||
/// ``` ebnf
|
/// ``` ebnf
|
||||||
/// PrefixOperator: '!';
|
/// ConditionalPrefixOperator: '!';
|
||||||
/// ```
|
/// ```
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
@ -185,8 +185,8 @@ impl SourceElement for ConditionalPrefixOperator {
|
||||||
/// Syntax Synopsis:
|
/// Syntax Synopsis:
|
||||||
///
|
///
|
||||||
/// ```ebnf
|
/// ```ebnf
|
||||||
/// Prefix:
|
/// ConditionalPrefix:
|
||||||
/// ConditionalPrefixOperator StringLiteral
|
/// ConditionalPrefixOperator PrimaryCondition
|
||||||
/// ;
|
/// ;
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
@ -216,7 +216,10 @@ impl ConditionalPrefix {
|
||||||
/// Syntax Synopsis:
|
/// Syntax Synopsis:
|
||||||
///
|
///
|
||||||
/// ``` ebnf
|
/// ``` ebnf
|
||||||
/// Condition: PrimaryCondition;
|
/// Condition:
|
||||||
|
/// PrimaryCondition
|
||||||
|
/// | BinaryCondition
|
||||||
|
/// ;
|
||||||
/// ```
|
/// ```
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
Handler,
|
Handler,
|
||||||
},
|
},
|
||||||
lexical::{
|
lexical::{
|
||||||
token::{Identifier, Punctuation, StringLiteral, Token},
|
token::{Identifier, Keyword, KeywordKind, Punctuation, StringLiteral, Token},
|
||||||
token_stream::Delimiter,
|
token_stream::Delimiter,
|
||||||
},
|
},
|
||||||
syntax::{
|
syntax::{
|
||||||
|
@ -47,12 +47,13 @@ impl SourceElement for Expression {
|
||||||
/// Primary:
|
/// Primary:
|
||||||
/// FunctionCall
|
/// FunctionCall
|
||||||
/// ```
|
/// ```
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs, clippy::large_enum_variant)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)]
|
||||||
pub enum Primary {
|
pub enum Primary {
|
||||||
FunctionCall(FunctionCall),
|
FunctionCall(FunctionCall),
|
||||||
StringLiteral(StringLiteral),
|
StringLiteral(StringLiteral),
|
||||||
|
Lua(LuaCode),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceElement for Primary {
|
impl SourceElement for Primary {
|
||||||
|
@ -60,6 +61,7 @@ impl SourceElement for Primary {
|
||||||
match self {
|
match self {
|
||||||
Self::FunctionCall(function_call) => function_call.span(),
|
Self::FunctionCall(function_call) => function_call.span(),
|
||||||
Self::StringLiteral(string_literal) => string_literal.span(),
|
Self::StringLiteral(string_literal) => string_literal.span(),
|
||||||
|
Self::Lua(lua_code) => lua_code.span(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,6 +99,60 @@ impl SourceElement for FunctionCall {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ```ebnf
|
||||||
|
/// LuaCode:
|
||||||
|
/// 'lua' '(' (Expression (',' Expression)*)? ')' '{' (.*?)* '}'
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||||
|
pub struct LuaCode {
|
||||||
|
/// The `lua` keyword.
|
||||||
|
#[get = "pub"]
|
||||||
|
lua_keyword: Keyword,
|
||||||
|
/// The left parenthesis of the lua code.
|
||||||
|
#[get = "pub"]
|
||||||
|
left_parenthesis: Punctuation,
|
||||||
|
/// The arguments of the lua code.
|
||||||
|
#[get = "pub"]
|
||||||
|
variables: Option<ConnectedList<Identifier, Punctuation>>,
|
||||||
|
/// The right parenthesis of the lua code.
|
||||||
|
#[get = "pub"]
|
||||||
|
right_parenthesis: Punctuation,
|
||||||
|
/// The left brace of the lua code.
|
||||||
|
#[get = "pub"]
|
||||||
|
left_brace: Punctuation,
|
||||||
|
/// The lua code contents.
|
||||||
|
#[get = "pub"]
|
||||||
|
code: String,
|
||||||
|
/// The right brace of the lua code.
|
||||||
|
#[get = "pub"]
|
||||||
|
right_brace: Punctuation,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for LuaCode {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
self.lua_keyword
|
||||||
|
.span()
|
||||||
|
.join(&self.right_brace.span)
|
||||||
|
.expect("Invalid lua code span")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LuaCode {
|
||||||
|
/// Dissolves the [`LuaCode`] into its components.
|
||||||
|
#[must_use]
|
||||||
|
pub fn dissolve(self) -> (Keyword, Punctuation, String, Punctuation) {
|
||||||
|
(
|
||||||
|
self.lua_keyword,
|
||||||
|
self.left_brace,
|
||||||
|
self.code,
|
||||||
|
self.right_brace,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
/// Parses an [`Expression`]
|
/// Parses an [`Expression`]
|
||||||
pub fn parse_expression(&mut self, handler: &impl Handler<Error>) -> Option<Expression> {
|
pub fn parse_expression(&mut self, handler: &impl Handler<Error>) -> Option<Expression> {
|
||||||
|
@ -141,6 +197,65 @@ impl<'a> Parser<'a> {
|
||||||
Some(Primary::StringLiteral(literal))
|
Some(Primary::StringLiteral(literal))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lua code expression
|
||||||
|
Reading::Atomic(Token::Keyword(lua_keyword))
|
||||||
|
if lua_keyword.keyword == KeywordKind::Lua =>
|
||||||
|
{
|
||||||
|
// eat the lua keyword
|
||||||
|
self.forward();
|
||||||
|
|
||||||
|
// parse the variable list
|
||||||
|
let variables = self.parse_enclosed_list(
|
||||||
|
Delimiter::Parenthesis,
|
||||||
|
',',
|
||||||
|
|parser| match parser.next_significant_token() {
|
||||||
|
Reading::Atomic(Token::Identifier(identifier)) => {
|
||||||
|
parser.forward();
|
||||||
|
Some(identifier)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
handler,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.stop_at_significant();
|
||||||
|
|
||||||
|
let tree = self.step_into(
|
||||||
|
Delimiter::Brace,
|
||||||
|
|parser| {
|
||||||
|
let first = parser.next_token();
|
||||||
|
let mut last = parser.next_token();
|
||||||
|
|
||||||
|
while !parser.is_end() {
|
||||||
|
last = parser.next_token();
|
||||||
|
}
|
||||||
|
|
||||||
|
let combined = first
|
||||||
|
.into_token()
|
||||||
|
.and_then(|first| {
|
||||||
|
first.span().join(&last.into_token().map_or_else(
|
||||||
|
|| first.span().to_owned(),
|
||||||
|
|last| last.span().to_owned(),
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.expect("Invalid lua code span");
|
||||||
|
|
||||||
|
Some(combined.str().trim().to_owned())
|
||||||
|
},
|
||||||
|
handler,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Some(Primary::Lua(LuaCode {
|
||||||
|
lua_keyword,
|
||||||
|
left_parenthesis: variables.open,
|
||||||
|
variables: variables.list,
|
||||||
|
right_parenthesis: variables.close,
|
||||||
|
left_brace: tree.open,
|
||||||
|
code: tree.tree?,
|
||||||
|
right_brace: tree.close,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
unexpected => {
|
unexpected => {
|
||||||
// make progress
|
// make progress
|
||||||
self.forward();
|
self.forward();
|
||||||
|
|
|
@ -10,6 +10,10 @@ pub enum TranspileError {
|
||||||
MissingFunctionDeclaration(String),
|
MissingFunctionDeclaration(String),
|
||||||
#[error("Unexpected expression: {}", .0.span().str())]
|
#[error("Unexpected expression: {}", .0.span().str())]
|
||||||
UnexpectedExpression(Expression),
|
UnexpectedExpression(Expression),
|
||||||
|
#[error("Lua code evaluation is disabled.")]
|
||||||
|
LuaDisabled,
|
||||||
|
#[error("Lua runtime error: {}", .0)]
|
||||||
|
LuaRuntimeError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of a transpilation operation.
|
/// The result of a transpilation operation.
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
//! Executes the Lua code and returns the resulting command.
|
||||||
|
|
||||||
|
#[cfg(feature = "lua")]
|
||||||
|
mod enabled {
|
||||||
|
use mlua::Lua;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
base::{source_file::SourceElement, Handler},
|
||||||
|
syntax::syntax_tree::expression::LuaCode,
|
||||||
|
transpile::error::{TranspileError, TranspileResult},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl LuaCode {
|
||||||
|
/// Evaluates the Lua code and returns the resulting command.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - If Lua code evaluation is disabled.
|
||||||
|
pub fn eval_string(
|
||||||
|
&self,
|
||||||
|
handler: &impl Handler<TranspileError>,
|
||||||
|
) -> TranspileResult<String> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let name = {
|
||||||
|
let span = self.span();
|
||||||
|
let file = span.source_file();
|
||||||
|
let path = file.path();
|
||||||
|
|
||||||
|
let start = span.start_location();
|
||||||
|
let end = span.end_location().unwrap_or_else(|| {
|
||||||
|
let content_size = file.content().len();
|
||||||
|
file.get_location(content_size - 1)
|
||||||
|
.expect("Failed to get location")
|
||||||
|
});
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"{}:{}:{}-{}:{}",
|
||||||
|
path.display(),
|
||||||
|
start.line,
|
||||||
|
start.column,
|
||||||
|
end.line,
|
||||||
|
end.column
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let lua_result = lua
|
||||||
|
.load(self.code())
|
||||||
|
.set_name(name)
|
||||||
|
.eval::<String>()
|
||||||
|
.map_err(|err| {
|
||||||
|
let err = TranspileError::from(err);
|
||||||
|
handler.receive(err.clone());
|
||||||
|
err
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(lua_result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<mlua::Error> for TranspileError {
|
||||||
|
fn from(value: mlua::Error) -> Self {
|
||||||
|
let string = value.to_string();
|
||||||
|
Self::LuaRuntimeError(
|
||||||
|
string
|
||||||
|
.strip_prefix("runtime error: ")
|
||||||
|
.unwrap_or(&string)
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "lua"))]
|
||||||
|
mod disabled {
|
||||||
|
use crate::{
|
||||||
|
base::Handler,
|
||||||
|
syntax::syntax_tree::expression::LuaCode,
|
||||||
|
transpile::error::{TranspileError, TranspileResult},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl LuaCode {
|
||||||
|
/// Will always return an error because Lua code evaluation is disabled.
|
||||||
|
/// Enable the feature `lua` to enable Lua code evaluation.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - If Lua code evaluation is disabled.
|
||||||
|
pub fn eval_string(
|
||||||
|
&self,
|
||||||
|
handler: &impl Handler<TranspileError>,
|
||||||
|
) -> TranspileResult<String> {
|
||||||
|
handler.receive(TranspileError::LuaDisabled);
|
||||||
|
Err(TranspileError::LuaDisabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,5 +4,7 @@
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
pub mod conversions;
|
pub mod conversions;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub mod lua;
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
pub mod transpiler;
|
pub mod transpiler;
|
||||||
|
|
|
@ -233,6 +233,9 @@ impl Transpiler {
|
||||||
Expression::Primary(Primary::StringLiteral(string)) => {
|
Expression::Primary(Primary::StringLiteral(string)) => {
|
||||||
Ok(Some(Command::Raw(string.str_content().to_string())))
|
Ok(Some(Command::Raw(string.str_content().to_string())))
|
||||||
}
|
}
|
||||||
|
Expression::Primary(Primary::Lua(code)) => {
|
||||||
|
Ok(Some(Command::Raw(code.eval_string(handler)?)))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Statement::Block(_) => {
|
Statement::Block(_) => {
|
||||||
unreachable!("Only literal commands are allowed in functions at this time.")
|
unreachable!("Only literal commands are allowed in functions at this time.")
|
||||||
|
|
Loading…
Reference in New Issue