///|
priv enum PatternPiece {
Element(Node)
Indent(String, Int32, Int32)
}
///|
fn Cursor::maybe_pattern(self : Cursor) -> Node? raise ParseFailure {
ignore(self.peek_blank_inline())
if self.is_value_start() {
self.skip_to_peek()
return Some(self.pattern(false))
}
ignore(self.peek_blank_block())
if self.is_value_continuation() {
self.skip_to_peek()
return Some(self.pattern(true))
}
None
}
///|
fn Cursor::text_element(self : Cursor) -> Node {
let start = self.index
let buf = StringBuilder()
while true {
match self.current() {
None | Some('{') | Some('}') | Some('\n') => break
Some(c) => {
buf.write_char(c)
ignore(self.next())
}
}
}
Node::with_span(TextElement(buf.to_string()), start, self.index)
}
///|
fn trim_pattern_end(value : String) -> String {
let c = Cursor::new(value)
let mut end = c.chars.length()
while end > 0 &&
(
c.chars[end - 1] == ' ' ||
c.chars[end - 1] == '\n' ||
c.chars[end - 1] == '\r'
) {
end -= 1
}
c.slice(0, end)
}
///|
fn dedent(pieces : Array[PatternPiece], common : Int32) -> Array[Node] {
let trimmed : Array[Node] = []
for piece in pieces {
let element = match piece {
Element(node) => node
Indent(value, start, end) => {
let c = Cursor::new(value)
let length = c.chars.length() - common
if length <= 0 {
continue
}
Node::with_span(TextElement(c.slice(0, length)), start, end)
}
}
match element.kind {
TextElement(value) => {
if !trimmed.is_empty() {
let last = trimmed[trimmed.length() - 1]
match last.kind {
TextElement(previous) => {
trimmed[trimmed.length() - 1] = Node::with_span(
TextElement(previous + value),
last.span.start,
element.span.end,
)
continue
}
_ => ()
}
}
trimmed.push(element)
}
_ => trimmed.push(element)
}
}
if !trimmed.is_empty() {
let i = trimmed.length() - 1
let last = trimmed[i]
match last.kind {
TextElement(value) => {
let value = trim_pattern_end(value)
if value.is_empty() {
ignore(trimmed.pop())
} else {
trimmed[i] = { kind: TextElement(value), span: last.span, }
}
}
_ => ()
}
}
trimmed
}
///|
fn Cursor::pattern(self : Cursor, is_block : Bool) -> Node raise ParseFailure {
let start = self.index
let pieces : Array[PatternPiece] = []
let mut common = 2147483647
if is_block {
let blank_start = self.index
let indent = self.skip_blank_inline()
pieces.push(Indent(indent, blank_start, self.index))
common = indent.length()
}
while self.current() != None {
if self.current() == Some('\n') {
let blank_start = self.index
let blank_lines = self.peek_blank_block()
if self.is_value_continuation() {
self.skip_to_peek()
let indent = self.skip_blank_inline()
if indent.length() < common {
common = indent.length()
}
pieces.push(Indent(blank_lines + indent, blank_start, self.index))
continue
}
self.reset_peek()
break
}
if self.current() == Some('}') {
raise ParseFailure("E0027", [])
}
let element = if self.current() == Some('{') {
self.placeable()
} else {
self.text_element()
}
pieces.push(Element(element))
}
Node::with_span(Pattern(dedent(pieces, common)), start, self.index)
}