Packages
caffeine_lang
0.0.24
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/deps/cql/parser.gleam
import gleam/result
import gleam/string
// ===== Types =====
pub type Query {
Query(exp: Exp)
}
pub type ExpContainer {
ExpContainer(exp: Exp)
}
pub type Operator {
Add
Sub
Mul
Div
}
pub type Exp {
OperatorExpr(numerator: Exp, denominator: Exp, operator: Operator)
Primary(primary: Primary)
}
pub type Primary {
PrimaryWord(word: Word)
PrimaryExp(exp: Exp)
}
pub type Word {
Word(value: String)
}
//==========================================
pub fn parse_expr(input: String) -> Result(ExpContainer, String) {
use exp <- result.try(do_parse_expr(input))
Ok(ExpContainer(exp))
}
pub fn do_parse_expr(input: String) -> Result(Exp, String) {
let trimmed = string.trim(input)
case is_fully_parenthesized(trimmed) {
True -> {
let inner = string.slice(trimmed, 1, string.length(trimmed) - 2)
use inner_exp <- result.try(do_parse_expr(inner))
Ok(Primary(PrimaryExp(inner_exp)))
}
False -> {
let operators = [#("+", Add), #("-", Sub), #("*", Mul), #("/", Div)]
try_operators(trimmed, operators)
}
}
}
fn is_fully_parenthesized(input: String) -> Bool {
string.starts_with(input, "(")
&& string.ends_with(input, ")")
&& { string.length(input) >= 2 && is_balanced_parens(input, 1, 1) }
}
fn try_operators(
input: String,
operators: List(#(String, Operator)),
) -> Result(Exp, String) {
case operators {
[] -> {
let word = Word(input)
Ok(Primary(PrimaryWord(word)))
}
[#(op_str, op), ..rest] -> {
case find_operator(input, op_str) {
Ok(#(left, right)) -> {
use left_exp <- result.try(do_parse_expr(left))
use right_exp <- result.try(do_parse_expr(right))
Ok(OperatorExpr(left_exp, right_exp, op))
}
Error(_) -> try_operators(input, rest)
}
}
}
}
fn find_operator(
input: String,
operator: String,
) -> Result(#(String, String), String) {
find_rightmost_operator_at_level(input, operator, 0, 0, -1)
}
pub fn is_balanced_parens(input: String, pos: Int, count: Int) -> Bool {
case pos >= string.length(input) {
True -> count == 0
False -> {
let new_count = count_parens(count, input, pos)
let does_not_close_too_early =
!{ { new_count == 0 } && !is_last_char(input, pos) }
does_not_close_too_early && is_balanced_parens(input, pos + 1, new_count)
}
}
}
pub fn is_last_char(input: String, pos: Int) -> Bool {
let is_empty = string.is_empty(input)
let is_last = pos == string.length(input) - 1
is_empty || is_last
}
pub fn find_rightmost_operator_at_level(
input: String,
operator: String,
start_pos: Int,
paren_level: Int,
rightmost_pos: Int,
) -> Result(#(String, String), String) {
let operator_length = string.length(operator)
case start_pos >= string.length(input) {
True ->
case rightmost_pos {
-1 -> Error("Operator not found")
pos -> {
// Split at the rightmost operator position
let left = string.trim(string.slice(input, 0, pos))
let right_start = pos + operator_length
let right_length = string.length(input) - right_start
let right =
string.trim(string.slice(input, right_start, right_length))
Ok(#(left, right))
}
}
False -> {
let new_paren_level = count_parens(paren_level, input, start_pos)
let new_rightmost_pos = case
new_paren_level == 0
&& string.slice(input, start_pos, operator_length) == operator
{
True -> start_pos
False -> rightmost_pos
}
find_rightmost_operator_at_level(
input,
operator,
start_pos + 1,
new_paren_level,
new_rightmost_pos,
)
}
}
}
fn count_parens(cur_count: Int, input: String, pos: Int) -> Int {
let char = string.slice(input, pos, 1)
case char {
"(" -> cur_count + 1
")" -> cur_count - 1
_ -> cur_count
}
}