Skip to content

Commit

Permalink
Treat Hurl string as template
Browse files Browse the repository at this point in the history
  • Loading branch information
fabricereix committed Nov 18, 2024
1 parent fd3890f commit eb16dde
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 93 deletions.
31 changes: 16 additions & 15 deletions docs/spec/grammar/hurl.grammar
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,11 @@ very-verbose-option: "very-verbose" ":" boolean-option lt

variable-definition: variable-name "=" variable-value

boolean-option: boolean | template
boolean-option: boolean | placeholder

integer-option: integer | template
integer-option: integer | placeholder

duration-option: (integer duration-unit?) | template
duration-option: (integer duration-unit?) | placeholder

duration-unit: "ms" | "s" | "m"

Expand Down Expand Up @@ -364,7 +364,7 @@ predicate-value:
| oneline-file
| oneline-hex
| quoted-string
| template
| placeholder


# Bytes
Expand All @@ -391,7 +391,7 @@ oneline-hex: "hex," hexdigit* ";"

# Strings

quoted-string: "\"" (quoted-string-content | template)* "\""
quoted-string: "\"" (quoted-string-content | placeholder)* "\""

quoted-string-content: (quoted-string-text | quoted-string-escaped-char)*

Expand All @@ -400,7 +400,7 @@ quoted-string-text: ~["\\]+
quoted-string-escaped-char: "\\" ("\"" | "\\" | "\b" | "\f" | "\n" | "\r" | "\t" | "\u" unicode-char)


key-string: (key-string-content | template)+
key-string: (key-string-content | placeholder)+

key-string-content: (key-string-text | key-string-escaped-char)*

Expand All @@ -409,7 +409,7 @@ key-string-text: (alphanum | "_" | "-" | "." | "[" | "]" | "@" | "$") +
key-string-escaped-char: "\\" ("#" | ":" | "\\" | "\b" | "\f" | "\n" | "\r" | "\t" | "\u" unicode-char )


value-string: (value-string-content | template)*
value-string: (value-string-content | placeholder)*

value-string-content: (value-string-text | value-string-escaped-char)*

Expand All @@ -418,7 +418,7 @@ value-string-text: ~[#\n\\]+
value-string-escaped-char: "\\" ("#" | "\\" | "\b" | "\f" | "\n" | "\r" | "\t" | "\u" unicode-char )


oneline-string: "`" (oneline-string-content | template)* "`"
oneline-string: "`" (oneline-string-content | placeholder)* "`"

oneline-string-content: (oneline-string-text | oneline-string-escaped-char)*

Expand All @@ -429,7 +429,7 @@ oneline-string-escaped-char: "\\" ("`" | "#" | "\\" | "b" | "f" | "u" unicode-ch

multiline-string:
"```" multiline-string-type? ("," multiline-string-attribute)* lt
(multiline-string-content | template)* lt
(multiline-string-content | placeholder)* lt
"```"

multiline-string-type:
Expand All @@ -449,7 +449,7 @@ multiline-string-text: ~[\\]+ ~"```"

multiline-string-escaped-char: "\\" ( "\\" | "b" | "f" | "n" | "r" | "t" | "`" | "u" unicode-char)

filename: (filename-content | template)*
filename: (filename-content | placeholder)*

filename-content: (filename-text | filename-escaped-char)*

Expand All @@ -458,7 +458,7 @@ filename-text: ~[#;{} \n\\]+
filename-escaped-char: "\\" ( "\\" | "b" | "f" | "n" | "r" | "t" | "#" | ";"| " " | "{" | "}" | "u" unicode-char)


filename-password: (filename-password-content | template)*
filename-password: (filename-password-content | placeholder)*

filename-password-content: (filename-password-text | filename-password-escaped-char)*

Expand All @@ -470,10 +470,11 @@ unicode-char: "{" hexdigit+ "}"




# JSON

json-value:
template
placeholder
| json-object
| json-array
| json-string
Expand All @@ -487,7 +488,7 @@ json-key-value: json-string ":" json-value

json-array: "[" json-value ("," json-value)* "]"

json-string: "\"" (json-string-content | template)* "\""
json-string: "\"" (json-string-content | placeholder)* "\""

json-string-content: json-string-text | json-string-escaped-char

Expand All @@ -499,9 +500,9 @@ json-string-escaped-char:
json-number: integer fraction? exponent?


# Template / Expression
# Expression

template: "{{" expr "}}"
placeholder: "{{" expr "}}"

expr: (variable-name | function) (sp filter)*

Expand Down
5 changes: 3 additions & 2 deletions packages/hurl_core/src/parser/filename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
use crate::ast::*;
use crate::parser::error::*;
use crate::parser::primitives::try_literal;
use crate::parser::template;
use crate::parser::{string, ParseResult};
use crate::reader::Reader;

use super::placeholder;

/// Parse a filename.
///
/// A few characters need to be escaped such as space
Expand All @@ -34,7 +35,7 @@ pub fn parse(reader: &mut Reader) -> ParseResult<Template> {
let mut elements = vec![];
loop {
let start = reader.cursor();
match template::parse(reader) {
match placeholder::parse(reader) {
Ok(expr) => {
let element = TemplateElement::Expression(expr);
elements.push(element);
Expand Down
5 changes: 3 additions & 2 deletions packages/hurl_core/src/parser/filename_password.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
use crate::ast::*;
use crate::parser::error::*;
use crate::parser::primitives::try_literal;
use crate::parser::template;
use crate::parser::{string, ParseResult};
use crate::reader::Reader;

use super::placeholder;

/// Parse a filename with an optional password
///
/// This is very similar to the filename parser.
Expand All @@ -35,7 +36,7 @@ pub fn parse(reader: &mut Reader) -> ParseResult<Template> {
let mut elements = vec![];
loop {
let start = reader.cursor();
match template::parse(reader) {
match placeholder::parse(reader) {
Ok(expr) => {
let element = TemplateElement::Expression(expr);
elements.push(element);
Expand Down
4 changes: 2 additions & 2 deletions packages/hurl_core/src/parser/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::parser::template::*;
use crate::parser::ParseResult;
use crate::reader::{Pos, Reader};

use super::template;
use super::placeholder;

pub fn parse(reader: &mut Reader) -> ParseResult<JsonValue> {
choice(
Expand Down Expand Up @@ -272,7 +272,7 @@ pub fn number_value(reader: &mut Reader) -> ParseResult<JsonValue> {
}

fn expression_value(reader: &mut Reader) -> ParseResult<JsonValue> {
let exp = template::parse(reader)?;
let exp = placeholder::parse(reader)?;
Ok(JsonValue::Expression(exp))
}

Expand Down
5 changes: 3 additions & 2 deletions packages/hurl_core/src/parser/key_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,17 @@
use crate::ast::*;
use crate::parser::error::*;
use crate::parser::primitives::*;
use crate::parser::template;
use crate::parser::{string, ParseResult};
use crate::reader::Reader;

use super::placeholder;

pub fn parse(reader: &mut Reader) -> ParseResult<Template> {
let start = reader.cursor();

let mut elements = vec![];
loop {
match template::parse(reader) {
match placeholder::parse(reader) {
Ok(expr) => {
let element = TemplateElement::Expression(expr);
elements.push(element);
Expand Down
1 change: 1 addition & 0 deletions packages/hurl_core/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ mod multiline;
mod number;
mod option;
mod parsers;
mod placeholder;
mod predicate;
mod predicate_value;
mod primitives;
Expand Down
10 changes: 5 additions & 5 deletions packages/hurl_core/src/parser/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::parser::{filename, filename_password, ParseResult};
use crate::reader::Reader;
use crate::typing::Count;

use super::template;
use super::placeholder;

/// Parse an option in an `[Options]` section.
pub fn parse(reader: &mut Reader) -> ParseResult<EntryOption> {
Expand Down Expand Up @@ -293,7 +293,7 @@ fn boolean_option(reader: &mut Reader) -> ParseResult<BooleanOption> {
Ok(v) => Ok(BooleanOption::Literal(v)),
Err(_) => {
reader.seek(start);
let exp = template::parse(reader).map_err(|e| {
let exp = placeholder::parse(reader).map_err(|e| {
let kind = ParseErrorKind::Expecting {
value: "true|false".to_string(),
};
Expand All @@ -310,7 +310,7 @@ fn natural_option(reader: &mut Reader) -> ParseResult<NaturalOption> {
Ok(v) => Ok(NaturalOption::Literal(v)),
Err(_) => {
reader.seek(start);
let exp = template::parse(reader).map_err(|e| {
let exp = placeholder::parse(reader).map_err(|e| {
let kind = ParseErrorKind::Expecting {
value: "integer >= 0".to_string(),
};
Expand All @@ -327,7 +327,7 @@ fn count_option(reader: &mut Reader) -> ParseResult<CountOption> {
Ok(v) => Ok(CountOption::Literal(v)),
Err(_) => {
reader.seek(start);
let exp = template::parse(reader).map_err(|e| {
let exp = placeholder::parse(reader).map_err(|e| {
let kind = ParseErrorKind::Expecting {
value: "integer >= -1".to_string(),
};
Expand All @@ -345,7 +345,7 @@ fn duration_option(reader: &mut Reader) -> ParseResult<DurationOption> {
Err(e) => {
if e.recoverable {
reader.seek(start);
let exp = template::parse(reader).map_err(|e| {
let exp = placeholder::parse(reader).map_err(|e| {
let kind = ParseErrorKind::Expecting {
value: "integer".to_string(),
};
Expand Down
85 changes: 85 additions & 0 deletions packages/hurl_core/src/parser/placeholder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use super::{expr, ParseResult};
/*
* Hurl (https://hurl.dev)
* Copyright (C) 2024 Orange
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
use crate::ast::*;
use crate::parser::primitives::*;
use crate::reader::Reader;

/// Parse a placeholder {{ expr }}
pub fn parse(reader: &mut Reader) -> ParseResult<Expr> {
try_literal("{{", reader)?;
let expression = expr::parse(reader)?;
literal("}}", reader)?;
Ok(expression)
}

#[cfg(test)]
mod tests {
use super::*;
use crate::{parser::ParseErrorKind, reader::Pos};

#[test]
fn test_expr() {
let mut reader = Reader::new("{{ name}}");
assert_eq!(
parse(&mut reader).unwrap(),
Expr {
space0: Whitespace {
value: String::from(" "),
source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 4)),
},
variable: Variable {
name: String::from("name"),
source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 8)),
},
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 8), Pos::new(1, 8)),
},
}
);
}

#[test]
fn test_expr_error() {
let mut reader = Reader::new("{{host>}}");
let error = parse(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 7 });
assert_eq!(
error.kind,
ParseErrorKind::Expecting {
value: String::from("}}")
}
);
assert!(!error.recoverable);
}

#[test]
fn test_expr_error_eof() {
let mut reader = Reader::new("{{host");
let error = parse(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 7 });
assert_eq!(
error.kind,
ParseErrorKind::Expecting {
value: String::from("}}")
}
);
assert!(!error.recoverable);
}
}
4 changes: 2 additions & 2 deletions packages/hurl_core/src/parser/predicate_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::parser::string::*;
use crate::parser::{ParseError, ParseErrorKind, ParseResult};
use crate::reader::Reader;

use super::template;
use super::placeholder;

pub fn predicate_value(reader: &mut Reader) -> ParseResult<PredicateValue> {
choice(
Expand Down Expand Up @@ -53,7 +53,7 @@ pub fn predicate_value(reader: &mut Reader) -> ParseResult<PredicateValue> {
Ok(value) => Ok(PredicateValue::Base64(value)),
Err(e) => Err(e),
},
|p1| match template::parse(p1) {
|p1| match placeholder::parse(p1) {
Ok(value) => Ok(PredicateValue::Expression(value)),
Err(e) => Err(e),
},
Expand Down
Loading

0 comments on commit eb16dde

Please sign in to comment.