From 0faeeae0ea43b52cba4b87ba0fbb70bc5e624725 Mon Sep 17 00:00:00 2001 From: Sofia Date: Thu, 19 Mar 2026 17:07:20 +0200 Subject: [PATCH] Implement labels and goto --- examples/test.lua | 5 +++-- src/ast.rs | 14 ++++++++++++++ src/compile.rs | 27 +++++++++++++++++++++++++++ src/token_stream/lexer.rs | 3 +++ src/vm.rs | 7 +++++++ 5 files changed, 54 insertions(+), 2 deletions(-) diff --git a/examples/test.lua b/examples/test.lua index 12a744f..bce02ce 100644 --- a/examples/test.lua +++ b/examples/test.lua @@ -43,6 +43,7 @@ print("hello") for i=1,#table do print(table[i]) if i > 2 then - break + goto test end -end \ No newline at end of file +end +::test:: diff --git a/src/ast.rs b/src/ast.rs index f2fd157..4af6c5f 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -264,6 +264,8 @@ pub enum Statement { Block, ), Break, + Label(Node), + GoTo(Node), } 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")) } diff --git a/src/compile.rs b/src/compile.rs index 04055b4..0f40dd9 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -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) -> Vec { 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 { diff --git a/src/token_stream/lexer.rs b/src/token_stream/lexer.rs index de85d74..0d98ebf 100644 --- a/src/token_stream/lexer.rs +++ b/src/token_stream/lexer.rs @@ -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() } diff --git a/src/vm.rs b/src/vm.rs index 8541b1e..6dccbc9 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -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