Feat: UI / UX
This commit is contained in:
@@ -160,6 +160,12 @@ fn compile(tokens: &[Token], dict: &Dictionary) -> Result<Vec<Op>, String> {
|
||||
ops.push(Op::Branch(else_ops.len()));
|
||||
ops.extend(else_ops);
|
||||
}
|
||||
} else if word == "case" {
|
||||
let (case_ops, consumed) = compile_case(&tokens[i + 1..], dict)?;
|
||||
i += consumed;
|
||||
ops.extend(case_ops);
|
||||
} else if word == "of" || word == "endof" || word == "endcase" {
|
||||
return Err(format!("unexpected '{word}'"));
|
||||
} else if !compile_word(word, Some(*span), &mut ops, dict) {
|
||||
return Err(format!("unknown word: {word}"));
|
||||
}
|
||||
@@ -300,3 +306,69 @@ fn compile_if(
|
||||
|
||||
Ok((then_ops, else_ops, then_pos + 1, then_span, else_span))
|
||||
}
|
||||
|
||||
fn compile_case(tokens: &[Token], dict: &Dictionary) -> Result<(Vec<Op>, usize), String> {
|
||||
let mut depth = 1;
|
||||
let mut endcase_pos = None;
|
||||
let mut clauses: Vec<(usize, usize)> = Vec::new();
|
||||
let mut last_of = None;
|
||||
|
||||
for (i, tok) in tokens.iter().enumerate() {
|
||||
if let Token::Word(w, _) = tok {
|
||||
match w.as_str() {
|
||||
"case" => depth += 1,
|
||||
"endcase" => {
|
||||
depth -= 1;
|
||||
if depth == 0 {
|
||||
endcase_pos = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
"of" if depth == 1 => last_of = Some(i),
|
||||
"endof" if depth == 1 => {
|
||||
let of_pos = last_of.ok_or("'endof' without matching 'of'")?;
|
||||
clauses.push((of_pos, i));
|
||||
last_of = None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let endcase_pos = endcase_pos.ok_or("missing 'endcase'")?;
|
||||
|
||||
let mut ops = Vec::new();
|
||||
let mut branch_fixups: Vec<usize> = Vec::new();
|
||||
let mut clause_start = 0;
|
||||
|
||||
for &(of_pos, endof_pos) in &clauses {
|
||||
let test_ops = compile(&tokens[clause_start..of_pos], dict)?;
|
||||
let body_ops = compile(&tokens[of_pos + 1..endof_pos], dict)?;
|
||||
|
||||
ops.extend(test_ops);
|
||||
ops.push(Op::Over);
|
||||
ops.push(Op::Eq);
|
||||
ops.push(Op::BranchIfZero(body_ops.len() + 2, None, None));
|
||||
ops.push(Op::Drop);
|
||||
ops.extend(body_ops);
|
||||
branch_fixups.push(ops.len());
|
||||
ops.push(Op::Branch(0));
|
||||
|
||||
clause_start = endof_pos + 1;
|
||||
}
|
||||
|
||||
let default_tokens = &tokens[clause_start..endcase_pos];
|
||||
if !default_tokens.is_empty() {
|
||||
let default_ops = compile(default_tokens, dict)?;
|
||||
ops.extend(default_ops);
|
||||
}
|
||||
|
||||
ops.push(Op::Drop);
|
||||
|
||||
let end = ops.len();
|
||||
for pos in branch_fixups {
|
||||
ops[pos] = Op::Branch(end - pos - 1);
|
||||
}
|
||||
|
||||
Ok((ops, endcase_pos + 1))
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ pub enum Op {
|
||||
Emit,
|
||||
Get,
|
||||
Set,
|
||||
SetKeep,
|
||||
GetContext(&'static str),
|
||||
Rand(Option<SourceSpan>),
|
||||
ExpRand(Option<SourceSpan>),
|
||||
|
||||
@@ -637,6 +637,16 @@ impl Forth {
|
||||
.expect("var_writes taken")
|
||||
.insert(name, val);
|
||||
}
|
||||
Op::SetKeep => {
|
||||
let name = pop(stack)?;
|
||||
let name = name.as_str()?.to_string();
|
||||
let val = stack.last().ok_or("Stack underflow")?.clone();
|
||||
var_writes_cell
|
||||
.borrow_mut()
|
||||
.as_mut()
|
||||
.expect("var_writes taken")
|
||||
.insert(name, val);
|
||||
}
|
||||
|
||||
Op::GetContext(name) => {
|
||||
let val = match *name {
|
||||
|
||||
@@ -272,6 +272,14 @@ pub(crate) fn compile_word(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(var_name) = name.strip_prefix(',') {
|
||||
if !var_name.is_empty() {
|
||||
ops.push(Op::PushStr(Arc::from(var_name), span));
|
||||
ops.push(Op::SetKeep);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(midi) = parse_note_name(name) {
|
||||
ops.push(Op::PushInt(midi, span));
|
||||
return true;
|
||||
|
||||
@@ -558,6 +558,16 @@ pub(super) const WORDS: &[Word] = &[
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
Word {
|
||||
name: ",<var>",
|
||||
aliases: &[],
|
||||
category: "Variables",
|
||||
stack: "(val -- val)",
|
||||
desc: "Store value in variable, keep on stack",
|
||||
example: "440 ,freq => 440",
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
// Definitions
|
||||
Word {
|
||||
name: ":",
|
||||
|
||||
Reference in New Issue
Block a user