Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 18 additions & 28 deletions src/compile/c_gen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3455,16 +3455,15 @@ std::string CGen::TryCompileBuiltinMathCall(const std::shared_ptr<SyntaxTreeFunc
}
if (method_name == "floor" && raw_args.size() == 1) {
std::string arg = CompileExp(raw_args[0]);
Out() << GenTab() << "if (" << arg << ".type_ == VAR_INT) { " << tmp << " = " << arg << "; } ";
Out() << "else if (" << arg << ".type_ == VAR_FLOAT) { " << tmp << " = (CVar){.type_ = VAR_FLOAT, .data_.f = floor(" << arg << ".data_.f)}; } ";
Out() << "else { " << tmp << " = FakeluaCallByName(_S, 0, \"math.floor\", 1, " << arg << "); }\n";
// 数值走共享的 FlMathFloor(pushnumint 规则);数字串等其它类型回退 native
Out() << GenTab() << tmp << " = ((" << arg << ".type_ == VAR_INT || " << arg << ".type_ == VAR_FLOAT) ? FlMathFloor(" << arg
<< ") : FakeluaCallByName(_S, 0, \"math.floor\", 1, " << arg << "));\n";
return tmp;
}
if (method_name == "ceil" && raw_args.size() == 1) {
std::string arg = CompileExp(raw_args[0]);
Out() << GenTab() << "if (" << arg << ".type_ == VAR_INT) { " << tmp << " = " << arg << "; } ";
Out() << "else if (" << arg << ".type_ == VAR_FLOAT) { " << tmp << " = (CVar){.type_ = VAR_FLOAT, .data_.f = ceil(" << arg << ".data_.f)}; } ";
Out() << "else { " << tmp << " = FakeluaCallByName(_S, 0, \"math.ceil\", 1, " << arg << "); }\n";
Out() << GenTab() << tmp << " = ((" << arg << ".type_ == VAR_INT || " << arg << ".type_ == VAR_FLOAT) ? FlMathCeil(" << arg
<< ") : FakeluaCallByName(_S, 0, \"math.ceil\", 1, " << arg << "));\n";
return tmp;
}
if (method_name == "max" && raw_args.size() == 2) {
Expand Down Expand Up @@ -3573,19 +3572,14 @@ std::string CGen::TryCompileBuiltinMathCall(const std::shared_ptr<SyntaxTreeFunc
std::string arg2 = CompileExp(raw_args[1]);
const auto a1_tmp = std::format("flua_fmod_a_{}", tmp_var_counter_++);
const auto a2_tmp = std::format("flua_fmod_b_{}", tmp_var_counter_++);
const auto val1_tmp = std::format("flua_val_{}", tmp_var_counter_++);
const auto val2_tmp = std::format("flua_val_{}", tmp_var_counter_++);
func_temp_decls_ << " CVar " << a1_tmp << ";\n";
func_temp_decls_ << " CVar " << a2_tmp << ";\n";
func_temp_decls_ << " double " << val1_tmp << ";\n";
func_temp_decls_ << " double " << val2_tmp << ";\n";
Out() << GenTab() << a1_tmp << " = " << arg1 << ";\n";
Out() << GenTab() << a2_tmp << " = " << arg2 << ";\n";
Out() << GenTab() << "if (LIKELY((" << a1_tmp << ".type_ == VAR_INT || " << a1_tmp << ".type_ == VAR_FLOAT) && " << "(" << a2_tmp << ".type_ == VAR_INT || " << a2_tmp
<< ".type_ == VAR_FLOAT))) {\n";
Out() << GenTab() << " " << val1_tmp << " = (" << a1_tmp << ".type_ == VAR_INT ? (double)" << a1_tmp << ".data_.i : " << a1_tmp << ".data_.f);\n";
Out() << GenTab() << " " << val2_tmp << " = (" << a2_tmp << ".type_ == VAR_INT ? (double)" << a2_tmp << ".data_.i : " << a2_tmp << ".data_.f);\n";
Out() << GenTab() << " " << tmp << " = (CVar){.type_ = VAR_FLOAT, .data_.f = fmod(" << val1_tmp << ", " << val2_tmp << ")};\n";
// 整数对/含浮点的区分与除零报错全部在 FlMathFmod 内,与 native 同一实现
Out() << GenTab() << "if (LIKELY((" << a1_tmp << ".type_ == VAR_INT || " << a1_tmp << ".type_ == VAR_FLOAT) && "
<< "(" << a2_tmp << ".type_ == VAR_INT || " << a2_tmp << ".type_ == VAR_FLOAT))) {\n";
Out() << GenTab() << " " << tmp << " = FlMathFmod(" << a1_tmp << ", " << a2_tmp << ");\n";
Out() << GenTab() << "} else {\n";
Out() << GenTab() << " " << tmp << " = FakeluaCallByName(_S, FAKELUA_JIT_TYPE, \"math.fmod\", 2, " << a1_tmp << ", " << a2_tmp << ");\n";
Out() << GenTab() << "}\n";
Expand Down Expand Up @@ -3765,20 +3759,10 @@ std::string CGen::TryCompileBuiltinMathCall(const std::shared_ptr<SyntaxTreeFunc
if (method_name == "modf" && raw_args.size() == 1) {
std::string arg = CompileExp(raw_args[0]);
const auto arg_tmp = std::format("flua_modf_a_{}", tmp_var_counter_++);
const auto iptr_tmp = std::format("flua_iptr_{}", tmp_var_counter_++);
const auto frac_tmp = std::format("flua_frac_{}", tmp_var_counter_++);
const auto val_tmp = std::format("flua_val_{}", tmp_var_counter_++);
func_temp_decls_ << " CVar " << arg_tmp << ";\n";
func_temp_decls_ << " double " << iptr_tmp << ";\n";
func_temp_decls_ << " double " << frac_tmp << ";\n";
func_temp_decls_ << " double " << val_tmp << ";\n";
Out() << GenTab() << arg_tmp << " = " << arg << ";\n";
Out() << GenTab() << "if (LIKELY(" << arg_tmp << ".type_ == VAR_INT || " << arg_tmp << ".type_ == VAR_FLOAT)) {\n";
Out() << GenTab() << " if (" << arg_tmp << ".type_ == VAR_INT) { " << iptr_tmp << " = (double)" << arg_tmp << ".data_.i; " << frac_tmp << " = 0.0; } else { " << val_tmp << " = " << arg_tmp
<< ".data_.f; " << frac_tmp << " = modf(" << val_tmp << ", &" << iptr_tmp << "); }\n";
Out() << GenTab() << " " << tmp << " = FlAllocMulti(_S, 2);\n";
Out() << GenTab() << " " << tmp << ".data_.m->vars[0] = (CVar){.type_ = VAR_FLOAT, .data_.f = " << iptr_tmp << "};\n";
Out() << GenTab() << " " << tmp << ".data_.m->vars[1] = (CVar){.type_ = VAR_FLOAT, .data_.f = " << frac_tmp << "};\n";
Out() << GenTab() << " " << tmp << " = FlMathModf(" << arg_tmp << ");\n";
Out() << GenTab() << "} else {\n";
Out() << GenTab() << " " << tmp << " = FakeluaCallByName(_S, FAKELUA_JIT_TYPE, \"math.modf\", 1, " << arg_tmp << ");\n";
Out() << GenTab() << "}\n";
Expand Down Expand Up @@ -3961,7 +3945,7 @@ std::string CGen::TryCompileBuiltinTableCall(const std::shared_ptr<SyntaxTreeFun
Out() << GenTab() << pos_c << " = " << pos_arg << ";\n";
Out() << GenTab() << val_tmp << " = " << val_arg << ";\n";
// 2.0 不是 INT:以前默认 pos=1,会插错位置。
// pos 必须在 [1, len+1]:越界不得 FlSetTableInt 挖洞;
// pos 必须在 [1, len+1]:越界必须按 Lua 5.4 报错(不得静默、不得挖洞);
// mininteger 若走进 for (idx >= pos; idx--) 会在 idx 下溢后死循环。
Out() << GenTab() << "if (LIKELY(" << pos_c << ".type_ == VAR_INT)) {\n";
Out() << GenTab() << " FlLenInt(" << tbl_tmp << ", " << len_tmp << ");\n";
Expand All @@ -3972,6 +3956,8 @@ std::string CGen::TryCompileBuiltinTableCall(const std::shared_ptr<SyntaxTreeFun
Out() << GenTab() << " FlSetTableInt(" << tbl_tmp << ", " << idx_tmp << " + 1, " << item_tmp << ");\n";
Out() << GenTab() << " }\n";
Out() << GenTab() << " FlSetTableInt(" << tbl_tmp << ", " << pos_tmp << ", " << val_tmp << ");\n";
Out() << GenTab() << " } else {\n";
Out() << GenTab() << " FakeluaThrowError(_S, \"bad argument #2 to 'insert' (position out of bounds)\");\n";
Out() << GenTab() << " }\n";
Out() << GenTab() << " " << tmp << " = kNil;\n";
Out() << GenTab() << "} else {\n";
Expand All @@ -3998,10 +3984,14 @@ std::string CGen::TryCompileBuiltinTableCall(const std::shared_ptr<SyntaxTreeFun
func_temp_decls_ << " CVar " << item_tmp << ";\n";
Out() << GenTab() << tbl_tmp << " = " << tbl_arg << ";\n";
Out() << GenTab() << pos_c << " = " << pos_arg << ";\n";
// 省略 pos 或 INT 走内联;2.0 以前会当成 #t,删错元素。
// 省略 pos 或显式 INT 走内联;2.0 以前会当成 #t,删错元素。
Out() << GenTab() << "if (LIKELY(" << pos_c << ".type_ == VAR_NIL || " << pos_c << ".type_ == VAR_INT)) {\n";
Out() << GenTab() << " FlLenInt(" << tbl_tmp << ", " << len_tmp << ");\n";
Out() << GenTab() << " " << pos_tmp << " = (" << pos_c << ".type_ == VAR_INT) ? " << pos_c << ".data_.i : " << len_tmp << ";\n";
// Lua:省略/nil 位置(即 #t,含空表)不做边界校验;只有显式 INT 才校验 [1, len+1]
Out() << GenTab() << " if (" << pos_c << ".type_ == VAR_INT && (" << pos_tmp << " < 1 || " << pos_tmp << " > " << len_tmp << " + 1)) {\n";
Out() << GenTab() << " FakeluaThrowError(_S, \"bad argument #2 to 'remove' (position out of bounds)\");\n";
Out() << GenTab() << " }\n";
Out() << GenTab() << " if (" << pos_tmp << " >= 1 && " << pos_tmp << " <= " << len_tmp << ") {\n";
Out() << GenTab() << " " << tmp << " = FlGetTableInt(" << tbl_tmp << ", " << pos_tmp << ");\n";
Out() << GenTab() << " for (" << idx_tmp << " = " << pos_tmp << "; " << idx_tmp << " < " << len_tmp << "; " << idx_tmp << "++) {\n";
Expand Down
66 changes: 66 additions & 0 deletions src/compile/c_runtime_header.h
Original file line number Diff line number Diff line change
Expand Up @@ -1616,6 +1616,72 @@ static inline CVar FlTableMove(CVar src, int64_t f, int64_t e, int64_t t, CVar d
(result) = __fmf_a - __fmf_b * floor(__fmf_a / __fmf_b); \
} while(0)

// Lua 5.4 pushnumint:d 能无损落进 int64 时返回 integer,否则保留 float
// (inf/-inf/nan/超出 [-2^63, 2^63) 均不报错,仍是 float)。
static inline CVar FlPushNumInt(double d) {
CVar r;
if (isfinite(d) && d >= -9223372036854775808.0 && d < 9223372036854775808.0) {
r.type_ = VAR_INT;
r.data_.i = (int64_t)d;
} else {
r.type_ = VAR_FLOAT;
r.data_.f = d;
}
return r;
}

static inline CVar FlMathFloor(CVar v) {
if (v.type_ == VAR_INT) return v;
return FlPushNumInt(floor((double)v.data_.f));
}

static inline CVar FlMathCeil(CVar v) {
if (v.type_ == VAR_INT) return v;
return FlPushNumInt(ceil((double)v.data_.f));
}

// Lua 5.4 math.fmod:两整数走整数取模(0 报错、-1 特判为 0 避开溢出),
// 任一浮点恒返回 float(浮点除零是 NaN,不报错)。
static inline CVar FlMathFmod(CVar a, CVar b) {
CVar r;
if (a.type_ == VAR_INT && b.type_ == VAR_INT) {
int64_t d = b.data_.i;
if (d == 0) {
// 错误串结尾不能紧贴右圆括号:本头文件整体以 R 圆括号原始串内嵌进 c_gen,
// 右圆括号紧跟双引号会提前终止原始串。
FakeluaThrowError(_S, "bad argument #2 to 'fmod': zero divisor");
}
r.type_ = VAR_INT;
r.data_.i = (d == -1) ? 0 : a.data_.i % d;
return r;
}
r.type_ = VAR_FLOAT;
r.data_.f = fmod(CVAR_TO_DOUBLE(a), CVAR_TO_DOUBLE(b));
return r;
}

// Lua 5.4 math.modf:整数部分向零取整并按 pushnumint 决定 int/float,
// 无小数部分(整数值或 inf)时小数部分为 +0.0。
static inline CVar FlMathModf(CVar v) {
double n;
double ip;
double fp;
CVar r;
r = FlAllocMulti(_S, 2);
if (v.type_ == VAR_INT) {
r.data_.m->vars[0] = v;
r.data_.m->vars[1] = (CVar){.type_ = VAR_FLOAT, .data_.f = 0.0};
return r;
}
n = (double)v.data_.f;
ip = (n < 0) ? ceil(n) : floor(n);
fp = (n == ip) ? 0.0 : n - ip;
r.data_.m->vars[0] = FlPushNumInt(ip);
r.data_.m->vars[1] = (CVar){.type_ = VAR_FLOAT, .data_.f = fp};
return r;
}


// 整数 for 步进:用无符号加法探测有符号溢出。溢出返回 0 且不改 *ctrl
//(避免 maxinteger++ 变成 mininteger 后死循环)。continue 仍走 for 的 incr 子句。
static inline int FlForIntAdvance(int64_t *ctrl, int64_t step) {
Expand Down
42 changes: 39 additions & 3 deletions src/native/basic/native_basic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,37 @@

namespace fakelua::basic {

// Lua 5.4 l_str2d 规则:
// 1) 含 n/N 的串(inf/nan/infinity 等)一律拒绝——strtod/from_chars 本来
// 会接受它们,但 Lua 只接受显式构造的 math.huge/0/0,不接受字面量;
// 2) ERANGE 不算失败:1e999 解析成 inf、1e-999 解析成 0,与 Lua 一致。
static bool LuaStrToFloat(std::string_view s, double &out) {
for (char c: s) {
if (c == 'n' || c == 'N') {
return false;
}
}
double dval = 0.0;
auto r = boost::charconv::from_chars(s.data(), s.data() + s.size(), dval);
if (r.ptr != s.data() + s.size()) {
return false;
}
if (r.ec == std::errc::result_out_of_range) {
// boost 溢出时不保证写回饱和值;strtod 按 C 标准写 ±inf/0.0
std::string buf(s);
char *endptr = nullptr;
errno = 0;
dval = std::strtod(buf.c_str(), &endptr);
if (endptr != buf.c_str() + buf.size()) {
return false;
}
} else if (r.ec != std::errc{}) {
return false;
}
out = dval;
return true;
}

using string::GetStringArgView;
using table::TableHelper;

Expand Down Expand Up @@ -295,10 +326,16 @@ void RegisterBasicLibraryApi(State *s) {
};
auto try_lua_float = [&]() -> CVar {
const std::string buf(trimmed);
// 与十进制路径同一规则:拒绝 inf/nan,接受 ERANGE 溢出(±inf)
for (char c: buf) {
if (c == 'n' || c == 'N') {
return inter::NativeToFakeluaNil(state);
}
}
char *endptr = nullptr;
errno = 0;
const double dval = std::strtod(buf.c_str(), &endptr);
if (endptr == buf.c_str() + buf.size() && errno != ERANGE && std::isfinite(dval)) {
if (endptr == buf.c_str() + buf.size()) {
return inter::NativeToFakeluaDouble(state, dval);
}
return inter::NativeToFakeluaNil(state);
Expand All @@ -319,8 +356,7 @@ void RegisterBasicLibraryApi(State *s) {
return inter::NativeToFakeluaInt(state, ival);
}
double dval = 0;
auto fr = boost::charconv::from_chars(s_view.data(), s_view.data() + s_view.size(), dval);
if (fr.ec == std::errc{} && fr.ptr == s_view.data() + s_view.size()) {
if (LuaStrToFloat(s_view, dval)) {
return inter::NativeToFakeluaDouble(state, dval);
}
return inter::NativeToFakeluaNil(state);
Expand Down
42 changes: 30 additions & 12 deletions src/native/math/native_math.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "native/math/native_math.h"
#include "native/native_common.h"
#include "util/number_util.h"
#include "var/var.h"
#include <algorithm>
#include <cmath>
Expand All @@ -8,6 +9,15 @@

namespace fakelua::math {

// Lua 5.4 pushnumint:floor/ceil/modf 的整数部分若能无损落进 int64 就返回
// integer,否则保留 float(inf/-inf/nan/超出 2^63 时不报错,仍是 float)。
static CVar PushNumInt(State *state, double d) {
if (TryConvertDoubleToInt64(d).has_value()) {
return inter::NativeToFakeluaInt(state, static_cast<int64_t>(d));
}
return inter::NativeToFakeluaFloat(state, d);
}

// Use shared CheckNumberArg from native_common.h

void RegisterMathLibraryApi(State *s) {
Expand Down Expand Up @@ -38,20 +48,16 @@ void RegisterMathLibraryApi(State *s) {
CVar a0 = inter::GetNativeArg(state, args, n, 0);
CheckNumberArg(a0, 1, "math.floor");
if (a0.type_ == static_cast<int>(VarType::Int)) return a0;
if (a0.type_ == static_cast<int>(VarType::Float)) return inter::NativeToFakeluaFloat(state, std::floor(a0.data_.f));
double f = inter::CVarToNumber(a0, std::numeric_limits<double>::quiet_NaN());
if (!std::isnan(f)) return inter::NativeToFakeluaFloat(state, std::floor(f));
return inter::NativeToFakeluaNil(state);
return PushNumInt(state, std::floor(f));
});

RegisterNativeFunction(s, "math.ceil", 1, false, [](State *state, CVar *args, int n) -> CVar {
CVar a0 = inter::GetNativeArg(state, args, n, 0);
CheckNumberArg(a0, 1, "math.ceil");
if (a0.type_ == static_cast<int>(VarType::Int)) return a0;
if (a0.type_ == static_cast<int>(VarType::Float)) return inter::NativeToFakeluaFloat(state, std::ceil(a0.data_.f));
double f = inter::CVarToNumber(a0, std::numeric_limits<double>::quiet_NaN());
if (!std::isnan(f)) return inter::NativeToFakeluaFloat(state, std::ceil(f));
return inter::NativeToFakeluaNil(state);
return PushNumInt(state, std::ceil(f));
});

RegisterNativeFunction(s, "math.max", 1, true, [](State *state, CVar *args, int n) -> CVar {
Expand Down Expand Up @@ -240,11 +246,21 @@ void RegisterMathLibraryApi(State *s) {
CVar a1 = inter::GetNativeArg(state, args, n, 1);
CheckNumberArg(a0, 1, "math.fmod");
CheckNumberArg(a1, 2, "math.fmod");
// Lua 5.4:两个参数都是 integer 时走整数取模(C 的截断向零语义)
if (a0.type_ == static_cast<int>(VarType::Int) && a1.type_ == static_cast<int>(VarType::Int)) {
const int64_t b = a1.data_.i;
if (b == 0) {
ThrowFakeluaException("bad argument #2 to 'fmod' (zero)");
}
// mininteger % -1 在 C 里是 UB(结果溢出),Lua 直接规定为 0
if (b == -1) {
return inter::NativeToFakeluaInt(state, 0);
}
return inter::NativeToFakeluaInt(state, a0.data_.i % b);
}
// 任一参数是 float:结果恒为 float;浮点除零得到 NaN(不报错)
double v0 = inter::CVarToNumber(a0, 0.0);
double v1 = inter::CVarToNumber(a1, 0.0);
if (v1 == 0.0) {
return inter::NativeToFakeluaFloat(state, std::numeric_limits<double>::quiet_NaN());
}
return inter::NativeToFakeluaFloat(state, std::fmod(v0, v1));
});

Expand Down Expand Up @@ -391,10 +407,12 @@ void RegisterMathLibraryApi(State *s) {
return multi;
}
double val = inter::CVarToNumber(a0, 0.0);
double iptr;
double frac = std::modf(val, &iptr);
// 整数部分向零取整(负数用 ceil),再按 pushnumint 决定 int/float
double iptr = (val < 0) ? std::ceil(val) : std::floor(val);
// 无小数部分(整数值或 inf)时小数部分是 +0.0
double frac = (val == iptr) ? 0.0 : val - iptr;
CVar multi = inter::AllocMultiCVar(state, 2);
inter::SetMultiCVarElement(multi, 0, inter::NativeToFakeluaFloat(state, iptr));
inter::SetMultiCVarElement(multi, 0, PushNumInt(state, iptr));
inter::SetMultiCVarElement(multi, 1, inter::NativeToFakeluaFloat(state, frac));
return multi;
});
Expand Down
Loading
Loading