///|
priv suberror SplitError {
SplitError(String)
}
///|
fn split_space(char : Char) -> Bool {
char is (' ' | '\t' | '\n' | '\r' | '\u000b' | '\u000c')
}
///|
fn variable_start(char : Char) -> Bool {
char is ('a'..<='z' | 'A'..<='Z' | '_')
}
///|
fn split_string(
text : String,
environment : Map[String, String],
) -> Array[String] raise SplitError {
let chars = text.iter().collect()
let result : Array[String] = []
let word = StringBuilder()
let mut quote : Char? = None
let mut started = false
let mut index = 0
while index < chars.length() {
let char = chars[index]
if (char == '\'' && quote != Some('"')) ||
(char == '"' && quote != Some('\'')) {
quote = if quote is Some(_) { None } else { Some(char) }
started = true
index += 1
continue
}
if split_space(char) && quote is None {
if started {
result.push(word.to_string())
word.reset()
started = false
}
index += 1
continue
}
if char == '#' && !started {
break
}
if char == '\\' {
if quote == Some('\'') &&
index + 1 < chars.length() &&
!(chars[index + 1] is ('\\' | '\'')) {
word.write_char(char)
started = true
index += 1
continue
}
if index + 1 == chars.length() {
if quote == Some('\'') {
word.write_char(char)
index += 1
continue
}
raise SplitError("invalid backslash at end of string in -S")
}
let escaped = chars[index + 1]
index += 2
if escaped == '_' && quote != Some('"') {
if started {
result.push(word.to_string())
word.reset()
started = false
}
continue
}
if escaped == 'c' {
if quote == Some('"') {
raise SplitError("'\\c' must not appear in double-quoted -S string")
}
break
}
let decoded = match escaped {
'"' | '#' | '$' | '\'' | '\\' => escaped
'_' => ' '
'f' => '\u000c'
'n' => '\n'
'r' => '\r'
't' => '\t'
'v' => '\u000b'
_ => raise SplitError("invalid sequence '\\\{escaped}' in -S")
}
word.write_char(decoded)
started = true
continue
}
if char == '$' && quote != Some('\'') {
let begin = index
if index + 2 >= chars.length() ||
chars[index + 1] != '{' ||
!variable_start(chars[index + 2]) {
raise SplitError(
"only ${VARNAME} expansion is supported, error at: " +
String::from_array(chars[begin:]),
)
}
index += 3
while index < chars.length() &&
(variable_start(chars[index]) || chars[index] is ('0'..<='9')) {
index += 1
}
if index == chars.length() || chars[index] != '}' {
raise SplitError(
"only ${VARNAME} expansion is supported, error at: " +
String::from_array(chars[begin:]),
)
}
let name = String::from_array(chars[begin + 2:index])
if environment.get(name) is Some(value) {
word.write_string(value)
started = true
}
index += 1
continue
}
word.write_char(char)
started = true
index += 1
}
if quote is Some(_) {
raise SplitError("no terminating quote in -S string")
}
if started {
result.push(word.to_string())
}
result
}