helix/dna/hel/
dispatch.rs1use crate::dna::atp::lexer::{tokenize_with_locations, SourceMap};
2use crate::dna::atp::parser::{Parser, ParseError};
3use crate::dna::atp::ast::HelixAst;
4use crate::dna::atp::value::Value;
5use crate::dna::atp::interpreter::HelixInterpreter;
6use crate::hel::error::HlxError;
7#[derive(Debug)]
8pub enum DispatchResult {
9 Parsed(HelixAst),
10 Executed(Value),
11 #[cfg(feature = "js")]
12 ParseError(napi::Error<ParseError>),
13 #[cfg(not(feature = "js"))]
14 ParseError(ParseError),
15 ExecutionError(HlxError),
16}
17pub struct HelixDispatcher {
18 interpreter: Option<HelixInterpreter>,
19}
20impl HelixDispatcher {
21 pub fn new() -> Self {
22 Self { interpreter: None }
23 }
24 pub async fn initialize(&mut self) -> Result<(), HlxError> {
25 self.interpreter = Some(HelixInterpreter::new().await?);
26 Ok(())
27 }
28 #[cfg(feature = "js")]
29 pub fn parse_only(&self, source: &str) -> Result<HelixAst, napi::Error<ParseError>> {
30 crate::parse(source).map_err(|e| e.into())
31 }
32 #[cfg(not(feature = "js"))]
33 pub fn parse_only(&self, source: &str) -> Result<HelixAst, ParseError> {
34 crate::parse(source)
35 }
36 pub async fn parse_and_execute(
37 &mut self,
38 source: &str,
39 ) -> Result<DispatchResult, HlxError> {
40 if self.interpreter.is_none() {
41 self.initialize().await?;
42 }
43 let ast = match crate::parse(source) {
44 Ok(ast) => ast,
45 Err(parse_err) => return Ok(DispatchResult::ParseError(parse_err)),
46 };
47 if let Some(ref mut interpreter) = self.interpreter {
48 match interpreter.execute_ast(&ast).await {
49 Ok(value) => Ok(DispatchResult::Executed(value.into())),
50 Err(exec_err) => Ok(DispatchResult::ExecutionError(exec_err)),
51 }
52 } else {
53 Err(
54 HlxError::execution_error(
55 "Interpreter not initialized",
56 "Call initialize() before executing code",
57 ),
58 )
59 }
60 }
61 pub async fn parse_dsl(&mut self, source: &str) -> Result<DispatchResult, HlxError> {
62 let tokens_with_loc = tokenize_with_locations(source)
63 .map_err(|e| {
64 HlxError::invalid_input(
65 format!("Lexer error: {}", e),
66 "Check syntax and try again",
67 )
68 })?;
69 let source_map = SourceMap {
70 tokens: tokens_with_loc.clone(),
71 source: source.to_string(),
72 };
73 let mut parser = Parser::new_with_source_map(source_map);
74 match parser.parse() {
75 Ok(ast) => Ok(DispatchResult::Parsed(ast)),
76 Err(parse_err) => {
77 let parse_error = ParseError {
78 message: parse_err,
79 location: None,
80 token_index: 0,
81 expected: None,
82 found: String::new(),
83 context: String::new(),
84 };
85 Ok(DispatchResult::ParseError(parse_error.into()))
86 }
87 }
88 }
89 pub fn interpreter(&self) -> Option<&HelixInterpreter> {
90 self.interpreter.as_ref()
91 }
92 pub fn interpreter_mut(&mut self) -> Option<&mut HelixInterpreter> {
93 self.interpreter.as_mut()
94 }
95 pub fn is_ready(&self) -> bool {
96 self.interpreter.is_some()
97 }
98}
99#[cfg(feature = "js")]
100pub fn parse_helix(source: &str) -> Result<HelixAst, napi::Error<ParseError>> {
101 crate::parse(source).map_err(|e| e.into())
102}
103#[cfg(not(feature = "js"))]
104pub fn parse_helix(source: &str) -> Result<HelixAst, ParseError> {
105 crate::parse(source)
106}
107#[cfg(feature = "js")]
108pub fn parse_and_validate(
109 source: &str,
110) -> Result<crate::atp::types::HelixConfig, napi::Error<String>> {
111 crate::parse_and_validate(source).map_err(|e| e.into())
112}
113#[cfg(not(feature = "js"))]
114pub fn parse_and_validate(source: &str) -> Result<crate::atp::types::HelixConfig, String> {
115 crate::parse_and_validate(source)
116}
117pub async fn execute_helix(source: &str) -> Result<Value, HlxError> {
118 let mut dispatcher = HelixDispatcher::new();
119 dispatcher.initialize().await?;
120 match dispatcher.parse_and_execute(source).await? {
121 DispatchResult::Executed(value) => Ok(value),
122 DispatchResult::ParseError(err) => {
123 Err(
124 HlxError::invalid_input(
125 format!("Parse error: {}", err),
126 "Check syntax and try again",
127 ),
128 )
129 }
130 DispatchResult::ExecutionError(err) => Err(err),
131 DispatchResult::Parsed(_) => {
132 Err(
133 HlxError::execution_error(
134 "Parsed AST but no execution result",
135 "Use parse_helix for parsing only",
136 ),
137 )
138 }
139 }
140}
141#[cfg(feature = "js")]
142pub async fn parse_helix_dsl(source: &str) -> Result<HelixAst, napi::Error<ParseError>> {
143 let mut dispatcher = HelixDispatcher::new();
144 match dispatcher.parse_dsl(source).await {
145 Ok(DispatchResult::Parsed(ast)) => Ok(ast),
146 Ok(DispatchResult::ParseError(err)) => Err(err),
147 _ => {
148 let parse_error = ParseError {
149 message: "Unexpected dispatch result".to_string(),
150 location: None,
151 token_index: 0,
152 expected: None,
153 found: String::new(),
154 context: String::new(),
155 };
156 Err(parse_error.into())
157 }
158 }
159}
160#[cfg(not(feature = "js"))]
161pub async fn parse_helix_dsl(source: &str) -> Result<HelixAst, ParseError> {
162 let mut dispatcher = HelixDispatcher::new();
163 match dispatcher.parse_dsl(source).await {
164 Ok(DispatchResult::Parsed(ast)) => Ok(ast),
165 Ok(DispatchResult::ParseError(err)) => Err(err),
166 _ => {
167 let parse_error = ParseError {
168 message: "Unexpected dispatch result".to_string(),
169 location: None,
170 token_index: 0,
171 expected: None,
172 found: String::new(),
173 context: String::new(),
174 };
175 Err(parse_error)
176 }
177 }
178}
179#[cfg(test)]
180mod tests {
181 use super::*;
182 #[tokio::test]
183 async fn test_parse_only() {
184 let source = r#"
185 agent "test_agent" {
186 capabilities ["coding", "analysis"]
187 backstory {
188 "A helpful AI assistant"
189 }
190 }
191 "#;
192 let dispatcher = HelixDispatcher::new();
193 let result = dispatcher.parse_only(source);
194 if let Err(ref e) = result {
195 println!("Parse error: {:?}", e);
196 }
197 assert!(result.is_ok());
198 }
199 #[tokio::test]
200 async fn test_parse_and_execute() {
201 let source = r#"
202 @date("Y-m-d")
203 "#;
204 let mut dispatcher = HelixDispatcher::new();
205 let result = dispatcher.parse_and_execute(source).await;
206 assert!(result.is_ok());
207 if let Ok(DispatchResult::Executed(value)) = result {
208 match value {
209 Value::String(date_str) => assert!(! date_str.is_empty()),
210 _ => panic!("Expected string value"),
211 }
212 }
213 }
214 #[tokio::test]
215 async fn test_execute_expression() {
216 let source = "@date(\"Y-m-d\")";
217 let mut dispatcher = HelixDispatcher::new();
218 dispatcher.initialize().await.unwrap();
219 let result = dispatcher.parse_and_execute(source).await;
220 assert!(result.is_ok());
221 if let Ok(DispatchResult::Executed(value)) = result {
222 match value {
223 Value::String(date_str) => assert!(! date_str.is_empty()),
224 _ => panic!("Expected string value"),
225 }
226 }
227 }
228}