Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2ed6200
Add initial imm attribute support
zero318 Jun 29, 2022
a8b2ad4
Improve unsigned/hex support and update padding
zero318 Jun 30, 2022
e66c92d
Several suggested changes
zero318 Jun 30, 2022
f3de737
Change _ back to 4 bytes and add - for single bytes
zero318 Jun 30, 2022
96c1d4c
First batch of updated signatures
zero318 Jul 1, 2022
7fe31f4
Second batch of updated signatures
zero318 Jul 2, 2022
4d089ac
Add scratch registers for IN-StB and fix size arg sign
zero318 Jul 2, 2022
9605ae3
Third batch of updated signatures
zero318 Jul 3, 2022
5ff6724
Fourth batch of updated signatures
zero318 Jul 5, 2022
b3e8508
please let the merge work
zero318 Jul 5, 2022
b70b2ed
Fifth batch of updated signatures
zero318 Jul 7, 2022
17ce209
Sixth batch of updated signatures
zero318 Jul 9, 2022
92292d5
Seventh batch of updated signatures
zero318 Jul 10, 2022
3d32e3b
Eigth batch of updated signatures
zero318 Jul 12, 2022
88e5a47
Ninth batch of updated signatures and a few misc fixes
zero318 Jul 15, 2022
26f14dc
e
zero318 Apr 26, 2024
6e5e625
Fixed roundtrip problems
zero318 Apr 27, 2024
8d9e7a8
First attempt at th20 support
zero318 May 5, 2025
7a8dd56
Fix th20 std support
khang06 May 5, 2025
4e2e6a2
Merge pull request #2 from khang06/th20-std
zero318 May 5, 2025
ac21a91
Fix th20 std support (again)
khang06 Aug 25, 2025
fba8534
Merge pull request #3 from khang06/th20-std
zero318 Aug 25, 2025
e04f291
Fix more STD/MSG/END stuff for th20
zero318 Aug 25, 2025
20a4da0
Merge commit '7517af7ac0352ae0f42d0b998f46af7946521e9e' into neww-zero
ExpHP Aug 26, 2025
b2dcd1c
Merge commit '88e5a47a2f9bbdd5fdc999bec32999350faf59c' into neww-zero
ExpHP Aug 26, 2025
7778c45
e
zero318 Apr 26, 2024
21c30dd
Fixed roundtrip problems
zero318 Apr 27, 2024
3bdc5fa
First attempt at th20 support
zero318 May 5, 2025
e9c3d28
Fix th20 std support
khang06 May 5, 2025
471b51d
Fix th20 std support (again)
khang06 Aug 25, 2025
1a5964e
Fix more STD/MSG/END stuff for th20
zero318 Aug 25, 2025
8eb4c4b
Merge branch 'format_improvements_test' of https://github.com/ExpHP/t…
zero318 Aug 26, 2025
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
2 changes: 1 addition & 1 deletion src/ast/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ 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 { unsigned: true, radix: ast::IntRadix::Bool } })) }
}
impl ToMeta for String {
fn to_meta(&self) -> Meta { Meta::Scalar(sp!(self.to_owned().into())) }
Expand Down
22 changes: 16 additions & 6 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ pub enum Expr {
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,
format: IntFormat,
},
LitFloat { value: raw::LangFloat },
LitString(LitString),
Expand All @@ -469,14 +469,18 @@ pub enum Expr {
},
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct IntFormat {
pub unsigned: bool,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might prefer signed rather than unsigned to avoid double negatives

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with unsigned since it's more of an exception to the norm than anything. I tend to structure my bools with true as the "do something different" value so that they don't need to be negated whenever being read, but I guess that doesn't matter as much with match since you have to be explicit in both cases.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true as "do something different" makes sense in C-likes where it's easy to zero-initialize things. It doesn't make so much sense in rust.

pub radix: IntRadix,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
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.
Expand Down Expand Up @@ -695,6 +699,9 @@ string_enum! {
#[strum(serialize = "~")] BitNot,
#[strum(serialize = "sin")] Sin,
#[strum(serialize = "cos")] Cos,
#[strum(serialize = "tan")] Tan,
#[strum(serialize = "acos")] Acos,
#[strum(serialize = "atan")] Atan,
#[strum(serialize = "sqrt")] Sqrt,
#[strum(serialize = "$")] EncodeI,
#[strum(serialize = "%")] EncodeF,
Expand All @@ -711,6 +718,9 @@ impl UnOpKind {
UnOpKind::BitNot => OpClass::Bitwise,
UnOpKind::Sin => OpClass::FloatMath,
UnOpKind::Cos => OpClass::FloatMath,
UnOpKind::Tan => OpClass::FloatMath,
UnOpKind::Acos => OpClass::FloatMath,
UnOpKind::Atan => OpClass::FloatMath,
UnOpKind::Sqrt => OpClass::FloatMath,
UnOpKind::CastI => OpClass::Cast,
UnOpKind::CastF => OpClass::Cast,
Expand Down Expand Up @@ -842,7 +852,7 @@ string_enum! {
}

impl From<raw::LangInt> 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 { unsigned: false, radix: IntRadix::Dec } } }
}
impl From<raw::LangFloat> for Expr {
fn from(value: raw::LangFloat) -> Expr { Expr::LitFloat { value } }
Expand Down Expand Up @@ -1257,7 +1267,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 { .. } => {},
Expand Down
19 changes: 16 additions & 3 deletions src/cli_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,10 @@ pub mod msg_compile {
truth.load_mapfiles_from_pragmas(game, &ast)?;
},
MsgMode::Mission => {},
MsgMode::Ending => return Err(truth.emit(error!("--ending is not yet implemented"))),
MsgMode::Ending => {
load_mapfiles(truth, game, &[LanguageKey::End], mapfile_options)?;
truth.load_mapfiles_from_pragmas(game, &ast)?;
},
}

let mut truth = truth.validate_defs()?;
Expand All @@ -590,7 +593,10 @@ pub mod msg_compile {
let msg = truth.compile_mission(game, &ast)?;
truth.write_mission(game, out_path, &msg)?;
},
MsgMode::Ending => unreachable!(),
MsgMode::Ending => {
let msg = truth.compile_msg(game, LanguageKey::End, &ast)?;
truth.write_msg(game, LanguageKey::End, out_path, &msg)?;
},
}
if let Some(debug_info_path) = debug_info_path {
truth.prepare_and_write_debug_info(debug_info_path)?;
Expand Down Expand Up @@ -636,7 +642,14 @@ pub mod msg_decompile {
let msg = truth.read_mission(game, in_path)?;
truth.decompile_mission(game, &msg)
},
MsgMode::Ending => return Err(truth.emit(error!("--ending is not yet implemented"))),
MsgMode::Ending => {
let mapfile_options = add_env_mapfile_for_decomp(mapfile_options, ".endm");
load_mapfiles(truth, game, &[LanguageKey::End], &mapfile_options)?;

let mut truth = truth.validate_defs()?;
let msg = truth.read_msg(game, LanguageKey::End, in_path)?;
truth.decompile_msg(game, LanguageKey::End, &msg, decompile_options)
},
}
}
}
Expand Down
65 changes: 7 additions & 58 deletions src/context/defs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -775,44 +775,9 @@ impl Defs {
/// Lookup table for decompiling constant values to enum consts.
#[derive(Debug, Clone)]
pub struct ConstNames {
pub enums: IdMap<Ident, ScalarValueMap<Sp<Ident>>>,
pub enums: IdMap<Ident, IdMap<i32, Sp<Ident>>>,
}

/// A map with ScalarValue keys, which uses bitwise-identity for floats.
#[derive(Debug, Clone)]
pub struct ScalarValueMap<T> {
ints: IdMap<i32, T>,
strings: IdMap<String, T>,
floats: IdMap<u32, T>,
}

impl<T> ScalarValueMap<T> {
pub fn new() -> Self {
ScalarValueMap {
ints: Default::default(),
strings: Default::default(),
floats: Default::default(),
}
}

pub fn insert(&mut self, key: ScalarValue, value: T) -> Option<T> {
match key {
ScalarValue::Int(x) => self.ints.insert(x, value),
ScalarValue::Float(x) => self.floats.insert(x.to_bits(), value),
ScalarValue::String(x) => self.strings.insert(x, value),
}
}

pub fn get(&self, key: &ScalarValue) -> Option<&T> {
match key {
ScalarValue::Int(x) => self.ints.get(x),
ScalarValue::Float(x) => self.floats.get(&x.to_bits()),
ScalarValue::String(x) => self.strings.get(x),
}
}
}


impl CompilerContext<'_> {
/// Get `value -> name` mappings for all enums and automatic consts, for decompilation or
/// pretty-printing purposes.
Expand All @@ -827,11 +792,11 @@ impl CompilerContext<'_> {
}

impl EnumData {
fn generate_lookup(&self, consts: &context::Consts) -> ScalarValueMap<Sp<Ident>> {
let mut out = ScalarValueMap::new();
fn generate_lookup(&self, consts: &context::Consts) -> IdMap<i32, Sp<Ident>> {
let mut out = IdMap::new();
for (ident, &const_id) in &self.consts {
let value = consts.get_cached_value(const_id).expect("evaluate_const_vars has not run! (bug!)");
out.insert(value.clone(), ident.clone());
out.insert(value.expect_int(), ident.clone());
}
out
}
Expand Down Expand Up @@ -1150,34 +1115,18 @@ impl Signature {
signature_from_func_ast(ty_keyword, params)
}

pub(crate) fn validate(&self, ctx: &CompilerContext) -> Result<(), ErrorReported> {
self._check_non_optional_after_optional(ctx)
}

fn _check_non_optional_after_optional(&self, ctx: &CompilerContext) -> Result<(), ErrorReported> {
let mut first_optional = None;
for param in self.params.iter() {
if param.default.is_some() {
first_optional = Some(param);
} else if let Some(optional) = first_optional {
return Err(ctx.emitter.emit(error!(
message("invalid function signature"),
secondary(optional.useful_span, "optional parameter"),
primary(param.useful_span, "non-optional parameter after optional"),
)));
}
}
pub(crate) fn validate(&self, _ctx: &CompilerContext) -> Result<(), ErrorReported> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was removed because its only purpose was to handle the padding arguments from old STD. This was refactored into the _ and - formats usable in any script type rather than optional arguments, so this check is unnecessary

Ok(())
}

/// Minimum number of arguments accepted.
pub fn min_args(&self) -> usize {
self.params.iter().take_while(|param| param.default.is_none()).count()
self.params.iter().fold(0, |count, param| count + param.default.is_none() as usize)
}

/// Maximum number of arguments accepted.
pub fn max_args(&self) -> usize {
self.params.len()
self.min_args()
}

/// Matches arguments at a call site to their corresponding parameters.
Expand Down
Loading