Skip to content
Open
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
53 changes: 46 additions & 7 deletions codegen/src/emit-typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,29 +75,48 @@ export function emitTypeScript(spec: SpecIR): string {
// the hand-written plugins (which only have raw.position).
const consumedByFormatter = collectFormatterRefs(spec.formatted);

// Binary decode chains (ascii85 / base64 / deflate / text_decode /
// hex_decode) throw on malformed input. The hand-written plugins wrapped
// those chains in try/catch and failed gracefully; mirror that.
const binaryKinds = new Set([
"decode_ascii85",
"base64",
"deflate",
"text_decode",
"hex_decode",
]);
const needsTryCatch = spec.parse.steps.some((s) => binaryKinds.has(s.kind));
const bodyIndent = needsTryCatch ? " " : " ";
if (needsTryCatch) out.push(` try {`);

for (const step of spec.parse.steps) {
emitParseStep(step, out, " ");
emitParseStep(step, out, bodyIndent);
}

// Fields or Variants.
if (spec.variants) {
emitVariants(spec.variants, out, " ", consumedByFormatter);
emitVariants(spec.variants, out, bodyIndent, consumedByFormatter);
} else if (spec.fields) {
for (const field of spec.fields) {
emitField(field, out, " ", consumedByFormatter);
emitField(field, out, bodyIndent, consumedByFormatter);
}
}

// Formatter.
emitFormatted(spec.formatted, out, " ");
emitFormatted(spec.formatted, out, bodyIndent);

// Success path.
if (!hasExplicitDecodeLevelSetting(spec)) {
const level = spec.plugin.decodeLevel.toLowerCase();
const tsLevel = level === "full" ? "'full'" : "'partial'";
out.push(` this.setDecodeLevel(result, true, ${tsLevel});`);
out.push(`${bodyIndent}this.setDecodeLevel(result, true, ${tsLevel});`);
}
out.push(`${bodyIndent}return result;`);
if (needsTryCatch) {
out.push(` } catch {`);
out.push(` return this.failUnknown(result, message.text, options);`);
out.push(` }`);
}
out.push(` return result;`);
out.push(` }`);
out.push(`}`);
return out.join("\n") + "\n";
Expand Down Expand Up @@ -205,7 +224,7 @@ function emitField(
const decodeExpr = field.decode
? renderDecodeCall(field.decode, renderExpr(field.from))
: renderExpr(field.from);
const skipAutoRaw = consumedByFormatter.has(field.name);
const skipAutoRaw = consumedByFormatter.has(field.name) || field.raw === false;
if (field.when) {
// Declare outside the if so downstream formatters / variant-shared code
// can still reference the variable when the guard fails — it'll be
Expand Down Expand Up @@ -294,19 +313,39 @@ function emitFormatterCall(item: FormatterCall, out: string[], indent: string):
out.push(`${indent}hatches.${item.customName}(result);`);
return;
}
// remaining_fields: trailing CSV fields → remaining.text (mirrors the
// Label_44_Base.addRemainingFields pattern).
if (item.type === "remaining_fields") {
const from = renderArg(item.args["from"]);
const start = Number(item.args["start"] ?? 0);
out.push(`${indent}if (${from}.length > ${start}) {`);
out.push(`${indent} ResultFormatter.unknownArr(result, ${from}.slice(${start}));`);
out.push(`${indent}}`);
return;
}
// Map IR formatter type → ResultFormatter method.
const methodMap: Record<string, string> = {
position: "position",
altitude: "altitude",
speed: "speed",
heading: "heading",
timestamp: "timestamp",
eta: "eta",
out: "out",
off: "off",
on: "on",
in: "in",
day: "day",
month: "month",
departure_day: "departureDay",
arrival_day: "arrivalDay",
callsign: "callsign",
flight_number: "flightNumber",
tail_number: "tail",
airport_origin: "departureAirport",
airport_destination: "arrivalAirport",
fuel: "currentFuel",
fuel_remaining: "remainingFuel",
free_text: "unknownArr",
};
const method = methodMap[item.type];
Expand Down
13 changes: 13 additions & 0 deletions codegen/src/ir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ export interface FieldIR {
decode?: DecodeCall;
when?: Condition;
default?: ValueExpr;
/** false = intermediate field; do not auto-emit into result.raw. */
raw?: boolean;
description?: string;
}

Expand Down Expand Up @@ -126,12 +128,23 @@ export interface FormatterCall {
| "speed"
| "heading"
| "timestamp"
| "eta"
| "out"
| "off"
| "on"
| "in"
Comment on lines +131 to +135

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Update non-TS emitters for new formatter types

When ads-gen is run with --target rust or --target c, these shared formatter types are now accepted by the schema/IR and used by the label 44 specs, but codegen/src/emit-rust.ts and codegen/src/emit-c.ts still only map the old formatter names and otherwise emit TODO formatter comments. Because those same emitters suppress auto-raw for fields referenced by formatters, generated Rust/C decoders for these specs will silently omit the formatted items and the canonical raw values for fields such as ETA/out/off/on/in/month/day/fuel_remaining rather than matching the updated TypeScript output.

Useful? React with 👍 / 👎.

| "day"
| "month"
| "departure_day"
| "arrival_day"
| "callsign"
| "flight_number"
| "tail_number"
| "airport_origin"
| "airport_destination"
| "fuel"
| "fuel_remaining"
| "remaining_fields"
| "free_text"
| "custom";
customName?: string;
Expand Down
1 change: 1 addition & 0 deletions codegen/src/parse-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ function lowerField(field: any): FieldIR {
decode: field.decode ? lowerDecode(field.decode) : undefined,
when: field.when ? lowerCondition(field.when) : undefined,
default: field.default !== undefined ? lowerExpr(field.default) : undefined,
raw: field.raw,
description: field.description,
};
}
Expand Down
6 changes: 5 additions & 1 deletion runtimes/typescript/utils/result_formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,11 @@ export class ResultFormatter {
});
}

static remainingFuel(decodeResult: DecodeResult, value: number) {
static remainingFuel(decodeResult: DecodeResult, value: number | undefined) {
// Tolerate undefined/NaN — mirrors the legacy parseFuel() isNaN guard
// (and the currentFuel precedent) so when-gated spec fields can call
// this unconditionally.
if (value === undefined || value === null || Number.isNaN(value)) return;
decodeResult.raw.fuel_remaining = value;
decodeResult.formatted.items.push({
type: 'fuel_remaining',
Expand Down
16 changes: 16 additions & 0 deletions schema/ads-v1.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@
"decode": { "$ref": "#/$defs/decode_call" },
"when": { "$ref": "#/$defs/condition" },
"default": { "$ref": "#/$defs/value_expr" },
"raw": {
"type": "boolean",
"default": true,
"description": "Set false for intermediate fields that feed later fields/formatters but must NOT auto-emit into result.raw."
},
"description": { "type": "string" }
}
},
Expand Down Expand Up @@ -468,6 +473,17 @@
"airport_origin",
"airport_destination",
"fuel",
"fuel_remaining",
"eta",
"out",
"off",
"on",
"in",
"day",
"month",
"departure_day",
"arrival_day",
"remaining_fields",
"free_text",
"custom"
]
Expand Down
4 changes: 3 additions & 1 deletion spec/labels/16/AUTPOS.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ parse:
# representable in current DSL.
custom: label_16_autpos_decode
formatted:
description: "Position Report"
# Legacy plugin used defaultResult() — description stays "Unknown" on
# failed decodes; the hatch sets "Position Report" on its success path.
description: "Unknown"
custom: label_16_autpos_format
11 changes: 6 additions & 5 deletions spec/labels/44/ETA.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ formatted:
- { type: altitude, value: $altitude }
- { type: airport_origin, value: $departure_icao }
- { type: airport_destination, value: $arrival_icao }
- { type: timestamp, kind: month, value: $month }
- { type: timestamp, kind: day, value: $day }
- { type: timestamp, kind: current, value: $timestamp }
- { type: timestamp, kind: eta, value: $eta_time }
- { type: fuel, kind: remaining, value: $fuel_remaining, when_present: true }
- { type: month, value: $month }
- { type: day, value: $day }
- { type: timestamp, value: $timestamp }
- { type: eta, value: $eta_time }
- { type: fuel_remaining, value: $fuel_remaining }
- { type: remaining_fields, from: $data, start: 9 }
9 changes: 5 additions & 4 deletions spec/labels/44/IN.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ formatted:
- { type: position, value: $position }
- { type: airport_origin, value: $departure_icao }
- { type: airport_destination, value: $arrival_icao }
- { type: timestamp, kind: month, value: $month }
- { type: timestamp, kind: day, value: $day }
- { type: timestamp, kind: in, value: $in_time }
- { type: fuel, kind: remaining, value: $fuel_remaining, when_present: true }
- { type: month, value: $month }
- { type: day, value: $day }
- { type: in, value: $in_time }
- { type: fuel_remaining, value: $fuel_remaining }
- { type: remaining_fields, from: $data, start: 7 }
11 changes: 6 additions & 5 deletions spec/labels/44/OFF.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ formatted:
- { type: position, value: $position }
- { type: airport_origin, value: $departure_icao }
- { type: airport_destination, value: $arrival_icao }
- { type: timestamp, kind: month, value: $month }
- { type: timestamp, kind: day, value: $day }
- { type: timestamp, kind: off, value: $off_time }
- { type: timestamp, kind: eta, value: $eta_time }
- { type: fuel, kind: remaining, value: $fuel_remaining, when_present: true }
- { type: month, value: $month }
- { type: day, value: $day }
- { type: off, value: $off_time }
- { type: eta, value: $eta_time }
- { type: fuel_remaining, value: $fuel_remaining }
- { type: remaining_fields, from: $data, start: 8 }
9 changes: 5 additions & 4 deletions spec/labels/44/ON.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ formatted:
- { type: position, value: $position }
- { type: airport_origin, value: $departure_icao }
- { type: airport_destination, value: $arrival_icao }
- { type: timestamp, kind: month, value: $month }
- { type: timestamp, kind: day, value: $day }
- { type: timestamp, kind: on, value: $on_time }
- { type: fuel, kind: remaining, value: $fuel_remaining, when_present: true }
- { type: month, value: $month }
- { type: day, value: $day }
- { type: on, value: $on_time }
- { type: fuel_remaining, value: $fuel_remaining }
- { type: remaining_fields, from: $data, start: 7 }
13 changes: 8 additions & 5 deletions spec/labels/44/POS.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ fields:

- name: flight_level_raw
from: $m.flight_level_or_ground
# Intermediate value feeding `altitude`; legacy never stored it in raw.
raw: false
Comment on lines +23 to +24

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor raw:false outside the TypeScript emitter

For --target rust and --target c, this new raw: false flag is lowered into the shared IR but the Rust/C emitters' skipAutoRaw checks still only look at consumedByFormatter, so regenerating Label_44_POS in those targets will continue to write the intermediate flight_level_raw into result.raw. This makes the new spec flag target-dependent and preserves the parity bug the flag is intended to fix whenever the non-TS codegen paths are used.

Useful? React with 👍 / 👎.

decode:
fn: custom
custom: parse_flight_level_or_ground
Expand Down Expand Up @@ -58,11 +60,12 @@ formatted:
description: "Position Report"
items:
- { type: position, value: $position }
- { type: timestamp, kind: month, value: $month }
- { type: timestamp, kind: day, value: $day }
- { type: timestamp, kind: current, value: $timestamp }
- { type: timestamp, kind: eta, value: $eta }
- { type: fuel, units: tons, value: $fuel_in_tons, when_present: true }
- { type: month, value: $month }
- { type: day, value: $day }
- { type: timestamp, value: $timestamp }
- { type: eta, value: $eta }
# fuel_in_tons is raw-only in the legacy plugin (no formatted item);
# the field's auto-raw emit covers it now that no formatter consumes it.
- { type: airport_origin, value: $departure_icao }
- { type: airport_destination, value: $arrival_icao }
- { type: altitude, value: $altitude }
4 changes: 3 additions & 1 deletion spec/labels/H1/Paren.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ parse:
# altitude is parsed * 100. Whole-plugin hatch for parity.
custom: label_h1_paren_parse
formatted:
description: "Position Report"
# Legacy plugin used defaultResult() — description stays "Unknown" on
# failed decodes; the hatch sets "Position Report" on its success path.
description: "Unknown"
custom: label_h1_paren_format
4 changes: 3 additions & 1 deletion spec/wildcards/arinc_702.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ parse:
# recursive H1 message decoding all live in hand-written per-language code.
custom: arinc_702_dispatch
formatted:
description: "ARINC 702 Message"
# Legacy plugin used defaultResult() — description stays "Unknown" unless
# Arinc702Helper sets one on a successful decode.
description: "Unknown"
custom: arinc_702_format
Loading