From 9e9106991e038deadf312da16f59c0c41fdfcceb Mon Sep 17 00:00:00 2001 From: Sofia Date: Tue, 17 Mar 2026 23:39:15 +0200 Subject: [PATCH] Implement constructors with one function call inside --- examples/test.lua | 6 ++++- luac.out | Bin 0 -> 187 bytes src/ast.rs | 48 ++++++++++++++++++++++++++++++++++++-- src/compile.rs | 57 ++++++++++++++++++++++++++++++++++++++++++---- src/vm.rs | 31 +++++++++++++++++++++++-- 5 files changed, 133 insertions(+), 9 deletions(-) create mode 100644 luac.out diff --git a/examples/test.lua b/examples/test.lua index 368bf4f..84e21ee 100644 --- a/examples/test.lua +++ b/examples/test.lua @@ -16,8 +16,12 @@ function min(x, y) return m end +function f(x) + return x + 5 +end + global sometable = {} -sometable["hello"] = {} +sometable["hello"] = { add(10)(15) } sometable["hello"].there = "my dude" print(sometable.hello.there) diff --git a/luac.out b/luac.out new file mode 100644 index 0000000000000000000000000000000000000000..7b9b0ba2bd9fb5f7f4f16ac5f9184324bd396a04 GIT binary patch literal 187 zcmYLvR_YH}=`W>, Block), FunctionCall(Box>, Node), Literal(Literal), - TableConstructor, + TableConstructor(Vec<(Option>, Node)>), IndexedAccess(Box>, Box>), } @@ -495,8 +495,19 @@ impl Parse for PrimaryExpression { Expression::Literal(Literal::Nil) } else if let Some(Token::Symbol('{')) = peeked { stream.next(); + + let mut entries = Vec::new(); + if let Ok(entry) = stream.parse::() { + entries.push((entry.0, entry.1)); + while let Some(Token::Symbol(',') | Token::Symbol(';')) = stream.peek() { + stream.next(); + let entry = stream.parse::()?; + entries.push((entry.0, entry.1)) + } + } + stream.expect_symbol('}')?; - Expression::TableConstructor + Expression::TableConstructor(entries) } else { Expression::ValueRef(stream.parse()?) }; @@ -588,6 +599,39 @@ fn parse_binop_rhs( Ok(lhs.0) } +#[derive(Debug, Clone)] +struct TableConstructorEntry(Option>, Node); + +impl Parse for TableConstructorEntry { + fn parse(mut stream: TokenStream) -> Result { + if let Some(Token::Symbol('[')) = stream.peek() { + stream.next(); + let key = stream.parse()?; + stream.expect_symbol(']')?; + stream.expect_symbol('=')?; + let value = stream.parse()?; + Ok(TableConstructorEntry(Some(key), value)) + } else { + if let (Some(Token::Word(_)), Some(Token::Symbol('='))) = + (stream.peek(), stream.peek2()) + { + let key = stream.parse::>()?; + let key = key.with(Expression::Literal(Literal::String(key.kind.clone()))); + if let Some(Token::Symbol('=')) = stream.peek() { + stream.next(); + let value = stream.parse()?; + Ok(TableConstructorEntry(Some(key), value)) + } else { + Ok(TableConstructorEntry(None, key)) + } + } else { + let value = stream.parse()?; + Ok(TableConstructorEntry(None, value)) + } + } + } +} + #[derive(Debug, Clone)] pub enum Literal { Float(LuaFloat), diff --git a/src/compile.rs b/src/compile.rs index 4348191..0949960 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet}; use crate::{ ast::{AccessModifier, BinaryOperator, Block, Expression, Literal, Statement, UnaryOperator}, - vm::{Constant, Instruction, LuaBool}, + vm::{Constant, Instruction, LuaBool, LuaInteger}, }; #[derive(Clone, Debug)] @@ -401,8 +401,18 @@ impl Expression { constants } }, - Expression::TableConstructor => { - let constants = HashSet::new(); + Expression::TableConstructor(entries) => { + let mut constants = HashSet::new(); + let mut counter = 1; + for (key, value) in entries { + if let Some(key) = key { + constants.extend(key.kind.find_constants(scope)); + } else { + constants.insert(Constant::Integer(LuaInteger(counter))); + counter += 1; + } + constants.extend(value.kind.find_constants(scope)); + } constants } Expression::IndexedAccess(expr, index) => { @@ -630,10 +640,49 @@ impl Expression { )); (instructions, vec![reg]) } - Expression::TableConstructor => { + Expression::TableConstructor(entries) => { let mut instructions = Vec::new(); let reg = scope.register_counter.next(); instructions.push(Instruction::NewTable(reg)); + + let mut counter = 1; + for (key, value) in entries { + if let Some(key) = key { + let (instr, key_regs) = key.kind.compile(state, scope, Some(1)); + instructions.extend(instr); + let (instr, value_regs) = value.kind.compile(state, scope, Some(1)); + instructions.extend(instr); + instructions.push(Instruction::SetTable( + reg, + *key_regs.first().unwrap(), + *value_regs.first().unwrap(), + )); + } else { + let (instr, value_regs) = value.kind.compile( + state, + scope, + if entries.len() == 1 { None } else { Some(1) }, + ); + instructions.extend(instr); + + if value_regs.len() > 0 { + let key_reg = scope.register_counter.next(); + instructions.push(Instruction::LoadK( + key_reg, + state.get_constant(&Constant::Integer(LuaInteger(counter))), + )); + instructions.push(Instruction::SetTable( + reg, + key_reg, + *value_regs.first().unwrap(), + )); + counter += 1; + } else { + instructions.push(Instruction::SetList(reg)); + } + } + } + (instructions, vec![reg]) } Expression::IndexedAccess(expr, index) => { diff --git a/src/vm.rs b/src/vm.rs index 3706173..02954c8 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -124,6 +124,8 @@ pub enum Instruction { SetUpVal(u16, u16), /// R(A)[R(B)] := R(C) SetTable(u16, u16, u16), + /// R(A) := all values returned from previous function + SetList(u16), /// R(A) := R(B)[R(C)] GetTable(u16, u16, u16), /// R(A) := {} @@ -172,6 +174,7 @@ impl Debug for Instruction { Instruction::GetTable(arg0, arg1, arg2) => { write!(f, "GETTABLE {} {} {}", arg0, arg1, arg2) } + Instruction::SetList(arg0) => write!(f, "SETLIST {}", arg0), Instruction::NewTable(arg0) => write!(f, "NEWTABLE {}", arg0), Instruction::Jmp(arg0) => write!(f, "JMP {}", arg0), Instruction::Test(arg0, arg1, arg2) => write!(f, "TEST {} {} {}", arg0, arg1, arg2), @@ -259,7 +262,7 @@ impl Value { pub fn as_indexable(self) -> Result { match self { Value::String(value) => Ok(IndexableValue::String(value)), - Value::Float(value) => Ok(IndexableValue::Number(value)), + Value::Float(value) => Ok(IndexableValue::Float(value)), Value::Integer(value) => Ok(IndexableValue::Integer(value)), Value::Boolean(value) => Ok(IndexableValue::Bool(value)), Value::RustFunction(value) => { @@ -292,7 +295,7 @@ impl Value { #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub enum IndexableValue { String(String), - Number(VMFloat), + Float(VMFloat), Integer(LuaInteger), Bool(LuaBool), RustFunction(String), @@ -768,6 +771,30 @@ impl ClosureRunner { self.set_stack(*res, value); } + Instruction::SetList(reg) => { + let table = self.stack.get(reg).cloned(); + match table { + Some(value) => { + let mut table = value.borrow_mut(); + if let Value::Table(table) = &mut *table { + let mut counter = 1; + for i in self.function_register..self.top { + let value = self.get_stack(i + 1); + match value { + StackValue::Value(value) => table.borrow_mut().insert( + IndexableValue::Integer(LuaInteger(counter)), + value.clone(), + ), + }; + counter += 1; + } + } else { + return Err(RuntimeError::NotTable(table.clone())); + } + } + None => todo!(), + } + } Instruction::NewTable(reg) => { self.set_stack( *reg,