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::Str(s) => HaskellExpr::Lit(HaskellLit::Str(s.clone())),
576 },
577 LcnfLetValue::Erased | LcnfLetValue::Reset(_) => HaskellExpr::Lit(HaskellLit::Unit),
578 LcnfLetValue::FVar(v) => HaskellExpr::Var(format!("{}", v)),
579 LcnfLetValue::Reuse(_, name, _tag, args) => {
580 let ctor_expr = HaskellExpr::Var(name.clone());
581 if args.is_empty() {
582 ctor_expr
583 } else {
584 let arg_exprs: Vec<HaskellExpr> =
585 args.iter().map(|a| self.compile_arg(a)).collect();
586 HaskellExpr::App(Box::new(ctor_expr), arg_exprs)
587 }
588 }
589 }
590 }
591 pub(super) fn compile_alt(&self, alt: &LcnfAlt) -> HaskellCaseAlt {
593 let body = self.compile_expr(&alt.body);
594 let pat = HaskellPattern::Constructor(
595 alt.ctor_name.clone(),
596 alt.params
597 .iter()
598 .map(|p| HaskellPattern::Var(p.name.clone()))
599 .collect(),
600 );
601 HaskellCaseAlt {
602 pattern: pat,
603 guards: Vec::new(),
604 body: Some(body),
605 }
606 }
607 pub(super) fn compile_arg(&self, arg: &LcnfArg) -> HaskellExpr {
609 match arg {
610 LcnfArg::Var(v) => HaskellExpr::Var(format!("{}", v)),
611 LcnfArg::Lit(lit) => match lit {
612 LcnfLit::Nat(n) => HaskellExpr::Lit(HaskellLit::Int(*n as i64)),
613 LcnfLit::Str(s) => HaskellExpr::Lit(HaskellLit::Str(s.clone())),
614 },
615 LcnfArg::Erased | LcnfArg::Type(_) => HaskellExpr::Lit(HaskellLit::Unit),
616 }
617 }
618 pub fn emit_module(&self) -> String {
620 self.module.emit()
621 }
622}
623#[derive(Debug, Clone, PartialEq)]
632pub struct HaskellInstance {
633 pub class: String,
635 pub instance_type: HaskellType,
637 pub context: Vec<HaskellType>,
639 pub where_clause: Vec<HaskellFunction>,
641}
642#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
644pub struct HsExtVersion {
645 pub major: u32,
646 pub minor: u32,
647 pub patch: u32,
648 pub pre: Option<String>,
649}
650impl HsExtVersion {
651 pub fn new(major: u32, minor: u32, patch: u32) -> Self {
652 HsExtVersion {
653 major,
654 minor,
655 patch,
656 pre: None,
657 }
658 }
659 pub fn with_pre(mut self, pre: impl Into<String>) -> Self {
660 self.pre = Some(pre.into());
661 self
662 }
663 pub fn is_stable(&self) -> bool {
664 self.pre.is_none()
665 }
666 pub fn is_compatible_with(&self, other: &HsExtVersion) -> bool {
667 self.major == other.major && self.minor >= other.minor
668 }
669}
670#[allow(dead_code)]
671pub struct HskConstantFoldingHelper;
672impl HskConstantFoldingHelper {
673 #[allow(dead_code)]
674 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
675 a.checked_add(b)
676 }
677 #[allow(dead_code)]
678 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
679 a.checked_sub(b)
680 }
681 #[allow(dead_code)]
682 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
683 a.checked_mul(b)
684 }
685 #[allow(dead_code)]
686 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
687 if b == 0 {
688 None
689 } else {
690 a.checked_div(b)
691 }
692 }
693 #[allow(dead_code)]
694 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
695 a + b
696 }
697 #[allow(dead_code)]
698 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
699 a * b
700 }
701 #[allow(dead_code)]
702 pub fn fold_neg_i64(a: i64) -> Option<i64> {
703 a.checked_neg()
704 }
705 #[allow(dead_code)]
706 pub fn fold_not_bool(a: bool) -> bool {
707 !a
708 }
709 #[allow(dead_code)]
710 pub fn fold_and_bool(a: bool, b: bool) -> bool {
711 a && b
712 }
713 #[allow(dead_code)]
714 pub fn fold_or_bool(a: bool, b: bool) -> bool {
715 a || b
716 }
717 #[allow(dead_code)]
718 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
719 a.checked_shl(b)
720 }
721 #[allow(dead_code)]
722 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
723 a.checked_shr(b)
724 }
725 #[allow(dead_code)]
726 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
727 if b == 0 {
728 None
729 } else {
730 Some(a % b)
731 }
732 }
733 #[allow(dead_code)]
734 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
735 a & b
736 }
737 #[allow(dead_code)]
738 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
739 a | b
740 }
741 #[allow(dead_code)]
742 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
743 a ^ b
744 }
745 #[allow(dead_code)]
746 pub fn fold_bitnot_i64(a: i64) -> i64 {
747 !a
748 }
749}
750#[derive(Debug, Clone, Default)]
752pub struct HsExtFeatures {
753 pub(super) flags: std::collections::HashSet<String>,
754}
755impl HsExtFeatures {
756 pub fn new() -> Self {
757 HsExtFeatures::default()
758 }
759 pub fn enable(&mut self, flag: impl Into<String>) {
760 self.flags.insert(flag.into());
761 }
762 pub fn disable(&mut self, flag: &str) {
763 self.flags.remove(flag);
764 }
765 pub fn is_enabled(&self, flag: &str) -> bool {
766 self.flags.contains(flag)
767 }
768 pub fn len(&self) -> usize {
769 self.flags.len()
770 }
771 pub fn is_empty(&self) -> bool {
772 self.flags.is_empty()
773 }
774 pub fn union(&self, other: &HsExtFeatures) -> HsExtFeatures {
775 HsExtFeatures {
776 flags: self.flags.union(&other.flags).cloned().collect(),
777 }
778 }
779 pub fn intersection(&self, other: &HsExtFeatures) -> HsExtFeatures {
780 HsExtFeatures {
781 flags: self.flags.intersection(&other.flags).cloned().collect(),
782 }
783 }
784}
785#[derive(Debug, Clone, PartialEq)]
787pub enum HsListQual {
788 Generator(String, HaskellExpr),
790 Guard(HaskellExpr),
792 LetBind(String, HaskellExpr),
794}
795#[derive(Debug)]
797pub struct HsExtEventLog {
798 pub(super) entries: std::collections::VecDeque<String>,
799 pub(super) capacity: usize,
800}
801impl HsExtEventLog {
802 pub fn new(capacity: usize) -> Self {
803 HsExtEventLog {
804 entries: std::collections::VecDeque::with_capacity(capacity),
805 capacity,
806 }
807 }
808 pub fn push(&mut self, event: impl Into<String>) {
809 if self.entries.len() >= self.capacity {
810 self.entries.pop_front();
811 }
812 self.entries.push_back(event.into());
813 }
814 pub fn iter(&self) -> impl Iterator<Item = &String> {
815 self.entries.iter()
816 }
817 pub fn len(&self) -> usize {
818 self.entries.len()
819 }
820 pub fn is_empty(&self) -> bool {
821 self.entries.is_empty()
822 }
823 pub fn capacity(&self) -> usize {
824 self.capacity
825 }
826 pub fn clear(&mut self) {
827 self.entries.clear();
828 }
829}
830#[allow(dead_code)]
831pub struct HskPassRegistry {
832 pub(super) configs: Vec<HskPassConfig>,
833 pub(super) stats: std::collections::HashMap<String, HskPassStats>,
834}
835impl HskPassRegistry {
836 #[allow(dead_code)]
837 pub fn new() -> Self {
838 HskPassRegistry {
839 configs: Vec::new(),
840 stats: std::collections::HashMap::new(),
841 }
842 }
843 #[allow(dead_code)]
844 pub fn register(&mut self, config: HskPassConfig) {
845 self.stats
846 .insert(config.pass_name.clone(), HskPassStats::new());
847 self.configs.push(config);
848 }
849 #[allow(dead_code)]
850 pub fn enabled_passes(&self) -> Vec<&HskPassConfig> {
851 self.configs.iter().filter(|c| c.enabled).collect()
852 }
853 #[allow(dead_code)]
854 pub fn get_stats(&self, name: &str) -> Option<&HskPassStats> {
855 self.stats.get(name)
856 }
857 #[allow(dead_code)]
858 pub fn total_passes(&self) -> usize {
859 self.configs.len()
860 }
861 #[allow(dead_code)]
862 pub fn enabled_count(&self) -> usize {
863 self.enabled_passes().len()
864 }
865 #[allow(dead_code)]
866 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
867 if let Some(stats) = self.stats.get_mut(name) {
868 stats.record_run(changes, time_ms, iter);
869 }
870 }
871}
872#[allow(dead_code)]
873#[derive(Debug, Clone, PartialEq)]
874pub enum HskPassPhase {
875 Analysis,
876 Transformation,
877 Verification,
878 Cleanup,
879}
880impl HskPassPhase {
881 #[allow(dead_code)]
882 pub fn name(&self) -> &str {
883 match self {
884 HskPassPhase::Analysis => "analysis",
885 HskPassPhase::Transformation => "transformation",
886 HskPassPhase::Verification => "verification",
887 HskPassPhase::Cleanup => "cleanup",
888 }
889 }
890 #[allow(dead_code)]
891 pub fn is_modifying(&self) -> bool {
892 matches!(self, HskPassPhase::Transformation | HskPassPhase::Cleanup)
893 }
894}
895#[derive(Debug, Clone, PartialEq)]
904pub struct HaskellFunction {
905 pub name: String,
907 pub type_annotation: Option<HaskellType>,
909 pub equations: Vec<HaskellEquation>,
911}
912#[derive(Debug, Default)]
914pub struct HsExtNameScope {
915 pub(super) declared: std::collections::HashSet<String>,
916 pub(super) depth: usize,
917 pub(super) parent: Option<Box<HsExtNameScope>>,
918}
919impl HsExtNameScope {
920 pub fn new() -> Self {
921 HsExtNameScope::default()
922 }
923 pub fn declare(&mut self, name: impl Into<String>) -> bool {
924 self.declared.insert(name.into())
925 }
926 pub fn is_declared(&self, name: &str) -> bool {
927 self.declared.contains(name)
928 }
929 pub fn push_scope(self) -> Self {
930 HsExtNameScope {
931 declared: std::collections::HashSet::new(),
932 depth: self.depth + 1,
933 parent: Some(Box::new(self)),
934 }
935 }
936 pub fn pop_scope(self) -> Self {
937 *self.parent.unwrap_or_default()
938 }
939 pub fn depth(&self) -> usize {
940 self.depth
941 }
942 pub fn len(&self) -> usize {
943 self.declared.len()
944 }
945}
946#[allow(dead_code)]
947#[derive(Debug, Clone)]
948pub struct HskCacheEntry {
949 pub key: String,
950 pub data: Vec<u8>,
951 pub timestamp: u64,
952 pub valid: bool,
953}
954#[derive(Debug, Clone, PartialEq)]
956pub enum HaskellPattern {
957 Wildcard,
959 Var(String),
961 Lit(HaskellLit),
963 Tuple(Vec<HaskellPattern>),
965 List(Vec<HaskellPattern>),
967 Cons(Box<HaskellPattern>, Box<HaskellPattern>),
969 Constructor(String, Vec<HaskellPattern>),
971 As(String, Box<HaskellPattern>),
973 LazyPat(Box<HaskellPattern>),
975}
976#[derive(Debug, Clone, PartialEq)]
978pub enum HaskellExpr {
979 Lit(HaskellLit),
981 Var(String),
983 App(Box<HaskellExpr>, Vec<HaskellExpr>),
985 Lambda(Vec<HaskellPattern>, Box<HaskellExpr>),
987 Let(String, Box<HaskellExpr>, Box<HaskellExpr>),
989 Where(Box<HaskellExpr>, Vec<HaskellFunction>),
991 If(Box<HaskellExpr>, Box<HaskellExpr>, Box<HaskellExpr>),
993 Case(Box<HaskellExpr>, Vec<HaskellCaseAlt>),
995 Do(Vec<HaskellDoStmt>),
997 ListComp(Box<HaskellExpr>, Vec<HsListQual>),
999 Tuple(Vec<HaskellExpr>),
1001 List(Vec<HaskellExpr>),
1003 Neg(Box<HaskellExpr>),
1005 InfixApp(Box<HaskellExpr>, String, Box<HaskellExpr>),
1007 Operator(String),
1009 TypeAnnotation(Box<HaskellExpr>, HaskellType),
1011}
1012#[derive(Debug, Clone, PartialEq)]
1020pub struct HaskellTypeClass {
1021 pub name: String,
1023 pub type_params: Vec<String>,
1025 pub superclasses: Vec<HaskellType>,
1027 pub methods: Vec<(String, HaskellType, Option<HaskellExpr>)>,
1029}
1030#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1032pub enum HaskellType {
1033 Int,
1035 Integer,
1037 Double,
1039 Float,
1041 Bool,
1043 Char,
1045 HsString,
1047 Unit,
1049 IO(Box<HaskellType>),
1051 List(Box<HaskellType>),
1053 Maybe(Box<HaskellType>),
1055 Either(Box<HaskellType>, Box<HaskellType>),
1057 Tuple(Vec<HaskellType>),
1059 Fun(Box<HaskellType>, Box<HaskellType>),
1061 Custom(String),
1063 Polymorphic(String),
1065 Constraint(String, Vec<HaskellType>),
1067}
1068#[derive(Debug, Clone, PartialEq)]
1072pub struct HaskellEquation {
1073 pub patterns: Vec<HaskellPattern>,
1075 pub guards: Vec<HaskellGuard>,
1077 pub body: Option<HaskellExpr>,
1079 pub where_clause: Vec<HaskellFunction>,
1081}
1082#[derive(Debug, Clone, PartialEq)]
1084pub struct HaskellImport {
1085 pub module: String,
1087 pub qualified: bool,
1089 pub alias: Option<String>,
1091 pub items: Vec<String>,
1093 pub hiding: Vec<String>,
1095}
1096#[derive(Debug, Clone)]
1098pub struct HsExtDiagMsg {
1099 pub severity: HsExtDiagSeverity,
1100 pub pass: String,
1101 pub message: String,
1102}
1103impl HsExtDiagMsg {
1104 pub fn error(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1105 HsExtDiagMsg {
1106 severity: HsExtDiagSeverity::Error,
1107 pass: pass.into(),
1108 message: msg.into(),
1109 }
1110 }
1111 pub fn warning(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1112 HsExtDiagMsg {
1113 severity: HsExtDiagSeverity::Warning,
1114 pass: pass.into(),
1115 message: msg.into(),
1116 }
1117 }
1118 pub fn note(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1119 HsExtDiagMsg {
1120 severity: HsExtDiagSeverity::Note,
1121 pass: pass.into(),
1122 message: msg.into(),
1123 }
1124 }
1125}
1126#[derive(Debug, Default)]
1128pub struct HsExtDiagCollector {
1129 pub(super) msgs: Vec<HsExtDiagMsg>,
1130}
1131impl HsExtDiagCollector {
1132 pub fn new() -> Self {
1133 HsExtDiagCollector::default()
1134 }
1135 pub fn emit(&mut self, d: HsExtDiagMsg) {
1136 self.msgs.push(d);
1137 }
1138 pub fn has_errors(&self) -> bool {
1139 self.msgs
1140 .iter()
1141 .any(|d| d.severity == HsExtDiagSeverity::Error)
1142 }
1143 pub fn errors(&self) -> Vec<&HsExtDiagMsg> {
1144 self.msgs
1145 .iter()
1146 .filter(|d| d.severity == HsExtDiagSeverity::Error)
1147 .collect()
1148 }
1149 pub fn warnings(&self) -> Vec<&HsExtDiagMsg> {
1150 self.msgs
1151 .iter()
1152 .filter(|d| d.severity == HsExtDiagSeverity::Warning)
1153 .collect()
1154 }
1155 pub fn len(&self) -> usize {
1156 self.msgs.len()
1157 }
1158 pub fn is_empty(&self) -> bool {
1159 self.msgs.is_empty()
1160 }
1161 pub fn clear(&mut self) {
1162 self.msgs.clear();
1163 }
1164}
1165#[allow(dead_code)]
1166#[derive(Debug, Clone)]
1167pub struct HskAnalysisCache {
1168 pub(super) entries: std::collections::HashMap<String, HskCacheEntry>,
1169 pub(super) max_size: usize,
1170 pub(super) hits: u64,
1171 pub(super) misses: u64,
1172}
1173impl HskAnalysisCache {
1174 #[allow(dead_code)]
1175 pub fn new(max_size: usize) -> Self {
1176 HskAnalysisCache {
1177 entries: std::collections::HashMap::new(),
1178 max_size,
1179 hits: 0,
1180 misses: 0,
1181 }
1182 }
1183 #[allow(dead_code)]
1184 pub fn get(&mut self, key: &str) -> Option<&HskCacheEntry> {
1185 if self.entries.contains_key(key) {
1186 self.hits += 1;
1187 self.entries.get(key)
1188 } else {
1189 self.misses += 1;
1190 None
1191 }
1192 }
1193 #[allow(dead_code)]
1194 pub fn insert(&mut self, key: String, data: Vec<u8>) {
1195 if self.entries.len() >= self.max_size {
1196 if let Some(oldest) = self.entries.keys().next().cloned() {
1197 self.entries.remove(&oldest);
1198 }
1199 }
1200 self.entries.insert(
1201 key.clone(),
1202 HskCacheEntry {
1203 key,
1204 data,
1205 timestamp: 0,
1206 valid: true,
1207 },
1208 );
1209 }
1210 #[allow(dead_code)]
1211 pub fn invalidate(&mut self, key: &str) {
1212 if let Some(entry) = self.entries.get_mut(key) {
1213 entry.valid = false;
1214 }
1215 }
1216 #[allow(dead_code)]
1217 pub fn clear(&mut self) {
1218 self.entries.clear();
1219 }
1220 #[allow(dead_code)]
1221 pub fn hit_rate(&self) -> f64 {
1222 let total = self.hits + self.misses;
1223 if total == 0 {
1224 return 0.0;
1225 }
1226 self.hits as f64 / total as f64
1227 }
1228 #[allow(dead_code)]
1229 pub fn size(&self) -> usize {
1230 self.entries.len()
1231 }
1232}
1233#[allow(dead_code)]
1234#[derive(Debug, Clone)]
1235pub struct HskDepGraph {
1236 pub(super) nodes: Vec<u32>,
1237 pub(super) edges: Vec<(u32, u32)>,
1238}
1239impl HskDepGraph {
1240 #[allow(dead_code)]
1241 pub fn new() -> Self {
1242 HskDepGraph {
1243 nodes: Vec::new(),
1244 edges: Vec::new(),
1245 }
1246 }
1247 #[allow(dead_code)]
1248 pub fn add_node(&mut self, id: u32) {
1249 if !self.nodes.contains(&id) {
1250 self.nodes.push(id);
1251 }
1252 }
1253 #[allow(dead_code)]
1254 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
1255 self.add_node(dep);
1256 self.add_node(dependent);
1257 self.edges.push((dep, dependent));
1258 }
1259 #[allow(dead_code)]
1260 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
1261 self.edges
1262 .iter()
1263 .filter(|(d, _)| *d == node)
1264 .map(|(_, dep)| *dep)
1265 .collect()
1266 }
1267 #[allow(dead_code)]
1268 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
1269 self.edges
1270 .iter()
1271 .filter(|(_, dep)| *dep == node)
1272 .map(|(d, _)| *d)
1273 .collect()
1274 }
1275 #[allow(dead_code)]
1276 pub fn topological_sort(&self) -> Vec<u32> {
1277 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
1278 for &n in &self.nodes {
1279 in_degree.insert(n, 0);
1280 }
1281 for (_, dep) in &self.edges {
1282 *in_degree.entry(*dep).or_insert(0) += 1;
1283 }
1284 let mut queue: std::collections::VecDeque<u32> = self
1285 .nodes
1286 .iter()
1287 .filter(|&&n| in_degree[&n] == 0)
1288 .copied()
1289 .collect();
1290 let mut result = Vec::new();
1291 while let Some(node) = queue.pop_front() {
1292 result.push(node);
1293 for dep in self.dependents_of(node) {
1294 let cnt = in_degree.entry(dep).or_insert(0);
1295 *cnt = cnt.saturating_sub(1);
1296 if *cnt == 0 {
1297 queue.push_back(dep);
1298 }
1299 }
1300 }
1301 result
1302 }
1303 #[allow(dead_code)]
1304 pub fn has_cycle(&self) -> bool {
1305 self.topological_sort().len() < self.nodes.len()
1306 }
1307}
1308#[derive(Debug, Default)]
1310pub struct HsExtProfiler {
1311 pub(super) timings: Vec<HsExtPassTiming>,
1312}
1313impl HsExtProfiler {
1314 pub fn new() -> Self {
1315 HsExtProfiler::default()
1316 }
1317 pub fn record(&mut self, t: HsExtPassTiming) {
1318 self.timings.push(t);
1319 }
1320 pub fn total_elapsed_us(&self) -> u64 {
1321 self.timings.iter().map(|t| t.elapsed_us).sum()
1322 }
1323 pub fn slowest_pass(&self) -> Option<&HsExtPassTiming> {
1324 self.timings.iter().max_by_key(|t| t.elapsed_us)
1325 }
1326 pub fn num_passes(&self) -> usize {
1327 self.timings.len()
1328 }
1329 pub fn profitable_passes(&self) -> Vec<&HsExtPassTiming> {
1330 self.timings.iter().filter(|t| t.is_profitable()).collect()
1331 }
1332}
1333#[derive(Debug, Clone)]
1335pub struct HsExtPassTiming {
1336 pub pass_name: String,
1337 pub elapsed_us: u64,
1338 pub items_processed: usize,
1339 pub bytes_before: usize,
1340 pub bytes_after: usize,
1341}
1342impl HsExtPassTiming {
1343 pub fn new(
1344 pass_name: impl Into<String>,
1345 elapsed_us: u64,
1346 items: usize,
1347 before: usize,
1348 after: usize,
1349 ) -> Self {
1350 HsExtPassTiming {
1351 pass_name: pass_name.into(),
1352 elapsed_us,
1353 items_processed: items,
1354 bytes_before: before,
1355 bytes_after: after,
1356 }
1357 }
1358 pub fn throughput_mps(&self) -> f64 {
1359 if self.elapsed_us == 0 {
1360 0.0
1361 } else {
1362 self.items_processed as f64 / (self.elapsed_us as f64 / 1_000_000.0)
1363 }
1364 }
1365 pub fn size_ratio(&self) -> f64 {
1366 if self.bytes_before == 0 {
1367 1.0
1368 } else {
1369 self.bytes_after as f64 / self.bytes_before as f64
1370 }
1371 }
1372 pub fn is_profitable(&self) -> bool {
1373 self.size_ratio() <= 1.05
1374 }
1375}
1376#[derive(Debug, Default)]
1378pub struct HsExtIdGen {
1379 pub(super) next: u32,
1380}
1381impl HsExtIdGen {
1382 pub fn new() -> Self {
1383 HsExtIdGen::default()
1384 }
1385 pub fn next_id(&mut self) -> u32 {
1386 let id = self.next;
1387 self.next += 1;
1388 id
1389 }
1390 pub fn peek_next(&self) -> u32 {
1391 self.next
1392 }
1393 pub fn reset(&mut self) {
1394 self.next = 0;
1395 }
1396 pub fn skip(&mut self, n: u32) {
1397 self.next += n;
1398 }
1399}
1400#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1402pub enum HsExtDiagSeverity {
1403 Note,
1404 Warning,
1405 Error,
1406}
1407#[derive(Debug, Clone, Default)]
1409pub struct HsExtConfig {
1410 pub(super) entries: std::collections::HashMap<String, String>,
1411}
1412impl HsExtConfig {
1413 pub fn new() -> Self {
1414 HsExtConfig::default()
1415 }
1416 pub fn set(&mut self, key: impl Into<String>, value: impl Into<String>) {
1417 self.entries.insert(key.into(), value.into());
1418 }
1419 pub fn get(&self, key: &str) -> Option<&str> {
1420 self.entries.get(key).map(|s| s.as_str())
1421 }
1422 pub fn get_bool(&self, key: &str) -> bool {
1423 matches!(self.get(key), Some("true") | Some("1") | Some("yes"))
1424 }
1425 pub fn get_int(&self, key: &str) -> Option<i64> {
1426 self.get(key)?.parse().ok()
1427 }
1428 pub fn len(&self) -> usize {
1429 self.entries.len()
1430 }
1431 pub fn is_empty(&self) -> bool {
1432 self.entries.is_empty()
1433 }
1434}