From 478d6848f2a1581f051de79de42c5f77c88e6312 Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Sat, 23 Aug 2025 16:45:17 -0400 Subject: [PATCH 1/2] update testcase snapshot --- ...n__integration__type_check__diffswitch__missing_first.snap | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/compile-fail/integration__integration__type_check__diffswitch__missing_first.snap b/tests/compile-fail/integration__integration__type_check__diffswitch__missing_first.snap index d50bc10..4ef8b19 100644 --- a/tests/compile-fail/integration__integration__type_check__diffswitch__missing_first.snap +++ b/tests/compile-fail/integration__integration__type_check__diffswitch__missing_first.snap @@ -9,6 +9,4 @@ error: unexpected token `:` │ ^ unexpected token │ = - Expected one of "!", "$", "%", "(", "++", "-", "--", "REG", "_S", "_f", "anim", "case", "cos", "default", "ecli", "entry", "float", "int", "mapfile", "offsetof", "script", "sin", "sqrt", "timeof", "~", FLOAT, FLOAT_RAD, IDENT, INSTR, INT or STRING - - + Expected one of "!", "$", "%", "(", "++", "-", "--", "REG", "_S", "_f", "acos", "anim", "asin", "atan", "case", "cos", "default", "ecli", "entry", "float", "int", "mapfile", "offsetof", "script", "sin", "sqrt", "tan", "timeof", "~", FLOAT, FLOAT_RAD, IDENT, INSTR, INT or STRING From 5a9eb9134156b65abdc05cd12fe4e71e02786cd6 Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Sat, 23 Aug 2025 16:46:45 -0400 Subject: [PATCH 2/2] add signed integers to AST I began doing this a long time ago and got pulled away. Finishing up the remaining work was straightforward. --- src/ast/meta.rs | 5 ++- src/ast/mod.rs | 27 +++++++++++---- src/fmt.rs | 65 ++++++++++++++++++++++++++++++++---- src/formats/anm/mod.rs | 2 +- src/llir/raise/early.rs | 8 ++--- src/parse/lalrparser.lalrpop | 2 +- 6 files changed, 89 insertions(+), 20 deletions(-) diff --git a/src/ast/meta.rs b/src/ast/meta.rs index a9ede23..1ce9b06 100644 --- a/src/ast/meta.rs +++ b/src/ast/meta.rs @@ -576,7 +576,10 @@ impl ToMeta for f32 { fn to_meta(&self) -> Meta { Meta::Scalar(sp!((*self).into())) } } impl ToMeta for bool { - fn to_meta(&self) -> Meta { Meta::Scalar(sp!(ast::Expr::LitInt { value: *self as i32, radix: ast::IntRadix::Bool })) } + fn to_meta(&self) -> Meta { Meta::Scalar(sp!(ast::Expr::LitInt { + value: *self as i32, + format: ast::IntFormat::BOOL, + }))} } impl ToMeta for String { fn to_meta(&self) -> Meta { Meta::Scalar(sp!(self.to_owned().into())) } diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 189008e..5c6921c 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -454,8 +454,8 @@ pub enum Expr { LitInt { value: raw::LangInt, /// A hint to the formatter on how it should write the integer. - /// (may not necessarily represent the original radix of a parsed token) - radix: IntRadix, + /// (not meaningful when parsing) + format: IntFormat, }, LitFloat { value: raw::LangFloat }, LitString(LitString), @@ -469,14 +469,29 @@ pub enum Expr { }, } + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct IntFormat { + pub signed: bool, + pub radix: IntRadix, +} + +impl IntFormat { + pub const UNSIGNED: IntFormat = IntFormat { signed: false, radix: IntRadix::Dec }; + pub const SIGNED: IntFormat = IntFormat { signed: true, radix: IntRadix::Dec }; + pub const HEX: IntFormat = IntFormat { signed: false, radix: IntRadix::Hex }; + pub const BIN: IntFormat = IntFormat { signed: false, radix: IntRadix::Bin }; + pub const BOOL: IntFormat = IntFormat { signed: true, radix: IntRadix::Bool }; + /// Used to decompile `jmp` in function syntax. + pub const SIGNED_HEX: IntFormat = IntFormat { signed: true, radix: IntRadix::Hex }; +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum IntRadix { /// Display as decimal. Dec, /// Display as hexadecimal, with an `0x` prefix. Hex, - /// Display as potentially negative hexadecimal, with an `0x` prefix. - SignedHex, /// Display as binary, with an `0b` prefix. Bin, /// Use `true` and `false` if the value is `1` or `0`. Otherwise, fall back to decimal. @@ -850,7 +865,7 @@ string_enum! { } impl From for Expr { - fn from(value: raw::LangInt) -> Expr { Expr::LitInt { value, radix: IntRadix::Dec } } + fn from(value: raw::LangInt) -> Expr { Expr::LitInt { value, format: IntFormat::SIGNED } } } impl From for Expr { fn from(value: raw::LangFloat) -> Expr { Expr::LitFloat { value } } @@ -1269,7 +1284,7 @@ macro_rules! generate_visitor_stuff { Expr::XcrementOp { op: _, order: _, var } => { v.visit_var(var); }, - Expr::LitInt { value: _, radix: _ } => {}, + Expr::LitInt { value: _, format: _ } => {}, Expr::LitFloat { value: _ } => {}, Expr::LitString(_s) => {}, Expr::LabelProperty { .. } => {}, diff --git a/src/fmt.rs b/src/fmt.rs index d6956d8..85c993b 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -899,13 +899,36 @@ impl Format for ast::Expr { ast::Expr::XcrementOp { order: ast::XcrementOpOrder::Pre, op, var } => out.fmt((op, var)), ast::Expr::XcrementOp { order: ast::XcrementOpOrder::Post, op, var } => out.fmt((var, op)), ast::Expr::EnumConst { enum_name, ident } => out.fmt((enum_name, ".", ident)), - ast::Expr::LitInt { value: 0, radix: ast::IntRadix::Bool } => out.fmt("false"), - ast::Expr::LitInt { value: 1, radix: ast::IntRadix::Bool } => out.fmt("true"), - ast::Expr::LitInt { value, radix: ast::IntRadix::Bool } => out.fmt(value), - ast::Expr::LitInt { value, radix: ast::IntRadix::Dec } => out.fmt(value), - ast::Expr::LitInt { value, radix: ast::IntRadix::Hex } => out.fmt(format_args!("{:#x}", value)), - ast::Expr::LitInt { value, radix: ast::IntRadix::SignedHex } => out.fmt(format_args!("{:#x}", SignedRadix(*value))), - ast::Expr::LitInt { value, radix: ast::IntRadix::Bin } => out.fmt(format_args!("{:#b}", value)), + ast::Expr::LitInt { value, format } => match format { + // These are the decimal formats + &ast::IntFormat::SIGNED => out.fmt(value), + &ast::IntFormat::UNSIGNED => { + out.fmt(format_args!("{}", *value as u32)) + }, + + &ast::IntFormat { radix: ast::IntRadix::Hex, signed } => { + match signed { + false => out.fmt(format_args!("{:#x}", value)), + true => out.fmt(format_args!("{:#x}", SignedRadix(*value))), + } + }, + + &ast::IntFormat { radix: ast::IntRadix::Bin, signed } => { + match signed { + false => out.fmt(format_args!("{:#b}", value)), + true => out.fmt(format_args!("{:#b}", SignedRadix(*value))), + } + }, + + &ast::IntFormat { radix: ast::IntRadix::Bool, signed } => { + match (value, signed) { + (0, _) => out.fmt("false"), + (1, _) => out.fmt("true"), + (_, true) => out.fmt(value), + (_, false) => out.fmt(format_args!("{:#x}", *value as u32)), + } + }, + }, ast::Expr::LitFloat { value } => out.fmt(value), ast::Expr::LitString(x) => out.fmt(x), ast::Expr::LabelProperty { label, keyword } => out.fmt((keyword, "(", label, ")")), @@ -1136,4 +1159,32 @@ mod tests { assert!(reformat::(3, r#"meta { x: 25 }"#).ends_with("\n")); assert!(reformat::(9999, r#" script lol { nop(); }"#).ends_with("\n")); } + + #[test] + fn integer_formats() { + use ast::IntRadix as R; + + fn fmt_int(value: i32, signed: bool, radix: R) -> String { + stringify(&ast::Expr::LitInt { + value, + format: ast::IntFormat { signed, radix } + }) + } + + assert_eq!(fmt_int(20, true, R::Dec), "20"); + assert_eq!(fmt_int(-20, true, R::Dec), "-20"); + assert_eq!(fmt_int(-0x30, true, R::Hex), "-0x30"); + assert_eq!(fmt_int(-0x30, false, R::Hex), "0xffffffd0"); + assert_eq!(fmt_int(-0b100, true, R::Bin), "-0b100"); + + assert_eq!(fmt_int(0, true, R::Bool), "false"); + assert_eq!(fmt_int(0, false, R::Bool), "false"); + assert_eq!(fmt_int(1, true, R::Bool), "true"); + assert_eq!(fmt_int(1, false, R::Bool), "true"); + assert_eq!(fmt_int(2, true, R::Bool), "2"); + assert_eq!(fmt_int(2, false, R::Bool), "0x2"); + assert_eq!(fmt_int(-2, true, R::Bool), "-2"); + assert_eq!(fmt_int(-2, false, R::Bool), "0xfffffffe"); + } } + diff --git a/src/formats/anm/mod.rs b/src/formats/anm/mod.rs index ac3b5c8..a1a8b31 100644 --- a/src/formats/anm/mod.rs +++ b/src/formats/anm/mod.rs @@ -686,7 +686,7 @@ fn format_to_meta(format_num: u32) -> Meta { } fn colorkey_to_meta(colorkey: u32) -> impl ToMeta { - ast::Expr::LitInt { value: colorkey as i32, radix: ast::IntRadix::Hex } + ast::Expr::LitInt { value: colorkey as i32, format: ast::IntFormat::HEX } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] diff --git a/src/llir/raise/early.rs b/src/llir/raise/early.rs index 7215ab4..60ebd8c 100644 --- a/src/llir/raise/early.rs +++ b/src/llir/raise/early.rs @@ -181,7 +181,7 @@ fn early_raise_intrinsics( fn raise_mask(value: raw::ParamMask) -> ast::Expr { ast::Expr::LitInt { value: value.into(), - radix: ast::IntRadix::Bin, + format: ast::IntFormat::BIN, } } @@ -192,7 +192,7 @@ fn raise_nargs(value: raw::ArgCount) -> ast::Expr { fn raise_pop(value: raw::StackPop) -> ast::Expr { ast::Expr::LitInt { value: value.into(), - radix: ast::IntRadix::Hex, + format: ast::IntFormat::HEX, } } @@ -698,7 +698,7 @@ impl AtomRaiser<'_, '_> { }, | ArgEncoding::Color - => Ok(ast::Expr::LitInt { value: raw.expect_int(), radix: ast::IntRadix::Hex }), + => Ok(ast::Expr::LitInt { value: raw.expect_int(), format: ast::IntFormat::HEX }), | ArgEncoding::Float => Ok(ast::Expr::from(raw.expect_float())), @@ -721,7 +721,7 @@ impl AtomRaiser<'_, '_> { | Err(IllegalOffset) => { emitter.emit(warning!("invalid offset in a jump instruction")).ignore(); - Ok(ast::Expr::LitInt { value: raw.expect_int(), radix: ast::IntRadix::SignedHex }) + Ok(ast::Expr::LitInt { value: raw.expect_int(), format: ast::IntFormat::SIGNED_HEX }) }, }, } diff --git a/src/parse/lalrparser.lalrpop b/src/parse/lalrparser.lalrpop index b219afe..1ce6dee 100644 --- a/src/parse/lalrparser.lalrpop +++ b/src/parse/lalrparser.lalrpop @@ -612,7 +612,7 @@ ExprTerm: ast::Expr = { > > => ast::Expr::XcrementOp { var, op, order: ast::XcrementOpOrder::Post }, - => ast::Expr::LitInt { value, radix: ast::IntRadix::Dec }, + => ast::Expr::LitInt { value, format: ast::IntFormat::SIGNED }, => ast::Expr::LitFloat { value },