shape_runtime/engine/
execution.rs1use super::types::{ExecutionMetrics, ExecutionResult, ExecutionType};
4use shape_ast::error::Result;
5use shape_ast::parser;
6
7impl super::ShapeEngine {
8 pub fn execute(
13 &mut self,
14 executor: &mut impl super::ProgramExecutor,
15 source: &str,
16 ) -> Result<ExecutionResult> {
17 if let Some(ctx) = self.runtime.persistent_context_mut() {
19 ctx.set_data_load_mode(crate::context::DataLoadMode::Sync);
20 }
21
22 self.execute_with_options(executor, source, false)
23 }
24
25 pub async fn execute_async(
42 &mut self,
43 executor: &mut impl super::ProgramExecutor,
44 source: &str,
45 ) -> Result<ExecutionResult> {
46 if let Some(ctx) = self.runtime.persistent_context_mut() {
48 ctx.set_data_load_mode(crate::context::DataLoadMode::Async);
49 }
50
51 let start_time = std::time::Instant::now();
52
53 let parse_start = std::time::Instant::now();
55 let mut program = parser::parse_program(source)?;
56 let parse_time_ms = parse_start.elapsed().as_millis() as u64;
57
58 shape_ast::transform::desugar_program(&mut program);
60
61 let analysis_start = std::time::Instant::now();
62 let analysis_time_ms = analysis_start.elapsed().as_millis() as u64;
63
64 let has_cache = self
66 .runtime
67 .persistent_context()
68 .map(|ctx| ctx.has_data_cache())
69 .unwrap_or(false);
70
71 if has_cache {
72 let queries = self.extract_data_queries(&program)?;
74
75 if let Some(ctx) = self.runtime.persistent_context_mut() {
77 ctx.prefetch_data(queries).await?;
78 }
79 }
80
81 self.set_source(source);
83
84 let runtime_start = std::time::Instant::now();
86 let result = executor.execute_program(self, &program)?;
87 let runtime_time_ms = runtime_start.elapsed().as_millis() as u64;
88
89 let total_time_ms = start_time.elapsed().as_millis() as u64;
90 let memory_used_bytes = self.estimate_memory_usage();
91 let rows_processed = Some(self.default_data.row_count());
92 let messages = self.collect_messages();
93
94 Ok(ExecutionResult {
95 value: result.wire_value,
96 type_info: result.type_info,
97 execution_type: result.execution_type,
98 metrics: ExecutionMetrics {
99 execution_time_ms: total_time_ms,
100 parse_time_ms,
101 analysis_time_ms,
102 runtime_time_ms,
103 memory_used_bytes,
104 rows_processed,
105 },
106 messages,
107 content_json: result.content_json,
108 content_html: result.content_html,
109 content_terminal: result.content_terminal,
110 })
111 }
112
113 pub async fn execute_repl(
119 &mut self,
120 executor: &mut impl super::ProgramExecutor,
121 source: &str,
122 ) -> Result<ExecutionResult> {
123 if let Some(ctx) = self.runtime.persistent_context_mut() {
125 ctx.set_data_load_mode(crate::context::DataLoadMode::Async);
126 }
127
128 let start_time = std::time::Instant::now();
129
130 let parse_start = std::time::Instant::now();
132 let mut program = parser::parse_program(source)?;
133 let parse_time_ms = parse_start.elapsed().as_millis() as u64;
134
135 shape_ast::transform::desugar_program(&mut program);
137
138 let analysis_start = std::time::Instant::now();
139 let analysis_time_ms = analysis_start.elapsed().as_millis() as u64;
140
141 let has_cache = self
143 .runtime
144 .persistent_context()
145 .map(|ctx| ctx.has_data_cache())
146 .unwrap_or(false);
147
148 if has_cache {
149 let queries = self.extract_data_queries(&program)?;
150 if let Some(ctx) = self.runtime.persistent_context_mut() {
151 ctx.prefetch_data(queries).await?;
152 }
153 }
154
155 self.runtime.load_program(&program, &self.default_data)?;
157
158 self.set_source(source);
160
161 let runtime_start = std::time::Instant::now();
163 let result = executor.execute_program(self, &program)?;
164 let runtime_time_ms = runtime_start.elapsed().as_millis() as u64;
165
166 let total_time_ms = start_time.elapsed().as_millis() as u64;
167 let memory_used_bytes = self.estimate_memory_usage();
168 let rows_processed = Some(self.default_data.row_count());
169 let messages = self.collect_messages();
170
171 Ok(ExecutionResult {
172 value: result.wire_value,
173 type_info: result.type_info,
174 execution_type: ExecutionType::Repl,
175 metrics: ExecutionMetrics {
176 execution_time_ms: total_time_ms,
177 parse_time_ms,
178 analysis_time_ms,
179 runtime_time_ms,
180 memory_used_bytes,
181 rows_processed,
182 },
183 messages,
184 content_json: result.content_json,
185 content_html: result.content_html,
186 content_terminal: result.content_terminal,
187 })
188 }
189
190 pub fn parse_and_analyze(&mut self, source: &str) -> Result<shape_ast::Program> {
195 if let Some(ctx) = self.runtime.persistent_context_mut() {
196 ctx.reset_for_new_execution();
197 }
198 let mut program = parser::parse_program(source)?;
199 shape_ast::transform::desugar_program(&mut program);
200 self.set_source(source);
201 Ok(program)
202 }
203
204 pub(super) fn execute_with_options(
206 &mut self,
207 executor: &mut impl super::ProgramExecutor,
208 source: &str,
209 _is_stdlib: bool,
210 ) -> Result<ExecutionResult> {
211 let start_time = std::time::Instant::now();
212
213 if let Some(ctx) = self.runtime.persistent_context_mut() {
215 ctx.reset_for_new_execution();
216 }
217
218 Self::check_deprecated_apis(source)?;
220
221 let parse_start = std::time::Instant::now();
223 let mut program = parser::parse_program(source)?;
224 let parse_time_ms = parse_start.elapsed().as_millis() as u64;
225
226 shape_ast::transform::desugar_program(&mut program);
228
229 let analysis_start = std::time::Instant::now();
230 let analysis_time_ms = analysis_start.elapsed().as_millis() as u64;
231
232 self.set_source(source);
234
235 let runtime_start = std::time::Instant::now();
237 let result = executor.execute_program(self, &program)?;
238 let runtime_time_ms = runtime_start.elapsed().as_millis() as u64;
239
240 let total_time_ms = start_time.elapsed().as_millis() as u64;
241
242 let memory_used_bytes = self.estimate_memory_usage();
244
245 let rows_processed = Some(self.default_data.row_count());
247
248 let messages = self.collect_messages();
250
251 Ok(ExecutionResult {
252 value: result.wire_value,
253 type_info: result.type_info,
254 execution_type: result.execution_type,
255 metrics: ExecutionMetrics {
256 execution_time_ms: total_time_ms,
257 parse_time_ms,
258 analysis_time_ms,
259 runtime_time_ms,
260 memory_used_bytes,
261 rows_processed,
262 },
263 messages,
264 content_json: result.content_json,
265 content_html: result.content_html,
266 content_terminal: result.content_terminal,
267 })
268 }
269
270 pub fn execute_repl_command(
272 &mut self,
273 executor: &mut impl super::ProgramExecutor,
274 command: &str,
275 ) -> Result<ExecutionResult> {
276 let mut result = self.execute(executor, command)?;
277 result.execution_type = ExecutionType::Repl;
278 Ok(result)
279 }
280
281 fn check_deprecated_apis(source: &str) -> Result<()> {
285 let trimmed = source.trim();
286 if trimmed.starts_with("csv.load") || trimmed.contains("csv.load(") {
287 return Err(shape_ast::error::ShapeError::SemanticError {
288 message: "csv.load has been removed. Use the csv package instead: import { read } from \"csv\""
289 .to_string(),
290 location: None,
291 });
292 }
293 if trimmed.starts_with("load(") || trimmed.starts_with("load (") {
295 return Err(shape_ast::error::ShapeError::SemanticError {
296 message: "load(provider, params) has been removed. Use typed data access instead: data(\"source\", { ... })"
297 .to_string(),
298 location: None,
299 });
300 }
301 Ok(())
302 }
303
304 pub(super) fn estimate_memory_usage(&self) -> Option<usize> {
306 let mut total = 0usize;
308
309 total += self.default_data.row_count() * 48;
311
312 total += 1024; Some(total)
317 }
318
319 pub(super) fn collect_messages(&self) -> Vec<super::types::Message> {
321 vec![]
324 }
325}