trace
This commit is contained in:
@@ -11,6 +11,7 @@ pub struct SourceSpan {
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ExecutionTrace {
|
||||
pub executed_spans: Vec<SourceSpan>,
|
||||
pub selected_spans: Vec<SourceSpan>,
|
||||
}
|
||||
|
||||
@@ -42,7 +43,7 @@ pub enum Value {
|
||||
Float(f64, Option<SourceSpan>),
|
||||
Str(String, Option<SourceSpan>),
|
||||
Marker,
|
||||
Quotation(Vec<Op>),
|
||||
Quotation(Vec<Op>, Option<SourceSpan>),
|
||||
}
|
||||
|
||||
impl PartialEq for Value {
|
||||
@@ -52,7 +53,7 @@ impl PartialEq for Value {
|
||||
(Value::Float(a, _), Value::Float(b, _)) => a == b,
|
||||
(Value::Str(a, _), Value::Str(b, _)) => a == b,
|
||||
(Value::Marker, Value::Marker) => true,
|
||||
(Value::Quotation(a), Value::Quotation(b)) => a == b,
|
||||
(Value::Quotation(a, _), Value::Quotation(b, _)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -110,7 +111,7 @@ impl Value {
|
||||
Value::Float(f, _) => *f != 0.0,
|
||||
Value::Str(s, _) => !s.is_empty(),
|
||||
Value::Marker => false,
|
||||
Value::Quotation(_) => true,
|
||||
Value::Quotation(..) => true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,14 +125,14 @@ impl Value {
|
||||
Value::Float(f, _) => f.to_string(),
|
||||
Value::Str(s, _) => s.clone(),
|
||||
Value::Marker => String::new(),
|
||||
Value::Quotation(_) => String::new(),
|
||||
Value::Quotation(..) => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn span(&self) -> Option<SourceSpan> {
|
||||
match self {
|
||||
Value::Int(_, s) | Value::Float(_, s) | Value::Str(_, s) => *s,
|
||||
Value::Marker | Value::Quotation(_) => None,
|
||||
Value::Marker | Value::Quotation(..) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,7 +171,7 @@ pub enum Op {
|
||||
And,
|
||||
Or,
|
||||
Not,
|
||||
BranchIfZero(usize),
|
||||
BranchIfZero(usize, Option<SourceSpan>, Option<SourceSpan>),
|
||||
Branch(usize),
|
||||
NewCmd,
|
||||
SetParam(String),
|
||||
@@ -201,7 +202,7 @@ pub enum Op {
|
||||
SetTempo,
|
||||
Each,
|
||||
Every,
|
||||
Quotation(Vec<Op>),
|
||||
Quotation(Vec<Op>, Option<SourceSpan>),
|
||||
When,
|
||||
Unless,
|
||||
Adsr,
|
||||
@@ -1826,8 +1827,8 @@ enum Token {
|
||||
Float(f64, SourceSpan),
|
||||
Str(String, SourceSpan),
|
||||
Word(String, SourceSpan),
|
||||
QuoteStart,
|
||||
QuoteEnd,
|
||||
QuoteStart(usize),
|
||||
QuoteEnd(usize),
|
||||
}
|
||||
|
||||
fn tokenize(input: &str) -> Vec<Token> {
|
||||
@@ -1869,13 +1870,13 @@ fn tokenize(input: &str) -> Vec<Token> {
|
||||
|
||||
if c == '{' {
|
||||
chars.next();
|
||||
tokens.push(Token::QuoteStart);
|
||||
tokens.push(Token::QuoteStart(pos));
|
||||
continue;
|
||||
}
|
||||
|
||||
if c == '}' {
|
||||
chars.next();
|
||||
tokens.push(Token::QuoteEnd);
|
||||
tokens.push(Token::QuoteEnd(pos));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1914,12 +1915,13 @@ fn compile(tokens: &[Token]) -> Result<Vec<Op>, String> {
|
||||
Token::Int(n, span) => ops.push(Op::PushInt(*n, Some(*span))),
|
||||
Token::Float(f, span) => ops.push(Op::PushFloat(*f, Some(*span))),
|
||||
Token::Str(s, span) => ops.push(Op::PushStr(s.clone(), Some(*span))),
|
||||
Token::QuoteStart => {
|
||||
let (quote_ops, consumed) = compile_quotation(&tokens[i + 1..])?;
|
||||
Token::QuoteStart(start_pos) => {
|
||||
let (quote_ops, consumed, end_pos) = compile_quotation(&tokens[i + 1..])?;
|
||||
i += consumed;
|
||||
ops.push(Op::Quotation(quote_ops));
|
||||
let body_span = SourceSpan { start: *start_pos, end: end_pos + 1 };
|
||||
ops.push(Op::Quotation(quote_ops, Some(body_span)));
|
||||
}
|
||||
Token::QuoteEnd => {
|
||||
Token::QuoteEnd(_) => {
|
||||
return Err("unexpected }".into());
|
||||
}
|
||||
Token::Word(w, span) => {
|
||||
@@ -1932,13 +1934,13 @@ fn compile(tokens: &[Token]) -> Result<Vec<Op>, String> {
|
||||
}
|
||||
pipe_parity = !pipe_parity;
|
||||
} else if word == "if" {
|
||||
let (then_ops, else_ops, consumed) = compile_if(&tokens[i + 1..])?;
|
||||
let (then_ops, else_ops, consumed, then_span, else_span) = compile_if(&tokens[i + 1..])?;
|
||||
i += consumed;
|
||||
if else_ops.is_empty() {
|
||||
ops.push(Op::BranchIfZero(then_ops.len()));
|
||||
ops.push(Op::BranchIfZero(then_ops.len(), then_span, None));
|
||||
ops.extend(then_ops);
|
||||
} else {
|
||||
ops.push(Op::BranchIfZero(then_ops.len() + 1));
|
||||
ops.push(Op::BranchIfZero(then_ops.len() + 1, then_span, else_span));
|
||||
ops.extend(then_ops);
|
||||
ops.push(Op::Branch(else_ops.len()));
|
||||
ops.extend(else_ops);
|
||||
@@ -1954,17 +1956,17 @@ fn compile(tokens: &[Token]) -> Result<Vec<Op>, String> {
|
||||
Ok(ops)
|
||||
}
|
||||
|
||||
fn compile_quotation(tokens: &[Token]) -> Result<(Vec<Op>, usize), String> {
|
||||
fn compile_quotation(tokens: &[Token]) -> Result<(Vec<Op>, usize, usize), String> {
|
||||
let mut depth = 1;
|
||||
let mut end_pos = None;
|
||||
let mut end_idx = None;
|
||||
|
||||
for (i, tok) in tokens.iter().enumerate() {
|
||||
match tok {
|
||||
Token::QuoteStart => depth += 1,
|
||||
Token::QuoteEnd => {
|
||||
Token::QuoteStart(_) => depth += 1,
|
||||
Token::QuoteEnd(_) => {
|
||||
depth -= 1;
|
||||
if depth == 0 {
|
||||
end_pos = Some(i);
|
||||
end_idx = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1972,12 +1974,31 @@ fn compile_quotation(tokens: &[Token]) -> Result<(Vec<Op>, usize), String> {
|
||||
}
|
||||
}
|
||||
|
||||
let end_pos = end_pos.ok_or("missing }")?;
|
||||
let quote_ops = compile(&tokens[..end_pos])?;
|
||||
Ok((quote_ops, end_pos + 1))
|
||||
let end_idx = end_idx.ok_or("missing }")?;
|
||||
let byte_pos = match &tokens[end_idx] {
|
||||
Token::QuoteEnd(pos) => *pos,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let quote_ops = compile(&tokens[..end_idx])?;
|
||||
Ok((quote_ops, end_idx + 1, byte_pos))
|
||||
}
|
||||
|
||||
fn compile_if(tokens: &[Token]) -> Result<(Vec<Op>, Vec<Op>, usize), String> {
|
||||
fn token_span(tok: &Token) -> Option<SourceSpan> {
|
||||
match tok {
|
||||
Token::Int(_, s) | Token::Float(_, s) | Token::Str(_, s) | Token::Word(_, s) => Some(*s),
|
||||
Token::QuoteStart(p) => Some(SourceSpan { start: *p, end: *p + 1 }),
|
||||
Token::QuoteEnd(p) => Some(SourceSpan { start: *p, end: *p + 1 }),
|
||||
}
|
||||
}
|
||||
|
||||
fn tokens_span(tokens: &[Token]) -> Option<SourceSpan> {
|
||||
let first = tokens.first().and_then(token_span)?;
|
||||
let last = tokens.last().and_then(token_span)?;
|
||||
Some(SourceSpan { start: first.start, end: last.end })
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn compile_if(tokens: &[Token]) -> Result<(Vec<Op>, Vec<Op>, usize, Option<SourceSpan>, Option<SourceSpan>), String> {
|
||||
let mut depth = 1;
|
||||
let mut else_pos = None;
|
||||
let mut then_pos = None;
|
||||
@@ -2001,16 +2022,22 @@ fn compile_if(tokens: &[Token]) -> Result<(Vec<Op>, Vec<Op>, usize), String> {
|
||||
|
||||
let then_pos = then_pos.ok_or("missing 'then'")?;
|
||||
|
||||
let (then_ops, else_ops) = if let Some(ep) = else_pos {
|
||||
let then_ops = compile(&tokens[..ep])?;
|
||||
let else_ops = compile(&tokens[ep + 1..then_pos])?;
|
||||
(then_ops, else_ops)
|
||||
let (then_ops, else_ops, then_span, else_span) = if let Some(ep) = else_pos {
|
||||
let then_slice = &tokens[..ep];
|
||||
let else_slice = &tokens[ep + 1..then_pos];
|
||||
let then_span = tokens_span(then_slice);
|
||||
let else_span = tokens_span(else_slice);
|
||||
let then_ops = compile(then_slice)?;
|
||||
let else_ops = compile(else_slice)?;
|
||||
(then_ops, else_ops, then_span, else_span)
|
||||
} else {
|
||||
let then_ops = compile(&tokens[..then_pos])?;
|
||||
(then_ops, Vec::new())
|
||||
let then_slice = &tokens[..then_pos];
|
||||
let then_span = tokens_span(then_slice);
|
||||
let then_ops = compile(then_slice)?;
|
||||
(then_ops, Vec::new(), then_span, None)
|
||||
};
|
||||
|
||||
Ok((then_ops, else_ops, then_pos + 1))
|
||||
Ok((then_ops, else_ops, then_pos + 1, then_span, else_span))
|
||||
}
|
||||
|
||||
pub type Stack = Arc<Mutex<Vec<Value>>>;
|
||||
@@ -2232,10 +2259,19 @@ impl Forth {
|
||||
stack.push(Value::Int(if v { 0 } else { 1 }, None));
|
||||
}
|
||||
|
||||
Op::BranchIfZero(offset) => {
|
||||
Op::BranchIfZero(offset, then_span, else_span) => {
|
||||
let v = stack.pop().ok_or("stack underflow")?;
|
||||
if !v.is_truthy() {
|
||||
if let Some(span) = else_span {
|
||||
if let Some(trace) = trace_cell.borrow_mut().as_mut() {
|
||||
trace.executed_spans.push(*span);
|
||||
}
|
||||
}
|
||||
pc += offset;
|
||||
} else if let Some(span) = then_span {
|
||||
if let Some(trace) = trace_cell.borrow_mut().as_mut() {
|
||||
trace.executed_spans.push(*span);
|
||||
}
|
||||
}
|
||||
}
|
||||
Op::Branch(offset) => {
|
||||
@@ -2386,7 +2422,12 @@ impl Forth {
|
||||
let val: f64 = self.rng.lock().unwrap().gen();
|
||||
if val < prob {
|
||||
match quot {
|
||||
Value::Quotation(quot_ops) => {
|
||||
Value::Quotation(quot_ops, body_span) => {
|
||||
if let Some(span) = body_span {
|
||||
if let Some(trace) = trace_cell.borrow_mut().as_mut() {
|
||||
trace.executed_spans.push(span);
|
||||
}
|
||||
}
|
||||
let mut trace_opt = trace_cell.borrow_mut().take();
|
||||
self.execute_ops("_ops, ctx, stack, outputs, time_stack, cmd, trace_opt.as_deref_mut())?;
|
||||
*trace_cell.borrow_mut() = trace_opt;
|
||||
@@ -2402,7 +2443,12 @@ impl Forth {
|
||||
let val: f64 = self.rng.lock().unwrap().gen();
|
||||
if val < pct / 100.0 {
|
||||
match quot {
|
||||
Value::Quotation(quot_ops) => {
|
||||
Value::Quotation(quot_ops, body_span) => {
|
||||
if let Some(span) = body_span {
|
||||
if let Some(trace) = trace_cell.borrow_mut().as_mut() {
|
||||
trace.executed_spans.push(span);
|
||||
}
|
||||
}
|
||||
let mut trace_opt = trace_cell.borrow_mut().take();
|
||||
self.execute_ops("_ops, ctx, stack, outputs, time_stack, cmd, trace_opt.as_deref_mut())?;
|
||||
*trace_cell.borrow_mut() = trace_opt;
|
||||
@@ -2426,8 +2472,8 @@ impl Forth {
|
||||
stack.push(Value::Int(if result { 1 } else { 0 }, None));
|
||||
}
|
||||
|
||||
Op::Quotation(quote_ops) => {
|
||||
stack.push(Value::Quotation(quote_ops.clone()));
|
||||
Op::Quotation(quote_ops, body_span) => {
|
||||
stack.push(Value::Quotation(quote_ops.clone(), *body_span));
|
||||
}
|
||||
|
||||
Op::When => {
|
||||
@@ -2435,7 +2481,12 @@ impl Forth {
|
||||
let quot = stack.pop().ok_or("stack underflow")?;
|
||||
if cond.is_truthy() {
|
||||
match quot {
|
||||
Value::Quotation(quot_ops) => {
|
||||
Value::Quotation(quot_ops, body_span) => {
|
||||
if let Some(span) = body_span {
|
||||
if let Some(trace) = trace_cell.borrow_mut().as_mut() {
|
||||
trace.executed_spans.push(span);
|
||||
}
|
||||
}
|
||||
let mut trace_opt = trace_cell.borrow_mut().take();
|
||||
self.execute_ops("_ops, ctx, stack, outputs, time_stack, cmd, trace_opt.as_deref_mut())?;
|
||||
*trace_cell.borrow_mut() = trace_opt;
|
||||
@@ -2450,7 +2501,12 @@ impl Forth {
|
||||
let quot = stack.pop().ok_or("stack underflow")?;
|
||||
if !cond.is_truthy() {
|
||||
match quot {
|
||||
Value::Quotation(quot_ops) => {
|
||||
Value::Quotation(quot_ops, body_span) => {
|
||||
if let Some(span) = body_span {
|
||||
if let Some(trace) = trace_cell.borrow_mut().as_mut() {
|
||||
trace.executed_spans.push(span);
|
||||
}
|
||||
}
|
||||
let mut trace_opt = trace_cell.borrow_mut().take();
|
||||
self.execute_ops("_ops, ctx, stack, outputs, time_stack, cmd, trace_opt.as_deref_mut())?;
|
||||
*trace_cell.borrow_mut() = trace_opt;
|
||||
@@ -2715,7 +2771,12 @@ impl Forth {
|
||||
.ok_or("for requires subdivide first")?;
|
||||
|
||||
match quot {
|
||||
Value::Quotation(quot_ops) => {
|
||||
Value::Quotation(quot_ops, body_span) => {
|
||||
if let Some(span) = body_span {
|
||||
if let Some(trace) = trace_cell.borrow_mut().as_mut() {
|
||||
trace.executed_spans.push(span);
|
||||
}
|
||||
}
|
||||
for (i, (sub_start, sub_dur)) in subs.iter().enumerate() {
|
||||
time_stack.push(TimeContext {
|
||||
start: *sub_start,
|
||||
|
||||
Reference in New Issue
Block a user