diff --git a/src/v2/test/claim/bash_program_fold_test.dag b/src/v2/test/claim/bash_program_fold_test.dag new file mode 100644 index 00000000000..fa1abd99458 --- /dev/null +++ b/src/v2/test/claim/bash_program_fold_test.dag @@ -0,0 +1,47 @@ +module v2.test.claim.bash_program_fold + +import extdeps.languages.bash.program { + ShellProgram, + assign, + command, + lit, + serialize_bash +} +import v2.workflow.bash_program_emit { bash_program_serialize } +import v2.std.diagnostic { Accepted, Rejected } + +fn bash_program_fold_sample() -> ShellProgram { + ShellProgram { + set_e: true, + statements: [ + command(words: [lit(text: "echo"), lit(text: "hi")]), + assign(name: "X", value: lit(text: "1")) + ] + } +} + +fn bash_program_fold_sample_no_set_e() -> ShellProgram { + ShellProgram { + set_e: false, + statements: [ + command(words: [lit(text: "echo"), lit(text: "hi")]), + assign(name: "X", value: lit(text: "1")) + ] + } +} + +test fn bash_program_serialize_matches_oracle_holds() -> Bool { + match bash_program_serialize(p: bash_program_fold_sample()) { + Accepted { value: folded, diagnostics: _ } => + folded == serialize_bash(p: bash_program_fold_sample()) + Rejected { diagnostics: _ } => false + } +} + +test fn bash_program_serialize_set_e_has_teeth_holds() -> Bool { + match bash_program_serialize(p: bash_program_fold_sample()) { + Accepted { value: folded, diagnostics: _ } => + folded != serialize_bash(p: bash_program_fold_sample_no_set_e()) + Rejected { diagnostics: _ } => false + } +} diff --git a/src/v2/workflow/bash_program_emit.dag b/src/v2/workflow/bash_program_emit.dag new file mode 100644 index 00000000000..69fa1ab3e94 --- /dev/null +++ b/src/v2/workflow/bash_program_emit.dag @@ -0,0 +1,96 @@ +module v2.workflow.bash_program_emit + +import extdeps.languages.bash.program { ShellProgram, ShellStmt } +import v2.compiler.emit { emit } +import v2.compiler.infer { InferredFacts, InferredTree } +import v2.extdeps.languages.bash_command_fold { + bash_fold_stmt_emitted_witness, + bash_fold_target_model_for_stmt_witness +} +import v2.std.algebra { list_head, list_tail, HeadFound, HeadAbsent, TailFound, TailAbsent } +import v2.std.collection { List, Map } +import v2.std.diagnostic { + Accepted, + Diagnostic, + ExternalContractUnknown, + None, + Outcome, + Rejected, + Some, + Unavailable, + bind_outcome, + node_locus +} +import v2.std.node { Node } +import v2.std.text { String } +import v2.std.witness { Holds, Violates, Witness } + +fn bash_program_fold_facts_lookup(key: Node) -> Witness { + Violates { + diagnostic: Diagnostic { + reason: ^bash_program_fold_facts_unexpected_consultation, + at: node_locus(node: key), + correction: Unavailable { reason: ExternalContractUnknown } + } + } +} + +fn bash_program_fold_serialize_stmt(stmt: ShellStmt) -> Outcome { + match bash_fold_stmt_emitted_witness(stmt: stmt) { + Holds { value: emitted } => + match bash_fold_target_model_for_stmt_witness(stmt: stmt) { + Holds { value: target } => + match emit( + tree: InferredTree { + root: emitted, + facts: Map { lookup: bash_program_fold_facts_lookup } + }, + target: target + ) { + Accepted { value: source, diagnostics: d } => + match d { + None => Accepted { value: source.carried, diagnostics: None } + Some { diagnostics: r } => Rejected { diagnostics: r } + } + Rejected { diagnostics: r } => Rejected { diagnostics: r } + } + Violates { diagnostic: dd } => Rejected { diagnostics: Some { diagnostics: [dd] } } + } + Violates { diagnostic: dd } => Rejected { diagnostics: Some { diagnostics: [dd] } } + } +} + +fn bash_program_serialize_body(stmts: List) -> Outcome { + match list_head(xs: stmts) { + HeadAbsent => Accepted { value: "", diagnostics: None } + HeadFound { value: s } => + bind_outcome( + o: bash_program_fold_serialize_stmt(stmt: s), + f: fn(part) { + match list_tail(xs: stmts) { + TailFound { tail: t } => + bind_outcome( + o: bash_program_serialize_body(stmts: t), + f: fn(rest) { + Accepted { value: concat(concat(part, "\n"), rest), diagnostics: None } + } + ) + TailAbsent => Accepted { value: concat(part, "\n"), diagnostics: None } + } + } + ) + } +} + +fn bash_program_serialize(p: ShellProgram) -> Outcome { + bind_outcome( + o: bash_program_serialize_body(stmts: p.statements), + f: fn(body) { + if p.set_e { + Accepted { value: concat("set -e\n", body), diagnostics: None } + } else { + Accepted { value: body, diagnostics: None } + } + } + ) +}