# Target-side conversion factories: dispatch, contracts and module visibility.
fn conversion_case($source: String, $stdout: String, $diagnostic: String)
{
let $fixture = $args[0]
let $reply = run_process [`$shelly, "-m", "-c", $source] [
"cwd": $fixture, "env": ["HOME": $fixture, "PATH": "/usr/bin:/bin",
"SHELLY_STD_PATH": "${fixture}/std", "TERM": "dumb", "LANG": "C.UTF-8"],
"stdin_file": "/dev/null", "stdout_file": "${fixture}/out",
"stderr_file": "${fixture}/err", "timeout_ms": 3000]
let $out: String
let $err: String
"${fixture}/out" -> $out
"${fixture}/err" -> $err
let $status = if $diagnostic == "" { 0 } else { 1 }
if ($out != $stdout) || ($reply["exit_code"] != $status) ||
($reply["error"] != ()) || ($reply["signal"] != ()) || $reply["timed_out"] ||
(($diagnostic == "") && ($err != "")) ||
(($diagnostic != "") && (!($err.contains $diagnostic)))
{ echo $source $reply $out $err; fail "conversion protocol mismatch" }
}
conversion_case "type Count = Integer; fn Count::from_string(\$v: String): Count { Integer(\$v) }; echo Count(\"7\")" "7\n" ""
conversion_case "type Count = Integer; fn Count::from_integer(\$v: Integer): Count { \$v + 1 }; echo Count(7); let \$v: Count = 7; echo \$v" "8\n7\n" ""
conversion_case "type Count = Integer; fn Count::from_count(\$v: Count): Count { Integer(\$v) + 1 }; let \$v: Count = 7; echo Count(\$v)" "8\n" ""
conversion_case "type FooBar = Integer; type Count = Integer; fn Count::from_foobar(\$v: FooBar): Count { Integer(\$v) + 2 }; echo Count(FooBar(7))" "9\n" ""
conversion_case "type Inner = Integer; type Outer = Inner; type Count = Integer; fn Count::from_outer(\$v: Outer): Count { Integer(\$v) + 3 }; echo Count(Outer(7))" "10\n" ""
conversion_case "struct Source { n: Integer }; fn Integer::from_source(\$v: Source): Integer { \$v.n }; echo Integer(Source(n: 7))" "7\n" ""
conversion_case "struct Target { n: Integer }; fn Target::from_integer(\$v: Integer): Target { Target(n: \$v) }; echo Target(3).n" "3\n" ""
conversion_case "enum State { A, B }; fn State::from_integer(\$v: Integer): State { if \$v == 0 { State::A } else { State::B } }; echo State(1).index" "1\n" ""
conversion_case "enum State { A, B }; fn String::from_state(\$v: State): String { \"state-\" + String(\$v.index) }; echo String(State::B)" "state-1\n" ""
conversion_case "fn Integer::from_string(\$v: String): Integer { 42 }; echo Integer(\"7\") Integer(7.0)" "42 7\n" ""
conversion_case "type Count = Integer; fn Count::from_string(\$v: String): Count { Integer(\$v) }; echo Count(7) Float(7) String(7) Integer(\"8\")" "7 7.0 7 8\n" ""
conversion_case "type Count = Integer; fn Count::from_string(\$v: String): Count { Integer(\$v) }; echo (Count::from_string \"7\"); let \$f = `Count::from_string; echo (\$f \"8\")" "7\n8\n" ""
conversion_case "type Count = Integer; fn Count::from_string(\$v: String): any { Integer(\$v) }; echo Count(\"7\")" "7\n" ""
conversion_case "type Count = Integer; fn Count::from_array_integer(\$v: Array): Count { Integer(\$v.count) }; echo Count([1,2])" "2\n" ""
conversion_case "type Count = Integer; fn Count::from_hash_string_integer(\$v: HashMap): Count { \$v[\"a\"] }; echo Count([\"a\":1])" "1\n" ""
conversion_case "type Count = Integer; fn Count::from_range(\$v: Range): Count { 3 }; echo Count(1..4)" "3\n" ""
conversion_case "let \$calls = 0; type Count = Integer; fn source(): String { \$calls = \$calls + 1; \"7\" }; fn Count::from_string(\$v: String): Count { \$calls = \$calls + 1; Integer(\$v) }; echo Count(source) \$calls" "7 2\n" ""
conversion_case "type Count = Integer; fn Count::from_integer(\$v: Integer): Count { \$v = \$v + 1; \$v }; let \$n = 7; echo Count(\$n) \$n" "8 7\n" ""
conversion_case "type Count = Integer; fn use(): Count { Count(\"7\") }; fn Count::from_string(\$v: String): Count { Integer(\$v) }; echo (use)" "7\n" ""
conversion_case "type Count = Integer; fn Count::from_integer(\$v: Integer): Count { \$v + 1 }; fn local(): Count { fn Count::from_integer(\$v: Integer): Count { \$v + 2 }; Count(7) }; echo (local); echo Count(7)" "9\n8\n" ""
conversion_case "type Count = Integer; fn Count::from_integer(\$v: Integer): Count { \$v + 1 }; fn use(\$v: Count): Count { \$v }; struct Box { n: Count }; let \$xs: [Count] = [7]; echo (use 7) Box(n: 7).n \$xs[0]" "7 7 7\n" ""
conversion_case "type Count = Integer; fn Count::from_integer(\$v: Integer): Count { \$v + 1 }; let \$n: Float = 7; echo \$n" "7.0\n" ""
conversion_case "type Count = Integer; fn Count::from_string(\$v: String): Count { Integer(\$v) }; let \$n: Count = \"7\"" "" "Expected Integer"
conversion_case "type Count = Integer; fn Count::from_integer(\$v: String): Count { 1 }; echo Count(7)" "" "Expected String"
conversion_case "type Count = Integer; fn Count::from_integer(\$v: Integer): any { \"bad\" }; echo Count(7)" "" "Expected Integer"
conversion_case "fn Integer::from_string(\$v: String): Integer { 1 / 0 }; echo Integer(\"7\")" "" "Arithmetic error"
conversion_case "type Count = Integer; fn Count::from_integer(): Count { 1 }" "" "exactly one required source argument"
conversion_case "type Count = Integer; fn Count::from_integer(\$a: Integer, \$b: Integer): Count { 1 }" "" "exactly one required source argument"
conversion_case "type Count = Integer; fn Count::from_integer(\$v: Integer | ()): Count { 1 }" "" "exactly one required source argument"
conversion_case "type Count = Integer; fn Count::from_integer(\$v: Integer...): Count { 1 }" "" "exactly one required source argument"
conversion_case "type Count = Integer; fn Count::from_integer(\$v: Integer) { 1 }" "" "must declare return type"
conversion_case "type Count = Integer; fn Count::from_integer(\$v: Integer): String { \"bad\" }" "" "must declare return type"
conversion_case "type Count = Integer; fn Count::from_Integer(\$v: Integer): Count { \$v }" "" "must be lowercase"
conversion_case "type Count = Integer; fn Count::from_(\$v: Integer): Count { \$v }" "" "must be lowercase"
conversion_case "type Count = Integer; fn Count::from_integer(\$self: Integer): Count { 1 }" "" "self"
conversion_case "type Count = Integer; fn Count::from_integer(\$v: Integer): Count { \$self }; echo Count(1)" "" "self"
conversion_case "type Count = Integer; fn Count::from_integer(\$v: Integer): Count { \$v }; let \$n: Count = 7; echo \$n.from_integer 1" "" "Conversion methods have no receiver"
conversion_case "struct Target { n: Integer }; echo Target(1)" "" "no positional constructor"
conversion_case "type Count = Integer; echo Count(\"7\")" "" "Expected Integer"
conversion_case "type Count = Integer; fn Count::from_integer(\$v: Integer): Count { \$v + 1 }; let \$n: Integer = 7.5" "" "Expected Integer"
conversion_case "fn Integer::from_string(\$v: String): Integer { exit 0 }; echo Integer(\"7\"); echo unreachable" "" ""
let $fixture = $args[0]
printf "%s" "pub type Count = Integer; pub fn Count::from_string(\$v: String): Count { Integer(\$v) + 1 }; fn Count::from_boolean(\$v: Boolean): Count { 42 }" -> "${fixture}/counts.shy"
printf "%s" "pub type Count = Integer; pub fn Count::from_string(\$v: String): Count { Integer(\$v) + 2 }" -> "${fixture}/other.shy"
printf "%s" "pub import counts::{ Count }; pub visible \"Count\"" -> "${fixture}/bridge.shy"
conversion_case "import counts; echo counts::Count(\"7\")" "8\n" ""
conversion_case "import counts::{ Count }; echo Count(\"7\") (Count::from_string \"8\")" "8 9\n" ""
conversion_case "import counts; echo counts::Count(true)" "" "Expected Integer"
conversion_case "import counts; echo (counts::Count::from_boolean true)" "" "not an enum"
conversion_case "import counts; import other; echo counts::Count(\"7\") other::Count(\"7\")" "8 9\n" ""
conversion_case "import bridge; echo bridge::Count(\"7\")" "8\n" ""
conversion_case "import counts when false; echo ok" "ok\n" ""
mkdir -p "${fixture}/std"
printf "%s" "pub type PreludeCount = Integer; pub fn PreludeCount::from_string(\$v: String): PreludeCount { Integer(\$v) + 3 }" -> "${fixture}/std/prelude.shy"
printf "%s" "pub fn value(): PreludeCount { PreludeCount(\"7\") }" -> "${fixture}/user.shy"
conversion_case "import user; echo PreludeCount(\"7\") (user::value)" "10 10\n" ""
# Collection source types select distinct factories.
conversion_case "type Count = Integer; fn Count::from_array_integer(\$v: [Integer]): Count { \$v.count }; fn Count::from_array_string(\$v: [String]): Count { 9 }; echo Count([1,2]) Count([\"x\"])" "2 9\n" ""
conversion_case "type Count = Integer; fn Count::from_array_any(\$v: Array): Count { \$v.count }; echo Count([1,\"x\"]) Count([])" "2 0\n" ""
conversion_case "type Count = Integer; fn Count::from_hash_string_integer(\$v: [String: Integer]): Count { \$v[\"x\"] }; fn Count::from_hash_integer_string(\$v: [Integer: String]): Count { 9 }; echo Count([\"x\":7]) Count([1:\"x\"])" "7 9\n" ""
conversion_case "type Count = Integer; fn Count::from_hash_string_any(\$v: HashMap): Count { 4 }; fn Count::from_hash_any_integer(\$v: HashMap): Count { 5 }; fn Count::from_hash_any_any(\$v: HashMap): Count { 6 }; echo Count([\"x\":1,\"y\":\"z\"]) Count([1:1,\"x\":2]) Count([:])" "4 5 6\n" ""
conversion_case "type Coin = Integer; type Count = Integer; fn Count::from_array_coin(\$v: [Coin]): Count { Integer(\$v[0]) }; echo Count([Coin(7)])" "7\n" ""
conversion_case "type Key = String; type Count = Integer; fn Count::from_hash_key_integer(\$v: [Key: Integer]): Count { \$v[Key(\"x\")] }; echo Count([Key(\"x\"):7])" "7\n" ""
conversion_case "type Count = Integer; fn Count::from_array_array_integer(\$v: [[Integer]]): Count { \$v[0].count }; echo Count([[1,2]])" "2\n" ""
conversion_case "type Count = Integer; fn Count::from_hash_string_array_integer(\$v: [String: [Integer]]): Count { \$v[\"x\"].count }; echo Count([\"x\":[1,2]])" "2\n" ""
conversion_case "type Count = Integer; fn Count::from_array_integer(\$v: [Integer]): Count { \$v.count }; let \$v: [Float] = [1]; echo Count(\$v)" "" "Expected Integer"
conversion_case "type Count = Integer; fn Count::from_array(\$v: Array): Count { 1 }; echo Count([1])" "" "Expected Integer"
conversion_case "type Count = Integer; fn Count::from_hashmap(\$v: HashMap): Count { 1 }; echo Count([\"x\":1])" "" "Expected Integer"
conversion_case "type Count = Integer; fn Count::from_array_integer(\$v: [Integer]): any { \"bad\" }; echo Count([1])" "" "Expected Integer"
conversion_case "let \$calls = 0; type Count = Integer; fn source(): [Integer] { \$calls = \$calls + 1; [1] }; fn Count::from_array_integer(\$v: [Integer]): Count { \$calls = \$calls + 1; \$v.count }; echo Count(source) \$calls" "1 2\n" ""
conversion_case "type Items = [Integer]; type Count = Integer; fn Count::from_items(\$v: Items): Count { \$v.count }; fn Count::from_array_integer(\$v: [Integer]): Count { 9 }; echo Count(Items([1,2]))" "2\n" ""
# Explicit collection contracts survive empty values and argument binding.
conversion_case "type Count = Integer; fn Count::from_array_string(\$v: [String]): Count { 9 }; fn Count::from_array_any(\$v: Array): Count { 1 }; let \$v: [String] = []; echo Count(\$v)" "9\n" ""
conversion_case "type Count = Integer; fn Count::from_array_any(\$v: Array): Count { 9 }; fn Count::from_array_integer(\$v: [Integer]): Count { 1 }; let \$v: [any] = [1,2]; echo Count(\$v)" "9\n" ""
conversion_case "type Count = Integer; fn Count::from_hash_string_float(\$v: [String: Float]): Count { 9 }; let \$v: [String: Float] = [:]; echo Count(\$v)" "9\n" ""
conversion_case "type Count = Integer; fn Count::from_array_float(\$v: [Float]): Count { 9 }; fn consume(\$v: [Float]): Count { Count(\$v) }; echo (consume [])" "9\n" ""
conversion_case "type Count = Integer; fn Count::from_array_float(\$v: [Float]): Count { 9 }; fn make(): [Float] { [] }; echo Count(make)" "9\n" ""
conversion_case "type Count = Integer; fn Count::from_array_float(\$v: [Float]): Count { 9 }; fn make(\$n: Integer): [Float] { [] }; echo Count(make 1)" "9\n" ""
conversion_case "type Count = Integer; fn Count::from_array_float(\$v: [Float]): Count { 9 }; fn call(): Count { Count(make) }; fn make(): [Float] { [] }; echo (call)" "9\n" ""
conversion_case "type Count = Integer; fn Count::from_array_float(\$v: [Float]): Count { 9 }; let \$v: [Float] = []; fn call(): Count { Count(\$v) }; echo (call)" "9\n" ""
conversion_case "type Count = Integer; fn Count::from_array_float(\$v: [Float]): Count { 9 }; let \$f: fn(): [Float] = fn(): [Float] { [] }; echo Count((\$f))" "9\n" ""
conversion_case "type Count = Integer; fn Count::from_array_float(\$v: [Float]): Count { 9 }; struct Holder { values: [Float] }; let \$v = Holder(values: []); echo Count(\$v.values)" "9\n" ""
conversion_case "type Count = Integer; fn Count::from_array_float(\$v: [Float]): Count { 9 }; let \$v: [[Float]] = [[]]; echo Count(\$v[0])" "9\n" ""
conversion_case "type Count = Integer; fn Count::from_array_float(\$v: [Float]): Count { 9 }; let \$v: [String: [Float]] = [\"x\": []]; echo Count(\$v[\"x\"])" "9\n" ""
conversion_case "type Count = Integer; fn Count::from_array_float(\$v: [Float]): Count { 9 }; let \$v: [Float] | (); \$v = []; echo Count(\$v)" "9\n" ""
conversion_case "type Count = Integer; fn Count::from_array_float(\$v: [Float]): Count { 9 }; let \$v: Integer | [Float] = []; echo Count(\$v)" "9\n" ""
conversion_case "type Count = Integer; fn Count::from_array_integer(\$v: [Integer]): Count { 1 }; fn Count::from_array_string(\$v: [String]): Count { 2 }; let \$v = [1]; \$v = [\"x\"]; echo Count(\$v)" "2\n" ""
# Implied typedef factories and explicit overrides.
conversion_case "type Count = Integer; let \$v = Count::from_integer 7; echo \$v (\$v.type == Count) Integer(\$v)" "7 true 7\n" ""
conversion_case "type Count = Integer; echo (Count::from_integer 7); let \$f: fn(Integer): Count = `Count::from_integer; echo (\$f 8)" "7\n8\n" ""
conversion_case "type Ratio = Float; echo (Ratio::from_float 7) Ratio(7) Float(Ratio(7))" "7.0 7.0 7.0\n" ""
conversion_case "type Text = String; echo (Text::from_string \"hello\")" "hello\n" ""
conversion_case "type Empty = (); let \$v = Empty::from_none (()); echo (\$v.type == Empty) None(\$v)" "true ()\n" ""
conversion_case "type Anything = any; let \$v = Anything::from_any \"hello\"; echo (\$v.type == Anything) String(\$v)" "true hello\n" ""
conversion_case "type Count = Integer; type Outer = Count; let \$v = Outer::from_count Count(7); echo (\$v.type == Outer) (Count(\$v).type == Count) Integer(\$v)" "true true 7\n" ""
conversion_case "type Count = Integer; type Outer = Count; echo (Outer::from_count 7)" "7\n" ""
conversion_case "type Items = [Integer]; let \$v = Items::from_array_integer [1,2]; echo \$v.count (\$v.type == Items) \$v[0]" "2 true 1\n" ""
conversion_case "type Items = [Float]; let \$v = Items::from_array_float [1,2]; echo \$v[0] \$v[1]" "1.0 2.0\n" ""
conversion_case "type Items = [Integer]; echo (Items::from_array_integer []).count" "0\n" ""
conversion_case "type Table = [String: Integer]; let \$v = Table::from_hash_string_integer [\"x\":7]; echo \$v[\"x\"] (\$v.type == Table)" "7 true\n" ""
conversion_case "type Table = [String: Integer]; let \$v = Table::from_hash_string_integer [:]; echo (HashMap(\$v) == [:])" "true\n" ""
conversion_case "struct Point { x: Integer }; type Position = Point; let \$v = Position::from_point Point(x: 7); echo \$v.x (\$v.type == Position)" "7 true\n" ""
conversion_case "enum State { A, B }; type Mode = State; let \$v = Mode::from_state State::B; echo \$v.index (\$v.type == Mode)" "1 true\n" ""
conversion_case "type Maybe = Integer | (); echo Maybe(7) Maybe(())" "7 ()\n" ""
conversion_case "type Callback = fn(Integer): String; let \$f: Callback = fn(\$n: Integer): String { String(\$n) }; echo (\$f 7)" "7\n" ""
conversion_case "type Count = Integer; fn Count::from_integer(\$v: Integer): Count { \$v + 10 }; echo Count(7) (Count::from_integer 7); let \$v: Count = 7; echo \$v" "17 17\n7\n" ""
conversion_case "fn Count::from_integer(\$v: Integer): Count { \$v + 10 }; type Count = Integer; echo Count(7) (Count::from_integer 7)" "17 17\n" ""
conversion_case "type Count = Integer; fn before(\$v: Integer): Count { Count(\$v) }; fn Count::from_integer(\$v: Integer): Count { \$v + 10 }; echo (before 7)" "17\n" ""
conversion_case "type Count = Integer; fn local(): Count { fn Count::from_integer(\$v: Integer): Count { \$v + 1 }; Count(7) }; echo (local) Count(7) (Count::from_integer 7)" "8 7 7\n" ""
conversion_case "fn local(): Integer { type Count = Integer; Integer((Count::from_integer 7)) }; echo (local)" "7\n" ""
conversion_case "type Count = Integer; type Outer = Count; fn Outer::from_count(\$v: Count): Outer { Integer(\$v) + 1 }; echo Outer(Count(7)) (Outer::from_count Count(7))" "8 8\n" ""
conversion_case "type Items = [Integer]; fn Items::from_array_integer(\$v: [Integer]): Items { [\$v...,9] }; echo Items([1]).count (Items::from_array_integer [1]).count" "2 2\n" ""
conversion_case "type Count = Integer; echo (Count::from_integer \"7\")" "" "Expected Integer"
conversion_case "type Count = Integer; echo (Count::from_integer 7.5)" "" "Expected Integer"
conversion_case "type Count = Integer; let \$f = `Count::from_integer; echo (\$f)" "" "expected 1 arguments"
conversion_case "type Count = Integer; echo (Count::from_integer 1 2)" "" "expected 1 arguments"
conversion_case "type Count = Integer; let \$v: Count = 7; echo \$v.from_integer 8" "" "Conversion methods have no receiver"
conversion_case "type Items = [Integer]; echo (Items::from_array_integer [\"bad\"])" "" "Expected Integer"
conversion_case "type Table = [String: Integer]; echo (Table::from_hash_string_integer [\"x\":\"bad\"])" "" "Expected Integer"
printf "%s" "pub type Count = Integer; pub type Items = [Integer]; pub type Outer = Count; type Private = Integer" -> "${fixture}/defaults.shy"
printf "%s" "pub import defaults::{ Count }; pub visible \"Count\"" -> "${fixture}/default_bridge.shy"
printf "%s" "pub type Count = Integer; fn Count::from_integer(\$v: Integer): Count { \$v + 1 }; pub fn make(): Count { Count(7) }" -> "${fixture}/private_override.shy"
printf "%s" "pub fn Count::from_integer(\$v: Integer): Count { \$v + 1 }; pub type Count = Integer" -> "${fixture}/public_override.shy"
conversion_case "import defaults; echo (defaults::Count::from_integer 7) defaults::Count(8)" "7 8\n" ""
conversion_case "import defaults::{ Count }; let \$f = `Count::from_integer; echo (\$f 7)" "7\n" ""
conversion_case "import defaults; echo (defaults::Items::from_array_integer [1,2]).count (defaults::Outer::from_count defaults::Count(7))" "2 7\n" ""
conversion_case "import default_bridge; echo (default_bridge::Count::from_integer 7)" "7\n" ""
conversion_case "import defaults; echo (defaults::Private::from_integer 7)" "" "is unavailable"
conversion_case "import private_override; echo (private_override::make) private_override::Count(7)" "8 7\n" ""
conversion_case "import private_override; echo (private_override::Count::from_integer 7)" "" "not an enum"
conversion_case "import public_override; echo public_override::Count(7) (public_override::Count::from_integer 7)" "8 8\n" ""
conversion_case "import user; echo (PreludeCount::from_integer 7)" "7\n" ""