Skip to content

Commit 4caf094

Browse files
committed
update
1 parent d9ee818 commit 4caf094

File tree

7 files changed

+316
-76
lines changed

7 files changed

+316
-76
lines changed

crates/luars/src/compiler/expr.rs

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use emmylua_parser::LuaParenExpr;
1616
use emmylua_parser::LuaTableExpr;
1717
use emmylua_parser::UnaryOperator;
1818
use emmylua_parser::{
19-
BinaryOperator, LuaBinaryExpr, LuaCallExpr, LuaExpr, LuaLiteralExpr, LuaLiteralToken,
19+
BinaryOperator, LuaAstToken, LuaBinaryExpr, LuaCallExpr, LuaExpr, LuaLiteralExpr, LuaLiteralToken,
2020
LuaNameExpr, LuaUnaryExpr, LuaVarExpr,
2121
};
2222

@@ -33,6 +33,24 @@ fn is_vararg_expr(expr: &LuaExpr) -> bool {
3333
}
3434
}
3535

36+
/// Parse a Lua integer literal from text, handling hex numbers that overflow i64
37+
/// Lua treats 0xFFFFFFFFFFFFFFFF as -1 (two's complement interpretation)
38+
fn parse_lua_int(text: &str) -> i64 {
39+
let text = text.trim();
40+
if text.starts_with("0x") || text.starts_with("0X") {
41+
// Hex number - parse as u64 first, then reinterpret as i64
42+
// This handles the case like 0xFFFFFFFFFFFFFFFF which should be -1
43+
let hex_part = &text[2..];
44+
// Remove any trailing decimal part (e.g., 0xFF.0)
45+
let hex_part = hex_part.split('.').next().unwrap_or(hex_part);
46+
if let Ok(val) = u64::from_str_radix(hex_part, 16) {
47+
return val as i64; // Reinterpret bits as signed
48+
}
49+
}
50+
// Default case: parse as i64
51+
text.parse::<i64>().unwrap_or(0)
52+
}
53+
3654
//======================================================================================
3755
// NEW API: ExpDesc-based expression compilation (Lua 5.4 compatible)
3856
//======================================================================================
@@ -102,8 +120,19 @@ fn compile_literal_expr_desc(c: &mut Compiler, expr: &LuaLiteralExpr) -> Result<
102120
}
103121
LuaLiteralToken::Nil(_) => Ok(ExpDesc::new_nil()),
104122
LuaLiteralToken::Number(num) => {
105-
if !num.is_float() {
106-
let int_val = num.get_int_value();
123+
// Get the raw text to handle hex numbers correctly
124+
let text = num.get_text();
125+
126+
// Check if this is a hex integer literal (0x... without decimal point or exponent)
127+
// emmylua_parser may incorrectly treat large hex numbers as floats
128+
let is_hex_int = (text.starts_with("0x") || text.starts_with("0X"))
129+
&& !text.contains('.')
130+
&& !text.to_lowercase().contains('p');
131+
132+
if !num.is_float() || is_hex_int {
133+
// Parse as integer - use our custom parser for hex numbers
134+
// Lua 5.4: 0xFFFFFFFFFFFFFFFF should be interpreted as -1 (two's complement)
135+
let int_val = parse_lua_int(text);
107136
// Use VKInt for integers
108137
Ok(ExpDesc::new_int(int_val))
109138
} else {
@@ -795,9 +824,18 @@ fn compile_literal_expr(
795824
emit_load_nil(c, reg);
796825
}
797826
LuaLiteralToken::Number(num) => {
827+
// Get the raw text to handle hex numbers correctly
828+
let text = num.get_text();
829+
830+
// Check if this is a hex integer literal (0x... without decimal point or exponent)
831+
// emmylua_parser may incorrectly treat large hex numbers as floats
832+
let is_hex_int = (text.starts_with("0x") || text.starts_with("0X"))
833+
&& !text.contains('.')
834+
&& !text.to_lowercase().contains('p');
835+
798836
// Lua 5.4 optimization: Try LoadI for integers, LoadF for simple floats
799-
if !num.is_float() {
800-
let int_val = num.get_int_value();
837+
if !num.is_float() || is_hex_int {
838+
let int_val = parse_lua_int(text);
801839
// Try LoadI first (fast path for small integers)
802840
if let Some(_) = emit_loadi(c, reg, int_val) {
803841
return Ok(reg);

crates/luars/src/stdlib/basic.rs

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,43 @@
55

66
use std::rc::Rc;
77

8-
use crate::lib_registry::{LibraryModule, get_arg, get_args, require_arg};
8+
use crate::lib_registry::{LibraryModule, LibraryEntry, get_arg, get_args, require_arg};
99
use crate::lua_value::{LuaValue, LuaValueKind, MultiValue};
1010
use crate::lua_vm::{LuaResult, LuaVM};
1111

1212
pub fn create_basic_lib() -> LibraryModule {
13-
crate::lib_module!("_G", {
14-
"print" => lua_print,
15-
"type" => lua_type,
16-
"assert" => lua_assert,
17-
"error" => lua_error,
18-
"tonumber" => lua_tonumber,
19-
"tostring" => lua_tostring,
20-
"select" => lua_select,
21-
"ipairs" => lua_ipairs,
22-
"pairs" => lua_pairs,
23-
"next" => lua_next,
24-
"pcall" => lua_pcall,
25-
"xpcall" => lua_xpcall,
26-
"getmetatable" => lua_getmetatable,
27-
"setmetatable" => lua_setmetatable,
28-
"rawget" => lua_rawget,
29-
"rawset" => lua_rawset,
30-
"rawlen" => lua_rawlen,
31-
"rawequal" => lua_rawequal,
32-
"collectgarbage" => lua_collectgarbage,
33-
"_VERSION" => lua_version,
34-
"require" => lua_require,
35-
"load" => lua_load,
36-
"loadfile" => lua_loadfile,
37-
"dofile" => lua_dofile,
38-
"warn" => lua_warn,
39-
})
13+
let mut module = LibraryModule::new("_G");
14+
15+
// Functions
16+
module.entries.push(("print", LibraryEntry::Function(lua_print)));
17+
module.entries.push(("type", LibraryEntry::Function(lua_type)));
18+
module.entries.push(("assert", LibraryEntry::Function(lua_assert)));
19+
module.entries.push(("error", LibraryEntry::Function(lua_error)));
20+
module.entries.push(("tonumber", LibraryEntry::Function(lua_tonumber)));
21+
module.entries.push(("tostring", LibraryEntry::Function(lua_tostring)));
22+
module.entries.push(("select", LibraryEntry::Function(lua_select)));
23+
module.entries.push(("ipairs", LibraryEntry::Function(lua_ipairs)));
24+
module.entries.push(("pairs", LibraryEntry::Function(lua_pairs)));
25+
module.entries.push(("next", LibraryEntry::Function(lua_next)));
26+
module.entries.push(("pcall", LibraryEntry::Function(lua_pcall)));
27+
module.entries.push(("xpcall", LibraryEntry::Function(lua_xpcall)));
28+
module.entries.push(("getmetatable", LibraryEntry::Function(lua_getmetatable)));
29+
module.entries.push(("setmetatable", LibraryEntry::Function(lua_setmetatable)));
30+
module.entries.push(("rawget", LibraryEntry::Function(lua_rawget)));
31+
module.entries.push(("rawset", LibraryEntry::Function(lua_rawset)));
32+
module.entries.push(("rawlen", LibraryEntry::Function(lua_rawlen)));
33+
module.entries.push(("rawequal", LibraryEntry::Function(lua_rawequal)));
34+
module.entries.push(("collectgarbage", LibraryEntry::Function(lua_collectgarbage)));
35+
module.entries.push(("require", LibraryEntry::Function(lua_require)));
36+
module.entries.push(("load", LibraryEntry::Function(lua_load)));
37+
module.entries.push(("loadfile", LibraryEntry::Function(lua_loadfile)));
38+
module.entries.push(("dofile", LibraryEntry::Function(lua_dofile)));
39+
module.entries.push(("warn", LibraryEntry::Function(lua_warn)));
40+
41+
// Values
42+
module.entries.push(("_VERSION", LibraryEntry::Value(|vm| vm.create_string("Lua 5.4"))));
43+
44+
module
4045
}
4146

4247
/// print(...) - Print values to stdout
@@ -694,12 +699,6 @@ fn lua_collectgarbage(vm: &mut LuaVM) -> LuaResult<MultiValue> {
694699
}
695700
}
696701

697-
/// _VERSION - Lua version string
698-
fn lua_version(vm: &mut LuaVM) -> LuaResult<MultiValue> {
699-
let version = vm.create_string("Lua 5.4");
700-
Ok(MultiValue::single(version))
701-
}
702-
703702
/// require(modname) - Load a module
704703
fn lua_require(vm: &mut LuaVM) -> LuaResult<MultiValue> {
705704
let modname_value = require_arg(vm, 1, "require")?;

crates/luars/src/stdlib/io/file.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::lib_registry::{get_arg, get_args, require_arg};
99
use crate::lua_value::{LuaValue, LuaValueKind, MultiValue};
1010
use crate::lua_vm::LuaResult;
1111

12-
/// File handle wrapper
12+
/// File handle wrapper - supports files and standard streams
1313
pub struct LuaFile {
1414
inner: FileInner,
1515
}
@@ -18,10 +18,28 @@ enum FileInner {
1818
Read(BufReader<File>),
1919
Write(BufWriter<File>),
2020
ReadWrite(File),
21+
Stdin,
22+
Stdout,
23+
Stderr,
2124
Closed,
2225
}
2326

2427
impl LuaFile {
28+
/// Create stdin handle
29+
pub fn stdin() -> Self {
30+
LuaFile { inner: FileInner::Stdin }
31+
}
32+
33+
/// Create stdout handle
34+
pub fn stdout() -> Self {
35+
LuaFile { inner: FileInner::Stdout }
36+
}
37+
38+
/// Create stderr handle
39+
pub fn stderr() -> Self {
40+
LuaFile { inner: FileInner::Stderr }
41+
}
42+
2543
pub fn open_read(path: &str) -> io::Result<Self> {
2644
let file = File::open(path)?;
2745
Ok(LuaFile {
@@ -69,6 +87,11 @@ impl LuaFile {
6987
pub fn is_closed(&self) -> bool {
7088
matches!(self.inner, FileInner::Closed)
7189
}
90+
91+
/// Check if this is a standard stream (stdin/stdout/stderr)
92+
pub fn is_std_stream(&self) -> bool {
93+
matches!(self.inner, FileInner::Stdin | FileInner::Stdout | FileInner::Stderr)
94+
}
7295

7396
/// Read operations
7497
pub fn read_line(&mut self) -> io::Result<Option<String>> {
@@ -104,6 +127,21 @@ impl LuaFile {
104127
Ok(Some(line))
105128
}
106129
}
130+
FileInner::Stdin => {
131+
let stdin = io::stdin();
132+
let n = stdin.lock().read_line(&mut line)?;
133+
if n == 0 {
134+
Ok(None)
135+
} else {
136+
if line.ends_with('\n') {
137+
line.pop();
138+
if line.ends_with('\r') {
139+
line.pop();
140+
}
141+
}
142+
Ok(Some(line))
143+
}
144+
}
107145
_ => Err(io::Error::new(
108146
io::ErrorKind::Other,
109147
"File not opened for reading",
@@ -122,6 +160,10 @@ impl LuaFile {
122160
file.read_to_string(&mut content)?;
123161
Ok(content)
124162
}
163+
FileInner::Stdin => {
164+
io::stdin().lock().read_to_string(&mut content)?;
165+
Ok(content)
166+
}
125167
_ => Err(io::Error::new(
126168
io::ErrorKind::Other,
127169
"File not opened for reading",
@@ -142,6 +184,11 @@ impl LuaFile {
142184
buffer.truncate(bytes_read);
143185
Ok(buffer)
144186
}
187+
FileInner::Stdin => {
188+
let bytes_read = io::stdin().lock().read(&mut buffer)?;
189+
buffer.truncate(bytes_read);
190+
Ok(buffer)
191+
}
145192
_ => Err(io::Error::new(
146193
io::ErrorKind::Other,
147194
"File not opened for reading",
@@ -160,6 +207,14 @@ impl LuaFile {
160207
file.write_all(data.as_bytes())?;
161208
Ok(())
162209
}
210+
FileInner::Stdout => {
211+
io::stdout().write_all(data.as_bytes())?;
212+
Ok(())
213+
}
214+
FileInner::Stderr => {
215+
io::stderr().write_all(data.as_bytes())?;
216+
Ok(())
217+
}
163218
_ => Err(io::Error::new(
164219
io::ErrorKind::Other,
165220
"File not opened for writing",
@@ -171,6 +226,8 @@ impl LuaFile {
171226
match &mut self.inner {
172227
FileInner::Write(writer) => writer.flush(),
173228
FileInner::ReadWrite(file) => file.flush(),
229+
FileInner::Stdout => io::stdout().flush(),
230+
FileInner::Stderr => io::stderr().flush(),
174231
_ => Ok(()),
175232
}
176233
}
@@ -491,6 +548,9 @@ fn file_seek(vm: &mut LuaVM) -> LuaResult<MultiValue> {
491548
FileInner::Closed => {
492549
return Err(vm.error("file is closed".to_string()));
493550
}
551+
FileInner::Stdin | FileInner::Stdout | FileInner::Stderr => {
552+
return Err(vm.error("cannot seek on standard stream".to_string()));
553+
}
494554
};
495555

496556
match pos {

0 commit comments

Comments
 (0)