A Gleam codegen library in Gleam

Refactor into seperate modules

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

lesbian.skin cfe6086d 21360986

verified
+8 -4
README.md
··· 9 ```gleam 10 import gleam/io 11 import stare 12 13 pub fn main() -> Nil { 14 let assert Ok(code) = stare.module( 15 imports: [ 16 - stare.import_( 17 module: ["gleam", "io"], 18 types: [], 19 constructors: [], ··· 22 ), 23 ], 24 functions: [ 25 - stare.function( 26 name: "main", 27 public: True, 28 parameters: [], 29 - return_type: Some(type_.nil()), 30 statements: [ 31 - stare.function_call( 32 module: Some("io"), 33 function: "println", 34 arguments: [stare.value(value.string("Hello World!"))],
··· 9 ```gleam 10 import gleam/io 11 import stare 12 + import stare/function 13 + import stare/value 14 + import stare/type_ 15 + import stare/import_ 16 17 pub fn main() -> Nil { 18 let assert Ok(code) = stare.module( 19 imports: [ 20 + import_.import_( 21 module: ["gleam", "io"], 22 types: [], 23 constructors: [], ··· 26 ), 27 ], 28 functions: [ 29 + function.function( 30 name: "main", 31 public: True, 32 parameters: [], 33 + return_type: Some(type_.nil), 34 statements: [ 35 + function.call( 36 module: Some("io"), 37 function: "println", 38 arguments: [stare.value(value.string("Hello World!"))],
+22 -187
src/stare.gleam
··· 1 import gleam/list 2 - import gleam/option.{type Option} 3 import gleam/result 4 - import gleam/string 5 import gleam/string_tree 6 import shellout 7 import simplifile 8 - import stare/type_ 9 import stare/value 10 11 @internal 12 pub opaque type Module(a, b) { 13 - Module(imports: List(Import), functions: List(Function(a, b))) 14 } 15 16 pub fn module( 17 - imports imports: List(Import), 18 - functions functions: List(Function(a, b)), 19 ) -> Module(a, b) { 20 Module(imports:, functions:) 21 } 22 23 - @internal 24 - pub opaque type Import { 25 - Import( 26 - module: List(String), 27 - types: List(String), 28 - constructors: List(String), 29 - functions: List(String), 30 - alias: Option(String), 31 - ) 32 } 33 34 - pub fn import_( 35 - module module: List(String), 36 - types types: List(String), 37 - constructors constructors: List(String), 38 - functions functions: List(String), 39 - alias alias: Option(String), 40 - ) -> Import { 41 - Import(module:, types:, constructors:, functions:, alias:) 42 } 43 44 - @internal 45 - pub opaque type Parameter { 46 - Parameter(name: String, label: Option(String), type_: Option(type_.Type)) 47 } 48 49 - pub fn parameter( 50 - label label: Option(String), 51 - name name: String, 52 - type_ type_: Option(type_.Type), 53 - ) -> Parameter { 54 - Parameter(name:, label:, type_:) 55 - } 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 - 69 - @internal 70 - pub opaque type Statement(a, b) { 71 - FunctionCall( 72 - module: Option(String), 73 - function_name: String, 74 - arguments: List(Statement(a, b)), 75 - ) 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)) 80 - } 81 - 82 - pub fn add_ints(left left: Int, right right: Int) -> Statement(a, b) { 83 - AddInts(value.int(left), value.int(right)) 84 - } 85 - 86 - pub fn add_floats(left left: Float, right right: Float) -> Statement(a, b) { 87 - AddFloats(value.float(left), value.float(right)) 88 - } 89 - 90 - pub fn value(value value: value.Value(a, b)) -> Statement(a, b) { 91 - Raw(value) 92 - } 93 - 94 - pub fn local_variable(name name: String) -> Statement(a, b) { 95 - LocalVariable(name) 96 - } 97 - 98 - pub fn function_call( 99 - module module: Option(String), 100 - function function_name: String, 101 - arguments arguments: List(Statement(a, b)), 102 - ) -> Statement(a, b) { 103 - FunctionCall(module:, function_name:, arguments:) 104 - } 105 - 106 - @internal 107 - pub opaque type Function(a, b) { 108 - Function( 109 - name: String, 110 - public: Bool, 111 - params: List(Parameter), 112 - type_: Option(type_.Type), 113 - statements: List(Statement(a, b)), 114 - ) 115 - } 116 - 117 - pub fn function( 118 - name name: String, 119 - public public: Bool, 120 - parameters params: List(Parameter), 121 - return_type type_: Option(type_.Type), 122 - statements statements: List(Statement(a, b)), 123 - ) -> Function(a, b) { 124 - Function(name:, public:, params:, type_:, statements:) 125 - } 126 - 127 - pub fn generate_import(import_ import_: Import) -> String { 128 - let Import(module:, types:, constructors:, functions:, alias:) = import_ 129 - let i = 130 - "import " 131 - <> list.fold(module, "", fn(acc, part) { acc <> part <> "/" }) 132 - |> string.drop_end(1) 133 - 134 - let i = case types, constructors, functions { 135 - [], [], [] -> i 136 - _, _, _ -> { 137 - i 138 - <> ".{" 139 - <> list.fold(types, "", fn(acc, type_) { acc <> "type " <> type_ <> "," }) 140 - <> list.fold(constructors, "", fn(acc, constructor) { 141 - acc <> constructor <> "," 142 - }) 143 - <> list.fold(functions, "", fn(acc, function) { acc <> function <> "," }) 144 - <> "}" 145 - } 146 - } 147 - case alias { 148 - option.None -> i 149 - option.Some(alias) -> i <> " as " <> alias 150 - } 151 - } 152 - 153 - pub fn generate_statement(statement statement: Statement(a, b)) -> String { 154 - case statement { 155 - FunctionCall(module:, function_name:, arguments:) -> { 156 - string_tree.new() 157 - |> string_tree.append(case module { 158 - option.None -> "" 159 - option.Some(module) -> module <> "." 160 - }) 161 - |> string_tree.append(function_name) 162 - |> string_tree.append("(") 163 - |> string_tree.append( 164 - list.fold(arguments, "", fn(acc, arg) { 165 - acc <> generate_statement(arg) <> "," 166 - }), 167 - ) 168 - |> string_tree.append(")") 169 - |> string_tree.to_string() 170 - } 171 - Raw(value) -> value.to_string(value) 172 - AddInts(left, right) -> 173 - value.to_string(left) <> "+" <> value.to_string(right) 174 - AddFloats(left, right) -> 175 - value.to_string(left) <> "+." <> value.to_string(right) 176 - LocalVariable(string) -> string 177 - } 178 - } 179 - 180 - pub fn generate_function(function function: Function(a, b)) -> String { 181 - string_tree.new() 182 - |> string_tree.append(case function.public { 183 - False -> "" 184 - True -> "pub " 185 - }) 186 - |> string_tree.append("fn ") 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 - ) 194 - |> string_tree.append(") ") 195 - |> string_tree.append(case function.type_ { 196 - option.None -> "" 197 - option.Some(type_) -> "-> " <> type_.to_string(type_:) <> " " 198 - }) 199 - |> string_tree.append("{\n") 200 - |> string_tree.append_tree( 201 - function.statements 202 - |> list.fold(string_tree.new(), fn(acc, statement) { 203 - string_tree.append(acc, generate_statement(statement) <> "\n") 204 - }), 205 - ) 206 - |> string_tree.append("}") 207 - |> string_tree.to_string() 208 } 209 210 pub fn generate(module: Module(a, b)) -> String { 211 let imports = 212 module.imports 213 |> list.fold(string_tree.new(), fn(acc, import_) { 214 - string_tree.append(acc, generate_import(import_:) <> "\n") 215 }) 216 217 let functions = 218 module.functions 219 |> list.fold(string_tree.new(), fn(acc, function) { 220 - string_tree.append(acc, generate_function(function:) <> "\n") 221 }) 222 223 string_tree.new()
··· 1 import gleam/list 2 import gleam/result 3 import gleam/string_tree 4 import shellout 5 import simplifile 6 + import stare/function 7 + import stare/import_ 8 + import stare/internal/statement 9 import stare/value 10 11 @internal 12 pub opaque type Module(a, b) { 13 + Module( 14 + imports: List(import_.Import), 15 + functions: List(function.Function(a, b)), 16 + ) 17 } 18 19 pub fn module( 20 + imports imports: List(import_.Import), 21 + functions functions: List(function.Function(a, b)), 22 ) -> Module(a, b) { 23 Module(imports:, functions:) 24 } 25 26 + pub fn add_ints(left left: Int, right right: Int) -> statement.Statement(a, b) { 27 + statement.AddInts(value.int(left), value.int(right)) 28 } 29 30 + pub fn add_floats( 31 + left left: Float, 32 + right right: Float, 33 + ) -> statement.Statement(a, b) { 34 + statement.AddFloats(value.float(left), value.float(right)) 35 } 36 37 + pub fn value(value value: value.Value(a, b)) -> statement.Statement(a, b) { 38 + statement.Raw(value) 39 } 40 41 + pub fn local_variable(name name: String) -> statement.Statement(a, b) { 42 + statement.LocalVariable(name) 43 } 44 45 pub fn generate(module: Module(a, b)) -> String { 46 let imports = 47 module.imports 48 |> list.fold(string_tree.new(), fn(acc, import_) { 49 + string_tree.append(acc, import_.generate_import(import_:) <> "\n") 50 }) 51 52 let functions = 53 module.functions 54 |> list.fold(string_tree.new(), fn(acc, function) { 55 + string_tree.append(acc, function.generate_function(function:) <> "\n") 56 }) 57 58 string_tree.new()
+95
src/stare/function.gleam
···
··· 1 + import gleam/list 2 + import gleam/option 3 + import gleam/string_tree 4 + import stare/internal/statement 5 + import stare/type_ 6 + 7 + @internal 8 + pub opaque type Function(a, b) { 9 + Function( 10 + name: String, 11 + public: Bool, 12 + params: List(Parameter), 13 + type_: option.Option(type_.Type), 14 + statements: List(statement.Statement(a, b)), 15 + ) 16 + } 17 + 18 + @internal 19 + pub opaque type Parameter { 20 + Parameter( 21 + name: String, 22 + label: option.Option(String), 23 + type_: option.Option(type_.Type), 24 + ) 25 + } 26 + 27 + pub fn call( 28 + module module: option.Option(String), 29 + function function_name: String, 30 + arguments arguments: List(statement.Statement(a, b)), 31 + ) -> statement.Statement(a, b) { 32 + statement.FunctionCall(module:, function_name:, arguments:) 33 + } 34 + 35 + pub fn parameter( 36 + label label: option.Option(String), 37 + name name: String, 38 + type_ type_: option.Option(type_.Type), 39 + ) -> Parameter { 40 + Parameter(name:, label:, type_:) 41 + } 42 + 43 + @internal 44 + pub fn generate_parameter(parameter param: Parameter) -> String { 45 + case param.label { 46 + option.Some(label) -> label <> " " 47 + option.None -> "" 48 + } 49 + <> param.name 50 + <> case param.type_ { 51 + option.Some(type_) -> ": " <> type_.to_string(type_:) 52 + option.None -> "" 53 + } 54 + } 55 + 56 + pub fn function( 57 + name name: String, 58 + public public: Bool, 59 + parameters params: List(Parameter), 60 + return_type type_: option.Option(type_.Type), 61 + statements statements: List(statement.Statement(a, b)), 62 + ) -> Function(a, b) { 63 + Function(name:, public:, params:, type_:, statements:) 64 + } 65 + 66 + @internal 67 + pub fn generate_function(function function: Function(a, b)) -> String { 68 + string_tree.new() 69 + |> string_tree.append(case function.public { 70 + False -> "" 71 + True -> "pub " 72 + }) 73 + |> string_tree.append("fn ") 74 + |> string_tree.append(function.name <> "(") 75 + |> string_tree.append_tree( 76 + function.params 77 + |> list.fold(string_tree.new(), fn(acc, param) { 78 + string_tree.append(acc, generate_parameter(param) <> ",") 79 + }), 80 + ) 81 + |> string_tree.append(") ") 82 + |> string_tree.append(case function.type_ { 83 + option.None -> "" 84 + option.Some(type_) -> "-> " <> type_.to_string(type_:) <> " " 85 + }) 86 + |> string_tree.append("{\n") 87 + |> string_tree.append_tree( 88 + function.statements 89 + |> list.fold(string_tree.new(), fn(acc, statement) { 90 + string_tree.append(acc, statement.generate_statement(statement) <> "\n") 91 + }), 92 + ) 93 + |> string_tree.append("}") 94 + |> string_tree.to_string() 95 + }
+51
src/stare/import_.gleam
···
··· 1 + import gleam/list 2 + import gleam/option 3 + import gleam/string 4 + 5 + @internal 6 + pub opaque type Import { 7 + Import( 8 + module: List(String), 9 + types: List(String), 10 + constructors: List(String), 11 + functions: List(String), 12 + alias: option.Option(String), 13 + ) 14 + } 15 + 16 + pub fn import_( 17 + module module: List(String), 18 + types types: List(String), 19 + constructors constructors: List(String), 20 + functions functions: List(String), 21 + alias alias: option.Option(String), 22 + ) -> Import { 23 + Import(module:, types:, constructors:, functions:, alias:) 24 + } 25 + 26 + @internal 27 + pub fn generate_import(import_ import_: Import) -> String { 28 + let Import(module:, types:, constructors:, functions:, alias:) = import_ 29 + let i = 30 + "import " 31 + <> list.fold(module, "", fn(acc, part) { acc <> part <> "/" }) 32 + |> string.drop_end(1) 33 + 34 + let i = case types, constructors, functions { 35 + [], [], [] -> i 36 + _, _, _ -> { 37 + i 38 + <> ".{" 39 + <> list.fold(types, "", fn(acc, type_) { acc <> "type " <> type_ <> "," }) 40 + <> list.fold(constructors, "", fn(acc, constructor) { 41 + acc <> constructor <> "," 42 + }) 43 + <> list.fold(functions, "", fn(acc, function) { acc <> function <> "," }) 44 + <> "}" 45 + } 46 + } 47 + case alias { 48 + option.None -> i 49 + option.Some(alias) -> i <> " as " <> alias 50 + } 51 + }
+45
src/stare/internal/statement.gleam
···
··· 1 + import gleam/list 2 + import gleam/option 3 + import gleam/string_tree 4 + import stare/value 5 + 6 + @internal 7 + pub type Statement(a, b) { 8 + FunctionCall( 9 + module: option.Option(String), 10 + function_name: String, 11 + arguments: List(Statement(a, b)), 12 + ) 13 + LocalVariable(String) 14 + Raw(value.Value(a, b)) 15 + AddInts(value.Value(a, b), value.Value(a, b)) 16 + AddFloats(value.Value(a, b), value.Value(a, b)) 17 + } 18 + 19 + @internal 20 + pub fn generate_statement(statement statement: Statement(a, b)) -> String { 21 + case statement { 22 + FunctionCall(module:, function_name:, arguments:) -> { 23 + string_tree.new() 24 + |> string_tree.append(case module { 25 + option.None -> "" 26 + option.Some(module) -> module <> "." 27 + }) 28 + |> string_tree.append(function_name) 29 + |> string_tree.append("(") 30 + |> string_tree.append( 31 + list.fold(arguments, "", fn(acc, arg) { 32 + acc <> generate_statement(arg) <> "," 33 + }), 34 + ) 35 + |> string_tree.append(")") 36 + |> string_tree.to_string() 37 + } 38 + Raw(value) -> value.to_string(value) 39 + AddInts(left, right) -> 40 + value.to_string(left) <> "+" <> value.to_string(right) 41 + AddFloats(left, right) -> 42 + value.to_string(left) <> "+." <> value.to_string(right) 43 + LocalVariable(string) -> string 44 + } 45 + }
+41 -45
test/stare_test.gleam
··· 2 import gleam/option.{None, Some} 3 import gleeunit 4 import stare 5 import stare/type_ 6 import stare/value 7 ··· 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), ··· 40 let module = 41 stare.module( 42 imports: [ 43 - stare.import_( 44 module: ["gleam", "option"], 45 types: [], 46 constructors: ["Some"], ··· 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)), ··· 61 ], 62 return_type: None, 63 statements: [ 64 - stare.function_call( 65 module: Some("option"), 66 function: "unwrap", 67 arguments: [ ··· 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("")), ··· 99 let module = 100 stare.module( 101 imports: [ 102 - stare.import_( 103 module: ["gleam", "io"], 104 types: [], 105 constructors: [], 106 functions: [], 107 alias: None, 108 ), 109 - stare.import_( 110 module: ["gleam", "int"], 111 types: [], 112 constructors: [], ··· 115 ), 116 ], 117 functions: [ 118 - stare.function( 119 name: "main", 120 public: True, 121 parameters: [], 122 return_type: None, 123 statements: [ 124 - stare.function_call( 125 - module: Some("io"), 126 - function: "println", 127 - arguments: [ 128 - stare.function_call( 129 - module: Some("int"), 130 - function: "to_string", 131 - arguments: [stare.add_ints(1, 2)], 132 - ), 133 - ], 134 - ), 135 ], 136 ), 137 ], ··· 148 let module = 149 stare.module( 150 imports: [ 151 - stare.import_( 152 module: ["gleam", "io"], 153 types: [], 154 constructors: [], 155 functions: [], 156 alias: None, 157 ), 158 - stare.import_( 159 module: ["gleam", "float"], 160 types: [], 161 constructors: [], ··· 164 ), 165 ], 166 functions: [ 167 - stare.function( 168 name: "main", 169 public: True, 170 parameters: [], 171 return_type: None, 172 statements: [ 173 - stare.function_call( 174 - module: Some("io"), 175 - function: "println", 176 - arguments: [ 177 - stare.function_call( 178 - module: Some("float"), 179 - function: "to_string", 180 - arguments: [stare.add_floats(1.1, 2.2)], 181 - ), 182 - ], 183 - ), 184 ], 185 ), 186 ], ··· 197 let module = 198 stare.module( 199 imports: [ 200 - stare.import_( 201 module: ["gleam", "io"], 202 types: [], 203 constructors: [], ··· 206 ), 207 ], 208 functions: [ 209 - stare.function( 210 name: "main", 211 public: True, 212 parameters: [], 213 return_type: Some(type_.nil), 214 statements: [ 215 - stare.function_call( 216 - module: Some("io"), 217 - function: "println", 218 - arguments: [stare.value(value.string("Hello World!"))], 219 - ), 220 ], 221 ), 222 ],
··· 2 import gleam/option.{None, Some} 3 import gleeunit 4 import stare 5 + import stare/function 6 + import stare/import_ 7 import stare/type_ 8 import stare/value 9 ··· 14 pub fn labelled_param_test() { 15 let module = 16 stare.module(imports: [], functions: [ 17 + function.function( 18 name: "my_function", 19 public: False, 20 parameters: [ 21 + function.parameter(label: None, name: "wibble", type_: None), 22 + function.parameter( 23 + label: None, 24 + name: "wobble", 25 + type_: Some(type_.int), 26 + ), 27 + function.parameter( 28 label: Some("label"), 29 name: "name", 30 type_: Some(type_.string), ··· 46 let module = 47 stare.module( 48 imports: [ 49 + import_.import_( 50 module: ["gleam", "option"], 51 types: [], 52 constructors: ["Some"], ··· 55 ), 56 ], 57 functions: [ 58 + function.function( 59 name: "do_thing", 60 public: False, 61 parameters: [ 62 + function.parameter( 63 label: None, 64 name: "option", 65 type_: Some(type_.option(qualified: True, some: type_.string)), ··· 67 ], 68 return_type: None, 69 statements: [ 70 + function.call( 71 module: Some("option"), 72 function: "unwrap", 73 arguments: [ ··· 77 ), 78 ], 79 ), 80 + function.function( 81 name: "main", 82 public: True, 83 parameters: [], 84 return_type: None, 85 statements: [ 86 + function.call(module: None, function: "do_thing", arguments: [ 87 stare.value(value.option( 88 qualified: False, 89 option: Some(value.string("")), ··· 105 let module = 106 stare.module( 107 imports: [ 108 + import_.import_( 109 module: ["gleam", "io"], 110 types: [], 111 constructors: [], 112 functions: [], 113 alias: None, 114 ), 115 + import_.import_( 116 module: ["gleam", "int"], 117 types: [], 118 constructors: [], ··· 121 ), 122 ], 123 functions: [ 124 + function.function( 125 name: "main", 126 public: True, 127 parameters: [], 128 return_type: None, 129 statements: [ 130 + function.call(module: Some("io"), function: "println", arguments: [ 131 + function.call( 132 + module: Some("int"), 133 + function: "to_string", 134 + arguments: [stare.add_ints(1, 2)], 135 + ), 136 + ]), 137 ], 138 ), 139 ], ··· 150 let module = 151 stare.module( 152 imports: [ 153 + import_.import_( 154 module: ["gleam", "io"], 155 types: [], 156 constructors: [], 157 functions: [], 158 alias: None, 159 ), 160 + import_.import_( 161 module: ["gleam", "float"], 162 types: [], 163 constructors: [], ··· 166 ), 167 ], 168 functions: [ 169 + function.function( 170 name: "main", 171 public: True, 172 parameters: [], 173 return_type: None, 174 statements: [ 175 + function.call(module: Some("io"), function: "println", arguments: [ 176 + function.call( 177 + module: Some("float"), 178 + function: "to_string", 179 + arguments: [stare.add_floats(1.1, 2.2)], 180 + ), 181 + ]), 182 ], 183 ), 184 ], ··· 195 let module = 196 stare.module( 197 imports: [ 198 + import_.import_( 199 module: ["gleam", "io"], 200 types: [], 201 constructors: [], ··· 204 ), 205 ], 206 functions: [ 207 + function.function( 208 name: "main", 209 public: True, 210 parameters: [], 211 return_type: Some(type_.nil), 212 statements: [ 213 + function.call(module: Some("io"), function: "println", arguments: [ 214 + stare.value(value.string("Hello World!")), 215 + ]), 216 ], 217 ), 218 ],