///|
priv enum ValueNode {
ValueWord(String)
ValueFunction(String, Array[ValueNode])
ValueSeparator(String)
} derive(Eq, Debug)
///|
fn is_value_separator(c : UInt16) -> Bool {
c == ':' ||
c == ',' ||
c == '=' ||
c == '>' ||
c == '<' ||
c == '\n' ||
c == ' ' ||
c == '\t'
}
///|
fn parse_value_nodes(
input : String,
start : Int,
nested : Bool,
) -> (Array[ValueNode], Int) {
let nodes : Array[ValueNode] = []
let mut buffer = ""
let mut index = start
while index < input.length() {
let current = input[index]
if current == '\\' {
buffer += input[index:index + 1].to_owned()
if index + 1 < input.length() {
buffer += input[index + 1:index + 2].to_owned()
index += 2
} else {
index += 1
}
continue
}
if current == '\'' || current == '"' {
let quote = current
buffer += input[index:index + 1].to_owned()
index += 1
while index < input.length() {
let c = input[index]
buffer += input[index:index + 1].to_owned()
index += 1
if c == '\\' && index < input.length() {
buffer += input[index:index + 1].to_owned()
index += 1
} else if c == quote {
break
}
}
continue
}
if current == '(' {
let function_name = buffer
buffer = ""
let (children, next) = parse_value_nodes(input, index + 1, true)
nodes.push(ValueFunction(function_name, children))
index = next
continue
}
if current == ')' {
if buffer != "" {
nodes.push(ValueWord(buffer))
buffer = ""
}
if nested {
return (nodes, index + 1)
}
index += 1
continue
}
if current == '/' {
if buffer != "" {
nodes.push(ValueWord(buffer))
buffer = ""
}
nodes.push(ValueWord("/"))
index += 1
continue
}
if is_value_separator(current) {
if buffer != "" {
nodes.push(ValueWord(buffer))
buffer = ""
}
let separator = StringBuilder()
while index < input.length() && is_value_separator(input[index]) {
separator.write_view(input[index:index + 1])
index += 1
}
nodes.push(ValueSeparator(separator.to_string()))
continue
}
buffer += input[index:index + 1].to_owned()
index += 1
}
if buffer != "" {
nodes.push(ValueWord(buffer))
}
(nodes, index)
}
///|
fn parse_value(input : String) -> Array[ValueNode] {
parse_value_nodes(replace_all(input, "\r\n", "\n"), 0, false).0
}
///|
fn render_value(nodes : ArrayView[ValueNode]) -> String {
let output = StringBuilder()
for node in nodes {
match node {
ValueWord(value) | ValueSeparator(value) => output.write_string(value)
ValueFunction(name, children) => {
output.write_string(name)
output.write_char('(')
output.write_string(render_value(children))
output.write_char(')')
}
}
}
output.to_string()
}