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
21 changes: 2 additions & 19 deletions crates/workshop-rs/src/element_count.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,8 @@ impl Program {
/// Count the canonical Workshop target represented by this WIR program.
///
/// The catalog is used to reject unknown action/value identities before a
/// report is produced. Presentation-only `Debug` and `Print` WIR nodes
/// are intentionally rejected because their emitted HUD expansion is not
/// yet represented as canonical WIR actions.
/// report is produced. Native display actions are represented by their
/// canonical catalog-backed action calls.
pub fn element_count(
&self,
catalog: &Catalog,
Expand Down Expand Up @@ -341,22 +340,6 @@ impl Counter<'_> {
children.push(self.action(*nested)?.node);
}
}
Action::Debug { .. } => {
return Err(ElementCountError::Unsupported {
kind: ElementNodeKind::Action,
name: "debug".to_string(),
span,
reason: "the emitter expands Debug into a HUD action; count the canonical HUD action instead".to_string(),
});
}
Action::Print { .. } => {
return Err(ElementCountError::Unsupported {
kind: ElementNodeKind::Action,
name: "print".to_string(),
span,
reason: "the emitter expands Print into a HUD action; count the canonical HUD action instead".to_string(),
});
}
Action::Call {
name: action_name,
args,
Expand Down
58 changes: 0 additions & 58 deletions crates/workshop-rs/src/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -873,16 +873,6 @@ impl Emitter<'_> {
let end = self.spelling(Kind::Structural, "end")?;
self.line(level, &format!("{end};"))?;
}
wir::Action::Debug { value, .. } => {
// `debug(value)` displays the value as HUD text. The
// reference formats values with type-aware machinery; Wright
// emits a semantically equivalent but presentation-simpler
// Create HUD Text (documented intentional difference).
self.emit_hud_text(*value, level, true)?;
}
wir::Action::Print { message, .. } => {
self.emit_hud_text(*message, level, false)?;
}
wir::Action::AssignMember {
target, op, value, ..
} => {
Expand Down Expand Up @@ -1042,54 +1032,6 @@ impl Emitter<'_> {
Ok(())
}

/// Emit a `debug`/`print` action as a `Create HUD Text` effect.
///
/// `debug` renders the value into the HUD body; `print` renders the
/// message directly (a `format` value already carries the text). Every
/// fixed token resolves through the catalog, so the effect is
/// locale-correct by data and fails explicitly on missing target-locale
/// mappings.
fn emit_hud_text(&mut self, value: wir::ValueId, level: usize, is_debug: bool) -> Result<()> {
let mut body = String::new();
if is_debug {
// Display the value in the HUD body: Custom String("{0}", value).
body.push_str(&self.spelling(Kind::Value, "customString")?);
body.push_str("(\"{0}\", ");
self.value(value, &mut body)?;
body.push(')');
} else {
self.value(value, &mut body)?;
}
// Create HUD Text(All Players(All Teams), Null, header, body, text,
// location, sort order, header color, subheader color, text color,
// reevaluation, spectators) — the canonical catalog layout (probe P6
// emission), so the emitted text reparses against the catalog's
// expected enum domains at the canonical positions.
let mut line = String::new();
line.push_str(&self.spelling(Kind::Action, "createHudText")?);
line.push('(');
line.push_str(&self.spelling(Kind::Value, "allPlayers")?);
line.push('(');
line.push_str(&self.enum_spelling("Team", "ALL")?);
line.push_str("), Null, ");
line.push_str(&body);
line.push_str(", Null, ");
line.push_str(&self.enum_spelling("HudPosition", "LEFT")?);
line.push_str(", -9999, Color(");
line.push_str(&self.enum_spelling("Color", "WHITE")?);
line.push_str("), Color(");
line.push_str(&self.enum_spelling("Color", "WHITE")?);
line.push_str("), Color(");
line.push_str(&self.enum_spelling("Color", "WHITE")?);
line.push_str("), ");
line.push_str(&self.enum_spelling("HudReeval", "VISIBILITY_AND_STRING")?);
line.push_str(", ");
line.push_str(&self.enum_spelling("SpecVisibility", "VISIBLE_ALWAYS")?);
line.push_str(");");
self.line(level, &line)?;
Ok(())
}

fn args(&mut self, args: &[wir::ValueId], out: &mut String) -> Result<()> {
for (index, arg) in args.iter().enumerate() {
if index > 0 {
Expand Down
4 changes: 1 addition & 3 deletions crates/workshop-rs/src/semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,7 @@ fn inspect_action(
| Action::SetPlayerVariable { .. }
| Action::ModifyPlayerVariable { .. }
| Action::AssignMember { .. }
| Action::CallSubroutine { .. }
| Action::Debug { .. }
| Action::Print { .. } => {}
| Action::CallSubroutine { .. } => {}
}
}

Expand Down
4 changes: 1 addition & 3 deletions crates/workshop-rs/src/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,7 @@ fn validate_action(
}
}
wir::Action::SetGlobalVariable { value, .. }
| wir::Action::ModifyGlobalVariable { value, .. }
| wir::Action::Debug { value, .. }
| wir::Action::Print { message: value, .. } => {
| wir::Action::ModifyGlobalVariable { value, .. } => {
validate_value(program, catalog, *value, errors);
}
wir::Action::SetPlayerVariable { player, value, .. }
Expand Down
10 changes: 0 additions & 10 deletions crates/workshop-rs/src/wir/dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,16 +311,6 @@ fn render_action(program: &Program, id: super::ActionId, out: &mut String, level
render_action(program, *action, out, level + 1);
}
}
Action::Debug { value, span } => {
out.push_str(&format!("{}debug ", indent(level)));
render_value(program, *value, out);
out.push_str(&format!("{}\n", span_suffix(*span)));
}
Action::Print { message, span } => {
out.push_str(&format!("{}print ", indent(level)));
render_value(program, *message, out);
out.push_str(&format!("{}\n", span_suffix(*span)));
}
Action::Call { name, args, span } => {
out.push_str(&format!("{}call {name}(", indent(level)));
for (index, arg) in args.iter().enumerate() {
Expand Down
13 changes: 1 addition & 12 deletions crates/workshop-rs/src/wir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
//!
//! Name policy: call/value `name` fields keep the canonical catalog ids
//! (`countOf`, `wait`, `createBeamEffect`); mapping those to localized
//! Workshop presentation spellings is an emission concern. `debug` and
//! `print` are represented as dedicated [`Action::Debug`]/[`Action::Print`]
//! nodes.
//! Workshop presentation spellings is an emission concern.
//!
//! Extracted from the Wright-authored `wright-ir` crate (the `wir`,
//! `settings`, and `source` modules); see
Expand Down Expand Up @@ -399,13 +397,6 @@ pub enum Action {
body: Vec<ActionId>,
span: Option<Span>,
},
/// The `debug(value)` HUD debug effect.
Debug { value: ValueId, span: Option<Span> },
/// The `print(message)` HUD message effect.
Print {
message: ValueId,
span: Option<Span>,
},
/// Any other action call with side effects.
Call {
name: String,
Expand All @@ -428,8 +419,6 @@ impl Action {
| Action::While { span, .. }
| Action::ForGlobalVariable { span, .. }
| Action::ForPlayerVariable { span, .. }
| Action::Debug { span, .. }
| Action::Print { span, .. }
| Action::Call { span, .. } => *span,
}
}
Expand Down
2 changes: 0 additions & 2 deletions crates/workshop-rs/src/wir/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,6 @@ fn check_action(program: &Program, id: super::ActionId) -> Result<(), IrError> {
}
Ok(())
}
Action::Debug { value, .. } => check_value(program, *value),
Action::Print { message, .. } => check_value(program, *message),
Action::Call { args, .. } => {
for arg in args {
check_value(program, *arg)?;
Expand Down
27 changes: 1 addition & 26 deletions crates/workshop-rs/tests/element_count.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use workshop_rs::catalog::{Catalog, Locale};
use workshop_rs::convert::{self, ConvertOptions};
use workshop_rs::element_count::{ElementCountError, ElementNodeKind};
use workshop_rs::element_count::ElementNodeKind;
use workshop_rs::parser;
use workshop_rs::settings::{Settings, SettingsNode};
use workshop_rs::source::SourceFile;
Expand Down Expand Up @@ -186,31 +186,6 @@ fn representative_corpus_program_produces_a_report() {
assert!(report.total > 2);
}

#[test]
fn debug_reports_an_explicit_incomplete_surface() {
let mut program = Program::default();
let value = number(&mut program, 1.0);
let action = program.actions.push(Action::Debug { value, span: None });
program.rules.push(Rule {
name: "debug".to_string(),
span: None,
name_span: None,
disabled: false,
event: Event::Global,
conditions: vec![],
actions: vec![action],
});

assert!(matches!(
program.element_count(&catalog()),
Err(ElementCountError::Unsupported {
kind: ElementNodeKind::Action,
name,
..
}) if name == "debug"
));
}

fn attach_value(mut program: Program, value: wir::ValueId) -> Program {
let variable = program.global_variables.push(wir::WorkshopVariable {
name: "result".to_string(),
Expand Down
62 changes: 25 additions & 37 deletions crates/workshop-rs/tests/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,44 +236,32 @@ fn emitted_condition_matches_reference_infix_form() {
}

#[test]
fn debug_actions_emit_hud_text() {
// Debug/Print emit a semantically equivalent Create HUD Text effect
// (documented intentional difference from the reference's type-aware
// formatting).
let mut program = wir::Program::default();
let file = program
.files
.push(workshop_rs::source::SourceFile::new("workshop.txt"));
let value = program.values.push(workshop_rs::wir::ValueNode::new(
workshop_rs::wir::Value::Number {
value: 1.0,
text: "1".to_string(),
},
None,
));
let debug = program
.actions
.push(wir::Action::Debug { value, span: None });
program.rules.push(wir::Rule {
name: "x".into(),
span: None,
name_span: None,
disabled: false,
event: wir::Event::Global,
conditions: vec![],
actions: vec![debug],
});
let _ = file;
let emitted = emitter::emit(&program, &catalog(), &en()).expect("Debug emits");
assert!(
emitted.contains("Create HUD Text(All Players(All Teams), Null, Custom String(\"{0}\", 1)"),
"debug emits the value as HUD text:\n{emitted}"
);
// The emitted text reparses to a createHudText action call.
fn native_hud_actions_use_the_generic_catalog_path() {
let catalog = catalog();
let reparsed =
workshop_rs::parser::parse_with_context(&emitted, &catalog, &en(), &catalog).unwrap();
assert_eq!(reparsed.rules.len(), 1);
let source = r#"
variables { global: 0: value }
rule ("hud") {
event { Ongoing - Global; }
actions {
Create HUD Text(All Players(All Teams), Null, Null, Custom String("{0}", Global.value), Left, 0, Null, Null, Null, Visible To and String, Default Visibility);
}
}
"#;
let program = parser::parse_with_context(source, &catalog, &en(), &catalog)
.expect("native HUD action parses");
let rule = program.rules.iter().next().expect("HUD rule");
assert!(matches!(
program.actions.get(rule.actions[0]),
Some(wir::Action::Call { name, args, .. })
if name == "createHudText" && args.len() == 11
));
workshop_rs::validate::validate_canonical_ids(&program, &catalog)
.expect("native HUD action validates");

let emitted = emitter::emit(&program, &catalog, &en()).expect("native HUD action emits");
let reparsed = parser::parse_with_context(&emitted, &catalog, &en(), &catalog)
.expect("emitted HUD action reparses");
assert!(workshop_rs::roundtrip::equivalent(&program, &reparsed));
}

#[test]
Expand Down
10 changes: 6 additions & 4 deletions crates/workshop-rs/tests/roundtrip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,14 @@ fn equivalence_detects_semantic_differences() {
},
None,
));
a.actions.push(workshop_rs::wir::Action::Debug {
value: value_a,
a.actions.push(workshop_rs::wir::Action::Call {
name: "wait".into(),
args: vec![value_a],
span: None,
});
b.actions.push(workshop_rs::wir::Action::Debug {
value: value_b,
b.actions.push(workshop_rs::wir::Action::Call {
name: "wait".into(),
args: vec![value_b],
span: None,
});
a.rules.push(workshop_rs::wir::Rule {
Expand Down
9 changes: 5 additions & 4 deletions crates/workshop-rs/tests/wir_expansion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,18 +317,19 @@ fn build_surface_program() -> wir::Program {
Some(s(6, 14, 24)),
));

let debug_value = program.values.push(ValueNode::new(
let wait_duration = program.values.push(ValueNode::new(
Value::Number {
value: 1.0,
text: "1".to_string(),
},
Some(s(7, 11, 12)),
));
let debug = program.actions.push(Action::Debug {
value: debug_value,
let wait = program.actions.push(Action::Call {
name: "wait".into(),
args: vec![wait_duration],
span: Some(s(7, 9, 13)),
});
let if_body = vec![debug];
let if_body = vec![wait];
let if_action = program.actions.push(Action::If {
branches: vec![wir::IfBranch {
condition: compare,
Expand Down
8 changes: 3 additions & 5 deletions docs/element-count.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,14 @@ The calculator is locale-independent: it reads canonical WIR identities and
never emitted spellings. It validates WIR and catalog identities before
producing a report. Unknown or unsupported constructs return
`ElementCountError` instead of yielding a misleading exact total. In
particular, the current `Debug` and `Print` nodes are presentation helpers that
the emitter expands into `Create HUD Text`; callers should count that canonical
action until a reviewed expansion contract is added.
Native display actions such as `Create HUD Text` are counted through their
canonical catalog-backed action calls.

The independent behavioral source for the supported rules is the
[Workshop.codes element-count calculation reference](https://workshop.codes/wiki/articles/element-count-calculation).

Known evidence gap: this initial API does not claim live-client/editor
validation, source-language debug-count compatibility, or an exact count for
presentation-only WIR helpers. Those belong to later
validation or source-language debug-count compatibility. Those belong to later
client-backed/consumer integration work after the canonical WIR surface is
stable. The current real-project `rework.ow` fixture still stops in the parser
on an ambiguous bare `None` enum spelling, so it is not counted as a passing
Expand Down
10 changes: 10 additions & 0 deletions docs/implementation-role.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ If no lossless lowering exists, `opy-rs` must report its explicit integration
boundary rather than extending this Workshop contract with source-language
carriers.

## Source-language display helpers

`Debug` and `Print` are not native Workshop action identities. The independent
[Workshop.codes action inventory](https://workshop.codes/wiki/categories/actions)
lists the native display operation as [`Create HUD Text`](https://workshop.codes/wiki/articles/create-hud-text),
whose documented parameters and persistent HUD behavior are represented by the
catalog-backed `createHudText` action call. Source-language helpers such as
OverPy `debug(...)` and `print(...)` therefore remain provider-owned lowering
concerns; canonical WIR does not carry dedicated variants for them.

This classifies the remaining interoperability probes without using OPY syntax
as Workshop evidence: the dictionary-literal probe is an OPY-owned lowering
gap unless it folds to one of the native indexed/member forms, while the
Expand Down
12 changes: 12 additions & 0 deletions docs/provenance.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@ license, reviewed) is embedded in the dataset itself and surfaced by
| Action/Value signature cross-check | Representative Workshop.codes article links remain recorded in the catalog provenance; fetched snapshots and acceptance results are CI evidence, not generator or runtime inputs. |
| Settings emission table (`src/settings/table.rs` and generated data files) | Hand-written fixture surface plus the reviewed `workshop-data` export at commit `d854bf01fc7bbf3b2169f67408c07a8da8989ad6`; generated entries, names, locale mappings, and source paths are committed together in the declared multi-locale projection, while pinned OverPy 9.7.10 output remains the behavioral check (classes 1/5). |

### Native display action boundary (`workshop-rs#129`)

The independent [Workshop.codes action inventory](https://workshop.codes/wiki/categories/actions)
contains no native `Debug` or `Print` action, while its [`Create HUD Text`]
article documents the native display action, its eleven parameters, and its
persistent HUD behavior. The catalog therefore represents HUD output through
the canonical `createHudText` action call. OverPy `debug(...)` and `print(...)`
remain source-language helpers whose lowering belongs to their provider, not to
canonical WIR.

[`Create HUD Text`]: https://workshop.codes/wiki/articles/create-hud-text

### Locale coverage

* `en-US` is the primary locale and is complete. The committed catalog
Expand Down
Loading