use ::syntax::custom_syntax::CustomSyntax;
use ::syntax::interpreter::lex_then_parse;
use grabapl::operation::builder::IntermediateState;
use grabapl::prelude::*;
use semantics::*;
type RustOperationContext = OperationContext<TheSemantics>;
type RustIntermediateState = IntermediateState<TheSemantics>;
type RustConcreteGraph = ConcreteGraph<TheSemantics>;
type RustOperationBuilder<'a> = OperationBuilder<'a, TheSemantics>;
fn parse_node_value(s: &str) -> Option<NodeValue> {
let parser = syntax::node_value_parser();
lex_then_parse(s, parser).ok()
}
fn parse_edge_value(s: &str) -> Option<EdgeValue> {
let parser = syntax::edge_value_parser();
lex_then_parse(s, parser).ok()
}
fn parse_node_type(s: &str) -> Result<NodeType, String> {
let parser = syntax::TheCustomSyntax::get_node_type_parser();
lex_then_parse(s, parser).map_err(|e| e.to_string())
}
fn parse_edge_type(s: &str) -> Result<EdgeType, String> {
let parser = syntax::TheCustomSyntax::get_edge_type_parser();
lex_then_parse(s, parser).map_err(|e| e.to_string())
}
#[diplomat::bridge]
pub mod ffi {
use std::collections::HashMap;
use super::RustIntermediateState;
use super::RustOperationBuilder;
use super::RustOperationContext;
use super::TheSemantics;
use super::{OperationId, RustConcreteGraph};
use error_stack::fmt::ColorMode;
use grabapl::NodeKey;
use std::fmt::Write as _;
#[diplomat::opaque]
pub struct Grabapl;
impl Grabapl {
pub fn init() {
console_error_panic_hook::set_once();
error_stack::Report::set_color_mode(ColorMode::None);
log::info!("Grabapl FFI initialized");
}
pub fn parse(src: &str) -> Box<CompileResult> {
let raw_res = syntax::try_parse_to_op_ctx_and_map(
src,
false,
);
let op_ctx_and_map_res = raw_res
.op_ctx_and_map
.map(|(op_ctx, map)| {
let state_map = map
.into_iter()
.map(|(k, v)| (k.into(), v))
.collect::<HashMap<String, _>>();
(op_ctx, state_map)
})
.map_err(|e| e.value);
Box::new(CompileResult {
op_ctx_and_map_res,
state_map: raw_res.state_map,
})
}
}
#[diplomat::opaque]
pub struct ConcreteGraph(RustConcreteGraph);
impl ConcreteGraph {
pub fn create() -> Box<ConcreteGraph> {
Box::new(ConcreteGraph(RustConcreteGraph::new()))
}
pub fn dot(&self, out: &mut DiplomatWrite) {
write!(out, "{}", self.0.dot()).unwrap();
}
pub fn add_node(&mut self, value: &str) -> Result<u32, Box<StringError>> {
let node_value = super::parse_node_value(value)
.ok_or_else(|| StringError::from_boxed(format!("Invalid node value: {}", value)))?;
let node_key = self.0.add_node(node_value);
Ok(node_key.0)
}
pub fn add_edge(
&mut self,
from: u32,
to: u32,
value: &str,
) -> Result<(), Box<StringError>> {
let edge_value = super::parse_edge_value(value)
.ok_or_else(|| StringError::from_boxed(format!("Invalid edge value: {}", value)))?;
let from_key = NodeKey(from);
let to_key = NodeKey(to);
self.0.add_edge(from_key, to_key, edge_value);
Ok(())
}
}
#[diplomat::opaque]
pub struct OperationContext(RustOperationContext);
impl OperationContext {
pub fn create() -> Box<OperationContext> {
let op_ctx = RustOperationContext::new();
Box::new(OperationContext(op_ctx))
}
}
#[diplomat::opaque]
pub struct CompileResult {
op_ctx_and_map_res: Result<(RustOperationContext, HashMap<String, OperationId>), String>,
state_map: HashMap<String, RustIntermediateState>,
}
impl CompileResult {
pub fn dot_of_state(&self, state: &str, dot_out: &mut DiplomatWrite) {
let Some(state) = self.state_map.get(state) else {
log::error!("state does not exist in state map");
return;
};
write!(dot_out, "{}", state.dot_with_aid()).unwrap();
}
pub fn get_program(&self) -> Result<Box<Program>, Box<StringError>> {
match &self.op_ctx_and_map_res {
Ok((op_ctx, fn_map)) => {
let program = Program {
op_ctx: op_ctx.clone(),
fn_map: fn_map.clone(),
};
Ok(Box::new(program))
}
Err(err) => Err(Box::new(StringError(err.to_string()))),
}
}
}
#[diplomat::opaque]
pub struct Program {
op_ctx: RustOperationContext,
fn_map: HashMap<String, OperationId>,
}
impl Program {
pub fn op_ctx(&self) -> Box<OperationContext> {
Box::new(OperationContext(self.op_ctx.clone()))
}
pub fn run_operation(
&self,
g: &mut ConcreteGraph,
op_name: &str,
args: &[u32],
) -> Result<(), Box<StringError>> {
let op_id = self
.fn_map
.get(op_name)
.ok_or_else(|| StringError(format!("Operation '{}' not found", op_name)))?;
let args: Vec<_> = args.iter().copied().map(NodeKey).collect();
let res = super::run_from_concrete(&mut g.0, &self.op_ctx, *op_id, &args);
res.map_err(|e| Box::new(StringError(e.to_string())))
.map(|_| ())
}
}
#[diplomat::opaque]
pub struct OperationBuilder<'a>(RustOperationBuilder<'a>);
impl<'a> OperationBuilder<'a> {
pub fn create(op_ctx: &'a OperationContext, self_op_id: u32) -> Box<OperationBuilder<'a>> {
let op_builder = RustOperationBuilder::new(&op_ctx.0, self_op_id);
Box::new(OperationBuilder(op_builder))
}
pub fn expect_parameter_node(
&mut self,
name: &str,
node_type: &str,
) -> Result<(), Box<StringError>> {
let node_type = super::parse_node_type(node_type)
.map_err(|e| StringError::from_boxed(format!("Invalid node type: {}", e)))?;
self.0
.expect_parameter_node(name, node_type)
.map_err(|e| StringError::from_boxed(e.to_string()))
}
}
#[diplomat::opaque]
pub struct StringError(String);
impl StringError {
fn from_boxed(s: String) -> Box<StringError> {
Box::new(StringError(s))
}
#[diplomat::attr(auto, stringifier)]
pub fn to_string(&self, out: &mut DiplomatWrite) {
write!(out, "{}", self.0).unwrap();
}
}
}