///|
fn parse_flow_node(
ctx : TagContext,
scanner : Scanner,
) -> Yaml raise ParseError {
scanner.skip_ws_and_comments()
match parse_alias(scanner) {
Some(name) => Yaml::Alias(name)
None => {
let (tag, anchor) = parse_node_props(ctx, scanner)
scanner.skip_ws_and_comments()
if (tag is Some(_) || anchor is Some(_)) && scanner.peek() is Some('*') {
raise ParseError::InvalidTagValue(tag="alias", value="*")
}
if tag is Some(_) || anchor is Some(_) {
match scanner.peek() {
Some(',') | Some(']') | Some('}') | Some(':') =>
return apply_node_props(Yaml::Null, tag, anchor, ctx.coerce_binary)
None =>
return apply_node_props(Yaml::Null, tag, anchor, ctx.coerce_binary)
_ => ()
}
}
let value = parse_flow_value(ctx, scanner)
apply_node_props(value, tag, anchor, ctx.coerce_binary)
}
}
}
///|
fn parse_flow_value(
ctx : TagContext,
scanner : Scanner,
) -> Yaml raise ParseError {
match scanner.peek() {
Some('[') => parse_flow_seq(ctx, scanner)
Some('{') => parse_flow_map(ctx, scanner)
Some('"') => parse_scalar(scanner)
Some('\'') => parse_scalar(scanner)
Some(_) => parse_flow_plain_scalar(scanner)
None => raise ParseError::UnexpectedEof(pos=scanner.position())
}
}
///|
fn parse_flow_plain_scalar(scanner : Scanner) -> Yaml raise ParseError {
let start = scanner.position()
let raw = parse_plain_scalar(scanner)
if raw is "-" {
raise ParseError::UnexpectedChar(pos=start, found='-')
}
parse_scalar_value(raw)
}
///|
fn parse_flow_value_from_string(
ctx : TagContext,
text : String,
) -> Yaml raise ParseError {
let scanner = Scanner::new(text)
scanner.skip_ws_and_comments()
let value = parse_flow_value(ctx, scanner)
scanner.skip_ws_and_comments()
match scanner.peek() {
None => value
Some(c) => raise ParseError::UnexpectedChar(pos=scanner.position(), found=c)
}
}
///|
fn parse_flow_seq(ctx : TagContext, scanner : Scanner) -> Yaml raise ParseError {
scanner.expect('[')
let items : Array[Yaml] = []
scanner.skip_ws_and_comments()
if scanner.eat(']') {
return Yaml::Seq(items)
}
let mut expect_item = true
while true {
scanner.skip_ws_and_comments()
if expect_item {
if is_flow_mapping_colon(scanner) {
ignore(scanner.advance())
scanner.skip_ws_and_comments()
let value = if scanner.peek() is Some(',') ||
scanner.peek() is Some(']') {
Yaml::Null
} else {
parse_flow_node(ctx, scanner)
}
items.push(Yaml::Map([(Yaml::Null, value)]))
expect_item = false
continue
}
match scanner.peek() {
Some('?') if is_flow_explicit_key(scanner) => {
ignore(scanner.advance())
scanner.skip_ws_and_comments()
let key = if scanner.peek() is Some(':') ||
scanner.peek() is Some(',') ||
scanner.peek() is Some(']') {
Yaml::Null
} else {
parse_flow_node(ctx, scanner)
}
scanner.skip_ws_and_comments()
let value = if scanner.eat(':') {
scanner.skip_ws_and_comments()
if scanner.peek() is Some(',') || scanner.peek() is Some(']') {
Yaml::Null
} else {
parse_flow_node(ctx, scanner)
}
} else if key is Yaml::Null {
Yaml::Null
} else {
match scanner.peek() {
None => raise ParseError::UnexpectedEof(pos=scanner.position())
Some(found) =>
raise ParseError::UnexpectedChar(pos=scanner.position(), found~)
}
}
items.push(Yaml::Map([(key, value)]))
expect_item = false
continue
}
Some(']') =>
raise ParseError::UnexpectedChar(pos=scanner.position(), found=']')
Some(',') => {
if items.length() == 0 {
raise ParseError::UnexpectedChar(pos=scanner.position(), found=',')
}
ignore(scanner.advance())
items.push(Yaml::Null)
expect_item = true
continue
}
_ => ()
}
let key_start_line = scanner.position().line
let key = parse_flow_node(ctx, scanner)
scanner.skip_ws_and_comments()
if scanner.peek() is Some(':') {
if scanner.position().line != key_start_line {
raise ParseError::UnexpectedChar(pos=scanner.position(), found=':')
}
ignore(scanner.advance())
scanner.skip_ws_and_comments()
let value = if scanner.peek() is Some(',') ||
scanner.peek() is Some(']') {
Yaml::Null
} else {
parse_flow_node(ctx, scanner)
}
items.push(Yaml::Map([(key, value)]))
} else {
items.push(key)
}
expect_item = false
} else {
if scanner.eat(']') {
break
}
scanner.expect(',')
scanner.skip_ws_and_comments()
if scanner.eat(']') {
break
}
expect_item = true
}
}
Yaml::Seq(items)
}
///|
fn parse_flow_map(ctx : TagContext, scanner : Scanner) -> Yaml raise ParseError {
scanner.expect('{')
let items : Array[(Yaml, Yaml)] = []
scanner.skip_ws_and_comments()
if scanner.eat('}') {
return Yaml::Map(items)
}
while true {
if is_flow_explicit_key(scanner) {
ignore(scanner.advance())
scanner.skip_ws_and_comments()
let key = if scanner.peek() is Some(':') ||
scanner.peek() is Some(',') ||
scanner.peek() is Some('}') {
Yaml::Null
} else {
parse_flow_node(ctx, scanner)
}
scanner.skip_ws_and_comments()
let value = if scanner.eat(':') {
scanner.skip_ws_and_comments()
if scanner.peek() is Some(',') || scanner.peek() is Some('}') {
Yaml::Null
} else {
parse_flow_node(ctx, scanner)
}
} else if key is Yaml::Null {
Yaml::Null
} else {
match scanner.peek() {
None => raise ParseError::UnexpectedEof(pos=scanner.position())
Some(found) =>
raise ParseError::UnexpectedChar(pos=scanner.position(), found~)
}
}
items.push((key, value))
} else if is_flow_mapping_colon(scanner) {
ignore(scanner.advance())
scanner.skip_ws_and_comments()
let value = if scanner.peek() is Some(',') || scanner.peek() is Some('}') {
Yaml::Null
} else {
parse_flow_node(ctx, scanner)
}
items.push((Yaml::Null, value))
} else {
let key = parse_flow_node(ctx, scanner)
scanner.skip_ws_and_comments()
match scanner.peek() {
Some(':') => {
ignore(scanner.advance())
scanner.skip_ws_and_comments()
let value = if scanner.peek() is Some(',') ||
scanner.peek() is Some('}') {
Yaml::Null
} else {
parse_flow_node(ctx, scanner)
}
items.push((key, value))
}
Some(',') | Some('}') => items.push((key, Yaml::Null))
_ => scanner.expect(':')
}
}
scanner.skip_ws_and_comments()
if scanner.eat('}') {
break
}
scanner.expect(',')
scanner.skip_ws_and_comments()
if scanner.eat('}') {
break
}
}
Yaml::Map(items)
}
///|
fn is_flow_explicit_key(scanner : Scanner) -> Bool {
match scanner.peek() {
Some('?') =>
match scanner.peek_next() {
Some(next) => next.is_whitespace()
None => false
}
_ => false
}
}
///|
fn is_flow_mapping_colon(scanner : Scanner) -> Bool {
match scanner.peek() {
Some(':') =>
match scanner.peek_next() {
None => true
Some(next) =>
next.is_whitespace() || next is ',' || next is ']' || next is '}'
}
_ => false
}
}