grabapl_template_ffi/
lib.rs1use ::syntax::custom_syntax::CustomSyntax;
14use ::syntax::interpreter::lex_then_parse;
15use grabapl::operation::builder::IntermediateState;
16use grabapl::prelude::*;
17use semantics::*;
18
19type RustOperationContext = OperationContext<TheSemantics>;
20type RustIntermediateState = IntermediateState<TheSemantics>;
21type RustConcreteGraph = ConcreteGraph<TheSemantics>;
22type RustOperationBuilder<'a> = OperationBuilder<'a, TheSemantics>;
23
24fn parse_node_value(s: &str) -> Option<NodeValue> {
25 let parser = syntax::node_value_parser();
26 lex_then_parse(s, parser).ok()
27}
28
29fn parse_edge_value(s: &str) -> Option<EdgeValue> {
30 let parser = syntax::edge_value_parser();
31 lex_then_parse(s, parser).ok()
32}
33
34fn parse_node_type(s: &str) -> Result<NodeType, String> {
35 let parser = syntax::TheCustomSyntax::get_node_type_parser();
36 lex_then_parse(s, parser).map_err(|e| e.to_string())
37}
38
39fn parse_edge_type(s: &str) -> Result<EdgeType, String> {
40 let parser = syntax::TheCustomSyntax::get_edge_type_parser();
41 lex_then_parse(s, parser).map_err(|e| e.to_string())
42}
43
44#[diplomat::bridge]
55pub mod ffi {
56 use std::collections::HashMap;
57 use super::RustIntermediateState;
59 use super::RustOperationBuilder;
60 use super::RustOperationContext;
61 use super::TheSemantics;
62 use super::{OperationId, RustConcreteGraph};
63 use error_stack::fmt::ColorMode;
64 use grabapl::NodeKey;
65 use std::fmt::Write as _;
66
67 #[diplomat::opaque]
69 pub struct Grabapl;
70
71 impl Grabapl {
72 pub fn init() {
75 console_error_panic_hook::set_once();
79 error_stack::Report::set_color_mode(ColorMode::None);
81 log::info!("Grabapl FFI initialized");
83 }
84
85 pub fn parse(src: &str) -> Box<CompileResult> {
87 let raw_res = syntax::try_parse_to_op_ctx_and_map(
88 src,
89 false, );
91 let op_ctx_and_map_res = raw_res
92 .op_ctx_and_map
93 .map(|(op_ctx, map)| {
95 let state_map = map
96 .into_iter()
97 .map(|(k, v)| (k.into(), v))
98 .collect::<HashMap<String, _>>();
99 (op_ctx, state_map)
100 })
101 .map_err(|e| e.value);
103
104 Box::new(CompileResult {
105 op_ctx_and_map_res,
106 state_map: raw_res.state_map,
107 })
108 }
109 }
110
111 #[diplomat::opaque]
113 pub struct ConcreteGraph(RustConcreteGraph);
114
115 impl ConcreteGraph {
116 pub fn create() -> Box<ConcreteGraph> {
118 Box::new(ConcreteGraph(RustConcreteGraph::new()))
119 }
120
121 pub fn dot(&self, out: &mut DiplomatWrite) {
123 write!(out, "{}", self.0.dot()).unwrap();
124 }
125
126 pub fn add_node(&mut self, value: &str) -> Result<u32, Box<StringError>> {
128 let node_value = super::parse_node_value(value)
129 .ok_or_else(|| StringError::from_boxed(format!("Invalid node value: {}", value)))?;
130 let node_key = self.0.add_node(node_value);
131 Ok(node_key.0)
132 }
133
134 pub fn add_edge(
136 &mut self,
137 from: u32,
138 to: u32,
139 value: &str,
140 ) -> Result<(), Box<StringError>> {
141 let edge_value = super::parse_edge_value(value)
142 .ok_or_else(|| StringError::from_boxed(format!("Invalid edge value: {}", value)))?;
143 let from_key = NodeKey(from);
144 let to_key = NodeKey(to);
145 self.0.add_edge(from_key, to_key, edge_value);
146 Ok(())
147 }
148 }
149
150 #[diplomat::opaque]
152 pub struct OperationContext(RustOperationContext);
153
154 impl OperationContext {
155 pub fn create() -> Box<OperationContext> {
157 let op_ctx = RustOperationContext::new();
158 Box::new(OperationContext(op_ctx))
166 }
167 }
168
169 #[diplomat::opaque]
174 pub struct CompileResult {
175 op_ctx_and_map_res: Result<(RustOperationContext, HashMap<String, OperationId>), String>,
176 state_map: HashMap<String, RustIntermediateState>,
177 }
178
179 impl CompileResult {
180 pub fn dot_of_state(&self, state: &str, dot_out: &mut DiplomatWrite) {
182 let Some(state) = self.state_map.get(state) else {
183 log::error!("state does not exist in state map");
184 return;
185 };
186 write!(dot_out, "{}", state.dot_with_aid()).unwrap();
187 }
188
189 pub fn get_program(&self) -> Result<Box<Program>, Box<StringError>> {
193 match &self.op_ctx_and_map_res {
194 Ok((op_ctx, fn_map)) => {
195 let program = Program {
196 op_ctx: op_ctx.clone(),
197 fn_map: fn_map.clone(),
198 };
199 Ok(Box::new(program))
200 }
201 Err(err) => Err(Box::new(StringError(err.to_string()))),
202 }
203 }
204 }
205
206 #[diplomat::opaque]
208 pub struct Program {
209 op_ctx: RustOperationContext,
210 fn_map: HashMap<String, OperationId>,
211 }
212
213 impl Program {
214 pub fn op_ctx(&self) -> Box<OperationContext> {
216 Box::new(OperationContext(self.op_ctx.clone()))
217 }
218
219 pub fn run_operation(
221 &self,
222 g: &mut ConcreteGraph,
223 op_name: &str,
224 args: &[u32],
225 ) -> Result<(), Box<StringError>> {
226 let op_id = self
227 .fn_map
228 .get(op_name)
229 .ok_or_else(|| StringError(format!("Operation '{}' not found", op_name)))?;
230 let args: Vec<_> = args.iter().copied().map(NodeKey).collect();
231 let res = super::run_from_concrete(&mut g.0, &self.op_ctx, *op_id, &args);
232 res.map_err(|e| Box::new(StringError(e.to_string())))
233 .map(|_| ())
234 }
235 }
236
237 #[diplomat::opaque]
242 pub struct OperationBuilder<'a>(RustOperationBuilder<'a>);
243
244 impl<'a> OperationBuilder<'a> {
245 pub fn create(op_ctx: &'a OperationContext, self_op_id: u32) -> Box<OperationBuilder<'a>> {
249 let op_builder = RustOperationBuilder::new(&op_ctx.0, self_op_id);
250 Box::new(OperationBuilder(op_builder))
251 }
252
253 pub fn expect_parameter_node(
255 &mut self,
256 name: &str,
257 node_type: &str,
258 ) -> Result<(), Box<StringError>> {
259 let node_type = super::parse_node_type(node_type)
260 .map_err(|e| StringError::from_boxed(format!("Invalid node type: {}", e)))?;
261 self.0
262 .expect_parameter_node(name, node_type)
263 .map_err(|e| StringError::from_boxed(e.to_string()))
264 }
265
266 }
268
269 #[diplomat::opaque]
271 pub struct StringError(String);
272
273 impl StringError {
274 fn from_boxed(s: String) -> Box<StringError> {
275 Box::new(StringError(s))
276 }
277
278 #[diplomat::attr(auto, stringifier)]
279 pub fn to_string(&self, out: &mut DiplomatWrite) {
280 write!(out, "{}", self.0).unwrap();
281 }
282 }
283}