1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
use crate::{ProcedureOperation, COLUMN_SEPARATOR, FRAME_SEPARATOR};
use stak_vm::{Cons, Memory, Profiler, StackSlot};
use std::{io::Write, time::Instant};

/// A stack profiler.
pub struct StackProfiler<T: Write> {
    writer: T,
    start_time: Instant,
}

impl<T: Write> StackProfiler<T> {
    /// Creates a stack profiler.
    pub fn new(writer: T) -> Self {
        Self {
            writer,
            start_time: Instant::now(),
        }
    }

    fn write_column_separator(&mut self) {
        write!(self.writer, "{COLUMN_SEPARATOR}").unwrap();
    }

    fn write_frame_separator(&mut self) {
        write!(self.writer, "{FRAME_SEPARATOR}").unwrap();
    }

    fn write_time(&mut self) {
        writeln!(
            &mut self.writer,
            "{}",
            Instant::now().duration_since(self.start_time).as_nanos()
        )
        .unwrap();
    }

    fn write_procedure(&mut self, memory: &Memory, code: Cons) {
        let operand = memory.car(code);

        if let Some(symbol) = operand.to_cons() {
            let mut string = memory.car_value(memory.car(symbol)).assume_cons();

            while string != memory.null() {
                write!(
                    self.writer,
                    "{}",
                    char::from_u32(memory.car(string).assume_number().to_i64() as _).unwrap_or('�')
                )
                .unwrap();
                string = memory.cdr(string).assume_cons();
            }
        }
    }

    fn write_stack(&mut self, memory: &Memory) {
        let mut stack = memory.stack();
        let mut first = true;

        while stack != memory.null() {
            stack = if memory.cdr(stack).tag() == StackSlot::Frame as _ {
                if !first {
                    self.write_frame_separator();
                }

                first = false;

                self.write_procedure(memory, memory.car_value(memory.car(stack)).assume_cons());

                memory.cdr_value(memory.car(stack)).assume_cons()
            } else {
                memory.cdr(stack).assume_cons()
            };
        }
    }
}

impl<T: Write> Profiler for StackProfiler<T> {
    fn profile_call(&mut self, memory: &Memory, call_code: Cons, r#return: bool) {
        write!(
            self.writer,
            "{}",
            if r#return {
                ProcedureOperation::ReturnCall
            } else {
                ProcedureOperation::Call
            }
        )
        .unwrap();
        self.write_column_separator();
        self.write_procedure(memory, call_code);
        self.write_frame_separator();
        self.write_stack(memory);
        self.write_column_separator();
        self.write_time();
    }

    fn profile_return(&mut self, memory: &Memory) {
        write!(self.writer, "{}", ProcedureOperation::Return).unwrap();
        self.write_column_separator();
        self.write_stack(memory);
        self.write_column_separator();
        self.write_time();
    }

    fn profile_event(&mut self, name: &str) {
        write!(self.writer, "{name}").unwrap();
        self.write_column_separator();
        self.write_time();
    }
}