Path: blob/main/vendor/gopkg.in/yaml.v3/scannerc.go
2872 views
//1// Copyright (c) 2011-2019 Canonical Ltd2// Copyright (c) 2006-2010 Kirill Simonov3//4// Permission is hereby granted, free of charge, to any person obtaining a copy of5// this software and associated documentation files (the "Software"), to deal in6// the Software without restriction, including without limitation the rights to7// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies8// of the Software, and to permit persons to whom the Software is furnished to do9// so, subject to the following conditions:10//11// The above copyright notice and this permission notice shall be included in all12// copies or substantial portions of the Software.13//14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE20// SOFTWARE.2122package yaml2324import (25"bytes"26"fmt"27)2829// Introduction30// ************31//32// The following notes assume that you are familiar with the YAML specification33// (http://yaml.org/spec/1.2/spec.html). We mostly follow it, although in34// some cases we are less restrictive that it requires.35//36// The process of transforming a YAML stream into a sequence of events is37// divided on two steps: Scanning and Parsing.38//39// The Scanner transforms the input stream into a sequence of tokens, while the40// parser transform the sequence of tokens produced by the Scanner into a41// sequence of parsing events.42//43// The Scanner is rather clever and complicated. The Parser, on the contrary,44// is a straightforward implementation of a recursive-descendant parser (or,45// LL(1) parser, as it is usually called).46//47// Actually there are two issues of Scanning that might be called "clever", the48// rest is quite straightforward. The issues are "block collection start" and49// "simple keys". Both issues are explained below in details.50//51// Here the Scanning step is explained and implemented. We start with the list52// of all the tokens produced by the Scanner together with short descriptions.53//54// Now, tokens:55//56// STREAM-START(encoding) # The stream start.57// STREAM-END # The stream end.58// VERSION-DIRECTIVE(major,minor) # The '%YAML' directive.59// TAG-DIRECTIVE(handle,prefix) # The '%TAG' directive.60// DOCUMENT-START # '---'61// DOCUMENT-END # '...'62// BLOCK-SEQUENCE-START # Indentation increase denoting a block63// BLOCK-MAPPING-START # sequence or a block mapping.64// BLOCK-END # Indentation decrease.65// FLOW-SEQUENCE-START # '['66// FLOW-SEQUENCE-END # ']'67// BLOCK-SEQUENCE-START # '{'68// BLOCK-SEQUENCE-END # '}'69// BLOCK-ENTRY # '-'70// FLOW-ENTRY # ','71// KEY # '?' or nothing (simple keys).72// VALUE # ':'73// ALIAS(anchor) # '*anchor'74// ANCHOR(anchor) # '&anchor'75// TAG(handle,suffix) # '!handle!suffix'76// SCALAR(value,style) # A scalar.77//78// The following two tokens are "virtual" tokens denoting the beginning and the79// end of the stream:80//81// STREAM-START(encoding)82// STREAM-END83//84// We pass the information about the input stream encoding with the85// STREAM-START token.86//87// The next two tokens are responsible for tags:88//89// VERSION-DIRECTIVE(major,minor)90// TAG-DIRECTIVE(handle,prefix)91//92// Example:93//94// %YAML 1.195// %TAG ! !foo96// %TAG !yaml! tag:yaml.org,2002:97// ---98//99// The correspoding sequence of tokens:100//101// STREAM-START(utf-8)102// VERSION-DIRECTIVE(1,1)103// TAG-DIRECTIVE("!","!foo")104// TAG-DIRECTIVE("!yaml","tag:yaml.org,2002:")105// DOCUMENT-START106// STREAM-END107//108// Note that the VERSION-DIRECTIVE and TAG-DIRECTIVE tokens occupy a whole109// line.110//111// The document start and end indicators are represented by:112//113// DOCUMENT-START114// DOCUMENT-END115//116// Note that if a YAML stream contains an implicit document (without '---'117// and '...' indicators), no DOCUMENT-START and DOCUMENT-END tokens will be118// produced.119//120// In the following examples, we present whole documents together with the121// produced tokens.122//123// 1. An implicit document:124//125// 'a scalar'126//127// Tokens:128//129// STREAM-START(utf-8)130// SCALAR("a scalar",single-quoted)131// STREAM-END132//133// 2. An explicit document:134//135// ---136// 'a scalar'137// ...138//139// Tokens:140//141// STREAM-START(utf-8)142// DOCUMENT-START143// SCALAR("a scalar",single-quoted)144// DOCUMENT-END145// STREAM-END146//147// 3. Several documents in a stream:148//149// 'a scalar'150// ---151// 'another scalar'152// ---153// 'yet another scalar'154//155// Tokens:156//157// STREAM-START(utf-8)158// SCALAR("a scalar",single-quoted)159// DOCUMENT-START160// SCALAR("another scalar",single-quoted)161// DOCUMENT-START162// SCALAR("yet another scalar",single-quoted)163// STREAM-END164//165// We have already introduced the SCALAR token above. The following tokens are166// used to describe aliases, anchors, tag, and scalars:167//168// ALIAS(anchor)169// ANCHOR(anchor)170// TAG(handle,suffix)171// SCALAR(value,style)172//173// The following series of examples illustrate the usage of these tokens:174//175// 1. A recursive sequence:176//177// &A [ *A ]178//179// Tokens:180//181// STREAM-START(utf-8)182// ANCHOR("A")183// FLOW-SEQUENCE-START184// ALIAS("A")185// FLOW-SEQUENCE-END186// STREAM-END187//188// 2. A tagged scalar:189//190// !!float "3.14" # A good approximation.191//192// Tokens:193//194// STREAM-START(utf-8)195// TAG("!!","float")196// SCALAR("3.14",double-quoted)197// STREAM-END198//199// 3. Various scalar styles:200//201// --- # Implicit empty plain scalars do not produce tokens.202// --- a plain scalar203// --- 'a single-quoted scalar'204// --- "a double-quoted scalar"205// --- |-206// a literal scalar207// --- >-208// a folded209// scalar210//211// Tokens:212//213// STREAM-START(utf-8)214// DOCUMENT-START215// DOCUMENT-START216// SCALAR("a plain scalar",plain)217// DOCUMENT-START218// SCALAR("a single-quoted scalar",single-quoted)219// DOCUMENT-START220// SCALAR("a double-quoted scalar",double-quoted)221// DOCUMENT-START222// SCALAR("a literal scalar",literal)223// DOCUMENT-START224// SCALAR("a folded scalar",folded)225// STREAM-END226//227// Now it's time to review collection-related tokens. We will start with228// flow collections:229//230// FLOW-SEQUENCE-START231// FLOW-SEQUENCE-END232// FLOW-MAPPING-START233// FLOW-MAPPING-END234// FLOW-ENTRY235// KEY236// VALUE237//238// The tokens FLOW-SEQUENCE-START, FLOW-SEQUENCE-END, FLOW-MAPPING-START, and239// FLOW-MAPPING-END represent the indicators '[', ']', '{', and '}'240// correspondingly. FLOW-ENTRY represent the ',' indicator. Finally the241// indicators '?' and ':', which are used for denoting mapping keys and values,242// are represented by the KEY and VALUE tokens.243//244// The following examples show flow collections:245//246// 1. A flow sequence:247//248// [item 1, item 2, item 3]249//250// Tokens:251//252// STREAM-START(utf-8)253// FLOW-SEQUENCE-START254// SCALAR("item 1",plain)255// FLOW-ENTRY256// SCALAR("item 2",plain)257// FLOW-ENTRY258// SCALAR("item 3",plain)259// FLOW-SEQUENCE-END260// STREAM-END261//262// 2. A flow mapping:263//264// {265// a simple key: a value, # Note that the KEY token is produced.266// ? a complex key: another value,267// }268//269// Tokens:270//271// STREAM-START(utf-8)272// FLOW-MAPPING-START273// KEY274// SCALAR("a simple key",plain)275// VALUE276// SCALAR("a value",plain)277// FLOW-ENTRY278// KEY279// SCALAR("a complex key",plain)280// VALUE281// SCALAR("another value",plain)282// FLOW-ENTRY283// FLOW-MAPPING-END284// STREAM-END285//286// A simple key is a key which is not denoted by the '?' indicator. Note that287// the Scanner still produce the KEY token whenever it encounters a simple key.288//289// For scanning block collections, the following tokens are used (note that we290// repeat KEY and VALUE here):291//292// BLOCK-SEQUENCE-START293// BLOCK-MAPPING-START294// BLOCK-END295// BLOCK-ENTRY296// KEY297// VALUE298//299// The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation300// increase that precedes a block collection (cf. the INDENT token in Python).301// The token BLOCK-END denote indentation decrease that ends a block collection302// (cf. the DEDENT token in Python). However YAML has some syntax pecularities303// that makes detections of these tokens more complex.304//305// The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators306// '-', '?', and ':' correspondingly.307//308// The following examples show how the tokens BLOCK-SEQUENCE-START,309// BLOCK-MAPPING-START, and BLOCK-END are emitted by the Scanner:310//311// 1. Block sequences:312//313// - item 1314// - item 2315// -316// - item 3.1317// - item 3.2318// -319// key 1: value 1320// key 2: value 2321//322// Tokens:323//324// STREAM-START(utf-8)325// BLOCK-SEQUENCE-START326// BLOCK-ENTRY327// SCALAR("item 1",plain)328// BLOCK-ENTRY329// SCALAR("item 2",plain)330// BLOCK-ENTRY331// BLOCK-SEQUENCE-START332// BLOCK-ENTRY333// SCALAR("item 3.1",plain)334// BLOCK-ENTRY335// SCALAR("item 3.2",plain)336// BLOCK-END337// BLOCK-ENTRY338// BLOCK-MAPPING-START339// KEY340// SCALAR("key 1",plain)341// VALUE342// SCALAR("value 1",plain)343// KEY344// SCALAR("key 2",plain)345// VALUE346// SCALAR("value 2",plain)347// BLOCK-END348// BLOCK-END349// STREAM-END350//351// 2. Block mappings:352//353// a simple key: a value # The KEY token is produced here.354// ? a complex key355// : another value356// a mapping:357// key 1: value 1358// key 2: value 2359// a sequence:360// - item 1361// - item 2362//363// Tokens:364//365// STREAM-START(utf-8)366// BLOCK-MAPPING-START367// KEY368// SCALAR("a simple key",plain)369// VALUE370// SCALAR("a value",plain)371// KEY372// SCALAR("a complex key",plain)373// VALUE374// SCALAR("another value",plain)375// KEY376// SCALAR("a mapping",plain)377// BLOCK-MAPPING-START378// KEY379// SCALAR("key 1",plain)380// VALUE381// SCALAR("value 1",plain)382// KEY383// SCALAR("key 2",plain)384// VALUE385// SCALAR("value 2",plain)386// BLOCK-END387// KEY388// SCALAR("a sequence",plain)389// VALUE390// BLOCK-SEQUENCE-START391// BLOCK-ENTRY392// SCALAR("item 1",plain)393// BLOCK-ENTRY394// SCALAR("item 2",plain)395// BLOCK-END396// BLOCK-END397// STREAM-END398//399// YAML does not always require to start a new block collection from a new400// line. If the current line contains only '-', '?', and ':' indicators, a new401// block collection may start at the current line. The following examples402// illustrate this case:403//404// 1. Collections in a sequence:405//406// - - item 1407// - item 2408// - key 1: value 1409// key 2: value 2410// - ? complex key411// : complex value412//413// Tokens:414//415// STREAM-START(utf-8)416// BLOCK-SEQUENCE-START417// BLOCK-ENTRY418// BLOCK-SEQUENCE-START419// BLOCK-ENTRY420// SCALAR("item 1",plain)421// BLOCK-ENTRY422// SCALAR("item 2",plain)423// BLOCK-END424// BLOCK-ENTRY425// BLOCK-MAPPING-START426// KEY427// SCALAR("key 1",plain)428// VALUE429// SCALAR("value 1",plain)430// KEY431// SCALAR("key 2",plain)432// VALUE433// SCALAR("value 2",plain)434// BLOCK-END435// BLOCK-ENTRY436// BLOCK-MAPPING-START437// KEY438// SCALAR("complex key")439// VALUE440// SCALAR("complex value")441// BLOCK-END442// BLOCK-END443// STREAM-END444//445// 2. Collections in a mapping:446//447// ? a sequence448// : - item 1449// - item 2450// ? a mapping451// : key 1: value 1452// key 2: value 2453//454// Tokens:455//456// STREAM-START(utf-8)457// BLOCK-MAPPING-START458// KEY459// SCALAR("a sequence",plain)460// VALUE461// BLOCK-SEQUENCE-START462// BLOCK-ENTRY463// SCALAR("item 1",plain)464// BLOCK-ENTRY465// SCALAR("item 2",plain)466// BLOCK-END467// KEY468// SCALAR("a mapping",plain)469// VALUE470// BLOCK-MAPPING-START471// KEY472// SCALAR("key 1",plain)473// VALUE474// SCALAR("value 1",plain)475// KEY476// SCALAR("key 2",plain)477// VALUE478// SCALAR("value 2",plain)479// BLOCK-END480// BLOCK-END481// STREAM-END482//483// YAML also permits non-indented sequences if they are included into a block484// mapping. In this case, the token BLOCK-SEQUENCE-START is not produced:485//486// key:487// - item 1 # BLOCK-SEQUENCE-START is NOT produced here.488// - item 2489//490// Tokens:491//492// STREAM-START(utf-8)493// BLOCK-MAPPING-START494// KEY495// SCALAR("key",plain)496// VALUE497// BLOCK-ENTRY498// SCALAR("item 1",plain)499// BLOCK-ENTRY500// SCALAR("item 2",plain)501// BLOCK-END502//503504// Ensure that the buffer contains the required number of characters.505// Return true on success, false on failure (reader error or memory error).506func cache(parser *yaml_parser_t, length int) bool {507// [Go] This was inlined: !cache(A, B) -> unread < B && !update(A, B)508return parser.unread >= length || yaml_parser_update_buffer(parser, length)509}510511// Advance the buffer pointer.512func skip(parser *yaml_parser_t) {513if !is_blank(parser.buffer, parser.buffer_pos) {514parser.newlines = 0515}516parser.mark.index++517parser.mark.column++518parser.unread--519parser.buffer_pos += width(parser.buffer[parser.buffer_pos])520}521522func skip_line(parser *yaml_parser_t) {523if is_crlf(parser.buffer, parser.buffer_pos) {524parser.mark.index += 2525parser.mark.column = 0526parser.mark.line++527parser.unread -= 2528parser.buffer_pos += 2529parser.newlines++530} else if is_break(parser.buffer, parser.buffer_pos) {531parser.mark.index++532parser.mark.column = 0533parser.mark.line++534parser.unread--535parser.buffer_pos += width(parser.buffer[parser.buffer_pos])536parser.newlines++537}538}539540// Copy a character to a string buffer and advance pointers.541func read(parser *yaml_parser_t, s []byte) []byte {542if !is_blank(parser.buffer, parser.buffer_pos) {543parser.newlines = 0544}545w := width(parser.buffer[parser.buffer_pos])546if w == 0 {547panic("invalid character sequence")548}549if len(s) == 0 {550s = make([]byte, 0, 32)551}552if w == 1 && len(s)+w <= cap(s) {553s = s[:len(s)+1]554s[len(s)-1] = parser.buffer[parser.buffer_pos]555parser.buffer_pos++556} else {557s = append(s, parser.buffer[parser.buffer_pos:parser.buffer_pos+w]...)558parser.buffer_pos += w559}560parser.mark.index++561parser.mark.column++562parser.unread--563return s564}565566// Copy a line break character to a string buffer and advance pointers.567func read_line(parser *yaml_parser_t, s []byte) []byte {568buf := parser.buffer569pos := parser.buffer_pos570switch {571case buf[pos] == '\r' && buf[pos+1] == '\n':572// CR LF . LF573s = append(s, '\n')574parser.buffer_pos += 2575parser.mark.index++576parser.unread--577case buf[pos] == '\r' || buf[pos] == '\n':578// CR|LF . LF579s = append(s, '\n')580parser.buffer_pos += 1581case buf[pos] == '\xC2' && buf[pos+1] == '\x85':582// NEL . LF583s = append(s, '\n')584parser.buffer_pos += 2585case buf[pos] == '\xE2' && buf[pos+1] == '\x80' && (buf[pos+2] == '\xA8' || buf[pos+2] == '\xA9'):586// LS|PS . LS|PS587s = append(s, buf[parser.buffer_pos:pos+3]...)588parser.buffer_pos += 3589default:590return s591}592parser.mark.index++593parser.mark.column = 0594parser.mark.line++595parser.unread--596parser.newlines++597return s598}599600// Get the next token.601func yaml_parser_scan(parser *yaml_parser_t, token *yaml_token_t) bool {602// Erase the token object.603*token = yaml_token_t{} // [Go] Is this necessary?604605// No tokens after STREAM-END or error.606if parser.stream_end_produced || parser.error != yaml_NO_ERROR {607return true608}609610// Ensure that the tokens queue contains enough tokens.611if !parser.token_available {612if !yaml_parser_fetch_more_tokens(parser) {613return false614}615}616617// Fetch the next token from the queue.618*token = parser.tokens[parser.tokens_head]619parser.tokens_head++620parser.tokens_parsed++621parser.token_available = false622623if token.typ == yaml_STREAM_END_TOKEN {624parser.stream_end_produced = true625}626return true627}628629// Set the scanner error and return false.630func yaml_parser_set_scanner_error(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string) bool {631parser.error = yaml_SCANNER_ERROR632parser.context = context633parser.context_mark = context_mark634parser.problem = problem635parser.problem_mark = parser.mark636return false637}638639func yaml_parser_set_scanner_tag_error(parser *yaml_parser_t, directive bool, context_mark yaml_mark_t, problem string) bool {640context := "while parsing a tag"641if directive {642context = "while parsing a %TAG directive"643}644return yaml_parser_set_scanner_error(parser, context, context_mark, problem)645}646647func trace(args ...interface{}) func() {648pargs := append([]interface{}{"+++"}, args...)649fmt.Println(pargs...)650pargs = append([]interface{}{"---"}, args...)651return func() { fmt.Println(pargs...) }652}653654// Ensure that the tokens queue contains at least one token which can be655// returned to the Parser.656func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool {657// While we need more tokens to fetch, do it.658for {659// [Go] The comment parsing logic requires a lookahead of two tokens660// so that foot comments may be parsed in time of associating them661// with the tokens that are parsed before them, and also for line662// comments to be transformed into head comments in some edge cases.663if parser.tokens_head < len(parser.tokens)-2 {664// If a potential simple key is at the head position, we need to fetch665// the next token to disambiguate it.666head_tok_idx, ok := parser.simple_keys_by_tok[parser.tokens_parsed]667if !ok {668break669} else if valid, ok := yaml_simple_key_is_valid(parser, &parser.simple_keys[head_tok_idx]); !ok {670return false671} else if !valid {672break673}674}675// Fetch the next token.676if !yaml_parser_fetch_next_token(parser) {677return false678}679}680681parser.token_available = true682return true683}684685// The dispatcher for token fetchers.686func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) {687// Ensure that the buffer is initialized.688if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {689return false690}691692// Check if we just started scanning. Fetch STREAM-START then.693if !parser.stream_start_produced {694return yaml_parser_fetch_stream_start(parser)695}696697scan_mark := parser.mark698699// Eat whitespaces and comments until we reach the next token.700if !yaml_parser_scan_to_next_token(parser) {701return false702}703704// [Go] While unrolling indents, transform the head comments of prior705// indentation levels observed after scan_start into foot comments at706// the respective indexes.707708// Check the indentation level against the current column.709if !yaml_parser_unroll_indent(parser, parser.mark.column, scan_mark) {710return false711}712713// Ensure that the buffer contains at least 4 characters. 4 is the length714// of the longest indicators ('--- ' and '... ').715if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) {716return false717}718719// Is it the end of the stream?720if is_z(parser.buffer, parser.buffer_pos) {721return yaml_parser_fetch_stream_end(parser)722}723724// Is it a directive?725if parser.mark.column == 0 && parser.buffer[parser.buffer_pos] == '%' {726return yaml_parser_fetch_directive(parser)727}728729buf := parser.buffer730pos := parser.buffer_pos731732// Is it the document start indicator?733if parser.mark.column == 0 && buf[pos] == '-' && buf[pos+1] == '-' && buf[pos+2] == '-' && is_blankz(buf, pos+3) {734return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_START_TOKEN)735}736737// Is it the document end indicator?738if parser.mark.column == 0 && buf[pos] == '.' && buf[pos+1] == '.' && buf[pos+2] == '.' && is_blankz(buf, pos+3) {739return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN)740}741742comment_mark := parser.mark743if len(parser.tokens) > 0 && (parser.flow_level == 0 && buf[pos] == ':' || parser.flow_level > 0 && buf[pos] == ',') {744// Associate any following comments with the prior token.745comment_mark = parser.tokens[len(parser.tokens)-1].start_mark746}747defer func() {748if !ok {749return750}751if len(parser.tokens) > 0 && parser.tokens[len(parser.tokens)-1].typ == yaml_BLOCK_ENTRY_TOKEN {752// Sequence indicators alone have no line comments. It becomes753// a head comment for whatever follows.754return755}756if !yaml_parser_scan_line_comment(parser, comment_mark) {757ok = false758return759}760}()761762// Is it the flow sequence start indicator?763if buf[pos] == '[' {764return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN)765}766767// Is it the flow mapping start indicator?768if parser.buffer[parser.buffer_pos] == '{' {769return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_MAPPING_START_TOKEN)770}771772// Is it the flow sequence end indicator?773if parser.buffer[parser.buffer_pos] == ']' {774return yaml_parser_fetch_flow_collection_end(parser,775yaml_FLOW_SEQUENCE_END_TOKEN)776}777778// Is it the flow mapping end indicator?779if parser.buffer[parser.buffer_pos] == '}' {780return yaml_parser_fetch_flow_collection_end(parser,781yaml_FLOW_MAPPING_END_TOKEN)782}783784// Is it the flow entry indicator?785if parser.buffer[parser.buffer_pos] == ',' {786return yaml_parser_fetch_flow_entry(parser)787}788789// Is it the block entry indicator?790if parser.buffer[parser.buffer_pos] == '-' && is_blankz(parser.buffer, parser.buffer_pos+1) {791return yaml_parser_fetch_block_entry(parser)792}793794// Is it the key indicator?795if parser.buffer[parser.buffer_pos] == '?' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) {796return yaml_parser_fetch_key(parser)797}798799// Is it the value indicator?800if parser.buffer[parser.buffer_pos] == ':' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) {801return yaml_parser_fetch_value(parser)802}803804// Is it an alias?805if parser.buffer[parser.buffer_pos] == '*' {806return yaml_parser_fetch_anchor(parser, yaml_ALIAS_TOKEN)807}808809// Is it an anchor?810if parser.buffer[parser.buffer_pos] == '&' {811return yaml_parser_fetch_anchor(parser, yaml_ANCHOR_TOKEN)812}813814// Is it a tag?815if parser.buffer[parser.buffer_pos] == '!' {816return yaml_parser_fetch_tag(parser)817}818819// Is it a literal scalar?820if parser.buffer[parser.buffer_pos] == '|' && parser.flow_level == 0 {821return yaml_parser_fetch_block_scalar(parser, true)822}823824// Is it a folded scalar?825if parser.buffer[parser.buffer_pos] == '>' && parser.flow_level == 0 {826return yaml_parser_fetch_block_scalar(parser, false)827}828829// Is it a single-quoted scalar?830if parser.buffer[parser.buffer_pos] == '\'' {831return yaml_parser_fetch_flow_scalar(parser, true)832}833834// Is it a double-quoted scalar?835if parser.buffer[parser.buffer_pos] == '"' {836return yaml_parser_fetch_flow_scalar(parser, false)837}838839// Is it a plain scalar?840//841// A plain scalar may start with any non-blank characters except842//843// '-', '?', ':', ',', '[', ']', '{', '}',844// '#', '&', '*', '!', '|', '>', '\'', '\"',845// '%', '@', '`'.846//847// In the block context (and, for the '-' indicator, in the flow context848// too), it may also start with the characters849//850// '-', '?', ':'851//852// if it is followed by a non-space character.853//854// The last rule is more restrictive than the specification requires.855// [Go] TODO Make this logic more reasonable.856//switch parser.buffer[parser.buffer_pos] {857//case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`':858//}859if !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '-' ||860parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' ||861parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '[' ||862parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' ||863parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '#' ||864parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '*' ||865parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '|' ||866parser.buffer[parser.buffer_pos] == '>' || parser.buffer[parser.buffer_pos] == '\'' ||867parser.buffer[parser.buffer_pos] == '"' || parser.buffer[parser.buffer_pos] == '%' ||868parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') ||869(parser.buffer[parser.buffer_pos] == '-' && !is_blank(parser.buffer, parser.buffer_pos+1)) ||870(parser.flow_level == 0 &&871(parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':') &&872!is_blankz(parser.buffer, parser.buffer_pos+1)) {873return yaml_parser_fetch_plain_scalar(parser)874}875876// If we don't determine the token type so far, it is an error.877return yaml_parser_set_scanner_error(parser,878"while scanning for the next token", parser.mark,879"found character that cannot start any token")880}881882func yaml_simple_key_is_valid(parser *yaml_parser_t, simple_key *yaml_simple_key_t) (valid, ok bool) {883if !simple_key.possible {884return false, true885}886887// The 1.2 specification says:888//889// "If the ? indicator is omitted, parsing needs to see past the890// implicit key to recognize it as such. To limit the amount of891// lookahead required, the “:” indicator must appear at most 1024892// Unicode characters beyond the start of the key. In addition, the key893// is restricted to a single line."894//895if simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index {896// Check if the potential simple key to be removed is required.897if simple_key.required {898return false, yaml_parser_set_scanner_error(parser,899"while scanning a simple key", simple_key.mark,900"could not find expected ':'")901}902simple_key.possible = false903return false, true904}905return true, true906}907908// Check if a simple key may start at the current position and add it if909// needed.910func yaml_parser_save_simple_key(parser *yaml_parser_t) bool {911// A simple key is required at the current position if the scanner is in912// the block context and the current column coincides with the indentation913// level.914915required := parser.flow_level == 0 && parser.indent == parser.mark.column916917//918// If the current position may start a simple key, save it.919//920if parser.simple_key_allowed {921simple_key := yaml_simple_key_t{922possible: true,923required: required,924token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head),925mark: parser.mark,926}927928if !yaml_parser_remove_simple_key(parser) {929return false930}931parser.simple_keys[len(parser.simple_keys)-1] = simple_key932parser.simple_keys_by_tok[simple_key.token_number] = len(parser.simple_keys) - 1933}934return true935}936937// Remove a potential simple key at the current flow level.938func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool {939i := len(parser.simple_keys) - 1940if parser.simple_keys[i].possible {941// If the key is required, it is an error.942if parser.simple_keys[i].required {943return yaml_parser_set_scanner_error(parser,944"while scanning a simple key", parser.simple_keys[i].mark,945"could not find expected ':'")946}947// Remove the key from the stack.948parser.simple_keys[i].possible = false949delete(parser.simple_keys_by_tok, parser.simple_keys[i].token_number)950}951return true952}953954// max_flow_level limits the flow_level955const max_flow_level = 10000956957// Increase the flow level and resize the simple key list if needed.958func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool {959// Reset the simple key on the next level.960parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{961possible: false,962required: false,963token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head),964mark: parser.mark,965})966967// Increase the flow level.968parser.flow_level++969if parser.flow_level > max_flow_level {970return yaml_parser_set_scanner_error(parser,971"while increasing flow level", parser.simple_keys[len(parser.simple_keys)-1].mark,972fmt.Sprintf("exceeded max depth of %d", max_flow_level))973}974return true975}976977// Decrease the flow level.978func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool {979if parser.flow_level > 0 {980parser.flow_level--981last := len(parser.simple_keys) - 1982delete(parser.simple_keys_by_tok, parser.simple_keys[last].token_number)983parser.simple_keys = parser.simple_keys[:last]984}985return true986}987988// max_indents limits the indents stack size989const max_indents = 10000990991// Push the current indentation level to the stack and set the new level992// the current column is greater than the indentation level. In this case,993// append or insert the specified token into the token queue.994func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml_token_type_t, mark yaml_mark_t) bool {995// In the flow context, do nothing.996if parser.flow_level > 0 {997return true998}9991000if parser.indent < column {1001// Push the current indentation level to the stack and set the new1002// indentation level.1003parser.indents = append(parser.indents, parser.indent)1004parser.indent = column1005if len(parser.indents) > max_indents {1006return yaml_parser_set_scanner_error(parser,1007"while increasing indent level", parser.simple_keys[len(parser.simple_keys)-1].mark,1008fmt.Sprintf("exceeded max depth of %d", max_indents))1009}10101011// Create a token and insert it into the queue.1012token := yaml_token_t{1013typ: typ,1014start_mark: mark,1015end_mark: mark,1016}1017if number > -1 {1018number -= parser.tokens_parsed1019}1020yaml_insert_token(parser, number, &token)1021}1022return true1023}10241025// Pop indentation levels from the indents stack until the current level1026// becomes less or equal to the column. For each indentation level, append1027// the BLOCK-END token.1028func yaml_parser_unroll_indent(parser *yaml_parser_t, column int, scan_mark yaml_mark_t) bool {1029// In the flow context, do nothing.1030if parser.flow_level > 0 {1031return true1032}10331034block_mark := scan_mark1035block_mark.index--10361037// Loop through the indentation levels in the stack.1038for parser.indent > column {10391040// [Go] Reposition the end token before potential following1041// foot comments of parent blocks. For that, search1042// backwards for recent comments that were at the same1043// indent as the block that is ending now.1044stop_index := block_mark.index1045for i := len(parser.comments) - 1; i >= 0; i-- {1046comment := &parser.comments[i]10471048if comment.end_mark.index < stop_index {1049// Don't go back beyond the start of the comment/whitespace scan, unless column < 0.1050// If requested indent column is < 0, then the document is over and everything else1051// is a foot anyway.1052break1053}1054if comment.start_mark.column == parser.indent+1 {1055// This is a good match. But maybe there's a former comment1056// at that same indent level, so keep searching.1057block_mark = comment.start_mark1058}10591060// While the end of the former comment matches with1061// the start of the following one, we know there's1062// nothing in between and scanning is still safe.1063stop_index = comment.scan_mark.index1064}10651066// Create a token and append it to the queue.1067token := yaml_token_t{1068typ: yaml_BLOCK_END_TOKEN,1069start_mark: block_mark,1070end_mark: block_mark,1071}1072yaml_insert_token(parser, -1, &token)10731074// Pop the indentation level.1075parser.indent = parser.indents[len(parser.indents)-1]1076parser.indents = parser.indents[:len(parser.indents)-1]1077}1078return true1079}10801081// Initialize the scanner and produce the STREAM-START token.1082func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool {10831084// Set the initial indentation.1085parser.indent = -110861087// Initialize the simple key stack.1088parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{})10891090parser.simple_keys_by_tok = make(map[int]int)10911092// A simple key is allowed at the beginning of the stream.1093parser.simple_key_allowed = true10941095// We have started.1096parser.stream_start_produced = true10971098// Create the STREAM-START token and append it to the queue.1099token := yaml_token_t{1100typ: yaml_STREAM_START_TOKEN,1101start_mark: parser.mark,1102end_mark: parser.mark,1103encoding: parser.encoding,1104}1105yaml_insert_token(parser, -1, &token)1106return true1107}11081109// Produce the STREAM-END token and shut down the scanner.1110func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool {11111112// Force new line.1113if parser.mark.column != 0 {1114parser.mark.column = 01115parser.mark.line++1116}11171118// Reset the indentation level.1119if !yaml_parser_unroll_indent(parser, -1, parser.mark) {1120return false1121}11221123// Reset simple keys.1124if !yaml_parser_remove_simple_key(parser) {1125return false1126}11271128parser.simple_key_allowed = false11291130// Create the STREAM-END token and append it to the queue.1131token := yaml_token_t{1132typ: yaml_STREAM_END_TOKEN,1133start_mark: parser.mark,1134end_mark: parser.mark,1135}1136yaml_insert_token(parser, -1, &token)1137return true1138}11391140// Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token.1141func yaml_parser_fetch_directive(parser *yaml_parser_t) bool {1142// Reset the indentation level.1143if !yaml_parser_unroll_indent(parser, -1, parser.mark) {1144return false1145}11461147// Reset simple keys.1148if !yaml_parser_remove_simple_key(parser) {1149return false1150}11511152parser.simple_key_allowed = false11531154// Create the YAML-DIRECTIVE or TAG-DIRECTIVE token.1155token := yaml_token_t{}1156if !yaml_parser_scan_directive(parser, &token) {1157return false1158}1159// Append the token to the queue.1160yaml_insert_token(parser, -1, &token)1161return true1162}11631164// Produce the DOCUMENT-START or DOCUMENT-END token.1165func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool {1166// Reset the indentation level.1167if !yaml_parser_unroll_indent(parser, -1, parser.mark) {1168return false1169}11701171// Reset simple keys.1172if !yaml_parser_remove_simple_key(parser) {1173return false1174}11751176parser.simple_key_allowed = false11771178// Consume the token.1179start_mark := parser.mark11801181skip(parser)1182skip(parser)1183skip(parser)11841185end_mark := parser.mark11861187// Create the DOCUMENT-START or DOCUMENT-END token.1188token := yaml_token_t{1189typ: typ,1190start_mark: start_mark,1191end_mark: end_mark,1192}1193// Append the token to the queue.1194yaml_insert_token(parser, -1, &token)1195return true1196}11971198// Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token.1199func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool {12001201// The indicators '[' and '{' may start a simple key.1202if !yaml_parser_save_simple_key(parser) {1203return false1204}12051206// Increase the flow level.1207if !yaml_parser_increase_flow_level(parser) {1208return false1209}12101211// A simple key may follow the indicators '[' and '{'.1212parser.simple_key_allowed = true12131214// Consume the token.1215start_mark := parser.mark1216skip(parser)1217end_mark := parser.mark12181219// Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token.1220token := yaml_token_t{1221typ: typ,1222start_mark: start_mark,1223end_mark: end_mark,1224}1225// Append the token to the queue.1226yaml_insert_token(parser, -1, &token)1227return true1228}12291230// Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token.1231func yaml_parser_fetch_flow_collection_end(parser *yaml_parser_t, typ yaml_token_type_t) bool {1232// Reset any potential simple key on the current flow level.1233if !yaml_parser_remove_simple_key(parser) {1234return false1235}12361237// Decrease the flow level.1238if !yaml_parser_decrease_flow_level(parser) {1239return false1240}12411242// No simple keys after the indicators ']' and '}'.1243parser.simple_key_allowed = false12441245// Consume the token.12461247start_mark := parser.mark1248skip(parser)1249end_mark := parser.mark12501251// Create the FLOW-SEQUENCE-END of FLOW-MAPPING-END token.1252token := yaml_token_t{1253typ: typ,1254start_mark: start_mark,1255end_mark: end_mark,1256}1257// Append the token to the queue.1258yaml_insert_token(parser, -1, &token)1259return true1260}12611262// Produce the FLOW-ENTRY token.1263func yaml_parser_fetch_flow_entry(parser *yaml_parser_t) bool {1264// Reset any potential simple keys on the current flow level.1265if !yaml_parser_remove_simple_key(parser) {1266return false1267}12681269// Simple keys are allowed after ','.1270parser.simple_key_allowed = true12711272// Consume the token.1273start_mark := parser.mark1274skip(parser)1275end_mark := parser.mark12761277// Create the FLOW-ENTRY token and append it to the queue.1278token := yaml_token_t{1279typ: yaml_FLOW_ENTRY_TOKEN,1280start_mark: start_mark,1281end_mark: end_mark,1282}1283yaml_insert_token(parser, -1, &token)1284return true1285}12861287// Produce the BLOCK-ENTRY token.1288func yaml_parser_fetch_block_entry(parser *yaml_parser_t) bool {1289// Check if the scanner is in the block context.1290if parser.flow_level == 0 {1291// Check if we are allowed to start a new entry.1292if !parser.simple_key_allowed {1293return yaml_parser_set_scanner_error(parser, "", parser.mark,1294"block sequence entries are not allowed in this context")1295}1296// Add the BLOCK-SEQUENCE-START token if needed.1297if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_SEQUENCE_START_TOKEN, parser.mark) {1298return false1299}1300} else {1301// It is an error for the '-' indicator to occur in the flow context,1302// but we let the Parser detect and report about it because the Parser1303// is able to point to the context.1304}13051306// Reset any potential simple keys on the current flow level.1307if !yaml_parser_remove_simple_key(parser) {1308return false1309}13101311// Simple keys are allowed after '-'.1312parser.simple_key_allowed = true13131314// Consume the token.1315start_mark := parser.mark1316skip(parser)1317end_mark := parser.mark13181319// Create the BLOCK-ENTRY token and append it to the queue.1320token := yaml_token_t{1321typ: yaml_BLOCK_ENTRY_TOKEN,1322start_mark: start_mark,1323end_mark: end_mark,1324}1325yaml_insert_token(parser, -1, &token)1326return true1327}13281329// Produce the KEY token.1330func yaml_parser_fetch_key(parser *yaml_parser_t) bool {13311332// In the block context, additional checks are required.1333if parser.flow_level == 0 {1334// Check if we are allowed to start a new key (not nessesary simple).1335if !parser.simple_key_allowed {1336return yaml_parser_set_scanner_error(parser, "", parser.mark,1337"mapping keys are not allowed in this context")1338}1339// Add the BLOCK-MAPPING-START token if needed.1340if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) {1341return false1342}1343}13441345// Reset any potential simple keys on the current flow level.1346if !yaml_parser_remove_simple_key(parser) {1347return false1348}13491350// Simple keys are allowed after '?' in the block context.1351parser.simple_key_allowed = parser.flow_level == 013521353// Consume the token.1354start_mark := parser.mark1355skip(parser)1356end_mark := parser.mark13571358// Create the KEY token and append it to the queue.1359token := yaml_token_t{1360typ: yaml_KEY_TOKEN,1361start_mark: start_mark,1362end_mark: end_mark,1363}1364yaml_insert_token(parser, -1, &token)1365return true1366}13671368// Produce the VALUE token.1369func yaml_parser_fetch_value(parser *yaml_parser_t) bool {13701371simple_key := &parser.simple_keys[len(parser.simple_keys)-1]13721373// Have we found a simple key?1374if valid, ok := yaml_simple_key_is_valid(parser, simple_key); !ok {1375return false13761377} else if valid {13781379// Create the KEY token and insert it into the queue.1380token := yaml_token_t{1381typ: yaml_KEY_TOKEN,1382start_mark: simple_key.mark,1383end_mark: simple_key.mark,1384}1385yaml_insert_token(parser, simple_key.token_number-parser.tokens_parsed, &token)13861387// In the block context, we may need to add the BLOCK-MAPPING-START token.1388if !yaml_parser_roll_indent(parser, simple_key.mark.column,1389simple_key.token_number,1390yaml_BLOCK_MAPPING_START_TOKEN, simple_key.mark) {1391return false1392}13931394// Remove the simple key.1395simple_key.possible = false1396delete(parser.simple_keys_by_tok, simple_key.token_number)13971398// A simple key cannot follow another simple key.1399parser.simple_key_allowed = false14001401} else {1402// The ':' indicator follows a complex key.14031404// In the block context, extra checks are required.1405if parser.flow_level == 0 {14061407// Check if we are allowed to start a complex value.1408if !parser.simple_key_allowed {1409return yaml_parser_set_scanner_error(parser, "", parser.mark,1410"mapping values are not allowed in this context")1411}14121413// Add the BLOCK-MAPPING-START token if needed.1414if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) {1415return false1416}1417}14181419// Simple keys after ':' are allowed in the block context.1420parser.simple_key_allowed = parser.flow_level == 01421}14221423// Consume the token.1424start_mark := parser.mark1425skip(parser)1426end_mark := parser.mark14271428// Create the VALUE token and append it to the queue.1429token := yaml_token_t{1430typ: yaml_VALUE_TOKEN,1431start_mark: start_mark,1432end_mark: end_mark,1433}1434yaml_insert_token(parser, -1, &token)1435return true1436}14371438// Produce the ALIAS or ANCHOR token.1439func yaml_parser_fetch_anchor(parser *yaml_parser_t, typ yaml_token_type_t) bool {1440// An anchor or an alias could be a simple key.1441if !yaml_parser_save_simple_key(parser) {1442return false1443}14441445// A simple key cannot follow an anchor or an alias.1446parser.simple_key_allowed = false14471448// Create the ALIAS or ANCHOR token and append it to the queue.1449var token yaml_token_t1450if !yaml_parser_scan_anchor(parser, &token, typ) {1451return false1452}1453yaml_insert_token(parser, -1, &token)1454return true1455}14561457// Produce the TAG token.1458func yaml_parser_fetch_tag(parser *yaml_parser_t) bool {1459// A tag could be a simple key.1460if !yaml_parser_save_simple_key(parser) {1461return false1462}14631464// A simple key cannot follow a tag.1465parser.simple_key_allowed = false14661467// Create the TAG token and append it to the queue.1468var token yaml_token_t1469if !yaml_parser_scan_tag(parser, &token) {1470return false1471}1472yaml_insert_token(parser, -1, &token)1473return true1474}14751476// Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens.1477func yaml_parser_fetch_block_scalar(parser *yaml_parser_t, literal bool) bool {1478// Remove any potential simple keys.1479if !yaml_parser_remove_simple_key(parser) {1480return false1481}14821483// A simple key may follow a block scalar.1484parser.simple_key_allowed = true14851486// Create the SCALAR token and append it to the queue.1487var token yaml_token_t1488if !yaml_parser_scan_block_scalar(parser, &token, literal) {1489return false1490}1491yaml_insert_token(parser, -1, &token)1492return true1493}14941495// Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens.1496func yaml_parser_fetch_flow_scalar(parser *yaml_parser_t, single bool) bool {1497// A plain scalar could be a simple key.1498if !yaml_parser_save_simple_key(parser) {1499return false1500}15011502// A simple key cannot follow a flow scalar.1503parser.simple_key_allowed = false15041505// Create the SCALAR token and append it to the queue.1506var token yaml_token_t1507if !yaml_parser_scan_flow_scalar(parser, &token, single) {1508return false1509}1510yaml_insert_token(parser, -1, &token)1511return true1512}15131514// Produce the SCALAR(...,plain) token.1515func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool {1516// A plain scalar could be a simple key.1517if !yaml_parser_save_simple_key(parser) {1518return false1519}15201521// A simple key cannot follow a flow scalar.1522parser.simple_key_allowed = false15231524// Create the SCALAR token and append it to the queue.1525var token yaml_token_t1526if !yaml_parser_scan_plain_scalar(parser, &token) {1527return false1528}1529yaml_insert_token(parser, -1, &token)1530return true1531}15321533// Eat whitespaces and comments until the next token is found.1534func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool {15351536scan_mark := parser.mark15371538// Until the next token is not found.1539for {1540// Allow the BOM mark to start a line.1541if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1542return false1543}1544if parser.mark.column == 0 && is_bom(parser.buffer, parser.buffer_pos) {1545skip(parser)1546}15471548// Eat whitespaces.1549// Tabs are allowed:1550// - in the flow context1551// - in the block context, but not at the beginning of the line or1552// after '-', '?', or ':' (complex value).1553if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1554return false1555}15561557for parser.buffer[parser.buffer_pos] == ' ' || ((parser.flow_level > 0 || !parser.simple_key_allowed) && parser.buffer[parser.buffer_pos] == '\t') {1558skip(parser)1559if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1560return false1561}1562}15631564// Check if we just had a line comment under a sequence entry that1565// looks more like a header to the following content. Similar to this:1566//1567// - # The comment1568// - Some data1569//1570// If so, transform the line comment to a head comment and reposition.1571if len(parser.comments) > 0 && len(parser.tokens) > 1 {1572tokenA := parser.tokens[len(parser.tokens)-2]1573tokenB := parser.tokens[len(parser.tokens)-1]1574comment := &parser.comments[len(parser.comments)-1]1575if tokenA.typ == yaml_BLOCK_SEQUENCE_START_TOKEN && tokenB.typ == yaml_BLOCK_ENTRY_TOKEN && len(comment.line) > 0 && !is_break(parser.buffer, parser.buffer_pos) {1576// If it was in the prior line, reposition so it becomes a1577// header of the follow up token. Otherwise, keep it in place1578// so it becomes a header of the former.1579comment.head = comment.line1580comment.line = nil1581if comment.start_mark.line == parser.mark.line-1 {1582comment.token_mark = parser.mark1583}1584}1585}15861587// Eat a comment until a line break.1588if parser.buffer[parser.buffer_pos] == '#' {1589if !yaml_parser_scan_comments(parser, scan_mark) {1590return false1591}1592}15931594// If it is a line break, eat it.1595if is_break(parser.buffer, parser.buffer_pos) {1596if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {1597return false1598}1599skip_line(parser)16001601// In the block context, a new line may start a simple key.1602if parser.flow_level == 0 {1603parser.simple_key_allowed = true1604}1605} else {1606break // We have found a token.1607}1608}16091610return true1611}16121613// Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token.1614//1615// Scope:1616// %YAML 1.1 # a comment \n1617// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^1618// %TAG !yaml! tag:yaml.org,2002: \n1619// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^1620//1621func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool {1622// Eat '%'.1623start_mark := parser.mark1624skip(parser)16251626// Scan the directive name.1627var name []byte1628if !yaml_parser_scan_directive_name(parser, start_mark, &name) {1629return false1630}16311632// Is it a YAML directive?1633if bytes.Equal(name, []byte("YAML")) {1634// Scan the VERSION directive value.1635var major, minor int81636if !yaml_parser_scan_version_directive_value(parser, start_mark, &major, &minor) {1637return false1638}1639end_mark := parser.mark16401641// Create a VERSION-DIRECTIVE token.1642*token = yaml_token_t{1643typ: yaml_VERSION_DIRECTIVE_TOKEN,1644start_mark: start_mark,1645end_mark: end_mark,1646major: major,1647minor: minor,1648}16491650// Is it a TAG directive?1651} else if bytes.Equal(name, []byte("TAG")) {1652// Scan the TAG directive value.1653var handle, prefix []byte1654if !yaml_parser_scan_tag_directive_value(parser, start_mark, &handle, &prefix) {1655return false1656}1657end_mark := parser.mark16581659// Create a TAG-DIRECTIVE token.1660*token = yaml_token_t{1661typ: yaml_TAG_DIRECTIVE_TOKEN,1662start_mark: start_mark,1663end_mark: end_mark,1664value: handle,1665prefix: prefix,1666}16671668// Unknown directive.1669} else {1670yaml_parser_set_scanner_error(parser, "while scanning a directive",1671start_mark, "found unknown directive name")1672return false1673}16741675// Eat the rest of the line including any comments.1676if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1677return false1678}16791680for is_blank(parser.buffer, parser.buffer_pos) {1681skip(parser)1682if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1683return false1684}1685}16861687if parser.buffer[parser.buffer_pos] == '#' {1688// [Go] Discard this inline comment for the time being.1689//if !yaml_parser_scan_line_comment(parser, start_mark) {1690// return false1691//}1692for !is_breakz(parser.buffer, parser.buffer_pos) {1693skip(parser)1694if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1695return false1696}1697}1698}16991700// Check if we are at the end of the line.1701if !is_breakz(parser.buffer, parser.buffer_pos) {1702yaml_parser_set_scanner_error(parser, "while scanning a directive",1703start_mark, "did not find expected comment or line break")1704return false1705}17061707// Eat a line break.1708if is_break(parser.buffer, parser.buffer_pos) {1709if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {1710return false1711}1712skip_line(parser)1713}17141715return true1716}17171718// Scan the directive name.1719//1720// Scope:1721// %YAML 1.1 # a comment \n1722// ^^^^1723// %TAG !yaml! tag:yaml.org,2002: \n1724// ^^^1725//1726func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool {1727// Consume the directive name.1728if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1729return false1730}17311732var s []byte1733for is_alpha(parser.buffer, parser.buffer_pos) {1734s = read(parser, s)1735if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1736return false1737}1738}17391740// Check if the name is empty.1741if len(s) == 0 {1742yaml_parser_set_scanner_error(parser, "while scanning a directive",1743start_mark, "could not find expected directive name")1744return false1745}17461747// Check for an blank character after the name.1748if !is_blankz(parser.buffer, parser.buffer_pos) {1749yaml_parser_set_scanner_error(parser, "while scanning a directive",1750start_mark, "found unexpected non-alphabetical character")1751return false1752}1753*name = s1754return true1755}17561757// Scan the value of VERSION-DIRECTIVE.1758//1759// Scope:1760// %YAML 1.1 # a comment \n1761// ^^^^^^1762func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool {1763// Eat whitespaces.1764if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1765return false1766}1767for is_blank(parser.buffer, parser.buffer_pos) {1768skip(parser)1769if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1770return false1771}1772}17731774// Consume the major version number.1775if !yaml_parser_scan_version_directive_number(parser, start_mark, major) {1776return false1777}17781779// Eat '.'.1780if parser.buffer[parser.buffer_pos] != '.' {1781return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive",1782start_mark, "did not find expected digit or '.' character")1783}17841785skip(parser)17861787// Consume the minor version number.1788if !yaml_parser_scan_version_directive_number(parser, start_mark, minor) {1789return false1790}1791return true1792}17931794const max_number_length = 217951796// Scan the version number of VERSION-DIRECTIVE.1797//1798// Scope:1799// %YAML 1.1 # a comment \n1800// ^1801// %YAML 1.1 # a comment \n1802// ^1803func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool {18041805// Repeat while the next character is digit.1806if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1807return false1808}1809var value, length int81810for is_digit(parser.buffer, parser.buffer_pos) {1811// Check if the number is too long.1812length++1813if length > max_number_length {1814return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive",1815start_mark, "found extremely long version number")1816}1817value = value*10 + int8(as_digit(parser.buffer, parser.buffer_pos))1818skip(parser)1819if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1820return false1821}1822}18231824// Check if the number was present.1825if length == 0 {1826return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive",1827start_mark, "did not find expected version number")1828}1829*number = value1830return true1831}18321833// Scan the value of a TAG-DIRECTIVE token.1834//1835// Scope:1836// %TAG !yaml! tag:yaml.org,2002: \n1837// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^1838//1839func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool {1840var handle_value, prefix_value []byte18411842// Eat whitespaces.1843if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1844return false1845}18461847for is_blank(parser.buffer, parser.buffer_pos) {1848skip(parser)1849if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1850return false1851}1852}18531854// Scan a handle.1855if !yaml_parser_scan_tag_handle(parser, true, start_mark, &handle_value) {1856return false1857}18581859// Expect a whitespace.1860if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1861return false1862}1863if !is_blank(parser.buffer, parser.buffer_pos) {1864yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive",1865start_mark, "did not find expected whitespace")1866return false1867}18681869// Eat whitespaces.1870for is_blank(parser.buffer, parser.buffer_pos) {1871skip(parser)1872if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1873return false1874}1875}18761877// Scan a prefix.1878if !yaml_parser_scan_tag_uri(parser, true, nil, start_mark, &prefix_value) {1879return false1880}18811882// Expect a whitespace or line break.1883if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1884return false1885}1886if !is_blankz(parser.buffer, parser.buffer_pos) {1887yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive",1888start_mark, "did not find expected whitespace or line break")1889return false1890}18911892*handle = handle_value1893*prefix = prefix_value1894return true1895}18961897func yaml_parser_scan_anchor(parser *yaml_parser_t, token *yaml_token_t, typ yaml_token_type_t) bool {1898var s []byte18991900// Eat the indicator character.1901start_mark := parser.mark1902skip(parser)19031904// Consume the value.1905if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1906return false1907}19081909for is_alpha(parser.buffer, parser.buffer_pos) {1910s = read(parser, s)1911if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {1912return false1913}1914}19151916end_mark := parser.mark19171918/*1919* Check if length of the anchor is greater than 0 and it is followed by1920* a whitespace character or one of the indicators:1921*1922* '?', ':', ',', ']', '}', '%', '@', '`'.1923*/19241925if len(s) == 0 ||1926!(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '?' ||1927parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' ||1928parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '}' ||1929parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' ||1930parser.buffer[parser.buffer_pos] == '`') {1931context := "while scanning an alias"1932if typ == yaml_ANCHOR_TOKEN {1933context = "while scanning an anchor"1934}1935yaml_parser_set_scanner_error(parser, context, start_mark,1936"did not find expected alphabetic or numeric character")1937return false1938}19391940// Create a token.1941*token = yaml_token_t{1942typ: typ,1943start_mark: start_mark,1944end_mark: end_mark,1945value: s,1946}19471948return true1949}19501951/*1952* Scan a TAG token.1953*/19541955func yaml_parser_scan_tag(parser *yaml_parser_t, token *yaml_token_t) bool {1956var handle, suffix []byte19571958start_mark := parser.mark19591960// Check if the tag is in the canonical form.1961if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {1962return false1963}19641965if parser.buffer[parser.buffer_pos+1] == '<' {1966// Keep the handle as ''19671968// Eat '!<'1969skip(parser)1970skip(parser)19711972// Consume the tag value.1973if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) {1974return false1975}19761977// Check for '>' and eat it.1978if parser.buffer[parser.buffer_pos] != '>' {1979yaml_parser_set_scanner_error(parser, "while scanning a tag",1980start_mark, "did not find the expected '>'")1981return false1982}19831984skip(parser)1985} else {1986// The tag has either the '!suffix' or the '!handle!suffix' form.19871988// First, try to scan a handle.1989if !yaml_parser_scan_tag_handle(parser, false, start_mark, &handle) {1990return false1991}19921993// Check if it is, indeed, handle.1994if handle[0] == '!' && len(handle) > 1 && handle[len(handle)-1] == '!' {1995// Scan the suffix now.1996if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) {1997return false1998}1999} else {2000// It wasn't a handle after all. Scan the rest of the tag.2001if !yaml_parser_scan_tag_uri(parser, false, handle, start_mark, &suffix) {2002return false2003}20042005// Set the handle to '!'.2006handle = []byte{'!'}20072008// A special case: the '!' tag. Set the handle to '' and the2009// suffix to '!'.2010if len(suffix) == 0 {2011handle, suffix = suffix, handle2012}2013}2014}20152016// Check the character which ends the tag.2017if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2018return false2019}2020if !is_blankz(parser.buffer, parser.buffer_pos) {2021yaml_parser_set_scanner_error(parser, "while scanning a tag",2022start_mark, "did not find expected whitespace or line break")2023return false2024}20252026end_mark := parser.mark20272028// Create a token.2029*token = yaml_token_t{2030typ: yaml_TAG_TOKEN,2031start_mark: start_mark,2032end_mark: end_mark,2033value: handle,2034suffix: suffix,2035}2036return true2037}20382039// Scan a tag handle.2040func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, handle *[]byte) bool {2041// Check the initial '!' character.2042if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2043return false2044}2045if parser.buffer[parser.buffer_pos] != '!' {2046yaml_parser_set_scanner_tag_error(parser, directive,2047start_mark, "did not find expected '!'")2048return false2049}20502051var s []byte20522053// Copy the '!' character.2054s = read(parser, s)20552056// Copy all subsequent alphabetical and numerical characters.2057if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2058return false2059}2060for is_alpha(parser.buffer, parser.buffer_pos) {2061s = read(parser, s)2062if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2063return false2064}2065}20662067// Check if the trailing character is '!' and copy it.2068if parser.buffer[parser.buffer_pos] == '!' {2069s = read(parser, s)2070} else {2071// It's either the '!' tag or not really a tag handle. If it's a %TAG2072// directive, it's an error. If it's a tag token, it must be a part of URI.2073if directive && string(s) != "!" {2074yaml_parser_set_scanner_tag_error(parser, directive,2075start_mark, "did not find expected '!'")2076return false2077}2078}20792080*handle = s2081return true2082}20832084// Scan a tag.2085func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool {2086//size_t length = head ? strlen((char *)head) : 02087var s []byte2088hasTag := len(head) > 020892090// Copy the head if needed.2091//2092// Note that we don't copy the leading '!' character.2093if len(head) > 1 {2094s = append(s, head[1:]...)2095}20962097// Scan the tag.2098if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2099return false2100}21012102// The set of characters that may appear in URI is as follows:2103//2104// '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&',2105// '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']',2106// '%'.2107// [Go] TODO Convert this into more reasonable logic.2108for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' ||2109parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' ||2110parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' ||2111parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '=' ||2112parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '$' ||2113parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '.' ||2114parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '~' ||2115parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '\'' ||2116parser.buffer[parser.buffer_pos] == '(' || parser.buffer[parser.buffer_pos] == ')' ||2117parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' ||2118parser.buffer[parser.buffer_pos] == '%' {2119// Check if it is a URI-escape sequence.2120if parser.buffer[parser.buffer_pos] == '%' {2121if !yaml_parser_scan_uri_escapes(parser, directive, start_mark, &s) {2122return false2123}2124} else {2125s = read(parser, s)2126}2127if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2128return false2129}2130hasTag = true2131}21322133if !hasTag {2134yaml_parser_set_scanner_tag_error(parser, directive,2135start_mark, "did not find expected tag URI")2136return false2137}2138*uri = s2139return true2140}21412142// Decode an URI-escape sequence corresponding to a single UTF-8 character.2143func yaml_parser_scan_uri_escapes(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, s *[]byte) bool {21442145// Decode the required number of characters.2146w := 10242147for w > 0 {2148// Check for a URI-escaped octet.2149if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) {2150return false2151}21522153if !(parser.buffer[parser.buffer_pos] == '%' &&2154is_hex(parser.buffer, parser.buffer_pos+1) &&2155is_hex(parser.buffer, parser.buffer_pos+2)) {2156return yaml_parser_set_scanner_tag_error(parser, directive,2157start_mark, "did not find URI escaped octet")2158}21592160// Get the octet.2161octet := byte((as_hex(parser.buffer, parser.buffer_pos+1) << 4) + as_hex(parser.buffer, parser.buffer_pos+2))21622163// If it is the leading octet, determine the length of the UTF-8 sequence.2164if w == 1024 {2165w = width(octet)2166if w == 0 {2167return yaml_parser_set_scanner_tag_error(parser, directive,2168start_mark, "found an incorrect leading UTF-8 octet")2169}2170} else {2171// Check if the trailing octet is correct.2172if octet&0xC0 != 0x80 {2173return yaml_parser_set_scanner_tag_error(parser, directive,2174start_mark, "found an incorrect trailing UTF-8 octet")2175}2176}21772178// Copy the octet and move the pointers.2179*s = append(*s, octet)2180skip(parser)2181skip(parser)2182skip(parser)2183w--2184}2185return true2186}21872188// Scan a block scalar.2189func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, literal bool) bool {2190// Eat the indicator '|' or '>'.2191start_mark := parser.mark2192skip(parser)21932194// Scan the additional block scalar indicators.2195if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2196return false2197}21982199// Check for a chomping indicator.2200var chomping, increment int2201if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' {2202// Set the chomping method and eat the indicator.2203if parser.buffer[parser.buffer_pos] == '+' {2204chomping = +12205} else {2206chomping = -12207}2208skip(parser)22092210// Check for an indentation indicator.2211if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2212return false2213}2214if is_digit(parser.buffer, parser.buffer_pos) {2215// Check that the indentation is greater than 0.2216if parser.buffer[parser.buffer_pos] == '0' {2217yaml_parser_set_scanner_error(parser, "while scanning a block scalar",2218start_mark, "found an indentation indicator equal to 0")2219return false2220}22212222// Get the indentation level and eat the indicator.2223increment = as_digit(parser.buffer, parser.buffer_pos)2224skip(parser)2225}22262227} else if is_digit(parser.buffer, parser.buffer_pos) {2228// Do the same as above, but in the opposite order.22292230if parser.buffer[parser.buffer_pos] == '0' {2231yaml_parser_set_scanner_error(parser, "while scanning a block scalar",2232start_mark, "found an indentation indicator equal to 0")2233return false2234}2235increment = as_digit(parser.buffer, parser.buffer_pos)2236skip(parser)22372238if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2239return false2240}2241if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' {2242if parser.buffer[parser.buffer_pos] == '+' {2243chomping = +12244} else {2245chomping = -12246}2247skip(parser)2248}2249}22502251// Eat whitespaces and comments to the end of the line.2252if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2253return false2254}2255for is_blank(parser.buffer, parser.buffer_pos) {2256skip(parser)2257if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2258return false2259}2260}2261if parser.buffer[parser.buffer_pos] == '#' {2262if !yaml_parser_scan_line_comment(parser, start_mark) {2263return false2264}2265for !is_breakz(parser.buffer, parser.buffer_pos) {2266skip(parser)2267if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2268return false2269}2270}2271}22722273// Check if we are at the end of the line.2274if !is_breakz(parser.buffer, parser.buffer_pos) {2275yaml_parser_set_scanner_error(parser, "while scanning a block scalar",2276start_mark, "did not find expected comment or line break")2277return false2278}22792280// Eat a line break.2281if is_break(parser.buffer, parser.buffer_pos) {2282if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {2283return false2284}2285skip_line(parser)2286}22872288end_mark := parser.mark22892290// Set the indentation level if it was specified.2291var indent int2292if increment > 0 {2293if parser.indent >= 0 {2294indent = parser.indent + increment2295} else {2296indent = increment2297}2298}22992300// Scan the leading line breaks and determine the indentation level if needed.2301var s, leading_break, trailing_breaks []byte2302if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) {2303return false2304}23052306// Scan the block scalar content.2307if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2308return false2309}2310var leading_blank, trailing_blank bool2311for parser.mark.column == indent && !is_z(parser.buffer, parser.buffer_pos) {2312// We are at the beginning of a non-empty line.23132314// Is it a trailing whitespace?2315trailing_blank = is_blank(parser.buffer, parser.buffer_pos)23162317// Check if we need to fold the leading line break.2318if !literal && !leading_blank && !trailing_blank && len(leading_break) > 0 && leading_break[0] == '\n' {2319// Do we need to join the lines by space?2320if len(trailing_breaks) == 0 {2321s = append(s, ' ')2322}2323} else {2324s = append(s, leading_break...)2325}2326leading_break = leading_break[:0]23272328// Append the remaining line breaks.2329s = append(s, trailing_breaks...)2330trailing_breaks = trailing_breaks[:0]23312332// Is it a leading whitespace?2333leading_blank = is_blank(parser.buffer, parser.buffer_pos)23342335// Consume the current line.2336for !is_breakz(parser.buffer, parser.buffer_pos) {2337s = read(parser, s)2338if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2339return false2340}2341}23422343// Consume the line break.2344if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {2345return false2346}23472348leading_break = read_line(parser, leading_break)23492350// Eat the following indentation spaces and line breaks.2351if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) {2352return false2353}2354}23552356// Chomp the tail.2357if chomping != -1 {2358s = append(s, leading_break...)2359}2360if chomping == 1 {2361s = append(s, trailing_breaks...)2362}23632364// Create a token.2365*token = yaml_token_t{2366typ: yaml_SCALAR_TOKEN,2367start_mark: start_mark,2368end_mark: end_mark,2369value: s,2370style: yaml_LITERAL_SCALAR_STYLE,2371}2372if !literal {2373token.style = yaml_FOLDED_SCALAR_STYLE2374}2375return true2376}23772378// Scan indentation spaces and line breaks for a block scalar. Determine the2379// indentation level if needed.2380func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool {2381*end_mark = parser.mark23822383// Eat the indentation spaces and line breaks.2384max_indent := 02385for {2386// Eat the indentation spaces.2387if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2388return false2389}2390for (*indent == 0 || parser.mark.column < *indent) && is_space(parser.buffer, parser.buffer_pos) {2391skip(parser)2392if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2393return false2394}2395}2396if parser.mark.column > max_indent {2397max_indent = parser.mark.column2398}23992400// Check for a tab character messing the indentation.2401if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) {2402return yaml_parser_set_scanner_error(parser, "while scanning a block scalar",2403start_mark, "found a tab character where an indentation space is expected")2404}24052406// Have we found a non-empty line?2407if !is_break(parser.buffer, parser.buffer_pos) {2408break2409}24102411// Consume the line break.2412if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {2413return false2414}2415// [Go] Should really be returning breaks instead.2416*breaks = read_line(parser, *breaks)2417*end_mark = parser.mark2418}24192420// Determine the indentation level if needed.2421if *indent == 0 {2422*indent = max_indent2423if *indent < parser.indent+1 {2424*indent = parser.indent + 12425}2426if *indent < 1 {2427*indent = 12428}2429}2430return true2431}24322433// Scan a quoted scalar.2434func yaml_parser_scan_flow_scalar(parser *yaml_parser_t, token *yaml_token_t, single bool) bool {2435// Eat the left quote.2436start_mark := parser.mark2437skip(parser)24382439// Consume the content of the quoted scalar.2440var s, leading_break, trailing_breaks, whitespaces []byte2441for {2442// Check that there are no document indicators at the beginning of the line.2443if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) {2444return false2445}24462447if parser.mark.column == 0 &&2448((parser.buffer[parser.buffer_pos+0] == '-' &&2449parser.buffer[parser.buffer_pos+1] == '-' &&2450parser.buffer[parser.buffer_pos+2] == '-') ||2451(parser.buffer[parser.buffer_pos+0] == '.' &&2452parser.buffer[parser.buffer_pos+1] == '.' &&2453parser.buffer[parser.buffer_pos+2] == '.')) &&2454is_blankz(parser.buffer, parser.buffer_pos+3) {2455yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar",2456start_mark, "found unexpected document indicator")2457return false2458}24592460// Check for EOF.2461if is_z(parser.buffer, parser.buffer_pos) {2462yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar",2463start_mark, "found unexpected end of stream")2464return false2465}24662467// Consume non-blank characters.2468leading_blanks := false2469for !is_blankz(parser.buffer, parser.buffer_pos) {2470if single && parser.buffer[parser.buffer_pos] == '\'' && parser.buffer[parser.buffer_pos+1] == '\'' {2471// Is is an escaped single quote.2472s = append(s, '\'')2473skip(parser)2474skip(parser)24752476} else if single && parser.buffer[parser.buffer_pos] == '\'' {2477// It is a right single quote.2478break2479} else if !single && parser.buffer[parser.buffer_pos] == '"' {2480// It is a right double quote.2481break24822483} else if !single && parser.buffer[parser.buffer_pos] == '\\' && is_break(parser.buffer, parser.buffer_pos+1) {2484// It is an escaped line break.2485if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) {2486return false2487}2488skip(parser)2489skip_line(parser)2490leading_blanks = true2491break24922493} else if !single && parser.buffer[parser.buffer_pos] == '\\' {2494// It is an escape sequence.2495code_length := 024962497// Check the escape character.2498switch parser.buffer[parser.buffer_pos+1] {2499case '0':2500s = append(s, 0)2501case 'a':2502s = append(s, '\x07')2503case 'b':2504s = append(s, '\x08')2505case 't', '\t':2506s = append(s, '\x09')2507case 'n':2508s = append(s, '\x0A')2509case 'v':2510s = append(s, '\x0B')2511case 'f':2512s = append(s, '\x0C')2513case 'r':2514s = append(s, '\x0D')2515case 'e':2516s = append(s, '\x1B')2517case ' ':2518s = append(s, '\x20')2519case '"':2520s = append(s, '"')2521case '\'':2522s = append(s, '\'')2523case '\\':2524s = append(s, '\\')2525case 'N': // NEL (#x85)2526s = append(s, '\xC2')2527s = append(s, '\x85')2528case '_': // #xA02529s = append(s, '\xC2')2530s = append(s, '\xA0')2531case 'L': // LS (#x2028)2532s = append(s, '\xE2')2533s = append(s, '\x80')2534s = append(s, '\xA8')2535case 'P': // PS (#x2029)2536s = append(s, '\xE2')2537s = append(s, '\x80')2538s = append(s, '\xA9')2539case 'x':2540code_length = 22541case 'u':2542code_length = 42543case 'U':2544code_length = 82545default:2546yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar",2547start_mark, "found unknown escape character")2548return false2549}25502551skip(parser)2552skip(parser)25532554// Consume an arbitrary escape code.2555if code_length > 0 {2556var value int25572558// Scan the character value.2559if parser.unread < code_length && !yaml_parser_update_buffer(parser, code_length) {2560return false2561}2562for k := 0; k < code_length; k++ {2563if !is_hex(parser.buffer, parser.buffer_pos+k) {2564yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar",2565start_mark, "did not find expected hexdecimal number")2566return false2567}2568value = (value << 4) + as_hex(parser.buffer, parser.buffer_pos+k)2569}25702571// Check the value and write the character.2572if (value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF {2573yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar",2574start_mark, "found invalid Unicode character escape code")2575return false2576}2577if value <= 0x7F {2578s = append(s, byte(value))2579} else if value <= 0x7FF {2580s = append(s, byte(0xC0+(value>>6)))2581s = append(s, byte(0x80+(value&0x3F)))2582} else if value <= 0xFFFF {2583s = append(s, byte(0xE0+(value>>12)))2584s = append(s, byte(0x80+((value>>6)&0x3F)))2585s = append(s, byte(0x80+(value&0x3F)))2586} else {2587s = append(s, byte(0xF0+(value>>18)))2588s = append(s, byte(0x80+((value>>12)&0x3F)))2589s = append(s, byte(0x80+((value>>6)&0x3F)))2590s = append(s, byte(0x80+(value&0x3F)))2591}25922593// Advance the pointer.2594for k := 0; k < code_length; k++ {2595skip(parser)2596}2597}2598} else {2599// It is a non-escaped non-blank character.2600s = read(parser, s)2601}2602if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {2603return false2604}2605}26062607if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2608return false2609}26102611// Check if we are at the end of the scalar.2612if single {2613if parser.buffer[parser.buffer_pos] == '\'' {2614break2615}2616} else {2617if parser.buffer[parser.buffer_pos] == '"' {2618break2619}2620}26212622// Consume blank characters.2623for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) {2624if is_blank(parser.buffer, parser.buffer_pos) {2625// Consume a space or a tab character.2626if !leading_blanks {2627whitespaces = read(parser, whitespaces)2628} else {2629skip(parser)2630}2631} else {2632if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {2633return false2634}26352636// Check if it is a first line break.2637if !leading_blanks {2638whitespaces = whitespaces[:0]2639leading_break = read_line(parser, leading_break)2640leading_blanks = true2641} else {2642trailing_breaks = read_line(parser, trailing_breaks)2643}2644}2645if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2646return false2647}2648}26492650// Join the whitespaces or fold line breaks.2651if leading_blanks {2652// Do we need to fold line breaks?2653if len(leading_break) > 0 && leading_break[0] == '\n' {2654if len(trailing_breaks) == 0 {2655s = append(s, ' ')2656} else {2657s = append(s, trailing_breaks...)2658}2659} else {2660s = append(s, leading_break...)2661s = append(s, trailing_breaks...)2662}2663trailing_breaks = trailing_breaks[:0]2664leading_break = leading_break[:0]2665} else {2666s = append(s, whitespaces...)2667whitespaces = whitespaces[:0]2668}2669}26702671// Eat the right quote.2672skip(parser)2673end_mark := parser.mark26742675// Create a token.2676*token = yaml_token_t{2677typ: yaml_SCALAR_TOKEN,2678start_mark: start_mark,2679end_mark: end_mark,2680value: s,2681style: yaml_SINGLE_QUOTED_SCALAR_STYLE,2682}2683if !single {2684token.style = yaml_DOUBLE_QUOTED_SCALAR_STYLE2685}2686return true2687}26882689// Scan a plain scalar.2690func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) bool {26912692var s, leading_break, trailing_breaks, whitespaces []byte2693var leading_blanks bool2694var indent = parser.indent + 126952696start_mark := parser.mark2697end_mark := parser.mark26982699// Consume the content of the plain scalar.2700for {2701// Check for a document indicator.2702if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) {2703return false2704}2705if parser.mark.column == 0 &&2706((parser.buffer[parser.buffer_pos+0] == '-' &&2707parser.buffer[parser.buffer_pos+1] == '-' &&2708parser.buffer[parser.buffer_pos+2] == '-') ||2709(parser.buffer[parser.buffer_pos+0] == '.' &&2710parser.buffer[parser.buffer_pos+1] == '.' &&2711parser.buffer[parser.buffer_pos+2] == '.')) &&2712is_blankz(parser.buffer, parser.buffer_pos+3) {2713break2714}27152716// Check for a comment.2717if parser.buffer[parser.buffer_pos] == '#' {2718break2719}27202721// Consume non-blank characters.2722for !is_blankz(parser.buffer, parser.buffer_pos) {27232724// Check for indicators that may end a plain scalar.2725if (parser.buffer[parser.buffer_pos] == ':' && is_blankz(parser.buffer, parser.buffer_pos+1)) ||2726(parser.flow_level > 0 &&2727(parser.buffer[parser.buffer_pos] == ',' ||2728parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == '[' ||2729parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' ||2730parser.buffer[parser.buffer_pos] == '}')) {2731break2732}27332734// Check if we need to join whitespaces and breaks.2735if leading_blanks || len(whitespaces) > 0 {2736if leading_blanks {2737// Do we need to fold line breaks?2738if leading_break[0] == '\n' {2739if len(trailing_breaks) == 0 {2740s = append(s, ' ')2741} else {2742s = append(s, trailing_breaks...)2743}2744} else {2745s = append(s, leading_break...)2746s = append(s, trailing_breaks...)2747}2748trailing_breaks = trailing_breaks[:0]2749leading_break = leading_break[:0]2750leading_blanks = false2751} else {2752s = append(s, whitespaces...)2753whitespaces = whitespaces[:0]2754}2755}27562757// Copy the character.2758s = read(parser, s)27592760end_mark = parser.mark2761if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {2762return false2763}2764}27652766// Is it the end?2767if !(is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos)) {2768break2769}27702771// Consume blank characters.2772if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2773return false2774}27752776for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) {2777if is_blank(parser.buffer, parser.buffer_pos) {27782779// Check for tab characters that abuse indentation.2780if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) {2781yaml_parser_set_scanner_error(parser, "while scanning a plain scalar",2782start_mark, "found a tab character that violates indentation")2783return false2784}27852786// Consume a space or a tab character.2787if !leading_blanks {2788whitespaces = read(parser, whitespaces)2789} else {2790skip(parser)2791}2792} else {2793if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {2794return false2795}27962797// Check if it is a first line break.2798if !leading_blanks {2799whitespaces = whitespaces[:0]2800leading_break = read_line(parser, leading_break)2801leading_blanks = true2802} else {2803trailing_breaks = read_line(parser, trailing_breaks)2804}2805}2806if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2807return false2808}2809}28102811// Check indentation level.2812if parser.flow_level == 0 && parser.mark.column < indent {2813break2814}2815}28162817// Create a token.2818*token = yaml_token_t{2819typ: yaml_SCALAR_TOKEN,2820start_mark: start_mark,2821end_mark: end_mark,2822value: s,2823style: yaml_PLAIN_SCALAR_STYLE,2824}28252826// Note that we change the 'simple_key_allowed' flag.2827if leading_blanks {2828parser.simple_key_allowed = true2829}2830return true2831}28322833func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t) bool {2834if parser.newlines > 0 {2835return true2836}28372838var start_mark yaml_mark_t2839var text []byte28402841for peek := 0; peek < 512; peek++ {2842if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) {2843break2844}2845if is_blank(parser.buffer, parser.buffer_pos+peek) {2846continue2847}2848if parser.buffer[parser.buffer_pos+peek] == '#' {2849seen := parser.mark.index+peek2850for {2851if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {2852return false2853}2854if is_breakz(parser.buffer, parser.buffer_pos) {2855if parser.mark.index >= seen {2856break2857}2858if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {2859return false2860}2861skip_line(parser)2862} else if parser.mark.index >= seen {2863if len(text) == 0 {2864start_mark = parser.mark2865}2866text = read(parser, text)2867} else {2868skip(parser)2869}2870}2871}2872break2873}2874if len(text) > 0 {2875parser.comments = append(parser.comments, yaml_comment_t{2876token_mark: token_mark,2877start_mark: start_mark,2878line: text,2879})2880}2881return true2882}28832884func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) bool {2885token := parser.tokens[len(parser.tokens)-1]28862887if token.typ == yaml_FLOW_ENTRY_TOKEN && len(parser.tokens) > 1 {2888token = parser.tokens[len(parser.tokens)-2]2889}28902891var token_mark = token.start_mark2892var start_mark yaml_mark_t2893var next_indent = parser.indent2894if next_indent < 0 {2895next_indent = 02896}28972898var recent_empty = false2899var first_empty = parser.newlines <= 129002901var line = parser.mark.line2902var column = parser.mark.column29032904var text []byte29052906// The foot line is the place where a comment must start to2907// still be considered as a foot of the prior content.2908// If there's some content in the currently parsed line, then2909// the foot is the line below it.2910var foot_line = -12911if scan_mark.line > 0 {2912foot_line = parser.mark.line-parser.newlines+12913if parser.newlines == 0 && parser.mark.column > 1 {2914foot_line++2915}2916}29172918var peek = 02919for ; peek < 512; peek++ {2920if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) {2921break2922}2923column++2924if is_blank(parser.buffer, parser.buffer_pos+peek) {2925continue2926}2927c := parser.buffer[parser.buffer_pos+peek]2928var close_flow = parser.flow_level > 0 && (c == ']' || c == '}')2929if close_flow || is_breakz(parser.buffer, parser.buffer_pos+peek) {2930// Got line break or terminator.2931if close_flow || !recent_empty {2932if close_flow || first_empty && (start_mark.line == foot_line && token.typ != yaml_VALUE_TOKEN || start_mark.column-1 < next_indent) {2933// This is the first empty line and there were no empty lines before,2934// so this initial part of the comment is a foot of the prior token2935// instead of being a head for the following one. Split it up.2936// Alternatively, this might also be the last comment inside a flow2937// scope, so it must be a footer.2938if len(text) > 0 {2939if start_mark.column-1 < next_indent {2940// If dedented it's unrelated to the prior token.2941token_mark = start_mark2942}2943parser.comments = append(parser.comments, yaml_comment_t{2944scan_mark: scan_mark,2945token_mark: token_mark,2946start_mark: start_mark,2947end_mark: yaml_mark_t{parser.mark.index + peek, line, column},2948foot: text,2949})2950scan_mark = yaml_mark_t{parser.mark.index + peek, line, column}2951token_mark = scan_mark2952text = nil2953}2954} else {2955if len(text) > 0 && parser.buffer[parser.buffer_pos+peek] != 0 {2956text = append(text, '\n')2957}2958}2959}2960if !is_break(parser.buffer, parser.buffer_pos+peek) {2961break2962}2963first_empty = false2964recent_empty = true2965column = 02966line++2967continue2968}29692970if len(text) > 0 && (close_flow || column-1 < next_indent && column != start_mark.column) {2971// The comment at the different indentation is a foot of the2972// preceding data rather than a head of the upcoming one.2973parser.comments = append(parser.comments, yaml_comment_t{2974scan_mark: scan_mark,2975token_mark: token_mark,2976start_mark: start_mark,2977end_mark: yaml_mark_t{parser.mark.index + peek, line, column},2978foot: text,2979})2980scan_mark = yaml_mark_t{parser.mark.index + peek, line, column}2981token_mark = scan_mark2982text = nil2983}29842985if parser.buffer[parser.buffer_pos+peek] != '#' {2986break2987}29882989if len(text) == 0 {2990start_mark = yaml_mark_t{parser.mark.index + peek, line, column}2991} else {2992text = append(text, '\n')2993}29942995recent_empty = false29962997// Consume until after the consumed comment line.2998seen := parser.mark.index+peek2999for {3000if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {3001return false3002}3003if is_breakz(parser.buffer, parser.buffer_pos) {3004if parser.mark.index >= seen {3005break3006}3007if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {3008return false3009}3010skip_line(parser)3011} else if parser.mark.index >= seen {3012text = read(parser, text)3013} else {3014skip(parser)3015}3016}30173018peek = 03019column = 03020line = parser.mark.line3021next_indent = parser.indent3022if next_indent < 0 {3023next_indent = 03024}3025}30263027if len(text) > 0 {3028parser.comments = append(parser.comments, yaml_comment_t{3029scan_mark: scan_mark,3030token_mark: start_mark,3031start_mark: start_mark,3032end_mark: yaml_mark_t{parser.mark.index + peek - 1, line, column},3033head: text,3034})3035}3036return true3037}303830393040