Packages

The gleam code generator's code generator

Current section

Files

Jump to
derived src derived.gleam
Raw

src/derived.gleam

import derived/ast
import gleam/list
import gleam/regexp
import gleam/result
import gleam/string
pub fn parse(input: String) {
ast.parse(input)
}
pub fn generate(
input: String,
callback: fn(ast.DerivedType) -> Result(String, Nil),
) -> String {
let derived_types = input |> parse |> list.reverse
use source_after_types, derived_type <- list.fold(derived_types, input)
use source_after_derivations, derived_name <- list.fold(
derived_type.derived_names |> list.reverse,
source_after_types,
)
derived_type
|> callback
|> result.map(create_derived_content(
_,
derived_name,
source_after_derivations,
derived_type,
))
|> result.unwrap(source_after_derivations)
}
fn create_derived_content(
generated_code: String,
derived_name: String,
source: String,
derived_type: ast.DerivedType,
) -> String {
let new_content =
start_marker(derived_name)
<> "\n"
<> generated_code
<> "\n"
<> end_marker(derived_name)
find_and_replace_markers(source, derived_name, new_content)
|> result.lazy_unwrap(fn() {
insert_after_type(source, derived_type, new_content)
})
}
fn find_and_replace_markers(
source: String,
derived_name: String,
new_content: String,
) -> Result(String, Nil) {
let pattern =
start_marker(derived_name) <> "[\\s\\S]*?" <> end_marker(derived_name)
case
regexp.compile(
pattern,
regexp.Options(case_insensitive: False, multi_line: True),
)
{
Ok(regex) -> {
case regexp.replace(regex, source, new_content) {
result if result != source -> Ok(result)
_ -> Error(Nil)
}
}
Error(_) -> Error(Nil)
}
}
fn insert_after_type(
source: String,
derived_type: ast.DerivedType,
content: String,
) -> String {
let #(_start, end) = derived_type.span
let before = string.slice(source, 0, end + 1)
let after = string.slice(source, end + 1, string.length(source) - end - 1)
before <> "\n\n" <> content <> after
}
fn start_marker(derived_name: String) -> String {
"// ---- BEGIN DERIVED " <> derived_name <> " DO NOT MODIFY ---- //"
}
fn end_marker(derived_name: String) -> String {
"// ---- END DERIVED " <> derived_name <> " //"
}