use crate::exec::{execute, execute_precompiled, serialize_tree, serialize_tree_to_writer, RuntimeFormat};
use crate::parser::ast::GelDocument;
use crate::parser::lexer::lex;
use crate::parser::syntax::parse_gel_document;
use crate::parser::validate;
use crate::parser::{OutputFormat, Parser};
use crate::{GelError, Result, Severity};
use pyo3::prelude::*;
use std::fs;
fn gel_to_pyerr(e: GelError) -> PyErr {
match &e {
GelError::Lex { .. } | GelError::Parse { .. } | GelError::Validation { .. } => {
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!("{e}"))
}
GelError::Runtime { .. } => PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(format!("{e}")),
GelError::Io(_) => PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("{e}")),
}
}
fn parse_format(format: &str) -> PyResult<RuntimeFormat> {
match format {
"json" => Ok(RuntimeFormat::Json),
"xml" => Ok(RuntimeFormat::Xml),
"yaml" | "yml" => Ok(RuntimeFormat::Yaml),
"none" => Ok(RuntimeFormat::None),
other => Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(format!(
"unknown format '{}' (expected json, xml, yaml, none)",
other
))),
}
}
#[pyclass]
pub struct GelContext {
doc: GelDocument,
}
#[pymethods]
impl GelContext {
#[new]
fn new(gel_source: &str) -> PyResult<Self> {
let tokens = lex(gel_source).map_err(gel_to_pyerr)?;
let doc = parse_gel_document(&tokens).map_err(gel_to_pyerr)?;
let diags = validate::validate(&doc);
for d in &diags {
if d.severity == Severity::Error {
return Err(gel_to_pyerr(GelError::validation(
d.message.clone(),
d.span.unwrap_or_default(),
)));
}
}
let mut doc = doc;
doc.compile_regexes();
Ok(Self { doc })
}
#[pyo3(signature = (input, grammar = "input"))]
fn run(&self, input: &str, grammar: &str) -> PyResult<String> {
let exec = execute_precompiled(&self.doc, grammar, input).map_err(gel_to_pyerr)?;
if let Some(err) = &exec.error {
return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(err.clone()));
}
Ok(serialize_tree(&exec, RuntimeFormat::Json))
}
#[pyo3(signature = (input, grammar = "input"))]
fn run_xml(&self, input: &str, grammar: &str) -> PyResult<String> {
let exec = execute_precompiled(&self.doc, grammar, input).map_err(gel_to_pyerr)?;
if let Some(err) = &exec.error {
return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(err.clone()));
}
Ok(serialize_tree(&exec, RuntimeFormat::Xml))
}
#[pyo3(signature = (input, grammar = "input"))]
fn run_yaml(&self, input: &str, grammar: &str) -> PyResult<String> {
let exec = execute_precompiled(&self.doc, grammar, input).map_err(gel_to_pyerr)?;
if let Some(err) = &exec.error {
return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(err.clone()));
}
Ok(serialize_tree(&exec, RuntimeFormat::Yaml))
}
#[pyo3(signature = (input, format = "json", grammar = "input"))]
fn generate_string(&self, input: &str, format: &str, grammar: &str) -> PyResult<String> {
let fmt = parse_format(format)?;
let exec = execute_precompiled(&self.doc, grammar, input).map_err(gel_to_pyerr)?;
if let Some(err) = &exec.error {
return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(err.clone()));
}
Ok(serialize_tree(&exec, fmt))
}
#[pyo3(signature = (input_file, format = "json", grammar = "input", encoding = "utf-8"))]
fn generate(&self, input_file: &str, format: &str, grammar: &str, encoding: &str) -> PyResult<String> {
let _ = encoding; let content = fs::read_to_string(input_file)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("{}: {}", input_file, e)))?;
self.generate_string(&content, format, grammar)
}
#[pyo3(signature = (input_file, output_file, format = "json", grammar = "input"))]
fn generate_to_file(&self, input_file: &str, output_file: &str, format: &str, grammar: &str) -> PyResult<()> {
let fmt = parse_format(format)?;
let content = fs::read_to_string(input_file)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("{}: {}", input_file, e)))?;
let exec = execute_precompiled(&self.doc, grammar, &content).map_err(gel_to_pyerr)?;
if let Some(err) = &exec.error {
return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(err.clone()));
}
let file = fs::File::create(output_file)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("{}: {}", output_file, e)))?;
let mut writer = std::io::BufWriter::new(file);
serialize_tree_to_writer(&exec, fmt, &mut writer)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("{}: {}", output_file, e)))?;
Ok(())
}
#[pyo3(signature = (input, output_file, format = "json", grammar = "input"))]
fn generate_string_to_file(&self, input: &str, output_file: &str, format: &str, grammar: &str) -> PyResult<()> {
let fmt = parse_format(format)?;
let exec = execute_precompiled(&self.doc, grammar, input).map_err(gel_to_pyerr)?;
if let Some(err) = &exec.error {
return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(err.clone()));
}
let file = fs::File::create(output_file)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("{}: {}", output_file, e)))?;
let mut writer = std::io::BufWriter::new(file);
serialize_tree_to_writer(&exec, fmt, &mut writer)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("{}: {}", output_file, e)))?;
Ok(())
}
}
#[pyfunction]
pub fn compile_grammar(gel_source: &str) -> PyResult<GelContext> {
GelContext::new(gel_source)
}
#[pyfunction]
pub fn compile_string(gel_source: &str) -> PyResult<GelContext> {
GelContext::new(gel_source)
}
#[pyfunction]
#[pyo3(signature = (syntax_file, encoding = "utf-8"))]
pub fn compile_file(syntax_file: &str, encoding: &str) -> PyResult<GelContext> {
let _ = encoding; let source = fs::read_to_string(syntax_file)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("{}: {}", syntax_file, e)))?;
GelContext::new(&source)
}
pub fn parse_gel_to_json(input: &str) -> Result<String> {
let parser = Parser::new(OutputFormat::Json);
parser.parse_str(input)
}
pub fn parse_gel_to_xml(input: &str) -> Result<String> {
let parser = Parser::new(OutputFormat::Xml);
parser.parse_str(input)
}
pub fn parse_gel_to_yaml(input: &str) -> Result<String> {
let parser = Parser::new(OutputFormat::Yaml);
parser.parse_str(input)
}
#[pyfunction]
pub fn parse_to_json(input: &str) -> PyResult<String> {
match parse_gel_to_json(input) {
Ok(result) => Ok(result),
Err(e) => Ok(format!("{{\"error\":\"{e}\"}}")),
}
}
#[pyfunction]
pub fn parse_to_xml(input: &str) -> PyResult<String> {
match parse_gel_to_xml(input) {
Ok(result) => Ok(result),
Err(e) => Ok(format!("<error>{e}</error>")),
}
}
#[pyfunction]
pub fn parse_to_yaml(input: &str) -> PyResult<String> {
match parse_gel_to_yaml(input) {
Ok(result) => Ok(result),
Err(e) => Ok(format!("error: {e}")), }
}
#[pyfunction]
pub fn run_grammar(gel_source: &str, grammar: &str, runtime_input: &str) -> PyResult<String> {
let tokens = lex(gel_source).map_err(gel_to_pyerr)?;
let mut doc = parse_gel_document(&tokens).map_err(gel_to_pyerr)?;
let exec = execute(&mut doc, grammar, runtime_input).map_err(gel_to_pyerr)?;
if let Some(err) = exec.error {
return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(err));
}
Ok(serialize_tree(&exec, RuntimeFormat::Json))
}
#[pyfunction]
pub fn run_grammar_xml(gel_source: &str, grammar: &str, runtime_input: &str) -> PyResult<String> {
let tokens = lex(gel_source).map_err(gel_to_pyerr)?;
let mut doc = parse_gel_document(&tokens).map_err(gel_to_pyerr)?;
let exec = execute(&mut doc, grammar, runtime_input).map_err(gel_to_pyerr)?;
if let Some(err) = &exec.error {
return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(err.clone()));
}
Ok(serialize_tree(&exec, RuntimeFormat::Xml))
}
#[pyfunction]
pub fn run_grammar_yaml(gel_source: &str, grammar: &str, runtime_input: &str) -> PyResult<String> {
let tokens = lex(gel_source).map_err(gel_to_pyerr)?;
let mut doc = parse_gel_document(&tokens).map_err(gel_to_pyerr)?;
let exec = execute(&mut doc, grammar, runtime_input).map_err(gel_to_pyerr)?;
if let Some(err) = &exec.error {
return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(err.clone()));
}
Ok(serialize_tree(&exec, RuntimeFormat::Yaml))
}