A Gleam codegen library in Gleam

All types/values from prelude and stdlib

Signed-off-by: Naomi Roberts <mia@naomieow.xyz>

lesbian.skin 21360986 5f628006

verified
+16
birdie_snapshots/imported_type.accepted
··· 1 + --- 2 + version: 1.4.0 3 + title: Imported type 4 + file: ./test/stare_test.gleam 5 + test_name: use_imported_types_test 6 + --- 7 + import gleam/option.{Some} 8 + 9 + fn do_thing(option: option.Option(String)) { 10 + option.unwrap(option, "unwrapped") 11 + } 12 + 13 + pub fn main() { 14 + do_thing(Some("")) 15 + } 16 +
+10
birdie_snapshots/mixed_parameters.accepted
··· 1 + --- 2 + version: 1.4.0 3 + title: Mixed Parameters 4 + file: ./test/stare_test.gleam 5 + test_name: labelled_param_test 6 + --- 7 + fn my_function(wibble, wobble: Int, label name: String) { 8 + "Testing!" 9 + } 10 +
+47 -23
src/stare.gleam
··· 9 9 import stare/value 10 10 11 11 @internal 12 - pub opaque type Module { 13 - Module(imports: List(Import), functions: List(Function)) 12 + pub opaque type Module(a, b) { 13 + Module(imports: List(Import), functions: List(Function(a, b))) 14 14 } 15 15 16 16 pub fn module( 17 17 imports imports: List(Import), 18 - functions functions: List(Function), 19 - ) -> Module { 18 + functions functions: List(Function(a, b)), 19 + ) -> Module(a, b) { 20 20 Module(imports:, functions:) 21 21 } 22 22 ··· 54 54 Parameter(name:, label:, type_:) 55 55 } 56 56 57 + pub fn generate_parameter(parameter param: Parameter) -> String { 58 + case param.label { 59 + option.Some(label) -> label <> " " 60 + option.None -> "" 61 + } 62 + <> param.name 63 + <> case param.type_ { 64 + option.Some(type_) -> ": " <> type_.to_string(type_:) 65 + option.None -> "" 66 + } 67 + } 68 + 57 69 @internal 58 - pub opaque type Statement { 70 + pub opaque type Statement(a, b) { 59 71 FunctionCall( 60 72 module: Option(String), 61 73 function_name: String, 62 - arguments: List(Statement), 74 + arguments: List(Statement(a, b)), 63 75 ) 64 - Raw(value.Value) 65 - AddInts(value.Value, value.Value) 66 - AddFloats(value.Value, value.Value) 76 + LocalVariable(String) 77 + Raw(value.Value(a, b)) 78 + AddInts(value.Value(a, b), value.Value(a, b)) 79 + AddFloats(value.Value(a, b), value.Value(a, b)) 67 80 } 68 81 69 - pub fn add_ints(left left: Int, right right: Int) -> Statement { 82 + pub fn add_ints(left left: Int, right right: Int) -> Statement(a, b) { 70 83 AddInts(value.int(left), value.int(right)) 71 84 } 72 85 73 - pub fn add_floats(left left: Float, right right: Float) -> Statement { 86 + pub fn add_floats(left left: Float, right right: Float) -> Statement(a, b) { 74 87 AddFloats(value.float(left), value.float(right)) 75 88 } 76 89 77 - pub fn value(value value: value.Value) -> Statement { 90 + pub fn value(value value: value.Value(a, b)) -> Statement(a, b) { 78 91 Raw(value) 79 92 } 80 93 94 + pub fn local_variable(name name: String) -> Statement(a, b) { 95 + LocalVariable(name) 96 + } 97 + 81 98 pub fn function_call( 82 99 module module: Option(String), 83 100 function function_name: String, 84 - arguments arguments: List(Statement), 85 - ) -> Statement { 101 + arguments arguments: List(Statement(a, b)), 102 + ) -> Statement(a, b) { 86 103 FunctionCall(module:, function_name:, arguments:) 87 104 } 88 105 89 106 @internal 90 - pub opaque type Function { 107 + pub opaque type Function(a, b) { 91 108 Function( 92 109 name: String, 93 110 public: Bool, 94 111 params: List(Parameter), 95 112 type_: Option(type_.Type), 96 - statements: List(Statement), 113 + statements: List(Statement(a, b)), 97 114 ) 98 115 } 99 116 ··· 102 119 public public: Bool, 103 120 parameters params: List(Parameter), 104 121 return_type type_: Option(type_.Type), 105 - statements statements: List(Statement), 106 - ) -> Function { 122 + statements statements: List(Statement(a, b)), 123 + ) -> Function(a, b) { 107 124 Function(name:, public:, params:, type_:, statements:) 108 125 } 109 126 ··· 133 150 } 134 151 } 135 152 136 - pub fn generate_statement(statement statement: Statement) -> String { 153 + pub fn generate_statement(statement statement: Statement(a, b)) -> String { 137 154 case statement { 138 - FunctionCall(module:, function_name:, arguments: _) -> { 155 + FunctionCall(module:, function_name:, arguments:) -> { 139 156 string_tree.new() 140 157 |> string_tree.append(case module { 141 158 option.None -> "" ··· 144 161 |> string_tree.append(function_name) 145 162 |> string_tree.append("(") 146 163 |> string_tree.append( 147 - list.fold(statement.arguments, "", fn(acc, arg) { 164 + list.fold(arguments, "", fn(acc, arg) { 148 165 acc <> generate_statement(arg) <> "," 149 166 }), 150 167 ) ··· 156 173 value.to_string(left) <> "+" <> value.to_string(right) 157 174 AddFloats(left, right) -> 158 175 value.to_string(left) <> "+." <> value.to_string(right) 176 + LocalVariable(string) -> string 159 177 } 160 178 } 161 179 162 - pub fn generate_function(function function: Function) -> String { 180 + pub fn generate_function(function function: Function(a, b)) -> String { 163 181 string_tree.new() 164 182 |> string_tree.append(case function.public { 165 183 False -> "" ··· 167 185 }) 168 186 |> string_tree.append("fn ") 169 187 |> string_tree.append(function.name <> "(") 188 + |> string_tree.append_tree( 189 + function.params 190 + |> list.fold(string_tree.new(), fn(acc, param) { 191 + string_tree.append(acc, generate_parameter(param) <> ",") 192 + }), 193 + ) 170 194 |> string_tree.append(") ") 171 195 |> string_tree.append(case function.type_ { 172 196 option.None -> "" ··· 183 207 |> string_tree.to_string() 184 208 } 185 209 186 - pub fn generate(module: Module) -> String { 210 + pub fn generate(module: Module(a, b)) -> String { 187 211 let imports = 188 212 module.imports 189 213 |> list.fold(string_tree.new(), fn(acc, import_) {
+154 -6
src/stare/type_.gleam
··· 1 + import gleam/list 2 + 1 3 @internal 2 4 pub type Constructor 3 5 ··· 9 11 opaque_: Bool, 10 12 constructors: List(Constructor), 11 13 ) 14 + GenericType(name: String) 12 15 NilType 13 16 StringType 17 + IntType 18 + FloatType 19 + BoolType 20 + ListType 21 + BitArrayType 22 + StringTreeType(qualified: Bool) 23 + BytesTreeType(qualified: Bool) 24 + DictType(qualified: Bool, key: Type, value: Type) 25 + DynamicType(qualified: Bool, decode: Bool) 26 + DecoderType(qualified: Bool, type_: Type) 27 + DecodeErrorType(qualified: Bool) 28 + ContinueOrStopType(qualified: Bool, type_: Type) 29 + OptionType(qualified: Bool, some: Type) 30 + OrderType(qualified: Bool) 31 + TupleType(items: List(Type)) 32 + ResultType(ok: Type, error: Type) 33 + SetType(qualified: Bool, type_: Type) 34 + UriType(qualified: Bool) 14 35 } 15 36 16 37 @internal 17 38 pub fn to_string(type_ type_: Type) -> String { 39 + let qual = fn(q, x, y) { 40 + case q { 41 + True -> x 42 + False -> y 43 + } 44 + } 45 + 18 46 case type_ { 19 47 CustomType(name:, public: _, opaque_: _, constructors: _) -> name 20 48 NilType -> "Nil" 21 49 StringType -> "String" 50 + BitArrayType -> "BitArray" 51 + BoolType -> "Bool" 52 + BytesTreeType(qualified:) -> 53 + qual(qualified, "bytes_tree.BytesTree", "BytesTree") 54 + ContinueOrStopType(qualified:, type_:) -> 55 + qual(qualified, "list.ContinueOrStop", "ContinueOrStop") 56 + <> "(" 57 + <> to_string(type_) 58 + <> ")" 59 + DecodeErrorType(qualified:) -> 60 + qual(qualified, "decode.DecodeError", "DecodeError") 61 + DecoderType(qualified:, type_:) -> 62 + qual(qualified, "decode.Decoder", "Decoder") 63 + <> "(" 64 + <> to_string(type_) 65 + <> ")" 66 + DictType(qualified:, key:, value:) -> 67 + qual(qualified, "dict.Dict", "Dict") 68 + <> "(" 69 + <> to_string(key) 70 + <> "," 71 + <> to_string(value) 72 + <> ")" 73 + DynamicType(qualified:, decode:) -> 74 + qual( 75 + qualified, 76 + qual(decode, "decode.Dynamic", "dynamic.Dynamic"), 77 + "Dynamic", 78 + ) 79 + FloatType -> "Float" 80 + IntType -> "Int" 81 + ListType -> "List" 82 + OptionType(qualified:, some:) -> 83 + qual(qualified, "option.Option", "Option") 84 + <> "(" 85 + <> to_string(some) 86 + <> ")" 87 + OrderType(qualified:) -> qual(qualified, "order.Order", "Order") 88 + ResultType(ok:, error:) -> 89 + "Result" <> "(" <> to_string(ok) <> "," <> to_string(error) <> ")" 90 + SetType(qualified:, type_:) -> 91 + qual(qualified, "set.Set", "Set") <> "(" <> to_string(type_) <> ")" 92 + StringTreeType(qualified:) -> 93 + qual(qualified, "string_tree.StringTree", "StringTree") 94 + TupleType(items:) -> 95 + "#(" 96 + <> list.fold(items, "", fn(acc, item) { acc <> to_string(item) <> "," }) 97 + <> ")" 98 + UriType(qualified:) -> qual(qualified, "uri.Uri", "Uri") 99 + GenericType(name:) -> name 22 100 } 23 101 } 24 102 25 - pub fn nil() -> Type { 26 - NilType 27 - } 103 + pub const nil: Type = NilType 28 104 29 - pub fn string() -> Type { 30 - StringType 31 - } 105 + pub const string: Type = StringType 106 + 107 + pub const int: Type = IntType 108 + 109 + pub const float: Type = FloatType 110 + 111 + pub const bool: Type = BoolType 112 + 113 + pub const list: Type = ListType 114 + 115 + pub const bit_array: Type = BitArrayType 32 116 33 117 pub fn custom( 34 118 name name: String, ··· 38 122 ) -> Type { 39 123 CustomType(name:, public:, opaque_:, constructors:) 40 124 } 125 + 126 + pub fn string_tree(qualified qualified: Bool) -> Type { 127 + StringTreeType(qualified:) 128 + } 129 + 130 + pub fn bytes_tree(qualified qualified: Bool) -> Type { 131 + BytesTreeType(qualified:) 132 + } 133 + 134 + pub fn dict(qualified qualified: Bool, key key: Type, value value: Type) -> Type { 135 + DictType(qualified:, key:, value:) 136 + } 137 + 138 + pub fn dynamic(qualified qualified: Bool) -> Type { 139 + DynamicType(qualified:, decode: False) 140 + } 141 + 142 + pub fn decode_dynamic(qualified qualified: Bool) -> Type { 143 + DynamicType(qualified:, decode: True) 144 + } 145 + 146 + pub fn decoder(qualified qualified: Bool, type_ type_: Type) -> Type { 147 + DecoderType(qualified:, type_:) 148 + } 149 + 150 + pub fn decode_error(qualified qualified: Bool) -> Type { 151 + DecodeErrorType(qualified) 152 + } 153 + 154 + pub fn continue_or_stop(qualified qualified: Bool, type_ type_: Type) -> Type { 155 + ContinueOrStopType(qualified:, type_:) 156 + } 157 + 158 + pub fn option(qualified qualified: Bool, some some: Type) -> Type { 159 + OptionType(qualified:, some:) 160 + } 161 + 162 + pub fn order(qualified qualified: Bool) -> Type { 163 + OrderType(qualified:) 164 + } 165 + 166 + pub fn tuple(items items: List(Type)) -> Type { 167 + TupleType(items:) 168 + } 169 + 170 + pub fn pair(left left: Type, right right: Type) -> Type { 171 + TupleType([left, right]) 172 + } 173 + 174 + pub fn result(ok ok: Type, error error: Type) -> Type { 175 + ResultType(ok:, error:) 176 + } 177 + 178 + pub fn set(qualified qualified: Bool, type_ type_: Type) -> Type { 179 + SetType(qualified:, type_:) 180 + } 181 + 182 + pub fn uri(qualified qualified: Bool) -> Type { 183 + UriType(qualified:) 184 + } 185 + 186 + pub fn generic(name name: String) -> Type { 187 + GenericType(name:) 188 + }
+167 -12
src/stare/value.gleam
··· 1 + import gleam/bit_array 2 + import gleam/bool 3 + import gleam/dynamic/decode 1 4 import gleam/float 2 5 import gleam/int 6 + import gleam/list 7 + import gleam/option 8 + import gleam/order 9 + import gleam/uri 3 10 4 11 @internal 5 - pub opaque type Value { 12 + pub opaque type Value(a, b) { 13 + // CustomValue 6 14 NilValue 7 - StringValue(String) 8 - IntValue(Int) 9 - FloatValue(Float) 15 + StringValue(string: String) 16 + IntValue(int: Int) 17 + FloatValue(float: Float) 18 + BoolValue(bool: Bool) 19 + ListValue(list: List(Value(a, b))) 20 + BitArrayValue(bit_array: BitArray) 21 + DecodeErrorValue(qualified: Bool, decode_error: decode.DecodeError) 22 + ContinueOrStopValue(qualified: Bool, cos: list.ContinueOrStop(Value(a, b))) 23 + OptionValue(qualified: Bool, option: option.Option(Value(a, b))) 24 + OrderValue(qualified: Bool, order: order.Order) 25 + TupleValue(items: List(Value(a, b))) 26 + ResultValue(result: Result(Value(a, b), Value(a, b))) 27 + UriValue(qualified: Bool, uri: uri.Uri) 10 28 } 11 29 12 30 @internal 13 - pub fn to_string(value value: Value) -> String { 31 + pub fn to_string(value value: Value(a, b)) -> String { 14 32 case value { 15 33 NilValue -> "Nil" 16 34 StringValue(v) -> "\"" <> v <> "\"" 17 35 IntValue(v) -> int.to_string(v) 18 36 FloatValue(v) -> float.to_string(v) 37 + BitArrayValue(bit_array:) -> bit_array.inspect(bit_array) 38 + BoolValue(bool:) -> bool.to_string(bool) 39 + ContinueOrStopValue(qualified:, cos:) -> { 40 + case qualified { 41 + True -> "list." 42 + _ -> "" 43 + } 44 + <> case cos { 45 + list.Continue(value) -> "Continue(" <> to_string(value:) <> ")" 46 + list.Stop(value) -> "Stop(" <> to_string(value:) <> ")" 47 + } 48 + } 49 + DecodeErrorValue(qualified:, decode_error:) -> 50 + case qualified { 51 + True -> "decode." 52 + False -> "" 53 + } 54 + <> "DecodeError(expected: " 55 + <> to_string(string(decode_error.expected)) 56 + <> ", " 57 + <> "found: " 58 + <> to_string(string(decode_error.found)) 59 + <> ", " 60 + <> "path: " 61 + <> to_string(list(decode_error.path |> list.map(string))) 62 + ListValue(list:) -> 63 + "[" 64 + <> list.fold(list, "", fn(acc, item) { acc <> to_string(item) <> "," }) 65 + <> "]" 66 + OptionValue(qualified:, option:) -> 67 + case qualified { 68 + True -> "option." 69 + _ -> "" 70 + } 71 + <> case option { 72 + option.None -> "None" 73 + option.Some(value) -> "Some(" <> to_string(value:) <> ")" 74 + } 75 + OrderValue(qualified:, order:) -> 76 + case qualified { 77 + True -> "order." 78 + False -> "" 79 + } 80 + <> case order { 81 + order.Eq -> "Eq" 82 + order.Gt -> "Gt" 83 + order.Lt -> "Lt" 84 + } 85 + ResultValue(result:) -> 86 + case result { 87 + Ok(value) -> "Ok(" <> to_string(value) <> ")" 88 + Error(value) -> "Error(" <> to_string(value) <> ")" 89 + } 90 + TupleValue(items:) -> 91 + "#(" 92 + <> list.fold(items, "", fn(acc, item) { acc <> to_string(item) <> "," }) 93 + <> ")" 94 + UriValue(qualified:, uri:) -> 95 + case qualified { 96 + True -> "uri." 97 + False -> "" 98 + } 99 + <> "Uri(" 100 + <> "scheme: " 101 + <> to_string(option(qualified, uri.scheme |> option.map(string))) 102 + <> "," 103 + <> "userinfo: " 104 + <> to_string(option(qualified, uri.userinfo |> option.map(string))) 105 + <> "," 106 + <> "host: " 107 + <> to_string(option(qualified, uri.host |> option.map(string))) 108 + <> "," 109 + <> "port: " 110 + <> to_string(option(qualified, uri.port |> option.map(int))) 111 + <> "," 112 + <> "path: " 113 + <> to_string(string(uri.path)) 114 + <> "," 115 + <> "query: " 116 + <> to_string(option(qualified, uri.query |> option.map(string))) 117 + <> "," 118 + <> "fragment: " 119 + <> to_string(option(qualified, uri.fragment |> option.map(string))) 120 + <> ")" 19 121 } 20 122 } 21 123 22 - pub fn nil() -> Value { 124 + pub fn nil() -> Value(a, b) { 23 125 NilValue 24 126 } 25 127 26 - pub fn string(string string: String) -> Value { 27 - StringValue(string) 128 + pub fn string(string string: String) -> Value(a, b) { 129 + StringValue(string:) 130 + } 131 + 132 + pub fn int(int int: Int) -> Value(a, b) { 133 + IntValue(int:) 134 + } 135 + 136 + pub fn float(float float: Float) -> Value(a, b) { 137 + FloatValue(float:) 138 + } 139 + 140 + pub fn bool(bool bool: Bool) -> Value(a, b) { 141 + BoolValue(bool:) 142 + } 143 + 144 + pub fn list(list list: List(Value(a, b))) -> Value(a, b) { 145 + ListValue(list:) 146 + } 147 + 148 + pub fn bit_array(bit_array bit_array: BitArray) -> Value(a, b) { 149 + BitArrayValue(bit_array:) 28 150 } 29 151 30 - pub fn int(int int: Int) -> Value { 31 - IntValue(int) 152 + pub fn decode_error( 153 + qualified qualified: Bool, 154 + decode_error decode_error: decode.DecodeError, 155 + ) -> Value(a, b) { 156 + DecodeErrorValue(qualified:, decode_error:) 32 157 } 33 158 34 - pub fn float(float float: Float) -> Value { 35 - FloatValue(float) 159 + pub fn continue_or_stop( 160 + qualified qualified: Bool, 161 + continue_or_stop cos: list.ContinueOrStop(Value(a, b)), 162 + ) -> Value(a, b) { 163 + ContinueOrStopValue(qualified:, cos:) 164 + } 165 + 166 + pub fn option( 167 + qualified qualified: Bool, 168 + option option: option.Option(Value(a, b)), 169 + ) -> Value(a, b) { 170 + OptionValue(qualified:, option:) 171 + } 172 + 173 + pub fn order(qualified qualified: Bool, order order: order.Order) -> Value(a, b) { 174 + OrderValue(qualified:, order:) 175 + } 176 + 177 + pub fn tuple(items items: List(Value(a, b))) -> Value(a, b) { 178 + TupleValue(items:) 179 + } 180 + 181 + pub fn pair(left left: Value(a, b), right right: Value(a, b)) -> Value(a, b) { 182 + TupleValue([left, right]) 183 + } 184 + 185 + pub fn result(result result: Result(Value(a, b), Value(a, b))) -> Value(a, b) { 186 + ResultValue(result:) 187 + } 188 + 189 + pub fn uri(qualified qualified: Bool, uri uri: uri.Uri) -> Value(a, b) { 190 + UriValue(qualified:, uri:) 36 191 }
+87 -1
test/stare_test.gleam
··· 9 9 gleeunit.main() 10 10 } 11 11 12 + pub fn labelled_param_test() { 13 + let module = 14 + stare.module(imports: [], functions: [ 15 + stare.function( 16 + name: "my_function", 17 + public: False, 18 + parameters: [ 19 + stare.parameter(label: None, name: "wibble", type_: None), 20 + stare.parameter(label: None, name: "wobble", type_: Some(type_.int)), 21 + stare.parameter( 22 + label: Some("label"), 23 + name: "name", 24 + type_: Some(type_.string), 25 + ), 26 + ], 27 + return_type: None, 28 + statements: [stare.value(value.string("Testing!"))], 29 + ), 30 + ]) 31 + 32 + module 33 + |> stare.generate() 34 + |> stare.format() 35 + |> assert_ok() 36 + |> birdie.snap("Mixed Parameters") 37 + } 38 + 39 + pub fn use_imported_types_test() { 40 + let module = 41 + stare.module( 42 + imports: [ 43 + stare.import_( 44 + module: ["gleam", "option"], 45 + types: [], 46 + constructors: ["Some"], 47 + functions: [], 48 + alias: None, 49 + ), 50 + ], 51 + functions: [ 52 + stare.function( 53 + name: "do_thing", 54 + public: False, 55 + parameters: [ 56 + stare.parameter( 57 + label: None, 58 + name: "option", 59 + type_: Some(type_.option(qualified: True, some: type_.string)), 60 + ), 61 + ], 62 + return_type: None, 63 + statements: [ 64 + stare.function_call( 65 + module: Some("option"), 66 + function: "unwrap", 67 + arguments: [ 68 + stare.local_variable("option"), 69 + stare.value(value.string("unwrapped")), 70 + ], 71 + ), 72 + ], 73 + ), 74 + stare.function( 75 + name: "main", 76 + public: True, 77 + parameters: [], 78 + return_type: None, 79 + statements: [ 80 + stare.function_call(module: None, function: "do_thing", arguments: [ 81 + stare.value(value.option( 82 + qualified: False, 83 + option: Some(value.string("")), 84 + )), 85 + ]), 86 + ], 87 + ), 88 + ], 89 + ) 90 + 91 + module 92 + |> stare.generate() 93 + |> stare.format() 94 + |> assert_ok() 95 + |> birdie.snap("Imported type") 96 + } 97 + 12 98 pub fn integer_addition_test() { 13 99 let module = 14 100 stare.module( ··· 124 210 name: "main", 125 211 public: True, 126 212 parameters: [], 127 - return_type: Some(type_.nil()), 213 + return_type: Some(type_.nil), 128 214 statements: [ 129 215 stare.function_call( 130 216 module: Some("io"),