diff --git a/src/hcl/README.md b/src/hcl/README.md index 75baa9b..23140fa 100644 --- a/src/hcl/README.md +++ b/src/hcl/README.md @@ -24,6 +24,7 @@ Typed HCL syntax parser for MoonBit, implemented with `cogna-dev/parkit/nom`. - Nested bodies (recursive block parsing) - Expressions: - string + - mustache template (`{{ ... }}`) - number - bool - null diff --git a/src/hcl/hcl_test.mbt b/src/hcl/hcl_test.mbt index ebb25e3..ed3d1f4 100644 --- a/src/hcl/hcl_test.mbt +++ b/src/hcl/hcl_test.mbt @@ -66,3 +66,19 @@ test "parse error on malformed input" { let result = try? @hcl.parse("service { name = \"x\"") inspect(result is Err(_), content="true") } + +///| +test "parse mustache template value" { + let input = + #|image = {{ repository }} + let doc = @hcl.parse(input) + guard doc.body.items[0] is @hcl.BodyItem::Attribute(image) else { + fail("expected image attribute") + } + inspect( + image.value, + content=( + #|MustacheTemplate(" repository ") + ), + ) +} diff --git a/src/hcl/parse.mbt b/src/hcl/parse.mbt index e432c5f..2dc9b6e 100644 --- a/src/hcl/parse.mbt +++ b/src/hcl/parse.mbt @@ -159,6 +159,40 @@ fn string_parser() -> @nom.Parser[String] { }) } +///| +fn mustache_template_parser() -> @nom.Parser[String] { + @nom.Parser::new(fn(input) { + let (rest_left, _) = match @nom.char_('{').parse(input) { + Ok(v) => v + Err(e) => return Err(e) + } + let (rest_open, _) = match @nom.char_('{').parse(rest_left) { + Ok(v) => v + Err(e) => return Err(e) + } + let mut i = 0 + let mut out = "" + while i < rest_open.length() { + let c = match rest_open.get_char(i) { + Some(c) => c + None => + return Err( + @nom.ParseError::new(rest_open, "unterminated mustache template"), + ) + } + if c == '}' && i + 1 < rest_open.length() { + match rest_open.get_char(i + 1) { + Some('}') => return Ok((rest_open[i + 2:].to_string(), out)) + _ => () + } + } + out = out + String::make(1, c) + i += 1 + } + Err(@nom.ParseError::new(rest_open, "unterminated mustache template")) + }) +} + ///| fn key_parser() -> @nom.Parser[String] { @nom.alt([string_parser(), identifier_parser()]) @@ -179,6 +213,14 @@ fn parse_value(input : String) -> @nom.IResult[Expression] { Err(_) => () } + match + @nom.map(mustache_template_parser(), fn(v) { + Expression::MustacheTemplate(v) + }).parse(source) { + Ok(v) => return Ok(v) + Err(_) => () + } + match parse_list(source) { Ok(v) => return Ok(v) Err(_) => () diff --git a/src/hcl/types.mbt b/src/hcl/types.mbt index 7c0d50e..1d671b8 100644 --- a/src/hcl/types.mbt +++ b/src/hcl/types.mbt @@ -47,6 +47,7 @@ pub(all) struct Block { /// HCL expression values parsed by this package. pub(all) enum Expression { String(String) + MustacheTemplate(String) Number(Double) Bool(Bool) Null