add: ffi node api for other languages
- WIP xml script parser Signed-off-by: Pakin <pakin.t@forth.co.th>
This commit is contained in:
parent
21984bdfba
commit
8a98f29c9d
23 changed files with 2591 additions and 279 deletions
2
.dockerignore
Normal file
2
.dockerignore
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
target
|
||||||
|
.tbcfg
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
||||||
/target
|
/target
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.jar
|
*.jar
|
||||||
**/*/android
|
**/*/android
|
||||||
|
out
|
||||||
1518
Cargo.lock
generated
1518
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
23
Cargo.toml
23
Cargo.toml
|
|
@ -3,17 +3,34 @@ name = "libtbr"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "tbr"
|
||||||
|
crate-type = ["rlib", "cdylib"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "uniffi-bindgen"
|
||||||
|
path = "uniffi-bindgen.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.41"
|
chrono = "0.4.44"
|
||||||
flate2 = "1.1.2"
|
flate2 = "1.1.2"
|
||||||
git2 = "0.20.2"
|
git2 = "0.20.2"
|
||||||
indexmap = "2.14.0"
|
indexmap = "2.14.0"
|
||||||
log = "0.4.27"
|
log = "0.4.27"
|
||||||
|
lalrpop-util = { version = "0.20.0", features = ["lexer"] }
|
||||||
|
openssl-sys = { version = "0.9.116", features = ["vendored"] }
|
||||||
quick-xml = "0.39.2"
|
quick-xml = "0.39.2"
|
||||||
rand = "0.9.2"
|
rand = "0.9.2"
|
||||||
rayon = "1.10.0"
|
rayon = "1.10.0"
|
||||||
serde = { version = "1.0.219", features = ["derive", "serde_derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.150"
|
||||||
tar = "0.4.44"
|
tar = "0.4.44"
|
||||||
|
uniffi = { version = "0.31.1", features = ["cli", "tokio"] }
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
zip = "5.0.0"
|
zip = "5.0.0"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
lalrpop = "0.20.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
uniffi = { version = "0.31.1", features = ["build"] }
|
||||||
|
|
|
||||||
52
Dockerfile
Normal file
52
Dockerfile
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
# =========================================================================
|
||||||
|
# Stage 1: Native Host Builder
|
||||||
|
# We use --platform=$BUILDPLATFORM so the container runs natively on your
|
||||||
|
# Mac's ultra-fast CPU instead of running slow ARM-to-Intel emulation.
|
||||||
|
# =========================================================================
|
||||||
|
FROM --platform=$BUILDPLATFORM rust:slim AS builder
|
||||||
|
|
||||||
|
# Install linkers and cross-compilers for Linux ARM64, Linux x86, and Windows
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
gcc-aarch64-linux-gnu \
|
||||||
|
gcc-x86-64-linux-gnu \
|
||||||
|
mingw-w64 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Capture target arguments from the docker buildx flag
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
|
||||||
|
# Dynamically cross-compile based on the platform requested by Buildx
|
||||||
|
RUN set -ex; \
|
||||||
|
if [ "$TARGETOS" = "linux" ] && [ "$TARGETARCH" = "amd64" ]; then \
|
||||||
|
rustup target add x86_64-unknown-linux-gnu; \
|
||||||
|
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-gnu-gcc; \
|
||||||
|
cargo build --release --target x86_64-unknown-linux-gnu; \
|
||||||
|
mkdir -p /out; cp target/x86_64-unknown-linux-gnu/release/libtbr.so /out/; \
|
||||||
|
\
|
||||||
|
elif [ "$TARGETOS" = "linux" ] && [ "$TARGETARCH" = "arm64" ]; then \
|
||||||
|
rustup target add aarch64-unknown-linux-gnu; \
|
||||||
|
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc; \
|
||||||
|
cargo build --release --target aarch64-unknown-linux-gnu; \
|
||||||
|
mkdir -p /out; cp target/aarch64-unknown-linux-gnu/release/libtbr.so /out/; \
|
||||||
|
\
|
||||||
|
elif [ "$TARGETOS" = "windows" ] && [ "$TARGETARCH" = "amd64" ]; then \
|
||||||
|
rustup target add x86_64-pc-windows-gnu; \
|
||||||
|
cargo build --release --target x86_64-pc-windows-gnu; \
|
||||||
|
mkdir -p /out; \
|
||||||
|
# Copy the compiled Windows DLL (supports both prefixed and non-prefixed outputs) \
|
||||||
|
cp target/x86_64-pc-windows-gnu/release/*.dll /out/tbr.dll; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =========================================================================
|
||||||
|
# Stage 2: Exporter Stage
|
||||||
|
# =========================================================================
|
||||||
|
FROM scratch AS exporter
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
# Copies the binaries into cleanly structured, platform-named folders
|
||||||
|
COPY --from=builder /out/ /
|
||||||
25
codegen_python.sh
Executable file
25
codegen_python.sh
Executable file
|
|
@ -0,0 +1,25 @@
|
||||||
|
mkdir -p out/binaries
|
||||||
|
|
||||||
|
# mac
|
||||||
|
cargo build --release --target aarch64-apple-darwin
|
||||||
|
cargo build --release --target x86_64-apple-darwin
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# other platforms
|
||||||
|
docker buildx build \
|
||||||
|
--platform linux/amd64,linux/arm64,windows/amd64 \
|
||||||
|
--target exporter \
|
||||||
|
--output type=local,dest=./out/binaries \
|
||||||
|
.
|
||||||
|
|
||||||
|
cp ./out/binaries/linux_amd64/libtbr.so ./out/libtbr_x86.so
|
||||||
|
cp ./out/binaries/linux_arm64/libtbr.so ./out/libtbr_arm64.so
|
||||||
|
cp ./out/binaries/windows_amd64/tbr.dll ./out/tbr.dll
|
||||||
|
|
||||||
|
# full conbined archs for mac
|
||||||
|
lipo -create target/aarch64-apple-darwin/release/libtbr.dylib target/x86_64-apple-darwin/release/libtbr.dylib -output target/release/libtbr.dylib
|
||||||
|
|
||||||
|
cp target/release/libtbr.dylib ./out/
|
||||||
|
|
||||||
|
cargo run --bin uniffi-bindgen generate --library target/release/libtbr.dylib --language python --out-dir out
|
||||||
|
|
@ -3,3 +3,5 @@ pub mod models;
|
||||||
pub mod previews;
|
pub mod previews;
|
||||||
pub mod recipe_functions;
|
pub mod recipe_functions;
|
||||||
pub mod xml;
|
pub mod xml;
|
||||||
|
|
||||||
|
uniffi::setup_scaffolding!();
|
||||||
|
|
|
||||||
1
src/xml/ast/mod.rs
Normal file
1
src/xml/ast/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod nodes;
|
||||||
88
src/xml/ast/nodes.rs
Normal file
88
src/xml/ast/nodes.rs
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
pub struct Program {
|
||||||
|
pub statements: Vec<Statement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Statement {
|
||||||
|
VarDecl {
|
||||||
|
name: String,
|
||||||
|
value: Option<Expression>,
|
||||||
|
assignment_type: AssignmentType,
|
||||||
|
},
|
||||||
|
IfStmt {
|
||||||
|
condition: Expression,
|
||||||
|
then_branch: Vec<Statement>,
|
||||||
|
else_branch: Vec<Statement>,
|
||||||
|
},
|
||||||
|
ForStmt {
|
||||||
|
condition: Expression,
|
||||||
|
body: Vec<Statement>,
|
||||||
|
},
|
||||||
|
ExprStmt {
|
||||||
|
expression: Expression,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Expression {
|
||||||
|
BinaryOp {
|
||||||
|
left: Box<Expression>,
|
||||||
|
op: BinaryOpKind, // Add, Sub, Mul, Div, Eq, Neq, Lt, Gt, Lte, Gte
|
||||||
|
right: Box<Expression>,
|
||||||
|
},
|
||||||
|
UnaryOp {
|
||||||
|
op: UnaryOpKind, // Neg, Not
|
||||||
|
operand: Box<Expression>,
|
||||||
|
},
|
||||||
|
Literal(LiteralValue),
|
||||||
|
Identifier(String),
|
||||||
|
ArrayAccess {
|
||||||
|
name: String,
|
||||||
|
indices: Vec<Expression>,
|
||||||
|
},
|
||||||
|
FunctionCall {
|
||||||
|
name: String,
|
||||||
|
args: Vec<Expression>,
|
||||||
|
},
|
||||||
|
SpecialVar {
|
||||||
|
name: String,
|
||||||
|
is_negative: bool, // For $- prefixed vars
|
||||||
|
},
|
||||||
|
AutoVarExpr {
|
||||||
|
variable: Box<Expression>, // Changed from SpecialVar to Expression
|
||||||
|
},
|
||||||
|
FlagExpr {
|
||||||
|
args: Vec<Expression>,
|
||||||
|
},
|
||||||
|
StringConcat {
|
||||||
|
// Special node for disambiguating +
|
||||||
|
parts: Vec<Expression>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum LiteralValue {
|
||||||
|
String(String),
|
||||||
|
Number(f64), // or i64/f64 distinction if needed
|
||||||
|
Bool(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum BinaryOpKind {
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
Eq, // =
|
||||||
|
Neq, // !=
|
||||||
|
Lt, // <
|
||||||
|
Gt, // >
|
||||||
|
Lte, // <=
|
||||||
|
Gte, // >=
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum UnaryOpKind {
|
||||||
|
Neg, // -
|
||||||
|
Not, // !
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum AssignmentType {
|
||||||
|
Equals, // =
|
||||||
|
NotAssigned, // !assigned
|
||||||
|
}
|
||||||
0
src/xml/codegen/java.rs
Normal file
0
src/xml/codegen/java.rs
Normal file
0
src/xml/codegen/javascript.rs
Normal file
0
src/xml/codegen/javascript.rs
Normal file
23
src/xml/codegen/mod.rs
Normal file
23
src/xml/codegen/mod.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
use crate::xml::ast::nodes::{AssignmentType, BinaryOpKind, LiteralValue, Program, UnaryOpKind};
|
||||||
|
|
||||||
|
pub trait CodeGen<T> {
|
||||||
|
fn generate_program(&self, program: &Program) -> T;
|
||||||
|
|
||||||
|
// Expression visitors
|
||||||
|
fn visit_binary_op(&self, left: &T, op: BinaryOpKind, right: &T) -> T;
|
||||||
|
fn visit_unary_op(&self, op: UnaryOpKind, operand: &T) -> T;
|
||||||
|
fn visit_literal(&self, lit: &LiteralValue) -> T;
|
||||||
|
fn visit_identifier(&self, name: &str) -> T;
|
||||||
|
fn visit_array_access(&self, name: &T, indices: &[T]) -> T;
|
||||||
|
fn visit_function_call(&self, name: &T, args: &[T]) -> T;
|
||||||
|
fn visit_special_var(&self, name: &str, is_negative: bool) -> T;
|
||||||
|
fn visit_auto_var_expr(&self, variable: &T) -> T;
|
||||||
|
fn visit_flag_expr(&self, args: &[T]) -> T;
|
||||||
|
fn visit_string_concat(&self, parts: &[T]) -> T;
|
||||||
|
|
||||||
|
// Statement visitors
|
||||||
|
fn visit_var_decl(&self, name: &T, value: Option<&T>, assignment_type: AssignmentType) -> T;
|
||||||
|
fn visit_if_stmt(&self, condition: &T, then_body: &[T], else_body: &[T]) -> T;
|
||||||
|
fn visit_for_stmt(&self, condition: &T, body: &[T]) -> T;
|
||||||
|
fn visit_expr_stmt(&self, expr: &T) -> T;
|
||||||
|
}
|
||||||
0
src/xml/codegen/python.rs
Normal file
0
src/xml/codegen/python.rs
Normal file
0
src/xml/codegen/rust.rs
Normal file
0
src/xml/codegen/rust.rs
Normal file
0
src/xml/error/formatter.rs
Normal file
0
src/xml/error/formatter.rs
Normal file
84
src/xml/error/mod.rs
Normal file
84
src/xml/error/mod.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
use lalrpop_util::ParseError as LalrpopParseError;
|
||||||
|
use crate::xml::parser::grammar::Token;
|
||||||
|
|
||||||
|
pub type ParserResult<T> = Result<T, ParseError>;
|
||||||
|
|
||||||
|
pub enum ParseError {
|
||||||
|
LexerError {
|
||||||
|
position: Position,
|
||||||
|
message: String,
|
||||||
|
},
|
||||||
|
ParserError {
|
||||||
|
position: Position,
|
||||||
|
message: String,
|
||||||
|
expected: Vec<String>,
|
||||||
|
},
|
||||||
|
SemanticError {
|
||||||
|
position: Position,
|
||||||
|
message: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Position {
|
||||||
|
pub line: usize,
|
||||||
|
pub column: usize,
|
||||||
|
pub file: Option<String>, // For tracking which XML file/script block
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParseError {
|
||||||
|
pub fn from_lalrpop_error(source: &str, e: LalrpopParseError<usize, Token<'_>, &'static str>) -> Self {
|
||||||
|
match e {
|
||||||
|
LalrpopParseError::User { error } => ParseError::SemanticError {
|
||||||
|
position: Position {
|
||||||
|
line: 0, // TODO: improve position tracking
|
||||||
|
column: 0,
|
||||||
|
file: None,
|
||||||
|
},
|
||||||
|
message: format!("User error: {}", error),
|
||||||
|
},
|
||||||
|
LalrpopParseError::InvalidToken { location } => ParseError::LexerError {
|
||||||
|
position: Position {
|
||||||
|
line: 0, // TODO: improve position tracking
|
||||||
|
column: 0,
|
||||||
|
file: None,
|
||||||
|
},
|
||||||
|
message: format!("Invalid token at location {}", location),
|
||||||
|
},
|
||||||
|
LalrpopParseError::UnrecognizedEof { location, expected } => ParseError::ParserError {
|
||||||
|
position: Position {
|
||||||
|
line: 0, // TODO: improve position tracking
|
||||||
|
column: 0,
|
||||||
|
file: None,
|
||||||
|
},
|
||||||
|
message: format!("Unexpected end of input, expected one of: {:?}", expected),
|
||||||
|
expected: expected.into_iter().map(|s| s.to_string()).collect(),
|
||||||
|
},
|
||||||
|
LalrpopParseError::UnrecognizedToken { token, expected } => ParseError::ParserError {
|
||||||
|
position: Position {
|
||||||
|
line: 0, // TODO: improve position tracking
|
||||||
|
column: token.0,
|
||||||
|
file: None,
|
||||||
|
},
|
||||||
|
message: format!("Unrecognized token: {:?}", token.1),
|
||||||
|
expected: expected.into_iter().map(|s| s.to_string()).collect(),
|
||||||
|
},
|
||||||
|
LalrpopParseError::ExtraToken { token } => ParseError::ParserError {
|
||||||
|
position: Position {
|
||||||
|
line: 0, // TODO: improve position tracking
|
||||||
|
column: token.0,
|
||||||
|
file: None,
|
||||||
|
},
|
||||||
|
message: format!("Extra token: {:?}", token.1),
|
||||||
|
expected: Vec::new(),
|
||||||
|
},
|
||||||
|
LalrpopParseError::User { error: e } => ParseError::SemanticError {
|
||||||
|
position: Position {
|
||||||
|
line: 0, // TODO: improve position tracking
|
||||||
|
column: 0,
|
||||||
|
file: None,
|
||||||
|
},
|
||||||
|
message: format!("User error: {}", e),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
167
src/xml/ffi_node.rs
Normal file
167
src/xml/ffi_node.rs
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::node::*;
|
||||||
|
|
||||||
|
/// Node FFI DTO object
|
||||||
|
///
|
||||||
|
/// Parsing xml as node structure
|
||||||
|
///
|
||||||
|
#[derive(uniffi::Object)]
|
||||||
|
pub struct FfiNode {
|
||||||
|
pub name: RwLock<String>,
|
||||||
|
pub value: RwLock<Option<String>>,
|
||||||
|
pub children: RwLock<Vec<Arc<FfiNode>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[uniffi::export]
|
||||||
|
impl FfiNode {
|
||||||
|
#[uniffi::constructor]
|
||||||
|
pub fn new(name: String, value: Option<String>) -> Arc<Self> {
|
||||||
|
Arc::new(FfiNode {
|
||||||
|
name: RwLock::new(name),
|
||||||
|
value: RwLock::new(value),
|
||||||
|
children: RwLock::new(Vec::new()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get name of this node
|
||||||
|
pub fn get_name(&self) -> String {
|
||||||
|
self.name.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get value of this node
|
||||||
|
pub fn get_value(&self) -> Option<String> {
|
||||||
|
self.value.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get child of this node by expected name
|
||||||
|
pub fn get_child(&self, name: String) -> Option<Arc<FfiNode>> {
|
||||||
|
self.children
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.find(|x| x.get_name() == name)
|
||||||
|
.cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add new spawn branch to this node
|
||||||
|
pub fn add_child(&self, child: Arc<FfiNode>) {
|
||||||
|
self.children.write().unwrap().push(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get children of this node
|
||||||
|
pub fn get_children(&self) -> Vec<Arc<FfiNode>> {
|
||||||
|
self.children.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set name
|
||||||
|
pub fn set_name(&self, new_value: String) {
|
||||||
|
let mut name_lock = self.name.write().unwrap();
|
||||||
|
*name_lock = new_value;
|
||||||
|
}
|
||||||
|
/// Set value
|
||||||
|
pub fn set_value(&self, new_value: Option<String>) {
|
||||||
|
let mut value_lock = self.value.write().unwrap();
|
||||||
|
*value_lock = new_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set children
|
||||||
|
pub fn set_children(&self, new_children: Vec<Arc<FfiNode>>) {
|
||||||
|
let mut children_lock = self.children.write().unwrap();
|
||||||
|
*children_lock = new_children;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find child node by expected value
|
||||||
|
pub fn find_by_child_value(
|
||||||
|
&self,
|
||||||
|
parent_name: String,
|
||||||
|
child_name: String,
|
||||||
|
target_value: String,
|
||||||
|
) -> Vec<Arc<FfiNode>> {
|
||||||
|
let mut matches = Vec::new();
|
||||||
|
|
||||||
|
if self.get_name() == parent_name {
|
||||||
|
if self.get_children().iter().any(|c| {
|
||||||
|
c.get_name() == child_name
|
||||||
|
&& c.get_value()
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|x| x.contains(&target_value))
|
||||||
|
}) {
|
||||||
|
matches.push(Arc::new(FfiNode {
|
||||||
|
name: RwLock::new(self.get_name()),
|
||||||
|
value: RwLock::new(self.get_value()),
|
||||||
|
children: RwLock::new(self.get_children()),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for child in &self.get_children() {
|
||||||
|
matches.extend(child.find_by_child_value(
|
||||||
|
parent_name.clone(),
|
||||||
|
child_name.clone(),
|
||||||
|
target_value.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
matches
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recursive converter
|
||||||
|
pub fn to_ffi_node(internal: Node) -> Arc<FfiNode> {
|
||||||
|
Arc::new(FfiNode {
|
||||||
|
name: RwLock::new(internal.name),
|
||||||
|
value: RwLock::new(internal.value),
|
||||||
|
children: RwLock::new(internal.children.into_iter().map(to_ffi_node).collect()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
#[uniffi::export]
|
||||||
|
pub fn parse_xml_to_node(xml: String) -> Vec<Arc<FfiNode>> {
|
||||||
|
let internal_roots = parse_xml_to_tree(&xml);
|
||||||
|
internal_roots.into_iter().map(to_ffi_node).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry point for python
|
||||||
|
// moved from parse xml to node because we have to clean file first
|
||||||
|
#[uniffi::export]
|
||||||
|
pub fn generate_node_from_xml(
|
||||||
|
mapping: HashMap<String, String>,
|
||||||
|
) -> HashMap<String, Vec<Arc<FfiNode>>> {
|
||||||
|
let mut result = HashMap::new();
|
||||||
|
|
||||||
|
for m in mapping {
|
||||||
|
let map_name = m.0;
|
||||||
|
// change from read path to entire file string instead, let user handle the file reading
|
||||||
|
let map_val = m.1;
|
||||||
|
|
||||||
|
let mut cleaned = String::new();
|
||||||
|
for line in map_val.lines() {
|
||||||
|
if line.contains(";") {
|
||||||
|
// special block
|
||||||
|
// either comment or include
|
||||||
|
if line.starts_with(";include") {
|
||||||
|
let converted =
|
||||||
|
format!("<include>{}</include>\n", line.replace(";include=", ""));
|
||||||
|
cleaned.push_str(&converted);
|
||||||
|
} else if line.starts_with(";") {
|
||||||
|
// into comment
|
||||||
|
let comment = format!("<comment>{}</comment>\n", line.replace(";", ""));
|
||||||
|
cleaned.push_str(&comment);
|
||||||
|
} else {
|
||||||
|
cleaned.push_str(format!("{line}\n").replace("&", "[amp]").as_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cleaned.push_str(format!("{line}\n").replace("&", "[amp]").as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let node = parse_xml_to_node(cleaned);
|
||||||
|
result.insert(map_name.to_string(), node);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
@ -1 +1,6 @@
|
||||||
|
pub mod ast;
|
||||||
|
pub mod codegen;
|
||||||
|
pub mod error;
|
||||||
|
pub mod ffi_node;
|
||||||
pub mod node;
|
pub mod node;
|
||||||
|
pub mod parser;
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,31 @@ impl Node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get node from path of node vector
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```
|
||||||
|
/// let current_menus_result: Option<&Node> = get_path!(root_node, ScrollableCatalog.Menus);
|
||||||
|
///
|
||||||
|
/// // Possible results
|
||||||
|
///
|
||||||
|
/// //Some(Node { name: "Menus", children: [Node { name: "Menu", children: [Node { name: "State",....
|
||||||
|
///
|
||||||
|
/// //None
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! get_path {
|
||||||
|
($node:expr, $last:ident) => {
|
||||||
|
$node.get_child(stringify!($last))
|
||||||
|
};
|
||||||
|
// recursive case
|
||||||
|
($node:expr, $next:ident . $($rest:ident).+) => {
|
||||||
|
$node
|
||||||
|
.get_child(stringify!($next))
|
||||||
|
.and_then(|child| get_path!(child, $($rest).+))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn generate_nodes_from_xml(
|
pub fn generate_nodes_from_xml(
|
||||||
catalog_map: Vec<(&str, String)>,
|
catalog_map: Vec<(&str, String)>,
|
||||||
) -> Result<IndexMap<String, Vec<Node>>, Box<dyn std::error::Error>> {
|
) -> Result<IndexMap<String, Vec<Node>>, Box<dyn std::error::Error>> {
|
||||||
|
|
@ -131,28 +156,3 @@ pub fn generate_nodes_from_xml(
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get node from path of node vector
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// ```
|
|
||||||
/// let current_menus_result: Option<&Node> = get_path!(root_node, ScrollableCatalog.Menus);
|
|
||||||
///
|
|
||||||
/// // Possible results
|
|
||||||
///
|
|
||||||
/// //Some(Node { name: "Menus", children: [Node { name: "Menu", children: [Node { name: "State",....
|
|
||||||
///
|
|
||||||
/// //None
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! get_path {
|
|
||||||
($node:expr, $last:ident) => {
|
|
||||||
$node.get_child(stringify!($last))
|
|
||||||
};
|
|
||||||
// recursive case
|
|
||||||
($node:expr, $next:ident . $($rest:ident).+) => {
|
|
||||||
$node
|
|
||||||
.get_child(stringify!($next))
|
|
||||||
.and_then(|child| get_path!(child, $($rest).+))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
||||||
9
src/xml/parser/grammar.lalrpop
Normal file
9
src/xml/parser/grammar.lalrpop
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
grammar;
|
||||||
|
|
||||||
|
pub Program: () = {
|
||||||
|
<Statement*> => ()
|
||||||
|
};
|
||||||
|
|
||||||
|
Statement: () = {
|
||||||
|
"hello" => ()
|
||||||
|
};
|
||||||
722
src/xml/parser/grammar.rs
Normal file
722
src/xml/parser/grammar.rs
Normal file
|
|
@ -0,0 +1,722 @@
|
||||||
|
// auto-generated: "lalrpop 0.20.0"
|
||||||
|
// sha3: ec3e9ad964a91b00df78376d9bb2e2c3df24aea537d87c1ca9bb028bad1ed06c
|
||||||
|
#[allow(unused_extern_crates)]
|
||||||
|
extern crate lalrpop_util as __lalrpop_util;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use self::__lalrpop_util::state_machine as __state_machine;
|
||||||
|
extern crate core;
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[allow(non_snake_case, non_camel_case_types, unused_mut, unused_variables, unused_imports, unused_parens, clippy::all)]
|
||||||
|
mod __parse__Program {
|
||||||
|
|
||||||
|
#[allow(unused_extern_crates)]
|
||||||
|
extern crate lalrpop_util as __lalrpop_util;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use self::__lalrpop_util::state_machine as __state_machine;
|
||||||
|
extern crate core;
|
||||||
|
extern crate alloc;
|
||||||
|
use self::__lalrpop_util::lexer::Token;
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) enum __Symbol<'input>
|
||||||
|
{
|
||||||
|
Variant0(&'input str),
|
||||||
|
Variant1(()),
|
||||||
|
Variant2(alloc::vec::Vec<()>),
|
||||||
|
}
|
||||||
|
const __ACTION: &[i8] = &[
|
||||||
|
// State 0
|
||||||
|
5,
|
||||||
|
// State 1
|
||||||
|
5,
|
||||||
|
// State 2
|
||||||
|
0,
|
||||||
|
// State 3
|
||||||
|
-6,
|
||||||
|
// State 4
|
||||||
|
-3,
|
||||||
|
// State 5
|
||||||
|
-7,
|
||||||
|
];
|
||||||
|
fn __action(state: i8, integer: usize) -> i8 {
|
||||||
|
__ACTION[(state as usize) * 1 + integer]
|
||||||
|
}
|
||||||
|
const __EOF_ACTION: &[i8] = &[
|
||||||
|
// State 0
|
||||||
|
-1,
|
||||||
|
// State 1
|
||||||
|
-2,
|
||||||
|
// State 2
|
||||||
|
-8,
|
||||||
|
// State 3
|
||||||
|
-6,
|
||||||
|
// State 4
|
||||||
|
-3,
|
||||||
|
// State 5
|
||||||
|
-7,
|
||||||
|
];
|
||||||
|
fn __goto(state: i8, nt: usize) -> i8 {
|
||||||
|
match nt {
|
||||||
|
0 => 2,
|
||||||
|
1 => match state {
|
||||||
|
1 => 5,
|
||||||
|
_ => 3,
|
||||||
|
},
|
||||||
|
3 => 1,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const __TERMINAL: &[&str] = &[
|
||||||
|
r###""hello""###,
|
||||||
|
];
|
||||||
|
fn __expected_tokens(__state: i8) -> alloc::vec::Vec<alloc::string::String> {
|
||||||
|
__TERMINAL.iter().enumerate().filter_map(|(index, terminal)| {
|
||||||
|
let next_state = __action(__state, index);
|
||||||
|
if next_state == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(alloc::string::ToString::to_string(terminal))
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
fn __expected_tokens_from_states<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
__states: &[i8],
|
||||||
|
_: core::marker::PhantomData<(&'input ())>,
|
||||||
|
) -> alloc::vec::Vec<alloc::string::String>
|
||||||
|
{
|
||||||
|
__TERMINAL.iter().enumerate().filter_map(|(index, terminal)| {
|
||||||
|
if __accepts(None, __states, Some(index), core::marker::PhantomData::<(&())>) {
|
||||||
|
Some(alloc::string::ToString::to_string(terminal))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
pub(crate) struct __StateMachine<'input>
|
||||||
|
where
|
||||||
|
{
|
||||||
|
input: &'input str,
|
||||||
|
__phantom: core::marker::PhantomData<(&'input ())>,
|
||||||
|
}
|
||||||
|
impl<'input> __state_machine::ParserDefinition for __StateMachine<'input>
|
||||||
|
where
|
||||||
|
{
|
||||||
|
type Location = usize;
|
||||||
|
type Error = &'static str;
|
||||||
|
type Token = Token<'input>;
|
||||||
|
type TokenIndex = usize;
|
||||||
|
type Symbol = __Symbol<'input>;
|
||||||
|
type Success = ();
|
||||||
|
type StateIndex = i8;
|
||||||
|
type Action = i8;
|
||||||
|
type ReduceIndex = i8;
|
||||||
|
type NonterminalIndex = usize;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn start_location(&self) -> Self::Location {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn start_state(&self) -> Self::StateIndex {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn token_to_index(&self, token: &Self::Token) -> Option<usize> {
|
||||||
|
__token_to_integer(token, core::marker::PhantomData::<(&())>)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn action(&self, state: i8, integer: usize) -> i8 {
|
||||||
|
__action(state, integer)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn error_action(&self, state: i8) -> i8 {
|
||||||
|
__action(state, 1 - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn eof_action(&self, state: i8) -> i8 {
|
||||||
|
__EOF_ACTION[state as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn goto(&self, state: i8, nt: usize) -> i8 {
|
||||||
|
__goto(state, nt)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn token_to_symbol(&self, token_index: usize, token: Self::Token) -> Self::Symbol {
|
||||||
|
__token_to_symbol(token_index, token, core::marker::PhantomData::<(&())>)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expected_tokens(&self, state: i8) -> alloc::vec::Vec<alloc::string::String> {
|
||||||
|
__expected_tokens(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expected_tokens_from_states(&self, states: &[i8]) -> alloc::vec::Vec<alloc::string::String> {
|
||||||
|
__expected_tokens_from_states(states, core::marker::PhantomData::<(&())>)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn uses_error_recovery(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn error_recovery_symbol(
|
||||||
|
&self,
|
||||||
|
recovery: __state_machine::ErrorRecovery<Self>,
|
||||||
|
) -> Self::Symbol {
|
||||||
|
panic!("error recovery not enabled for this grammar")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reduce(
|
||||||
|
&mut self,
|
||||||
|
action: i8,
|
||||||
|
start_location: Option<&Self::Location>,
|
||||||
|
states: &mut alloc::vec::Vec<i8>,
|
||||||
|
symbols: &mut alloc::vec::Vec<__state_machine::SymbolTriple<Self>>,
|
||||||
|
) -> Option<__state_machine::ParseResult<Self>> {
|
||||||
|
__reduce(
|
||||||
|
self.input,
|
||||||
|
action,
|
||||||
|
start_location,
|
||||||
|
states,
|
||||||
|
symbols,
|
||||||
|
core::marker::PhantomData::<(&())>,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn simulate_reduce(&self, action: i8) -> __state_machine::SimulatedReduce<Self> {
|
||||||
|
__simulate_reduce(action, core::marker::PhantomData::<(&())>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn __token_to_integer<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
__token: &Token<'input>,
|
||||||
|
_: core::marker::PhantomData<(&'input ())>,
|
||||||
|
) -> Option<usize>
|
||||||
|
{
|
||||||
|
match *__token {
|
||||||
|
Token(0, _) if true => Some(0),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn __token_to_symbol<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
__token_index: usize,
|
||||||
|
__token: Token<'input>,
|
||||||
|
_: core::marker::PhantomData<(&'input ())>,
|
||||||
|
) -> __Symbol<'input>
|
||||||
|
{
|
||||||
|
match __token_index {
|
||||||
|
0 => match __token {
|
||||||
|
Token(0, __tok0) if true => __Symbol::Variant0(__tok0),
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn __simulate_reduce<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
__reduce_index: i8,
|
||||||
|
_: core::marker::PhantomData<(&'input ())>,
|
||||||
|
) -> __state_machine::SimulatedReduce<__StateMachine<'input>>
|
||||||
|
{
|
||||||
|
match __reduce_index {
|
||||||
|
0 => {
|
||||||
|
__state_machine::SimulatedReduce::Reduce {
|
||||||
|
states_to_pop: 0,
|
||||||
|
nonterminal_produced: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
__state_machine::SimulatedReduce::Reduce {
|
||||||
|
states_to_pop: 1,
|
||||||
|
nonterminal_produced: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
__state_machine::SimulatedReduce::Reduce {
|
||||||
|
states_to_pop: 1,
|
||||||
|
nonterminal_produced: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
__state_machine::SimulatedReduce::Reduce {
|
||||||
|
states_to_pop: 0,
|
||||||
|
nonterminal_produced: 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
__state_machine::SimulatedReduce::Reduce {
|
||||||
|
states_to_pop: 1,
|
||||||
|
nonterminal_produced: 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
5 => {
|
||||||
|
__state_machine::SimulatedReduce::Reduce {
|
||||||
|
states_to_pop: 1,
|
||||||
|
nonterminal_produced: 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
6 => {
|
||||||
|
__state_machine::SimulatedReduce::Reduce {
|
||||||
|
states_to_pop: 2,
|
||||||
|
nonterminal_produced: 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
7 => __state_machine::SimulatedReduce::Accept,
|
||||||
|
_ => panic!("invalid reduction index {}", __reduce_index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct ProgramParser {
|
||||||
|
builder: __lalrpop_util::lexer::MatcherBuilder,
|
||||||
|
_priv: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProgramParser {
|
||||||
|
pub fn new() -> ProgramParser {
|
||||||
|
let __builder = super::__intern_token::new_builder();
|
||||||
|
ProgramParser {
|
||||||
|
builder: __builder,
|
||||||
|
_priv: (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn parse<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
&self,
|
||||||
|
input: &'input str,
|
||||||
|
) -> Result<(), __lalrpop_util::ParseError<usize, Token<'input>, &'static str>>
|
||||||
|
{
|
||||||
|
let mut __tokens = self.builder.matcher(input);
|
||||||
|
__state_machine::Parser::drive(
|
||||||
|
__StateMachine {
|
||||||
|
input,
|
||||||
|
__phantom: core::marker::PhantomData::<(&())>,
|
||||||
|
},
|
||||||
|
__tokens,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn __accepts<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
__error_state: Option<i8>,
|
||||||
|
__states: &[i8],
|
||||||
|
__opt_integer: Option<usize>,
|
||||||
|
_: core::marker::PhantomData<(&'input ())>,
|
||||||
|
) -> bool
|
||||||
|
{
|
||||||
|
let mut __states = __states.to_vec();
|
||||||
|
__states.extend(__error_state);
|
||||||
|
loop {
|
||||||
|
let mut __states_len = __states.len();
|
||||||
|
let __top = __states[__states_len - 1];
|
||||||
|
let __action = match __opt_integer {
|
||||||
|
None => __EOF_ACTION[__top as usize],
|
||||||
|
Some(__integer) => __action(__top, __integer),
|
||||||
|
};
|
||||||
|
if __action == 0 { return false; }
|
||||||
|
if __action > 0 { return true; }
|
||||||
|
let (__to_pop, __nt) = match __simulate_reduce(-(__action + 1), core::marker::PhantomData::<(&())>) {
|
||||||
|
__state_machine::SimulatedReduce::Reduce {
|
||||||
|
states_to_pop, nonterminal_produced
|
||||||
|
} => (states_to_pop, nonterminal_produced),
|
||||||
|
__state_machine::SimulatedReduce::Accept => return true,
|
||||||
|
};
|
||||||
|
__states_len -= __to_pop;
|
||||||
|
__states.truncate(__states_len);
|
||||||
|
let __top = __states[__states_len - 1];
|
||||||
|
let __next_state = __goto(__top, __nt);
|
||||||
|
__states.push(__next_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(crate) fn __reduce<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
__action: i8,
|
||||||
|
__lookahead_start: Option<&usize>,
|
||||||
|
__states: &mut alloc::vec::Vec<i8>,
|
||||||
|
__symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>,
|
||||||
|
_: core::marker::PhantomData<(&'input ())>,
|
||||||
|
) -> Option<Result<(),__lalrpop_util::ParseError<usize, Token<'input>, &'static str>>>
|
||||||
|
{
|
||||||
|
let (__pop_states, __nonterminal) = match __action {
|
||||||
|
0 => {
|
||||||
|
__reduce0(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>)
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
__reduce1(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>)
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
__reduce2(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>)
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
__reduce3(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>)
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
__reduce4(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>)
|
||||||
|
}
|
||||||
|
5 => {
|
||||||
|
__reduce5(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>)
|
||||||
|
}
|
||||||
|
6 => {
|
||||||
|
__reduce6(input, __lookahead_start, __symbols, core::marker::PhantomData::<(&())>)
|
||||||
|
}
|
||||||
|
7 => {
|
||||||
|
// __Program = Program => ActionFn(0);
|
||||||
|
let __sym0 = __pop_Variant1(__symbols);
|
||||||
|
let __start = __sym0.0;
|
||||||
|
let __end = __sym0.2;
|
||||||
|
let __nt = super::__action0::<>(input, __sym0);
|
||||||
|
return Some(Ok(__nt));
|
||||||
|
}
|
||||||
|
_ => panic!("invalid action code {}", __action)
|
||||||
|
};
|
||||||
|
let __states_len = __states.len();
|
||||||
|
__states.truncate(__states_len - __pop_states);
|
||||||
|
let __state = *__states.last().unwrap();
|
||||||
|
let __next_state = __goto(__state, __nonterminal);
|
||||||
|
__states.push(__next_state);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
#[inline(never)]
|
||||||
|
fn __symbol_type_mismatch() -> ! {
|
||||||
|
panic!("symbol type mismatch")
|
||||||
|
}
|
||||||
|
fn __pop_Variant1<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
__symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>
|
||||||
|
) -> (usize, (), usize)
|
||||||
|
{
|
||||||
|
match __symbols.pop() {
|
||||||
|
Some((__l, __Symbol::Variant1(__v), __r)) => (__l, __v, __r),
|
||||||
|
_ => __symbol_type_mismatch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn __pop_Variant2<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
__symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>
|
||||||
|
) -> (usize, alloc::vec::Vec<()>, usize)
|
||||||
|
{
|
||||||
|
match __symbols.pop() {
|
||||||
|
Some((__l, __Symbol::Variant2(__v), __r)) => (__l, __v, __r),
|
||||||
|
_ => __symbol_type_mismatch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn __pop_Variant0<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
__symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>
|
||||||
|
) -> (usize, &'input str, usize)
|
||||||
|
{
|
||||||
|
match __symbols.pop() {
|
||||||
|
Some((__l, __Symbol::Variant0(__v), __r)) => (__l, __v, __r),
|
||||||
|
_ => __symbol_type_mismatch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(crate) fn __reduce0<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
__lookahead_start: Option<&usize>,
|
||||||
|
__symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>,
|
||||||
|
_: core::marker::PhantomData<(&'input ())>,
|
||||||
|
) -> (usize, usize)
|
||||||
|
{
|
||||||
|
// Program = => ActionFn(7);
|
||||||
|
let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default();
|
||||||
|
let __end = __start.clone();
|
||||||
|
let __nt = super::__action7::<>(input, &__start, &__end);
|
||||||
|
__symbols.push((__start, __Symbol::Variant1(__nt), __end));
|
||||||
|
(0, 0)
|
||||||
|
}
|
||||||
|
pub(crate) fn __reduce1<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
__lookahead_start: Option<&usize>,
|
||||||
|
__symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>,
|
||||||
|
_: core::marker::PhantomData<(&'input ())>,
|
||||||
|
) -> (usize, usize)
|
||||||
|
{
|
||||||
|
// Program = Statement+ => ActionFn(8);
|
||||||
|
let __sym0 = __pop_Variant2(__symbols);
|
||||||
|
let __start = __sym0.0;
|
||||||
|
let __end = __sym0.2;
|
||||||
|
let __nt = super::__action8::<>(input, __sym0);
|
||||||
|
__symbols.push((__start, __Symbol::Variant1(__nt), __end));
|
||||||
|
(1, 0)
|
||||||
|
}
|
||||||
|
pub(crate) fn __reduce2<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
__lookahead_start: Option<&usize>,
|
||||||
|
__symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>,
|
||||||
|
_: core::marker::PhantomData<(&'input ())>,
|
||||||
|
) -> (usize, usize)
|
||||||
|
{
|
||||||
|
// Statement = "hello" => ActionFn(2);
|
||||||
|
let __sym0 = __pop_Variant0(__symbols);
|
||||||
|
let __start = __sym0.0;
|
||||||
|
let __end = __sym0.2;
|
||||||
|
let __nt = super::__action2::<>(input, __sym0);
|
||||||
|
__symbols.push((__start, __Symbol::Variant1(__nt), __end));
|
||||||
|
(1, 1)
|
||||||
|
}
|
||||||
|
pub(crate) fn __reduce3<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
__lookahead_start: Option<&usize>,
|
||||||
|
__symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>,
|
||||||
|
_: core::marker::PhantomData<(&'input ())>,
|
||||||
|
) -> (usize, usize)
|
||||||
|
{
|
||||||
|
// Statement* = => ActionFn(3);
|
||||||
|
let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default();
|
||||||
|
let __end = __start.clone();
|
||||||
|
let __nt = super::__action3::<>(input, &__start, &__end);
|
||||||
|
__symbols.push((__start, __Symbol::Variant2(__nt), __end));
|
||||||
|
(0, 2)
|
||||||
|
}
|
||||||
|
pub(crate) fn __reduce4<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
__lookahead_start: Option<&usize>,
|
||||||
|
__symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>,
|
||||||
|
_: core::marker::PhantomData<(&'input ())>,
|
||||||
|
) -> (usize, usize)
|
||||||
|
{
|
||||||
|
// Statement* = Statement+ => ActionFn(4);
|
||||||
|
let __sym0 = __pop_Variant2(__symbols);
|
||||||
|
let __start = __sym0.0;
|
||||||
|
let __end = __sym0.2;
|
||||||
|
let __nt = super::__action4::<>(input, __sym0);
|
||||||
|
__symbols.push((__start, __Symbol::Variant2(__nt), __end));
|
||||||
|
(1, 2)
|
||||||
|
}
|
||||||
|
pub(crate) fn __reduce5<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
__lookahead_start: Option<&usize>,
|
||||||
|
__symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>,
|
||||||
|
_: core::marker::PhantomData<(&'input ())>,
|
||||||
|
) -> (usize, usize)
|
||||||
|
{
|
||||||
|
// Statement+ = Statement => ActionFn(5);
|
||||||
|
let __sym0 = __pop_Variant1(__symbols);
|
||||||
|
let __start = __sym0.0;
|
||||||
|
let __end = __sym0.2;
|
||||||
|
let __nt = super::__action5::<>(input, __sym0);
|
||||||
|
__symbols.push((__start, __Symbol::Variant2(__nt), __end));
|
||||||
|
(1, 3)
|
||||||
|
}
|
||||||
|
pub(crate) fn __reduce6<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
__lookahead_start: Option<&usize>,
|
||||||
|
__symbols: &mut alloc::vec::Vec<(usize,__Symbol<'input>,usize)>,
|
||||||
|
_: core::marker::PhantomData<(&'input ())>,
|
||||||
|
) -> (usize, usize)
|
||||||
|
{
|
||||||
|
// Statement+ = Statement+, Statement => ActionFn(6);
|
||||||
|
assert!(__symbols.len() >= 2);
|
||||||
|
let __sym1 = __pop_Variant1(__symbols);
|
||||||
|
let __sym0 = __pop_Variant2(__symbols);
|
||||||
|
let __start = __sym0.0;
|
||||||
|
let __end = __sym1.2;
|
||||||
|
let __nt = super::__action6::<>(input, __sym0, __sym1);
|
||||||
|
__symbols.push((__start, __Symbol::Variant2(__nt), __end));
|
||||||
|
(2, 3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub use self::__parse__Program::ProgramParser;
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
mod __intern_token {
|
||||||
|
#![allow(unused_imports)]
|
||||||
|
#[allow(unused_extern_crates)]
|
||||||
|
extern crate lalrpop_util as __lalrpop_util;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use self::__lalrpop_util::state_machine as __state_machine;
|
||||||
|
extern crate core;
|
||||||
|
extern crate alloc;
|
||||||
|
pub fn new_builder() -> __lalrpop_util::lexer::MatcherBuilder {
|
||||||
|
let __strs: &[(&str, bool)] = &[
|
||||||
|
("^((?:hello))", false),
|
||||||
|
(r"^(\s*)", true),
|
||||||
|
];
|
||||||
|
__lalrpop_util::lexer::MatcherBuilder::new(__strs.iter().copied()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(crate) use self::__lalrpop_util::lexer::Token;
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn __action0<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
(_, __0, _): (usize, (), usize),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn __action1<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
(_, __0, _): (usize, alloc::vec::Vec<()>, usize),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn __action2<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
(_, __0, _): (usize, &'input str, usize),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn __action3<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
__lookbehind: &usize,
|
||||||
|
__lookahead: &usize,
|
||||||
|
) -> alloc::vec::Vec<()>
|
||||||
|
{
|
||||||
|
alloc::vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn __action4<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
(_, v, _): (usize, alloc::vec::Vec<()>, usize),
|
||||||
|
) -> alloc::vec::Vec<()>
|
||||||
|
{
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn __action5<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
(_, __0, _): (usize, (), usize),
|
||||||
|
) -> alloc::vec::Vec<()>
|
||||||
|
{
|
||||||
|
alloc::vec![__0]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn __action6<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
(_, v, _): (usize, alloc::vec::Vec<()>, usize),
|
||||||
|
(_, e, _): (usize, (), usize),
|
||||||
|
) -> alloc::vec::Vec<()>
|
||||||
|
{
|
||||||
|
{ let mut v = v; v.push(e); v }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn __action7<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
__lookbehind: &usize,
|
||||||
|
__lookahead: &usize,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
let __start0 = *__lookbehind;
|
||||||
|
let __end0 = *__lookahead;
|
||||||
|
let __temp0 = __action3(
|
||||||
|
input,
|
||||||
|
&__start0,
|
||||||
|
&__end0,
|
||||||
|
);
|
||||||
|
let __temp0 = (__start0, __temp0, __end0);
|
||||||
|
__action1(
|
||||||
|
input,
|
||||||
|
__temp0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn __action8<
|
||||||
|
'input,
|
||||||
|
>(
|
||||||
|
input: &'input str,
|
||||||
|
__0: (usize, alloc::vec::Vec<()>, usize),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
let __start0 = __0.0;
|
||||||
|
let __end0 = __0.2;
|
||||||
|
let __temp0 = __action4(
|
||||||
|
input,
|
||||||
|
__0,
|
||||||
|
);
|
||||||
|
let __temp0 = (__start0, __temp0, __end0);
|
||||||
|
__action1(
|
||||||
|
input,
|
||||||
|
__temp0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
|
||||||
|
pub trait __ToTriple<'input, >
|
||||||
|
{
|
||||||
|
fn to_triple(value: Self) -> Result<(usize,Token<'input>,usize), __lalrpop_util::ParseError<usize, Token<'input>, &'static str>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'input, > __ToTriple<'input, > for (usize, Token<'input>, usize)
|
||||||
|
{
|
||||||
|
fn to_triple(value: Self) -> Result<(usize,Token<'input>,usize), __lalrpop_util::ParseError<usize, Token<'input>, &'static str>> {
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'input, > __ToTriple<'input, > for Result<(usize, Token<'input>, usize), &'static str>
|
||||||
|
{
|
||||||
|
fn to_triple(value: Self) -> Result<(usize,Token<'input>,usize), __lalrpop_util::ParseError<usize, Token<'input>, &'static str>> {
|
||||||
|
match value {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(error) => Err(__lalrpop_util::ParseError::User { error }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/xml/parser/mod.rs
Normal file
37
src/xml/parser/mod.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
use crate::xml::ast::nodes::{AssignmentType, BinaryOpKind, Expression, LiteralValue, Program, Statement, UnaryOpKind};
|
||||||
|
use crate::xml::error::{ParseError, ParserResult};
|
||||||
|
|
||||||
|
// The LALRPOP-generated parser will be in this module
|
||||||
|
pub mod grammar;
|
||||||
|
|
||||||
|
pub use grammar::ProgramParser;
|
||||||
|
|
||||||
|
// Wrapper around the LALRPOP parser to provide a cleaner interface
|
||||||
|
pub struct ScriptParser;
|
||||||
|
|
||||||
|
impl ScriptParser {
|
||||||
|
/// Parse a script string into an AST
|
||||||
|
pub fn parse(source: &str) -> ParserResult<Program> {
|
||||||
|
let parser = grammar::ProgramParser::new();
|
||||||
|
match parser.parse(source) {
|
||||||
|
Ok(_) => {
|
||||||
|
// For now, just return an empty program since our grammar returns unit
|
||||||
|
Ok(Program { statements: vec![] })
|
||||||
|
},
|
||||||
|
Err(e) => Err(ParseError::from_lalrpop_error(source, e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty() {
|
||||||
|
let result = ScriptParser::parse("");
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let program = result.unwrap();
|
||||||
|
assert_eq!(program.statements.len(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/xml/parser/mod.rs.bak
Normal file
59
src/xml/parser/mod.rs.bak
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
use crate::xml::ast::nodes::{AssignmentType, BinaryOpKind, Expression, LiteralValue, Program, Statement, UnaryOpKind};
|
||||||
|
use crate::xml::error::{ParseError, ParserResult};
|
||||||
|
|
||||||
|
// The LALRPOP-generated parser will be in this module
|
||||||
|
mod grammar;
|
||||||
|
|
||||||
|
pub use grammar::ProgramParser;
|
||||||
|
|
||||||
|
// Wrapper around the LALRPOP parser to provide a cleaner interface
|
||||||
|
pub struct ScriptParser;
|
||||||
|
|
||||||
|
impl ScriptParser {
|
||||||
|
/// Parse a script string into an AST
|
||||||
|
pub fn parse(source: &str) -> ParserResult<Program> {
|
||||||
|
let parser = grammar::ProgramParser::new();
|
||||||
|
match parser.parse(source) {
|
||||||
|
Ok(program) => Ok(program),
|
||||||
|
Err(e) => Err(ParseError::from_lalrpop_error(source, e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty() {
|
||||||
|
let result = ScriptParser::parse("");
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let program = result.unwrap();
|
||||||
|
assert_eq!(program.statements.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_simple_var_decl() {
|
||||||
|
let result = ScriptParser::parse("Var x = 5");
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let program = result.unwrap();
|
||||||
|
assert_eq!(program.statements.len(), 1);
|
||||||
|
if let Statement::VarDecl { name, value, assignment_type } = &program.statements[0] {
|
||||||
|
assert_eq!(name, "x");
|
||||||
|
assert_eq!(assignment_type, &AssignmentType::Equals);
|
||||||
|
if let Some(Expression::Literal(LiteralValue::Number(val))) = value {
|
||||||
|
assert_eq!(*val, 5.0);
|
||||||
|
} else {
|
||||||
|
panic!("Expected number literal");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("Expected VarDecl statement");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_stmt() {
|
||||||
|
let result = ScriptParser::parse("If x = 5 Then Var y = 10 EndIf");
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue