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
64 changes: 62 additions & 2 deletions src/v2/compiler/manual/bash_emit_command_test.dag
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ module v2.test.manual.bash_emit_command
// Slice 4: AndOr / Pipe / WithRedir compound constructs.
// Slice 5a: Conj-positional emitted + FormalProduction lhs-exact structural rows.
// Slice 5b: If / Subshell / Heredoc + pipe|precedence brace-group keystone.
// Slice 5c: Assign / EnvPrefixed / CmdSubst + Concat (FINAL emit-vocab).
// Slice 5c: Assign / EnvPrefixed / CmdSubst + Concat.
// Slice 5d: Exit { code: Int } (LAST emit-vocab construct).
//
// Run:
// gunbc run --source-root src/v2 \
Expand Down Expand Up @@ -79,7 +80,16 @@ import v2.extdeps.languages.bash {
bash_assign_concat_gunbc_spaced_wrong_target_model,
bash_env_prefixed_cargo_emitted,
bash_env_prefixed_cargo_source_text,
bash_env_prefixed_cargo_target_model
bash_env_prefixed_cargo_target_model,
bash_exit_code_1_emitted,
bash_exit_code_1_source_text,
bash_exit_code_1_target_model,
bash_exit_code_0_emitted,
bash_exit_code_0_source_text,
bash_exit_code_0_target_model,
bash_exit_code_1_quoted_wrong_emitted,
bash_exit_code_1_quoted_wrong_source_text,
bash_exit_code_1_quoted_wrong_target_model
}
import v2.std.collection { Map, empty_map }
import v2.std.host_transport { target_emit_host_runtime_row_unconfigured }
Expand Down Expand Up @@ -481,3 +491,53 @@ test fn bash_emit_env_prefixed_cargo_holds() -> Bool {
target: bash_env_prefixed_cargo_target_model()
)
}

// Slice 5d — Exit { code: Int } (LAST emit-vocab construct).

// Exit { code: 1 } — host_prelude OrElse failure arm; byte-identical to serialize_bash.
test fn bash_emit_exit_code_1_holds() -> Bool {
bash_emit_slice4_holds(
emitted: bash_exit_code_1_emitted,
source_text: bash_exit_code_1_source_text,
target: bash_exit_code_1_target_model()
)
}

// Exit { code: 0 } — dsl_compile_clean_transport success terminator.
test fn bash_emit_exit_code_0_holds() -> Bool {
bash_emit_slice4_holds(
emitted: bash_exit_code_0_emitted,
source_text: bash_exit_code_0_source_text,
target: bash_exit_code_0_target_model()
)
}

// Discriminating — exit code is bare decimal, not single-quoted.
test fn bash_emit_exit_code_1_quoted_wrong_ne_correct_holds() -> Bool {
bash_exit_code_1_quoted_wrong_source_text != bash_exit_code_1_source_text
}

test fn bash_emit_exit_code_1_quoted_wrong_model_holds() -> Bool {
bash_emit_slice4_holds(
emitted: bash_exit_code_1_quoted_wrong_emitted,
source_text: bash_exit_code_1_quoted_wrong_source_text,
target: bash_exit_code_1_quoted_wrong_target_model()
)
}

// RED — correct unquoted exit code through quoted-wrong model must not fabricate bare decimal.
fn bash_emit_exit_code_1_wrong_quoted_model_rejects() -> Bool {
let tree = InferredTree {
root: bash_exit_code_1_emitted,
facts: Map { lookup: bash_facts_lookup }
}
match emit(tree: tree, target: bash_exit_code_1_quoted_wrong_target_model()) {
Accepted { value: source, diagnostics: d } =>
(source.carried != bash_exit_code_1_source_text) && (d == None)
Rejected { diagnostics: _ } => true
}
}

test fn bash_emit_exit_code_1_wrong_quoted_model_rejects_holds() -> Bool {
bash_emit_exit_code_1_wrong_quoted_model_rejects()
}
136 changes: 131 additions & 5 deletions src/v2/extdeps/languages/bash.dag
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@
// MIGRATION SLICE 5b — If / Subshell / Heredoc + pipe|precedence brace-group keystone
// (`Pipe { AndOr left }` must emit `{ a && b; } | c`, not flat `a && b | c`). Subshell
// nesting AndOr is a separate recursion fixture. Witnesses in bash_emit_command_test.
// MIGRATION SLICE 5c — Assign / EnvPrefixed / CmdSubst + Concat (FINAL emit-vocab before
// Exit). Byte-identical to serialize_bash for each fixture; witnesses in bash_emit_command_test.
// Next slices: Exit → migrate sidecar consumers → delete program.dag.
// MIGRATION SLICE 5c — Assign / EnvPrefixed / CmdSubst + Concat. Byte-identical to
// serialize_bash for each fixture; witnesses in bash_emit_command_test.
// MIGRATION SLICE 5d — Exit { code: Int } (LAST emit-vocab construct; host_prelude uses
// `Exit { code: 1 }` → `exit 1`). Byte-identical to serialize_bash; witnesses in
// bash_emit_command_test. Next slices: migrate sidecar consumers → delete program.dag.
//
// v2-clean (no std.render / std.unicode imports) so witnesses execute under the v1 interpreter.

Expand Down Expand Up @@ -800,7 +802,7 @@ fn bash_pipe_and_or_flat_wrong_target_model() -> TargetModel {
}

// =========================================================================
// Slice 5c — Assign / EnvPrefixed / CmdSubst + Concat (FINAL emit-vocab before Exit).
// Slice 5c — Assign / EnvPrefixed / CmdSubst + Concat.
// Byte-identical to serialize_bash for each fixture.
// Shared operator tokens: ^bash_token_assign_name (BoundToken + binding_spellings),
// ^bash_token_assign_eq ("="), ^bash_token_cmdsubst_open ("$(") — one row per construct,
Expand Down Expand Up @@ -1012,6 +1014,128 @@ fn bash_env_prefixed_cargo_target_model() -> TargetModel {
)
}

// =========================================================================
// Slice 5d — Exit { code: Int } (LAST emit-vocab construct).
// `Exit { code: 1 }` — host_prelude OrElse failure arm; `Exit { code: 0 }` — compile-clean
// success path. Byte-identical to serialize_bash(Exit { code: c }) => concat("exit ", to_string(c)).
// Shared operator token: ^bash_token_kw_exit ("exit ") — one row per construct, not per code (§2).
// =========================================================================

// Exit { code: 1 } — host_prelude `|| exit 1` failure arm.
fn bash_exit_code_1_concrete_tokens() -> List<ConcreteSyntaxToken> {
[
FixedToken { token_class: ^bash_token_kw_exit },
BoundToken { token_class: ^bash_token_exit_code, binding: ^bash_binding_exit_code_1 }
]
}

data bash_exit_code_1_emitted: Node = bash_conj_emitted_from_tokens(
tokens: bash_exit_code_1_concrete_tokens()
)

data bash_exit_code_1_source_text: String = "exit 1"

fn bash_exit_code_1_translation_rules_node() -> Node {
bash_relation_translation_rules_node(
rule_name: ^bash_relation_rule_exit_code_1,
lhs: ^bash_production_exit_code_1,
tokens: bash_exit_code_1_concrete_tokens(),
emitted: bash_exit_code_1_emitted
)
}

// Exit { code: 0 } — dsl_compile_clean_transport success terminator.
fn bash_exit_code_0_concrete_tokens() -> List<ConcreteSyntaxToken> {
[
FixedToken { token_class: ^bash_token_kw_exit },
BoundToken { token_class: ^bash_token_exit_code, binding: ^bash_binding_exit_code_0 }
]
}

data bash_exit_code_0_emitted: Node = bash_conj_emitted_from_tokens(
tokens: bash_exit_code_0_concrete_tokens()
)

data bash_exit_code_0_source_text: String = "exit 0"

fn bash_exit_code_0_translation_rules_node() -> Node {
bash_relation_translation_rules_node(
rule_name: ^bash_relation_rule_exit_code_0,
lhs: ^bash_production_exit_code_0,
tokens: bash_exit_code_0_concrete_tokens(),
emitted: bash_exit_code_0_emitted
)
}

// Discriminating — exit code is a bare decimal word, not a single-quoted lit.
fn bash_exit_code_1_quoted_wrong_concrete_tokens() -> List<ConcreteSyntaxToken> {
[
FixedToken { token_class: ^bash_token_kw_exit },
BoundToken { token_class: ^bash_token_lit, binding: ^bash_binding_exit_code_1 }
]
}

data bash_exit_code_1_quoted_wrong_emitted: Node = bash_conj_emitted_from_tokens(
tokens: bash_exit_code_1_quoted_wrong_concrete_tokens()
)

data bash_exit_code_1_quoted_wrong_source_text: String = "exit '1'"

fn bash_exit_code_1_quoted_wrong_translation_rules_node() -> Node {
bash_relation_translation_rules_node(
rule_name: ^bash_relation_rule_exit_code_1_quoted_wrong,
lhs: ^bash_production_exit_code_1_quoted_wrong,
tokens: bash_exit_code_1_quoted_wrong_concrete_tokens(),
emitted: bash_exit_code_1_quoted_wrong_emitted
)
}

fn bash_exit_code_lex() -> LexRules {
ModeledLexRules {
root: LexRuleSet {
rules: [
bash_lex_rule(token_class: ^bash_token_kw_exit, text: "exit ")
]
}
}
}

fn bash_exit_code_1_target_model() -> TargetModel {
bash_make_bound_tokens_target_model(
rules: bash_exit_code_1_translation_rules_node(),
source_literal: ^bash_exit_code_1_source_literal,
production_name: ^bash_production_exit_code_1,
tokens: bash_exit_code_1_concrete_tokens(),
source_text: bash_exit_code_1_source_text,
lex: bash_exit_code_lex(),
binding_spellings: bash_binding_spellings()
)
}

fn bash_exit_code_0_target_model() -> TargetModel {
bash_make_bound_tokens_target_model(
rules: bash_exit_code_0_translation_rules_node(),
source_literal: ^bash_exit_code_0_source_literal,
production_name: ^bash_production_exit_code_0,
tokens: bash_exit_code_0_concrete_tokens(),
source_text: bash_exit_code_0_source_text,
lex: bash_exit_code_lex(),
binding_spellings: bash_binding_spellings()
)
}

fn bash_exit_code_1_quoted_wrong_target_model() -> TargetModel {
bash_make_bound_tokens_target_model(
rules: bash_exit_code_1_quoted_wrong_translation_rules_node(),
source_literal: ^bash_exit_code_1_quoted_wrong_source_literal,
production_name: ^bash_production_exit_code_1_quoted_wrong,
tokens: bash_exit_code_1_quoted_wrong_concrete_tokens(),
source_text: bash_exit_code_1_quoted_wrong_source_text,
lex: bash_exit_code_lex(),
binding_spellings: bash_binding_spellings()
)
}

fn bash_binding_spellings() -> Map<Symbol, String> {
let m0 = map_insert(empty_map(), ^bash_binding_var_ROOT, "ROOT")
let m1 = map_insert(m0, ^bash_binding_lit_ROOT, "ROOT")
Expand All @@ -1021,7 +1145,9 @@ fn bash_binding_spellings() -> Map<Symbol, String> {
let m5 = map_insert(m4, ^bash_binding_var_GUNBC, "GUNBC")
let m6 = map_insert(m5, ^bash_binding_var_CARGO_TARGET_DIR, "CARGO_TARGET_DIR")
let m7 = map_insert(m6, ^bash_binding_lit_target_release_gunbc, "/target/release/gunbc")
map_insert(m7, ^bash_binding_lit_target, "/target")
let m8 = map_insert(m7, ^bash_binding_lit_target, "/target")
let m9 = map_insert(m8, ^bash_binding_exit_code_1, "1")
map_insert(m9, ^bash_binding_exit_code_0, "0")
}

// POSIX §2.2.2 — inside single quotes every character is literal; embedded `'` → `'\''`.
Expand Down