Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Treat Hurl string as template #3405

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 21 additions & 16 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 | value-expr

integer-option: integer | template
integer-option: integer | value-expr

duration-option: (integer duration-unit?) | template
duration-option: (integer duration-unit?) | value-expr

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

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

value-expr: "{{" expr "}}"


# Bytes
Expand All @@ -391,7 +393,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 +402,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 +411,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 +420,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 +431,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 +451,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,36 +460,41 @@ 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)*

filename-password-text: ~[#;{} \n\\]+

filename-password-escaped-char: "\\" ( "\\" | "b" | "f" | "n" | "r" | "t" | "#" | ";" | " " | "{" | "}" | ":" | "u" unicode-char)

placeholder: "{{" expr "}}"

unicode-char: "{" hexdigit+ "}"




# JSON

json-value:
template
json-expr
| json-object
| json-array
| json-string
| json-number
| boolean
| null

json-expr: "{{" expr "}}"

json-object: "{" json-key-value ("," json-key-value)* "}"

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 +506,7 @@ json-string-escaped-char:
json-number: integer fraction? exponent?


# Template / Expression

template: "{{" expr "}}"
# Expression

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
Loading