1use crate::lcnf::*;
6
7use super::functions::*;
8use std::collections::{HashMap, HashSet, VecDeque};
9
10#[derive(Debug, Clone, PartialEq)]
12pub enum HaskellDecl {
13 Data(HaskellDataDecl),
14 Newtype(HaskellNewtype),
15 TypeClass(HaskellTypeClass),
16 Instance(HaskellInstance),
17 Function(HaskellFunction),
18 TypeSynonym(String, Vec<String>, HaskellType),
19 Comment(String),
20 RawLine(String),
21}
22#[derive(Debug, Clone, PartialEq)]
26pub struct HaskellDataDecl {
27 pub name: String,
29 pub type_params: Vec<String>,
31 pub constructors: Vec<(String, Vec<HaskellType>)>,
33 pub deriving_clauses: Vec<String>,
35}
36#[derive(Debug, Clone, PartialEq)]
38pub struct HaskellCaseAlt {
39 pub pattern: HaskellPattern,
40 pub guards: Vec<HaskellGuard>,
42 pub body: Option<HaskellExpr>,
43}
44#[derive(Debug, Clone, PartialEq)]
46pub enum HaskellLit {
47 Int(i64),
49 Float(f64),
51 Char(char),
53 Str(String),
55 Bool(bool),
57 Unit,
59}
60#[derive(Debug, Clone, PartialEq)]
62pub enum HaskellDoStmt {
63 Bind(String, HaskellExpr),
65 Stmt(HaskellExpr),
67 LetBind(String, HaskellExpr),
69}
70#[derive(Debug, Clone, PartialEq)]
74pub struct HaskellNewtype {
75 pub name: String,
77 pub type_param: Option<String>,
79 pub constructor: String,
81 pub field: (String, HaskellType),
83 pub deriving_clauses: Vec<String>,
85}
86#[derive(Debug, Clone, PartialEq)]
88pub struct HaskellModule {
89 pub name: String,
91 pub exports: Vec<String>,
93 pub imports: Vec<HaskellImport>,
95 pub declarations: Vec<HaskellDecl>,
97}
98impl HaskellModule {
99 pub fn new(name: impl Into<String>) -> Self {
101 HaskellModule {
102 name: name.into(),
103 exports: Vec::new(),
104 imports: Vec::new(),
105 declarations: Vec::new(),
106 }
107 }
108 pub fn add_import(&mut self, imp: HaskellImport) {
110 self.imports.push(imp);
111 }
112 pub fn add_decl(&mut self, decl: HaskellDecl) {
114 self.declarations.push(decl);
115 }
116 pub fn emit(&self) -> String {
118 let mut out = String::new();
119 if !self.exports.is_empty() {
120 out.push_str(&format!("module {} (\n", self.name));
121 for (i, exp) in self.exports.iter().enumerate() {
122 if i > 0 {
123 out.push_str(",\n");
124 }
125 out.push_str(&format!(" {}", exp));
126 }
127 out.push_str("\n) where\n\n");
128 } else {
129 out.push_str(&format!("module {} where\n\n", self.name));
130 }
131 for imp in &self.imports {
132 out.push_str(&format!("{}\n", imp));
133 }
134 if !self.imports.is_empty() {
135 out.push('\n');
136 }
137 for decl in &self.declarations {
138 out.push_str(&format!("{}\n", decl));
139 }
140 out
141 }
142}
143#[derive(Debug, Clone, Default)]
145pub struct HsExtEmitStats {
146 pub bytes_emitted: usize,
147 pub items_emitted: usize,
148 pub errors: usize,
149 pub warnings: usize,
150 pub elapsed_ms: u64,
151}
152impl HsExtEmitStats {
153 pub fn new() -> Self {
154 HsExtEmitStats::default()
155 }
156 pub fn throughput_bps(&self) -> f64 {
157 if self.elapsed_ms == 0 {
158 0.0
159 } else {
160 self.bytes_emitted as f64 / (self.elapsed_ms as f64 / 1000.0)
161 }
162 }
163 pub fn is_clean(&self) -> bool {
164 self.errors == 0
165 }
166}
167#[allow(dead_code)]
168#[derive(Debug, Clone)]
169pub struct HskLivenessInfo {
170 pub live_in: Vec<std::collections::HashSet<u32>>,
171 pub live_out: Vec<std::collections::HashSet<u32>>,
172 pub defs: Vec<std::collections::HashSet<u32>>,
173 pub uses: Vec<std::collections::HashSet<u32>>,
174}
175impl HskLivenessInfo {
176 #[allow(dead_code)]
177 pub fn new(block_count: usize) -> Self {
178 HskLivenessInfo {
179 live_in: vec![std::collections::HashSet::new(); block_count],
180 live_out: vec![std::collections::HashSet::new(); block_count],
181 defs: vec![std::collections::HashSet::new(); block_count],
182 uses: vec![std::collections::HashSet::new(); block_count],
183 }
184 }
185 #[allow(dead_code)]
186 pub fn add_def(&mut self, block: usize, var: u32) {
187 if block < self.defs.len() {
188 self.defs[block].insert(var);
189 }
190 }
191 #[allow(dead_code)]
192 pub fn add_use(&mut self, block: usize, var: u32) {
193 if block < self.uses.len() {
194 self.uses[block].insert(var);
195 }
196 }
197 #[allow(dead_code)]
198 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
199 self.live_in
200 .get(block)
201 .map(|s| s.contains(&var))
202 .unwrap_or(false)
203 }
204 #[allow(dead_code)]
205 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
206 self.live_out
207 .get(block)
208 .map(|s| s.contains(&var))
209 .unwrap_or(false)
210 }
211}
212#[allow(dead_code)]
213#[derive(Debug, Clone)]
214pub struct HskDominatorTree {
215 pub idom: Vec<Option<u32>>,
216 pub dom_children: Vec<Vec<u32>>,
217 pub dom_depth: Vec<u32>,
218}
219impl HskDominatorTree {
220 #[allow(dead_code)]
221 pub fn new(size: usize) -> Self {
222 HskDominatorTree {
223 idom: vec![None; size],
224 dom_children: vec![Vec::new(); size],
225 dom_depth: vec![0; size],
226 }
227 }
228 #[allow(dead_code)]
229 pub fn set_idom(&mut self, node: usize, idom: u32) {
230 self.idom[node] = Some(idom);
231 }
232 #[allow(dead_code)]
233 pub fn dominates(&self, a: usize, b: usize) -> bool {
234 if a == b {
235 return true;
236 }
237 let mut cur = b;
238 loop {
239 match self.idom[cur] {
240 Some(parent) if parent as usize == a => return true,
241 Some(parent) if parent as usize == cur => return false,
242 Some(parent) => cur = parent as usize,
243 None => return false,
244 }
245 }
246 }
247 #[allow(dead_code)]
248 pub fn depth(&self, node: usize) -> u32 {
249 self.dom_depth.get(node).copied().unwrap_or(0)
250 }
251}
252#[allow(dead_code)]
253#[derive(Debug, Clone)]
254pub struct HskWorklist {
255 pub(super) items: std::collections::VecDeque<u32>,
256 pub(super) in_worklist: std::collections::HashSet<u32>,
257}
258impl HskWorklist {
259 #[allow(dead_code)]
260 pub fn new() -> Self {
261 HskWorklist {
262 items: std::collections::VecDeque::new(),
263 in_worklist: std::collections::HashSet::new(),
264 }
265 }
266 #[allow(dead_code)]
267 pub fn push(&mut self, item: u32) -> bool {
268 if self.in_worklist.insert(item) {
269 self.items.push_back(item);
270 true
271 } else {
272 false
273 }
274 }
275 #[allow(dead_code)]
276 pub fn pop(&mut self) -> Option<u32> {
277 let item = self.items.pop_front()?;
278 self.in_worklist.remove(&item);
279 Some(item)
280 }
281 #[allow(dead_code)]
282 pub fn is_empty(&self) -> bool {
283 self.items.is_empty()
284 }
285 #[allow(dead_code)]
286 pub fn len(&self) -> usize {
287 self.items.len()
288 }
289 #[allow(dead_code)]
290 pub fn contains(&self, item: u32) -> bool {
291 self.in_worklist.contains(&item)
292 }
293}
294#[derive(Debug, Default)]
296pub struct HsExtSourceBuffer {
297 pub(super) buf: String,
298 pub(super) indent_level: usize,
299 pub(super) indent_str: String,
300}
301impl HsExtSourceBuffer {
302 pub fn new() -> Self {
303 HsExtSourceBuffer {
304 buf: String::new(),
305 indent_level: 0,
306 indent_str: " ".to_string(),
307 }
308 }
309 pub fn with_indent(mut self, indent: impl Into<String>) -> Self {
310 self.indent_str = indent.into();
311 self
312 }
313 pub fn push_line(&mut self, line: &str) {
314 for _ in 0..self.indent_level {
315 self.buf.push_str(&self.indent_str);
316 }
317 self.buf.push_str(line);
318 self.buf.push('\n');
319 }
320 pub fn push_raw(&mut self, s: &str) {
321 self.buf.push_str(s);
322 }
323 pub fn indent(&mut self) {
324 self.indent_level += 1;
325 }
326 pub fn dedent(&mut self) {
327 self.indent_level = self.indent_level.saturating_sub(1);
328 }
329 pub fn as_str(&self) -> &str {
330 &self.buf
331 }
332 pub fn len(&self) -> usize {
333 self.buf.len()
334 }
335 pub fn is_empty(&self) -> bool {
336 self.buf.is_empty()
337 }
338 pub fn line_count(&self) -> usize {
339 self.buf.lines().count()
340 }
341 pub fn into_string(self) -> String {
342 self.buf
343 }
344 pub fn reset(&mut self) {
345 self.buf.clear();
346 self.indent_level = 0;
347 }
348}
349#[allow(dead_code)]
350#[derive(Debug, Clone)]
351pub struct HskPassConfig {
352 pub phase: HskPassPhase,
353 pub enabled: bool,
354 pub max_iterations: u32,
355 pub debug_output: bool,
356 pub pass_name: String,
357}
358impl HskPassConfig {
359 #[allow(dead_code)]
360 pub fn new(name: impl Into<String>, phase: HskPassPhase) -> Self {
361 HskPassConfig {
362 phase,
363 enabled: true,
364 max_iterations: 10,
365 debug_output: false,
366 pass_name: name.into(),
367 }
368 }
369 #[allow(dead_code)]
370 pub fn disabled(mut self) -> Self {
371 self.enabled = false;
372 self
373 }
374 #[allow(dead_code)]
375 pub fn with_debug(mut self) -> Self {
376 self.debug_output = true;
377 self
378 }
379 #[allow(dead_code)]
380 pub fn max_iter(mut self, n: u32) -> Self {
381 self.max_iterations = n;
382 self
383 }
384}
385#[derive(Debug, Clone, PartialEq, Eq, Hash)]
387pub struct HsExtIncrKey {
388 pub content_hash: u64,
389 pub config_hash: u64,
390}
391impl HsExtIncrKey {
392 pub fn new(content: u64, config: u64) -> Self {
393 HsExtIncrKey {
394 content_hash: content,
395 config_hash: config,
396 }
397 }
398 pub fn combined_hash(&self) -> u64 {
399 self.content_hash.wrapping_mul(0x9e3779b97f4a7c15) ^ self.config_hash
400 }
401 pub fn matches(&self, other: &HsExtIncrKey) -> bool {
402 self.content_hash == other.content_hash && self.config_hash == other.config_hash
403 }
404}
405#[derive(Debug, Clone, PartialEq)]
407pub struct HaskellGuard {
408 pub condition: HaskellExpr,
409 pub body: HaskellExpr,
410}
411#[allow(dead_code)]
412#[derive(Debug, Clone, Default)]
413pub struct HskPassStats {
414 pub total_runs: u32,
415 pub successful_runs: u32,
416 pub total_changes: u64,
417 pub time_ms: u64,
418 pub iterations_used: u32,
419}
420impl HskPassStats {
421 #[allow(dead_code)]
422 pub fn new() -> Self {
423 Self::default()
424 }
425 #[allow(dead_code)]
426 pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
427 self.total_runs += 1;
428 self.successful_runs += 1;
429 self.total_changes += changes;
430 self.time_ms += time_ms;
431 self.iterations_used = iterations;
432 }
433 #[allow(dead_code)]
434 pub fn average_changes_per_run(&self) -> f64 {
435 if self.total_runs == 0 {
436 return 0.0;
437 }
438 self.total_changes as f64 / self.total_runs as f64
439 }
440 #[allow(dead_code)]
441 pub fn success_rate(&self) -> f64 {
442 if self.total_runs == 0 {
443 return 0.0;
444 }
445 self.successful_runs as f64 / self.total_runs as f64
446 }
447 #[allow(dead_code)]
448 pub fn format_summary(&self) -> String {
449 format!(
450 "Runs: {}/{}, Changes: {}, Time: {}ms",
451 self.successful_runs, self.total_runs, self.total_changes, self.time_ms
452 )
453 }
454}
455pub struct HaskellBackend {
457 pub(super) module: HaskellModule,
458}
459impl HaskellBackend {
460 pub fn new(module_name: impl Into<String>) -> Self {
462 let mut module = HaskellModule::new(module_name);
463 module.add_import(HaskellImport {
464 module: "Prelude".to_string(),
465 qualified: false,
466 alias: None,
467 items: Vec::new(),
468 hiding: Vec::new(),
469 });
470 HaskellBackend { module }
471 }
472 pub fn compile_decl(&mut self, decl: &LcnfFunDecl) {
474 let hs_fn = self.compile_fun(decl);
475 self.module.add_decl(HaskellDecl::Function(hs_fn));
476 }
477 pub(super) fn compile_fun(&self, decl: &LcnfFunDecl) -> HaskellFunction {
479 let params: Vec<HaskellPattern> = decl
480 .params
481 .iter()
482 .map(|p| HaskellPattern::Var(p.name.clone()))
483 .collect();
484 let body = self.compile_expr(&decl.body);
485 HaskellFunction {
486 name: sanitize_hs_ident(&decl.name),
487 type_annotation: None,
488 equations: vec![HaskellEquation {
489 patterns: params,
490 guards: Vec::new(),
491 body: Some(body),
492 where_clause: Vec::new(),
493 }],
494 }
495 }
496 pub(super) fn compile_expr(&self, expr: &LcnfExpr) -> HaskellExpr {
498 match expr {
499 LcnfExpr::Return(arg) => self.compile_arg(arg),
500 LcnfExpr::Let {
501 name, value, body, ..
502 } => {
503 let rhs_expr = self.compile_let_value(value);
504 let cont_expr = self.compile_expr(body);
505 HaskellExpr::Let(name.clone(), Box::new(rhs_expr), Box::new(cont_expr))
506 }
507 LcnfExpr::Case {
508 scrutinee,
509 alts,
510 default,
511 ..
512 } => {
513 let scrut = HaskellExpr::Var(format!("{}", scrutinee));
514 let mut hs_alts: Vec<HaskellCaseAlt> =
515 alts.iter().map(|alt| self.compile_alt(alt)).collect();
516 if let Some(def) = default {
517 let def_expr = self.compile_expr(def);
518 hs_alts.push(HaskellCaseAlt {
519 pattern: HaskellPattern::Wildcard,
520 guards: Vec::new(),
521 body: Some(def_expr),
522 });
523 }
524 HaskellExpr::Case(Box::new(scrut), hs_alts)
525 }
526 LcnfExpr::TailCall(func, args) => {
527 let func_expr = self.compile_arg(func);
528 if args.is_empty() {
529 func_expr
530 } else {
531 let arg_exprs: Vec<HaskellExpr> =
532 args.iter().map(|a| self.compile_arg(a)).collect();
533 HaskellExpr::App(Box::new(func_expr), arg_exprs)
534 }
535 }
536 LcnfExpr::Unreachable => HaskellExpr::Var("undefined".to_string()),
537 }
538 }
539 pub(super) fn compile_let_value(&self, val: &LcnfLetValue) -> HaskellExpr {
541 match val {
542 LcnfLetValue::App(func, args) => {
543 let func_expr = self.compile_arg(func);
544 if args.is_empty() {
545 func_expr
546 } else {
547 let arg_exprs: Vec<HaskellExpr> =
548 args.iter().map(|a| self.compile_arg(a)).collect();
549 HaskellExpr::App(Box::new(func_expr), arg_exprs)
550 }
551 }
552 LcnfLetValue::Ctor(name, _tag, args) => {
553 let ctor_expr = HaskellExpr::Var(name.clone());
554 if args.is_empty() {
555 ctor_expr
556 } else {
557 let arg_exprs: Vec<HaskellExpr> =
558 args.iter().map(|a| self.compile_arg(a)).collect();
559 HaskellExpr::App(Box::new(ctor_expr), arg_exprs)
560 }
561 }
562 LcnfLetValue::Proj(_name, idx, var) => {
563 let accessor = match idx {
564 0 => "fst",
565 1 => "snd",
566 n => return HaskellExpr::Var(format!("_proj{}_{}", n, var)),
567 };
568 HaskellExpr::App(
569 Box::new(HaskellExpr::Var(accessor.to_string())),
570 vec![HaskellExpr::Var(format!("{}", var))],
571 )
572 }
573 LcnfLetValue::Lit(lit) => match lit {
574 LcnfLit::Nat(n) => HaskellExpr::Lit(HaskellLit::Int(*n as i64)),
575 LcnfLit::Int(i) => HaskellExpr::Lit(HaskellLit::Int(*i)),
576 LcnfLit::Str(s) => HaskellExpr::Lit(HaskellLit::Str(s.clone())),
577 },
578 LcnfLetValue::Erased | LcnfLetValue::Reset(_) => HaskellExpr::Lit(HaskellLit::Unit),
579 LcnfLetValue::FVar(v) => HaskellExpr::Var(format!("{}", v)),
580 LcnfLetValue::Reuse(_, name, _tag, args) => {
581 let ctor_expr = HaskellExpr::Var(name.clone());
582 if args.is_empty() {
583 ctor_expr
584 } else {
585 let arg_exprs: Vec<HaskellExpr> =
586 args.iter().map(|a| self.compile_arg(a)).collect();
587 HaskellExpr::App(Box::new(ctor_expr), arg_exprs)
588 }
589 }
590 }
591 }
592 pub(super) fn compile_alt(&self, alt: &LcnfAlt) -> HaskellCaseAlt {
594 let body = self.compile_expr(&alt.body);
595 let pat = HaskellPattern::Constructor(
596 alt.ctor_name.clone(),
597 alt.params
598 .iter()
599 .map(|p| HaskellPattern::Var(p.name.clone()))
600 .collect(),
601 );
602 HaskellCaseAlt {
603 pattern: pat,
604 guards: Vec::new(),
605 body: Some(body),
606 }
607 }
608 pub(super) fn compile_arg(&self, arg: &LcnfArg) -> HaskellExpr {
610 match arg {
611 LcnfArg::Var(v) => HaskellExpr::Var(format!("{}", v)),
612 LcnfArg::Lit(lit) => match lit {
613 LcnfLit::Nat(n) => HaskellExpr::Lit(HaskellLit::Int(*n as i64)),
614 LcnfLit::Int(i) => HaskellExpr::Lit(HaskellLit::Int(*i)),
615 LcnfLit::Str(s) => HaskellExpr::Lit(HaskellLit::Str(s.clone())),
616 },
617 LcnfArg::Erased | LcnfArg::Type(_) => HaskellExpr::Lit(HaskellLit::Unit),
618 }
619 }
620 pub fn emit_module(&self) -> String {
622 self.module.emit()
623 }
624}
625#[derive(Debug, Clone, PartialEq)]
634pub struct HaskellInstance {
635 pub class: String,
637 pub instance_type: HaskellType,
639 pub context: Vec<HaskellType>,
641 pub where_clause: Vec<HaskellFunction>,
643}
644#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
646pub struct HsExtVersion {
647 pub major: u32,
648 pub minor: u32,
649 pub patch: u32,
650 pub pre: Option<String>,
651}
652impl HsExtVersion {
653 pub fn new(major: u32, minor: u32, patch: u32) -> Self {
654 HsExtVersion {
655 major,
656 minor,
657 patch,
658 pre: None,
659 }
660 }
661 pub fn with_pre(mut self, pre: impl Into<String>) -> Self {
662 self.pre = Some(pre.into());
663 self
664 }
665 pub fn is_stable(&self) -> bool {
666 self.pre.is_none()
667 }
668 pub fn is_compatible_with(&self, other: &HsExtVersion) -> bool {
669 self.major == other.major && self.minor >= other.minor
670 }
671}
672#[allow(dead_code)]
673pub struct HskConstantFoldingHelper;
674impl HskConstantFoldingHelper {
675 #[allow(dead_code)]
676 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
677 a.checked_add(b)
678 }
679 #[allow(dead_code)]
680 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
681 a.checked_sub(b)
682 }
683 #[allow(dead_code)]
684 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
685 a.checked_mul(b)
686 }
687 #[allow(dead_code)]
688 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
689 if b == 0 {
690 None
691 } else {
692 a.checked_div(b)
693 }
694 }
695 #[allow(dead_code)]
696 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
697 a + b
698 }
699 #[allow(dead_code)]
700 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
701 a * b
702 }
703 #[allow(dead_code)]
704 pub fn fold_neg_i64(a: i64) -> Option<i64> {
705 a.checked_neg()
706 }
707 #[allow(dead_code)]
708 pub fn fold_not_bool(a: bool) -> bool {
709 !a
710 }
711 #[allow(dead_code)]
712 pub fn fold_and_bool(a: bool, b: bool) -> bool {
713 a && b
714 }
715 #[allow(dead_code)]
716 pub fn fold_or_bool(a: bool, b: bool) -> bool {
717 a || b
718 }
719 #[allow(dead_code)]
720 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
721 a.checked_shl(b)
722 }
723 #[allow(dead_code)]
724 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
725 a.checked_shr(b)
726 }
727 #[allow(dead_code)]
728 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
729 if b == 0 {
730 None
731 } else {
732 Some(a % b)
733 }
734 }
735 #[allow(dead_code)]
736 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
737 a & b
738 }
739 #[allow(dead_code)]
740 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
741 a | b
742 }
743 #[allow(dead_code)]
744 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
745 a ^ b
746 }
747 #[allow(dead_code)]
748 pub fn fold_bitnot_i64(a: i64) -> i64 {
749 !a
750 }
751}
752#[derive(Debug, Clone, Default)]
754pub struct HsExtFeatures {
755 pub(super) flags: std::collections::HashSet<String>,
756}
757impl HsExtFeatures {
758 pub fn new() -> Self {
759 HsExtFeatures::default()
760 }
761 pub fn enable(&mut self, flag: impl Into<String>) {
762 self.flags.insert(flag.into());
763 }
764 pub fn disable(&mut self, flag: &str) {
765 self.flags.remove(flag);
766 }
767 pub fn is_enabled(&self, flag: &str) -> bool {
768 self.flags.contains(flag)
769 }
770 pub fn len(&self) -> usize {
771 self.flags.len()
772 }
773 pub fn is_empty(&self) -> bool {
774 self.flags.is_empty()
775 }
776 pub fn union(&self, other: &HsExtFeatures) -> HsExtFeatures {
777 HsExtFeatures {
778 flags: self.flags.union(&other.flags).cloned().collect(),
779 }
780 }
781 pub fn intersection(&self, other: &HsExtFeatures) -> HsExtFeatures {
782 HsExtFeatures {
783 flags: self.flags.intersection(&other.flags).cloned().collect(),
784 }
785 }
786}
787#[derive(Debug, Clone, PartialEq)]
789pub enum HsListQual {
790 Generator(String, HaskellExpr),
792 Guard(HaskellExpr),
794 LetBind(String, HaskellExpr),
796}
797#[derive(Debug)]
799pub struct HsExtEventLog {
800 pub(super) entries: std::collections::VecDeque<String>,
801 pub(super) capacity: usize,
802}
803impl HsExtEventLog {
804 pub fn new(capacity: usize) -> Self {
805 HsExtEventLog {
806 entries: std::collections::VecDeque::with_capacity(capacity),
807 capacity,
808 }
809 }
810 pub fn push(&mut self, event: impl Into<String>) {
811 if self.entries.len() >= self.capacity {
812 self.entries.pop_front();
813 }
814 self.entries.push_back(event.into());
815 }
816 pub fn iter(&self) -> impl Iterator<Item = &String> {
817 self.entries.iter()
818 }
819 pub fn len(&self) -> usize {
820 self.entries.len()
821 }
822 pub fn is_empty(&self) -> bool {
823 self.entries.is_empty()
824 }
825 pub fn capacity(&self) -> usize {
826 self.capacity
827 }
828 pub fn clear(&mut self) {
829 self.entries.clear();
830 }
831}
832#[allow(dead_code)]
833pub struct HskPassRegistry {
834 pub(super) configs: Vec<HskPassConfig>,
835 pub(super) stats: std::collections::HashMap<String, HskPassStats>,
836}
837impl HskPassRegistry {
838 #[allow(dead_code)]
839 pub fn new() -> Self {
840 HskPassRegistry {
841 configs: Vec::new(),
842 stats: std::collections::HashMap::new(),
843 }
844 }
845 #[allow(dead_code)]
846 pub fn register(&mut self, config: HskPassConfig) {
847 self.stats
848 .insert(config.pass_name.clone(), HskPassStats::new());
849 self.configs.push(config);
850 }
851 #[allow(dead_code)]
852 pub fn enabled_passes(&self) -> Vec<&HskPassConfig> {
853 self.configs.iter().filter(|c| c.enabled).collect()
854 }
855 #[allow(dead_code)]
856 pub fn get_stats(&self, name: &str) -> Option<&HskPassStats> {
857 self.stats.get(name)
858 }
859 #[allow(dead_code)]
860 pub fn total_passes(&self) -> usize {
861 self.configs.len()
862 }
863 #[allow(dead_code)]
864 pub fn enabled_count(&self) -> usize {
865 self.enabled_passes().len()
866 }
867 #[allow(dead_code)]
868 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
869 if let Some(stats) = self.stats.get_mut(name) {
870 stats.record_run(changes, time_ms, iter);
871 }
872 }
873}
874#[allow(dead_code)]
875#[derive(Debug, Clone, PartialEq)]
876pub enum HskPassPhase {
877 Analysis,
878 Transformation,
879 Verification,
880 Cleanup,
881}
882impl HskPassPhase {
883 #[allow(dead_code)]
884 pub fn name(&self) -> &str {
885 match self {
886 HskPassPhase::Analysis => "analysis",
887 HskPassPhase::Transformation => "transformation",
888 HskPassPhase::Verification => "verification",
889 HskPassPhase::Cleanup => "cleanup",
890 }
891 }
892 #[allow(dead_code)]
893 pub fn is_modifying(&self) -> bool {
894 matches!(self, HskPassPhase::Transformation | HskPassPhase::Cleanup)
895 }
896}
897#[derive(Debug, Clone, PartialEq)]
906pub struct HaskellFunction {
907 pub name: String,
909 pub type_annotation: Option<HaskellType>,
911 pub equations: Vec<HaskellEquation>,
913}
914#[derive(Debug, Default)]
916pub struct HsExtNameScope {
917 pub(super) declared: std::collections::HashSet<String>,
918 pub(super) depth: usize,
919 pub(super) parent: Option<Box<HsExtNameScope>>,
920}
921impl HsExtNameScope {
922 pub fn new() -> Self {
923 HsExtNameScope::default()
924 }
925 pub fn declare(&mut self, name: impl Into<String>) -> bool {
926 self.declared.insert(name.into())
927 }
928 pub fn is_declared(&self, name: &str) -> bool {
929 self.declared.contains(name)
930 }
931 pub fn push_scope(self) -> Self {
932 HsExtNameScope {
933 declared: std::collections::HashSet::new(),
934 depth: self.depth + 1,
935 parent: Some(Box::new(self)),
936 }
937 }
938 pub fn pop_scope(self) -> Self {
939 *self.parent.unwrap_or_default()
940 }
941 pub fn depth(&self) -> usize {
942 self.depth
943 }
944 pub fn len(&self) -> usize {
945 self.declared.len()
946 }
947}
948#[allow(dead_code)]
949#[derive(Debug, Clone)]
950pub struct HskCacheEntry {
951 pub key: String,
952 pub data: Vec<u8>,
953 pub timestamp: u64,
954 pub valid: bool,
955}
956#[derive(Debug, Clone, PartialEq)]
958pub enum HaskellPattern {
959 Wildcard,
961 Var(String),
963 Lit(HaskellLit),
965 Tuple(Vec<HaskellPattern>),
967 List(Vec<HaskellPattern>),
969 Cons(Box<HaskellPattern>, Box<HaskellPattern>),
971 Constructor(String, Vec<HaskellPattern>),
973 As(String, Box<HaskellPattern>),
975 LazyPat(Box<HaskellPattern>),
977}
978#[derive(Debug, Clone, PartialEq)]
980pub enum HaskellExpr {
981 Lit(HaskellLit),
983 Var(String),
985 App(Box<HaskellExpr>, Vec<HaskellExpr>),
987 Lambda(Vec<HaskellPattern>, Box<HaskellExpr>),
989 Let(String, Box<HaskellExpr>, Box<HaskellExpr>),
991 Where(Box<HaskellExpr>, Vec<HaskellFunction>),
993 If(Box<HaskellExpr>, Box<HaskellExpr>, Box<HaskellExpr>),
995 Case(Box<HaskellExpr>, Vec<HaskellCaseAlt>),
997 Do(Vec<HaskellDoStmt>),
999 ListComp(Box<HaskellExpr>, Vec<HsListQual>),
1001 Tuple(Vec<HaskellExpr>),
1003 List(Vec<HaskellExpr>),
1005 Neg(Box<HaskellExpr>),
1007 InfixApp(Box<HaskellExpr>, String, Box<HaskellExpr>),
1009 Operator(String),
1011 TypeAnnotation(Box<HaskellExpr>, HaskellType),
1013}
1014#[derive(Debug, Clone, PartialEq)]
1022pub struct HaskellTypeClass {
1023 pub name: String,
1025 pub type_params: Vec<String>,
1027 pub superclasses: Vec<HaskellType>,
1029 pub methods: Vec<(String, HaskellType, Option<HaskellExpr>)>,
1031}
1032#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1034pub enum HaskellType {
1035 Int,
1037 Integer,
1039 Double,
1041 Float,
1043 Bool,
1045 Char,
1047 HsString,
1049 Unit,
1051 IO(Box<HaskellType>),
1053 List(Box<HaskellType>),
1055 Maybe(Box<HaskellType>),
1057 Either(Box<HaskellType>, Box<HaskellType>),
1059 Tuple(Vec<HaskellType>),
1061 Fun(Box<HaskellType>, Box<HaskellType>),
1063 Custom(String),
1065 Polymorphic(String),
1067 Constraint(String, Vec<HaskellType>),
1069}
1070#[derive(Debug, Clone, PartialEq)]
1074pub struct HaskellEquation {
1075 pub patterns: Vec<HaskellPattern>,
1077 pub guards: Vec<HaskellGuard>,
1079 pub body: Option<HaskellExpr>,
1081 pub where_clause: Vec<HaskellFunction>,
1083}
1084#[derive(Debug, Clone, PartialEq)]
1086pub struct HaskellImport {
1087 pub module: String,
1089 pub qualified: bool,
1091 pub alias: Option<String>,
1093 pub items: Vec<String>,
1095 pub hiding: Vec<String>,
1097}
1098#[derive(Debug, Clone)]
1100pub struct HsExtDiagMsg {
1101 pub severity: HsExtDiagSeverity,
1102 pub pass: String,
1103 pub message: String,
1104}
1105impl HsExtDiagMsg {
1106 pub fn error(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1107 HsExtDiagMsg {
1108 severity: HsExtDiagSeverity::Error,
1109 pass: pass.into(),
1110 message: msg.into(),
1111 }
1112 }
1113 pub fn warning(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1114 HsExtDiagMsg {
1115 severity: HsExtDiagSeverity::Warning,
1116 pass: pass.into(),
1117 message: msg.into(),
1118 }
1119 }
1120 pub fn note(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1121 HsExtDiagMsg {
1122 severity: HsExtDiagSeverity::Note,
1123 pass: pass.into(),
1124 message: msg.into(),
1125 }
1126 }
1127}
1128#[derive(Debug, Default)]
1130pub struct HsExtDiagCollector {
1131 pub(super) msgs: Vec<HsExtDiagMsg>,
1132}
1133impl HsExtDiagCollector {
1134 pub fn new() -> Self {
1135 HsExtDiagCollector::default()
1136 }
1137 pub fn emit(&mut self, d: HsExtDiagMsg) {
1138 self.msgs.push(d);
1139 }
1140 pub fn has_errors(&self) -> bool {
1141 self.msgs
1142 .iter()
1143 .any(|d| d.severity == HsExtDiagSeverity::Error)
1144 }
1145 pub fn errors(&self) -> Vec<&HsExtDiagMsg> {
1146 self.msgs
1147 .iter()
1148 .filter(|d| d.severity == HsExtDiagSeverity::Error)
1149 .collect()
1150 }
1151 pub fn warnings(&self) -> Vec<&HsExtDiagMsg> {
1152 self.msgs
1153 .iter()
1154 .filter(|d| d.severity == HsExtDiagSeverity::Warning)
1155 .collect()
1156 }
1157 pub fn len(&self) -> usize {
1158 self.msgs.len()
1159 }
1160 pub fn is_empty(&self) -> bool {
1161 self.msgs.is_empty()
1162 }
1163 pub fn clear(&mut self) {
1164 self.msgs.clear();
1165 }
1166}
1167#[allow(dead_code)]
1168#[derive(Debug, Clone)]
1169pub struct HskAnalysisCache {
1170 pub(super) entries: std::collections::HashMap<String, HskCacheEntry>,
1171 pub(super) max_size: usize,
1172 pub(super) hits: u64,
1173 pub(super) misses: u64,
1174}
1175impl HskAnalysisCache {
1176 #[allow(dead_code)]
1177 pub fn new(max_size: usize) -> Self {
1178 HskAnalysisCache {
1179 entries: std::collections::HashMap::new(),
1180 max_size,
1181 hits: 0,
1182 misses: 0,
1183 }
1184 }
1185 #[allow(dead_code)]
1186 pub fn get(&mut self, key: &str) -> Option<&HskCacheEntry> {
1187 if self.entries.contains_key(key) {
1188 self.hits += 1;
1189 self.entries.get(key)
1190 } else {
1191 self.misses += 1;
1192 None
1193 }
1194 }
1195 #[allow(dead_code)]
1196 pub fn insert(&mut self, key: String, data: Vec<u8>) {
1197 if self.entries.len() >= self.max_size {
1198 if let Some(oldest) = self.entries.keys().next().cloned() {
1199 self.entries.remove(&oldest);
1200 }
1201 }
1202 self.entries.insert(
1203 key.clone(),
1204 HskCacheEntry {
1205 key,
1206 data,
1207 timestamp: 0,
1208 valid: true,
1209 },
1210 );
1211 }
1212 #[allow(dead_code)]
1213 pub fn invalidate(&mut self, key: &str) {
1214 if let Some(entry) = self.entries.get_mut(key) {
1215 entry.valid = false;
1216 }
1217 }
1218 #[allow(dead_code)]
1219 pub fn clear(&mut self) {
1220 self.entries.clear();
1221 }
1222 #[allow(dead_code)]
1223 pub fn hit_rate(&self) -> f64 {
1224 let total = self.hits + self.misses;
1225 if total == 0 {
1226 return 0.0;
1227 }
1228 self.hits as f64 / total as f64
1229 }
1230 #[allow(dead_code)]
1231 pub fn size(&self) -> usize {
1232 self.entries.len()
1233 }
1234}
1235#[allow(dead_code)]
1236#[derive(Debug, Clone)]
1237pub struct HskDepGraph {
1238 pub(super) nodes: Vec<u32>,
1239 pub(super) edges: Vec<(u32, u32)>,
1240}
1241impl HskDepGraph {
1242 #[allow(dead_code)]
1243 pub fn new() -> Self {
1244 HskDepGraph {
1245 nodes: Vec::new(),
1246 edges: Vec::new(),
1247 }
1248 }
1249 #[allow(dead_code)]
1250 pub fn add_node(&mut self, id: u32) {
1251 if !self.nodes.contains(&id) {
1252 self.nodes.push(id);
1253 }
1254 }
1255 #[allow(dead_code)]
1256 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
1257 self.add_node(dep);
1258 self.add_node(dependent);
1259 self.edges.push((dep, dependent));
1260 }
1261 #[allow(dead_code)]
1262 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
1263 self.edges
1264 .iter()
1265 .filter(|(d, _)| *d == node)
1266 .map(|(_, dep)| *dep)
1267 .collect()
1268 }
1269 #[allow(dead_code)]
1270 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
1271 self.edges
1272 .iter()
1273 .filter(|(_, dep)| *dep == node)
1274 .map(|(d, _)| *d)
1275 .collect()
1276 }
1277 #[allow(dead_code)]
1278 pub fn topological_sort(&self) -> Vec<u32> {
1279 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
1280 for &n in &self.nodes {
1281 in_degree.insert(n, 0);
1282 }
1283 for (_, dep) in &self.edges {
1284 *in_degree.entry(*dep).or_insert(0) += 1;
1285 }
1286 let mut queue: std::collections::VecDeque<u32> = self
1287 .nodes
1288 .iter()
1289 .filter(|&&n| in_degree[&n] == 0)
1290 .copied()
1291 .collect();
1292 let mut result = Vec::new();
1293 while let Some(node) = queue.pop_front() {
1294 result.push(node);
1295 for dep in self.dependents_of(node) {
1296 let cnt = in_degree.entry(dep).or_insert(0);
1297 *cnt = cnt.saturating_sub(1);
1298 if *cnt == 0 {
1299 queue.push_back(dep);
1300 }
1301 }
1302 }
1303 result
1304 }
1305 #[allow(dead_code)]
1306 pub fn has_cycle(&self) -> bool {
1307 self.topological_sort().len() < self.nodes.len()
1308 }
1309}
1310#[derive(Debug, Default)]
1312pub struct HsExtProfiler {
1313 pub(super) timings: Vec<HsExtPassTiming>,
1314}
1315impl HsExtProfiler {
1316 pub fn new() -> Self {
1317 HsExtProfiler::default()
1318 }
1319 pub fn record(&mut self, t: HsExtPassTiming) {
1320 self.timings.push(t);
1321 }
1322 pub fn total_elapsed_us(&self) -> u64 {
1323 self.timings.iter().map(|t| t.elapsed_us).sum()
1324 }
1325 pub fn slowest_pass(&self) -> Option<&HsExtPassTiming> {
1326 self.timings.iter().max_by_key(|t| t.elapsed_us)
1327 }
1328 pub fn num_passes(&self) -> usize {
1329 self.timings.len()
1330 }
1331 pub fn profitable_passes(&self) -> Vec<&HsExtPassTiming> {
1332 self.timings.iter().filter(|t| t.is_profitable()).collect()
1333 }
1334}
1335#[derive(Debug, Clone)]
1337pub struct HsExtPassTiming {
1338 pub pass_name: String,
1339 pub elapsed_us: u64,
1340 pub items_processed: usize,
1341 pub bytes_before: usize,
1342 pub bytes_after: usize,
1343}
1344impl HsExtPassTiming {
1345 pub fn new(
1346 pass_name: impl Into<String>,
1347 elapsed_us: u64,
1348 items: usize,
1349 before: usize,
1350 after: usize,
1351 ) -> Self {
1352 HsExtPassTiming {
1353 pass_name: pass_name.into(),
1354 elapsed_us,
1355 items_processed: items,
1356 bytes_before: before,
1357 bytes_after: after,
1358 }
1359 }
1360 pub fn throughput_mps(&self) -> f64 {
1361 if self.elapsed_us == 0 {
1362 0.0
1363 } else {
1364 self.items_processed as f64 / (self.elapsed_us as f64 / 1_000_000.0)
1365 }
1366 }
1367 pub fn size_ratio(&self) -> f64 {
1368 if self.bytes_before == 0 {
1369 1.0
1370 } else {
1371 self.bytes_after as f64 / self.bytes_before as f64
1372 }
1373 }
1374 pub fn is_profitable(&self) -> bool {
1375 self.size_ratio() <= 1.05
1376 }
1377}
1378#[derive(Debug, Default)]
1380pub struct HsExtIdGen {
1381 pub(super) next: u32,
1382}
1383impl HsExtIdGen {
1384 pub fn new() -> Self {
1385 HsExtIdGen::default()
1386 }
1387 pub fn next_id(&mut self) -> u32 {
1388 let id = self.next;
1389 self.next += 1;
1390 id
1391 }
1392 pub fn peek_next(&self) -> u32 {
1393 self.next
1394 }
1395 pub fn reset(&mut self) {
1396 self.next = 0;
1397 }
1398 pub fn skip(&mut self, n: u32) {
1399 self.next += n;
1400 }
1401}
1402#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1404pub enum HsExtDiagSeverity {
1405 Note,
1406 Warning,
1407 Error,
1408}
1409#[derive(Debug, Clone, Default)]
1411pub struct HsExtConfig {
1412 pub(super) entries: std::collections::HashMap<String, String>,
1413}
1414impl HsExtConfig {
1415 pub fn new() -> Self {
1416 HsExtConfig::default()
1417 }
1418 pub fn set(&mut self, key: impl Into<String>, value: impl Into<String>) {
1419 self.entries.insert(key.into(), value.into());
1420 }
1421 pub fn get(&self, key: &str) -> Option<&str> {
1422 self.entries.get(key).map(|s| s.as_str())
1423 }
1424 pub fn get_bool(&self, key: &str) -> bool {
1425 matches!(self.get(key), Some("true") | Some("1") | Some("yes"))
1426 }
1427 pub fn get_int(&self, key: &str) -> Option<i64> {
1428 self.get(key)?.parse().ok()
1429 }
1430 pub fn len(&self) -> usize {
1431 self.entries.len()
1432 }
1433 pub fn is_empty(&self) -> bool {
1434 self.entries.is_empty()
1435 }
1436}