Implement labels and goto

This commit is contained in:
Sofia 2026-03-19 17:07:20 +02:00
parent a7908fb39a
commit 0faeeae0ea
5 changed files with 54 additions and 2 deletions

View File

@ -43,6 +43,7 @@ print("hello")
for i=1,#table do
print(table[i])
if i > 2 then
break
goto test
end
end
::test::

View File

@ -264,6 +264,8 @@ pub enum Statement {
Block,
),
Break,
Label(Node<String>),
GoTo(Node<String>),
}
impl Parse for Statement {
@ -358,6 +360,18 @@ impl Parse for Statement {
} else if let Some(Token::Keyword(Keyword::Break)) = stream.peek() {
stream.next();
Ok(Self::Break)
} else if let (Some(Token::Symbol(':')), Some(Token::Symbol(':'))) =
(stream.peek(), stream.peek2())
{
stream.next();
stream.next();
let name = stream.parse()?;
stream.expect_symbol(':')?;
stream.expect_symbol(':')?;
Ok(Self::Label(name))
} else if let Some(Token::Keyword(Keyword::GoTo)) = stream.peek() {
stream.next();
Ok(Self::GoTo(stream.parse()?))
} else {
Err(stream.expecting_err("statement"))
}

View File

@ -80,14 +80,37 @@ impl LocalCounter {
pub(crate) enum PreInstr {
Instr(Instruction),
Break,
Label(String),
GoTo(String),
}
pub(crate) fn process_pre_instrs(pre_instructions: Vec<PreInstr>) -> Vec<Instruction> {
let mut instructions = Vec::new();
let mut label_positions = HashMap::new();
let mut counter: u32 = 0;
for pre_instr in &pre_instructions {
match pre_instr {
PreInstr::Label(name) => {
label_positions.insert(name.clone(), counter);
continue;
}
_ => {}
}
counter += 1;
}
for pre_instr in pre_instructions {
match pre_instr {
PreInstr::Instr(instruction) => instructions.push(instruction),
PreInstr::Break => panic!(),
PreInstr::GoTo(name) => {
instructions.push(Instruction::GoTo(*label_positions.get(&name).unwrap()))
}
PreInstr::Label(_) => {
continue;
}
}
}
@ -180,6 +203,8 @@ impl Statement {
constants
}
Statement::Break => HashSet::new(),
Statement::Label(_) => HashSet::new(),
Statement::GoTo(_) => HashSet::new(),
}
}
@ -432,6 +457,8 @@ impl Statement {
instructions.push(PreInstr::Instr(Instruction::Jmp(-(instr_len + 4))));
}
Statement::Break => instructions.push(PreInstr::Break),
Statement::Label(node) => instructions.push(PreInstr::Label(node.kind.clone())),
Statement::GoTo(node) => instructions.push(PreInstr::GoTo(node.kind.clone())),
}
for reg in 0..scope.register_counter.0 {

View File

@ -23,6 +23,7 @@ pub enum Keyword {
For,
Do,
Break,
GoTo,
}
impl Keyword {
@ -42,6 +43,7 @@ impl Keyword {
"for" => Keyword::For,
"do" => Keyword::Do,
"break" => Keyword::Break,
"goto" => Keyword::GoTo,
_ => None?,
})
}
@ -64,6 +66,7 @@ impl ToString for Keyword {
Keyword::For => "for",
Keyword::Do => "do",
Keyword::Break => "break",
Keyword::GoTo => "goto",
}
.to_string()
}

View File

@ -150,6 +150,8 @@ pub enum Instruction {
And(u16, u16, u16),
/// PC += sAx
Jmp(i32),
/// PC = Ax
GoTo(u32),
/// if (R(B) <=> C) then R(A) := R(B) else PC++
Test(u16, u16, u16),
/// if R(C) >= 0
@ -190,6 +192,7 @@ impl Debug for Instruction {
Instruction::SetList(arg0, arg1) => write!(f, "SETLIST {} {}", arg0, arg1),
Instruction::NewTable(arg0) => write!(f, "NEWTABLE {}", arg0),
Instruction::Jmp(arg0) => write!(f, "JMP {}", arg0),
Instruction::GoTo(arg0) => write!(f, "GOTO {}", arg0),
Instruction::Test(arg0, arg1, arg2) => write!(f, "TEST {} {} {}", arg0, arg1, arg2),
Instruction::ForTest(arg0, arg1, arg2) => {
write!(f, "FORTEST {} {} {}", arg0, arg1, arg2)
@ -890,6 +893,10 @@ impl ClosureRunner {
Instruction::Jmp(b) => {
self.program_counter = (self.program_counter as i32 + *b) as usize
}
Instruction::GoTo(a) => {
self.program_counter = *a as usize;
return Ok(None);
}
Instruction::Test(a, b, c) => {
let is_true = match self
.stack