Add table length

This commit is contained in:
Sofia 2026-03-18 00:03:13 +02:00
parent 9e9106991e
commit 73e33444c9
5 changed files with 66 additions and 5 deletions

View File

@ -22,6 +22,7 @@ end
global sometable = {}
sometable["hello"] = { add(10)(15) }
print(#sometable["hello"])
sometable["hello"].there = "my dude"
print(sometable.hello.there)

BIN
luac.out

Binary file not shown.

View File

@ -372,6 +372,7 @@ pub struct PrimaryExpression(Node<Expression>);
#[derive(Debug, Clone, Copy)]
pub enum UnaryOperator {
Negation,
Length,
}
impl Parse for UnaryOperator {
@ -379,6 +380,7 @@ impl Parse for UnaryOperator {
if let Some(token) = stream.next() {
match token {
Token::Symbol('-') => Ok(UnaryOperator::Negation),
Token::Symbol('#') => Ok(UnaryOperator::Length),
_ => Err(stream.expected_err("unop")),
}
} else {
@ -530,7 +532,6 @@ impl Parse for PrimaryExpression {
Token::Symbol('[') | Token::Symbol('.') => {
let meta = Metadata::produce(&mut stream, pre.clone());
let IndexedAccess(accesses) = stream.parse()?;
dbg!(&accesses, stream.peek());
for access in accesses {
expression = Expression::IndexedAccess(
Box::new(Node {

View File

@ -525,6 +525,7 @@ impl Expression {
for reg in &registers {
match op {
UnaryOperator::Negation => instructions.push(Instruction::Unm(*reg, *reg)),
UnaryOperator::Length => instructions.push(Instruction::Len(*reg, *reg)),
}
}
(instructions, registers)

View File

@ -7,7 +7,7 @@ use crate::{
ast::{BinaryOperator, UnaryOperator},
};
#[derive(Clone, Hash, PartialEq, Eq, Copy)]
#[derive(Clone, Hash, PartialEq, Eq, Copy, PartialOrd, Ord)]
pub struct VMFloat(u64);
impl VMFloat {
@ -37,7 +37,7 @@ impl Debug for LuaFloat {
}
}
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct LuaInteger(pub i64);
impl Debug for LuaInteger {
@ -58,7 +58,7 @@ impl From<&LuaInteger> for LuaFloat {
}
}
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct LuaBool(pub bool);
impl Debug for LuaBool {
@ -134,6 +134,8 @@ pub enum Instruction {
Add(u16, u16, u16),
/// R(A) := -R(B)
Unm(u16, u16),
/// R(A) := #R(B) (length operator)
Len(u16, u16),
/// R(A) := R(B) == R(C)
Equal(u16, u16, u16),
/// R(A) := R(B) < R(C)
@ -190,6 +192,7 @@ impl Debug for Instruction {
Instruction::Add(arg0, arg1, arg2) => write!(f, "ADD {} {} {}", arg0, arg1, arg2),
Instruction::LoadNil(arg0, arg1) => write!(f, "LOADNIL {} {}", arg0, arg1),
Instruction::Unm(arg0, arg1) => write!(f, "UNM {} {}", arg0, arg1),
Instruction::Len(arg0, arg1) => write!(f, "LEN {} {}", arg0, arg1),
Instruction::Or(arg0, arg1, arg2) => write!(f, "OR {} {} {}", arg0, arg1, arg2),
Instruction::And(arg0, arg1, arg2) => write!(f, "AND {} {} {}", arg0, arg1, arg2),
}
@ -212,6 +215,8 @@ pub enum RuntimeError {
NotTable(Value),
#[error("Value is not coercable to a float: {0:?}")]
NotFloatable(Value),
#[error("Value does not have a length: {0:?}")]
NotLengthable(Value),
#[error("{0}")]
Custom(String),
}
@ -292,7 +297,7 @@ impl Value {
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Ord, PartialOrd)]
pub enum IndexableValue {
String(String),
Float(VMFloat),
@ -396,6 +401,47 @@ impl Value {
}
}
pub fn len(&self) -> Result<Value, RuntimeError> {
match self {
Value::String(value) => Ok(Self::Integer(LuaInteger(value.len() as i64))),
Value::Table(table) => {
let table = table.borrow();
let mut int_indexes = table
.keys()
.filter(|v| match v {
IndexableValue::Integer(_) => true,
_ => false,
})
.map(|v| match v {
IndexableValue::Integer(int) => int.0,
_ => panic!(),
})
.collect::<Vec<_>>();
int_indexes.push(0);
int_indexes.sort();
for idx in int_indexes {
let idx_value = table.get(&IndexableValue::Integer(LuaInteger(idx)));
let idx_value_after = idx
.checked_add_unsigned(1)
.and_then(|i| table.get(&IndexableValue::Integer(LuaInteger(i))));
// Directly from https://www.lua.org/manual/5.5/manual.html#3.4.7
if (idx == 0 || idx_value.is_some()) && idx_value_after.is_none() {
return Ok(Self::Integer(LuaInteger(idx)));
}
}
panic!()
}
Value::Float(_) => Err(RuntimeError::NotLengthable(self.clone())),
Value::Integer(_) => Err(RuntimeError::NotLengthable(self.clone())),
Value::Boolean(_) => Err(RuntimeError::NotLengthable(self.clone())),
Value::RustFunction(_) => Err(RuntimeError::NotLengthable(self.clone())),
Value::Function(_) => Err(RuntimeError::NotLengthable(self.clone())),
Value::Nil => Err(RuntimeError::NotLengthable(self.clone())),
}
}
pub fn and(&self, other: &Value) -> Result<Value, RuntimeError> {
if self.is_truthy() {
Ok(self.clone())
@ -986,6 +1032,18 @@ impl ClosureRunner {
),
);
}
Instruction::Len(res, reg) => {
self.set_stack(
*res,
StackValue::Value(
self.stack
.get(reg)
.map(|v| v.borrow().clone())
.unwrap_or(Value::Nil)
.len()?,
),
);
}
Instruction::Or(res, lhs, rhs) => {
let lhs = self
.stack