Feat: lots of convenience stuff
This commit is contained in:
@@ -143,6 +143,19 @@ fn compile(tokens: &[Token], dict: &Dictionary) -> Result<Vec<Op>, String> {
|
||||
ops.push(Op::Quotation(Arc::from(quote_ops), Some(body_span)));
|
||||
} else if word == "}" {
|
||||
return Err("unexpected }".into());
|
||||
} else if word == "[" {
|
||||
let (bracket_ops, consumed, end_span) =
|
||||
compile_bracket(&tokens[i + 1..], dict)?;
|
||||
i += consumed;
|
||||
ops.push(Op::Mark);
|
||||
ops.extend(bracket_ops);
|
||||
let count_span = SourceSpan {
|
||||
start: span.start,
|
||||
end: end_span.end,
|
||||
};
|
||||
ops.push(Op::Count(Some(count_span)));
|
||||
} else if word == "]" {
|
||||
return Err("unexpected ]".into());
|
||||
} else if word == ":" {
|
||||
let (consumed, name, body) = compile_colon_def(&tokens[i + 1..], dict)?;
|
||||
i += consumed;
|
||||
@@ -211,6 +224,38 @@ fn compile_quotation(
|
||||
Ok((quote_ops, end_idx + 1, end_span))
|
||||
}
|
||||
|
||||
fn compile_bracket(
|
||||
tokens: &[Token],
|
||||
dict: &Dictionary,
|
||||
) -> Result<(Vec<Op>, usize, SourceSpan), String> {
|
||||
let mut depth = 1;
|
||||
let mut end_idx = None;
|
||||
|
||||
for (i, tok) in tokens.iter().enumerate() {
|
||||
if let Token::Word(w, _) = tok {
|
||||
match w.as_str() {
|
||||
"[" => depth += 1,
|
||||
"]" => {
|
||||
depth -= 1;
|
||||
if depth == 0 {
|
||||
end_idx = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let end_idx = end_idx.ok_or("missing ]")?;
|
||||
let end_span = match &tokens[end_idx] {
|
||||
Token::Word(_, span) => *span,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let body_ops = compile(&tokens[..end_idx], dict)?;
|
||||
Ok((body_ops, end_idx + 1, end_span))
|
||||
}
|
||||
|
||||
fn token_span(tok: &Token) -> Option<SourceSpan> {
|
||||
match tok {
|
||||
Token::Int(_, s) | Token::Float(_, s) | Token::Str(_, s) | Token::Word(_, s) => Some(*s),
|
||||
|
||||
@@ -136,4 +136,8 @@ pub enum Op {
|
||||
MidiStart,
|
||||
MidiStop,
|
||||
MidiContinue,
|
||||
// Bracket syntax (mark/count for auto-counting)
|
||||
Mark,
|
||||
Count(Option<SourceSpan>),
|
||||
Index(Option<SourceSpan>),
|
||||
}
|
||||
|
||||
@@ -140,6 +140,7 @@ impl Forth {
|
||||
var_writes: &mut HashMap<String, Value>,
|
||||
) -> Result<(), String> {
|
||||
let mut pc = 0;
|
||||
let mut marks: Vec<usize> = Vec::new();
|
||||
let trace_cell = std::cell::RefCell::new(trace);
|
||||
let var_writes_cell = std::cell::RefCell::new(Some(var_writes));
|
||||
|
||||
@@ -1541,6 +1542,29 @@ impl Forth {
|
||||
.unwrap_or(0);
|
||||
stack.push(Value::Int(val as i64, None));
|
||||
}
|
||||
Op::Mark => {
|
||||
marks.push(stack.len());
|
||||
}
|
||||
Op::Count(span) => {
|
||||
let mark = marks.pop().ok_or("count without mark")?;
|
||||
stack.push(Value::Int((stack.len() - mark) as i64, *span));
|
||||
}
|
||||
Op::Index(word_span) => {
|
||||
let idx = pop_int(stack)?;
|
||||
let count = pop_int(stack)? as usize;
|
||||
if count == 0 {
|
||||
return Err("index count must be > 0".into());
|
||||
}
|
||||
let resolved_idx = ((idx % count as i64 + count as i64) % count as i64) as usize;
|
||||
if let Some(span) = word_span {
|
||||
if stack.len() >= count {
|
||||
let start = stack.len() - count;
|
||||
let selected = &stack[start + resolved_idx];
|
||||
record_resolved_from_value(&trace_cell, Some(*span), selected);
|
||||
}
|
||||
}
|
||||
drain_select_run(count, resolved_idx, stack, outputs, cmd)?;
|
||||
}
|
||||
Op::Forget => {
|
||||
let name = pop(stack)?;
|
||||
self.dict.lock().remove(name.as_str()?);
|
||||
|
||||
@@ -110,6 +110,7 @@ pub(super) fn simple_op(name: &str) -> Option<Op> {
|
||||
"mstop" => Op::MidiStop,
|
||||
"mcont" => Op::MidiContinue,
|
||||
"forget" => Op::Forget,
|
||||
"index" => Op::Index(None),
|
||||
"key!" => Op::SetKey,
|
||||
"tp" => Op::Transpose,
|
||||
"inv" => Op::Invert,
|
||||
@@ -205,7 +206,8 @@ fn attach_span(op: &mut Op, span: SourceSpan) {
|
||||
| Op::Choose(s) | Op::WChoose(s) | Op::Cycle(s) | Op::PCycle(s)
|
||||
| Op::Bounce(s) | Op::ChanceExec(s) | Op::ProbExec(s)
|
||||
| Op::Every(s)
|
||||
| Op::Bjork(s) | Op::PBjork(s) => *s = Some(span),
|
||||
| Op::Bjork(s) | Op::PBjork(s)
|
||||
| Op::Count(s) | Op::Index(s) => *s = Some(span),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,16 @@ pub(super) const WORDS: &[Word] = &[
|
||||
compile: Simple,
|
||||
varargs: true,
|
||||
},
|
||||
Word {
|
||||
name: "index",
|
||||
aliases: &[],
|
||||
category: "Probability",
|
||||
stack: "(v1..vn n idx -- selected)",
|
||||
desc: "Select item at explicit index",
|
||||
example: "[ c4 e4 g4 ] step index",
|
||||
compile: Simple,
|
||||
varargs: true,
|
||||
},
|
||||
Word {
|
||||
name: "wchoose",
|
||||
aliases: &[],
|
||||
|
||||
Reference in New Issue
Block a user