Packages
caffeine_lang
4.5.0
6.3.1
6.3.0
6.2.2
6.2.1
6.2.0
6.1.2
6.1.1
6.1.0
6.0.0
5.6.0
5.5.0
5.4.4
5.4.3
5.4.2
5.4.1
5.4.0
5.3.0
5.2.0
5.1.1
5.1.0
5.0.12
5.0.11
5.0.10
5.0.8
5.0.7
5.0.6
5.0.5
5.0.4
5.0.1
5.0.0
4.10.0
4.9.0
4.8.3
4.8.2
4.8.1
4.8.0
4.7.9
4.7.8
4.7.7
4.7.6
4.7.5
4.6.7
4.6.6
4.6.5
4.6.4
4.6.3
4.6.2
4.6.0
4.5.1
4.5.0
4.4.4
4.4.3
4.4.1
4.4.0
4.3.7
4.3.6
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.0.2
1.0.1
0.1.0
0.0.24
0.0.23
0.0.22
0.0.21
0.0.20
0.0.19
0.0.18
0.0.17
0.0.16
0.0.15
0.0.14
0.0.13
0.0.12
0.0.11
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.2
0.0.1
A compiler for generating reliability artifacts from service expectation definitions.
Current section
Files
Jump to
Current section
Files
src/caffeine_lang/parsing_utils.gleam
import gleam/list
import gleam/result
import gleam/string
/// Splits a string at commas that are not inside parentheses or curly braces.
/// Used by collection and modifier type parsers for nested type arguments.
@internal
pub fn split_at_top_level_comma(s: String) -> List(String) {
let chars = string.to_graphemes(s)
do_split_at_top_level_comma(chars, 0, 0, "", [])
}
/// Extracts the content inside the outermost pair of parentheses,
/// properly handling nested parentheses.
/// Returns Error if there's content after the closing paren (e.g., refinement constraints).
@internal
pub fn extract_paren_content(raw: String) -> Result(String, Nil) {
case string.split_once(raw, "(") {
Error(_) -> Error(Nil)
Ok(#(_, after_open)) -> {
// Find the matching close paren by tracking nesting depth
use #(content, rest) <- result.try(find_matching_close_paren(
after_open,
1,
"",
))
// If there's non-whitespace content after the closing paren,
// this is likely a refinement type and we should fail
case string.trim(rest) {
"" -> Ok(string.trim(content))
_ -> Error(Nil)
}
}
}
}
/// Finds the matching closing parenthesis, tracking nesting depth.
/// Returns the content before the matching close and the rest after it.
fn find_matching_close_paren(
s: String,
depth: Int,
acc: String,
) -> Result(#(String, String), Nil) {
case string.pop_grapheme(s) {
Error(_) -> Error(Nil)
Ok(#("(", rest)) -> find_matching_close_paren(rest, depth + 1, acc <> "(")
Ok(#(")", rest)) -> {
case depth {
1 -> Ok(#(acc, rest))
_ -> find_matching_close_paren(rest, depth - 1, acc <> ")")
}
}
Ok(#(char, rest)) -> find_matching_close_paren(rest, depth, acc <> char)
}
}
/// Extracts the trimmed content inside the outermost parentheses.
/// Falls back to trimming the raw string if no valid parenthesized content is found.
@internal
pub fn paren_innerds_trimmed(raw: String) -> String {
case extract_paren_content(raw) {
Ok(content) -> content
Error(_) -> string.trim(raw)
}
}
/// Splits a parenthesized type string at the top-level comma only.
/// Handles nested parentheses correctly.
/// Example: "(String, Dict(String, Integer))" -> ["String", "Dict(String, Integer)"]
@internal
pub fn paren_innerds_split_and_trimmed(raw: String) -> List(String) {
case extract_paren_content(raw) {
Ok(content) -> split_at_top_level_comma(content)
Error(_) -> []
}
}
fn do_split_at_top_level_comma(
chars: List(String),
paren_depth: Int,
brace_depth: Int,
current: String,
acc: List(String),
) -> List(String) {
case chars {
[] -> {
let trimmed = string.trim(current)
case trimmed {
"" -> list.reverse(acc)
_ -> list.reverse([trimmed, ..acc])
}
}
["(", ..rest] ->
do_split_at_top_level_comma(
rest,
paren_depth + 1,
brace_depth,
current <> "(",
acc,
)
[")", ..rest] ->
do_split_at_top_level_comma(
rest,
paren_depth - 1,
brace_depth,
current <> ")",
acc,
)
["{", ..rest] ->
do_split_at_top_level_comma(
rest,
paren_depth,
brace_depth + 1,
current <> "{",
acc,
)
["}", ..rest] ->
do_split_at_top_level_comma(
rest,
paren_depth,
brace_depth - 1,
current <> "}",
acc,
)
[",", ..rest] if paren_depth == 0 && brace_depth == 0 -> {
let trimmed = string.trim(current)
do_split_at_top_level_comma(rest, paren_depth, brace_depth, "", [
trimmed,
..acc
])
}
[char, ..rest] ->
do_split_at_top_level_comma(
rest,
paren_depth,
brace_depth,
current <> char,
acc,
)
}
}