///|
/// Errors that can occur when a cucumber expression is parsed or compiled.
///
/// `position` is the code point offset of the problem in the expression.
/// `message` is the full message, in the same format as the reference
/// implementation.
pub suberror ExpressionError {
/// A `{` has no matching `}`.
UnmatchedBrace(position~ : Int, message~ : String)
/// A `(` has no matching `)`.
UnmatchedParen(position~ : Int, message~ : String)
/// A character that can not be escaped follows a `\`.
CannotEscape(position~ : Int, character~ : Char, message~ : String)
/// The expression ends with a `\`.
UnexpectedEscapeEnd(position~ : Int, message~ : String)
/// The expression has a structure that is not permitted, for example an
/// optional inside an optional.
ValidationError(position~ : Int, message~ : String)
/// The expression uses a parameter type that is not registered.
UnknownParameterType(name~ : String, position~ : Int, message~ : String)
} derive(Debug)
///|
/// The full error message.
pub fn ExpressionError::message(self : ExpressionError) -> String {
match self {
UnmatchedBrace(message~, ..)
| UnmatchedParen(message~, ..)
| CannotEscape(message~, ..)
| UnexpectedEscapeEnd(message~, ..)
| ValidationError(message~, ..)
| UnknownParameterType(message~, ..) => message
}
}
///|
/// The code point offset of the problem in the expression.
pub fn ExpressionError::position(self : ExpressionError) -> Int {
match self {
UnmatchedBrace(position~, ..)
| UnmatchedParen(position~, ..)
| CannotEscape(position~, ..)
| UnexpectedEscapeEnd(position~, ..)
| ValidationError(position~, ..)
| UnknownParameterType(position~, ..) => position
}
}
///|
pub impl Show for ExpressionError with fn output(self, logger) {
logger.write_string(self.message())
}
///|
pub extend ExpressionError with Show::{to_string, output}
///|
/// Build a message in the reference format.
fn error_message(
index : Int,
expression : String,
pointer : String,
problem : String,
solution : String,
) -> String {
"This Cucumber Expression has a problem at column \{index + 1}:\n\n\{expression}\n\{pointer}\n\{problem}.\n\{solution}"
}
///|
fn point_at(index : Int) -> String {
" ".repeat(index) + "^"
}
///|
fn point_at_range(start : Int, end : Int) -> String {
let buf = StringBuilder()
buf.write_string(point_at(start))
if start + 1 < end {
buf.write_string("-".repeat(end - start - 2))
buf.write_char('^')
}
buf.to_string()
}
///|
fn cant_escape_error(expression : String, index : Int) -> ExpressionError {
let chars = expression.to_array()
CannotEscape(
position=index,
character=chars[index],
message=error_message(
index,
expression,
point_at(index),
"Only the characters '{', '}', '(', ')', '\\', '/' and whitespace can be escaped",
"If you did mean to use an '\\' you can use '\\\\' to escape it",
),
)
}
///|
fn end_of_line_escaped_error(expression : String) -> ExpressionError {
let index = expression.to_array().length() - 1
UnexpectedEscapeEnd(
position=index,
message=error_message(
index,
expression,
point_at(index),
"The end of line can not be escaped",
"You can use '\\\\' to escape the '\\'",
),
)
}
///|
fn missing_end_token_error(
expression : String,
begin : TokenType,
end : TokenType,
current : Token,
) -> ExpressionError {
let begin_symbol = symbol_of(begin)
let end_symbol = symbol_of(end)
let purpose = purpose_of(begin)
let message = error_message(
current.start,
expression,
point_at_range(current.start, current.end),
"The '\{begin_symbol}' does not have a matching '\{end_symbol}'",
"If you did not intend to use \{purpose} you can use '\\\{begin_symbol}' to escape the \{purpose}",
)
match begin {
BeginParameter => UnmatchedBrace(position=current.start, message~)
_ => UnmatchedParen(position=current.start, message~)
}
}
///|
fn alternation_in_optional_error(
expression : String,
current : Token,
) -> ExpressionError {
ValidationError(
position=current.start,
message=error_message(
current.start,
expression,
point_at_range(current.start, current.end),
"An alternation can not be used inside an optional",
"If you did not mean to use an alternation you can use '\\/' to escape the '/'. Otherwise rephrase your expression or consider using a regular expression instead.",
),
)
}
///|
fn invalid_parameter_name_in_node_error(
expression : String,
token : Token,
) -> ExpressionError {
ValidationError(
position=token.start,
message=error_message(
token.start,
expression,
point_at_range(token.start, token.end),
"Parameter names may not contain '{', '}', '(', ')', '\\' or '/'",
"Did you mean to use a regular expression?",
),
)
}
///|
/// An error about a node, with the pointer under the whole node.
fn node_error(
expression : String,
node : Node,
problem : String,
solution : String,
) -> ExpressionError {
ValidationError(
position=node.start,
message=error_message(
node.start,
expression,
point_at_range(node.start, node.end),
problem,
solution,
),
)
}
///|
fn undefined_parameter_type_error(
expression : String,
node : Node,
name : String,
) -> ExpressionError {
UnknownParameterType(
name~,
position=node.start,
message=error_message(
node.start,
expression,
point_at_range(node.start, node.end),
"Undefined parameter type '\{name}'",
"Please register a ParameterType for '\{name}'",
),
)
}
///|
/// The compiled regex does not compile, for example because a parameter
/// type has a regexp that is not valid. There is no column in the
/// expression for this problem, so the position is 0.
fn regex_does_not_compile_error(
regex : String,
error : String,
) -> ExpressionError {
ValidationError(
position=0,
message="The regex \{regex} of this Cucumber Expression does not compile: \{error}",
)
}
///|
#deprecated
pub extend ExpressionError with @debug.Debug::{to_repr}
///|
/// Errors that can occur when a parameter type is registered.
pub suberror ParameterTypeError {
/// A parameter type with the name is already registered.
DuplicateParameterType(name~ : String)
/// The name has a character that is not permitted.
IllegalParameterName(name~ : String)
/// The parameter type has no regexps.
NoRegexps(name~ : String)
/// Two preferential parameter types have the same regexp.
DuplicatePreferentialParameterType(
regexp~ : String,
first~ : String,
second~ : String
)
} derive(Debug)
///|
/// The error message.
pub fn ParameterTypeError::message(self : ParameterTypeError) -> String {
match self {
DuplicateParameterType(name="") =>
"The anonymous parameter type has already been defined"
DuplicateParameterType(name~) =>
"There is already a parameter type with name \{name}"
IllegalParameterName(name~) =>
"Illegal character in parameter name {\{name}}. Parameter names may not contain '{', '}', '(', ')', '\\' or '/'"
NoRegexps(name~) =>
"The parameter type {\{name}} must have at least one regexp"
DuplicatePreferentialParameterType(regexp~, first~, second~) =>
"There can only be one preferential parameter type per regexp. The regexp /\{regexp}/ is used for two preferential parameter types, {\{first}} and {\{second}}"
}
}
///|
pub impl Show for ParameterTypeError with fn output(self, logger) {
logger.write_string(self.message())
}
///|
pub extend ParameterTypeError with Show::{to_string, output}
///|
#deprecated
pub extend ParameterTypeError with @debug.Debug::{to_repr}
///|
/// A capture group of a regular expression matches more than one parameter
/// type, and none of them is preferential.
pub suberror AmbiguousParameterTypeError {
AmbiguousParameterType(
regexp~ : String,
expression_regexp~ : String,
names~ : Array[String],
generated~ : Array[String]
)
} derive(Debug)
///|
/// The error message, in the same format as the reference implementation.
pub fn AmbiguousParameterTypeError::message(
self : AmbiguousParameterTypeError,
) -> String {
let AmbiguousParameterType(regexp~, expression_regexp~, names~, generated~) = self
let names = names.map(n => "{\{n}}").join("\n ")
let generated = generated.join("\n ")
(
$|Your Regular Expression /\{expression_regexp}/
$|matches multiple parameter types with regexp \{regexp}:
$| \{names}
$|
$|I couldn't decide which one to use. You have two options:
$|
$|1) Use a Cucumber Expression instead of a Regular Expression. Try one of these:
$| \{generated}
$|
$|2) Make one of the parameter types preferential and continue to use a Regular Expression.
$|
)
}
///|
pub impl Show for AmbiguousParameterTypeError with fn output(self, logger) {
logger.write_string(self.message())
}
///|
pub extend AmbiguousParameterTypeError with Show::{to_string, output}
///|
#deprecated
pub extend AmbiguousParameterTypeError with @debug.Debug::{to_repr}