yaml/0000755000176200001440000000000013615210612011211 5ustar liggesusersyaml/NAMESPACE0000644000176200001440000000016713614635636012454 0ustar liggesusersuseDynLib(yaml, .registration = TRUE, .fixes = "C_") export(as.yaml, yaml.load_file, yaml.load, read_yaml, write_yaml) yaml/LICENSE0000644000176200001440000000017213614635636012236 0ustar liggesusersYEAR: 2008-2018 COPYRIGHT HOLDER: Vanderbilt University Medical Center ORGANIZATION: Vanderbilt University Medical Center yaml/man/0000755000176200001440000000000013614635636012004 5ustar liggesusersyaml/man/write_yaml.Rd0000644000176200001440000000237213614635636014453 0ustar liggesusers\name{write_yaml} \alias{write_yaml} \title{Write a YAML file} \description{ Write the YAML representation of an R object to a file } \usage{ write_yaml(x, file, fileEncoding = "UTF-8", \dots) } \arguments{ \item{x}{the object to be converted} \item{file}{either a character string naming a file or a \link{connection} open for writing} \item{fileEncoding}{character string: if non-empty declares the encoding to be used on a file (not a connection) so the character data can be re-encoded as they are written. See \code{\link{file}}.} \item{\dots}{arguments to \code{\link{as.yaml}}} } \details{ If \code{file} is a non-open connection, an attempt is made to open it and then close it after use. This function is a convenient wrapper around \code{\link{as.yaml}}. } \author{Jeremy Stephens } \seealso{\code{\link{as.yaml}}, \code{\link{read_yaml}}, \code{\link{yaml.load_file}}} \examples{ \dontrun{ # writing to a file connection filename <- tempfile() con <- file(filename, "w") write_yaml(data.frame(a=1:10, b=letters[1:10], c=11:20), con) close(con) # using a filename to specify output file write_yaml(data.frame(a=1:10, b=letters[1:10], c=11:20), filename) } } \keyword{data} \keyword{manip} yaml/man/as.yaml.Rd0000644000176200001440000001153013614635636013637 0ustar liggesusers\name{as.yaml} \alias{as.yaml} \title{ Convert an R object into a YAML string } \description{ Convert an R object into a YAML string } \usage{ as.yaml(x, line.sep = c("\n", "\r\n", "\r"), indent = 2, omap = FALSE, column.major = TRUE, unicode = TRUE, precision = getOption('digits'), indent.mapping.sequence = FALSE, handlers = NULL) } \arguments{ \item{x}{ the object to be converted } \item{line.sep}{ the line separator character(s) to use } \item{indent}{ the number of spaces to use for indenting } \item{omap}{ determines whether or not to convert a list to a YAML omap; see Details } \item{column.major}{ determines how to convert a data.frame; see Details } \item{unicode}{ determines whether or not to allow unescaped unicode characters in output } \item{precision}{ number of significant digits to use when formatting numeric values } \item{indent.mapping.sequence}{ determines whether or not to indent sequences in mapping context } \item{handlers}{ named list of custom handler functions for R objects; see Details } } \details{ If you set the \code{omap} option to TRUE, as.yaml will create ordered maps (or omaps) instead of normal maps. The \code{column.major} option determines how a data frame is converted. If TRUE, the data frame is converted into a map of sequences where the name of each column is a key. If FALSE, the data frame is converted into a sequence of maps, where each element in the sequence is a row. You'll probably almost always want to leave this as TRUE (which is the default), because using \code{\link{yaml.load}} on the resulting string returns an object which is much more easily converted into a data frame via \code{\link{as.data.frame}}. You can specify custom handler functions via the \code{handlers} argument. This argument must be a named list of functions, where the names are R object class names (i.e., 'numeric', 'data.frame', 'list', etc). The function(s) you provide will be passed one argument (the R object) and can return any R object. The returned object will be emitted normally. Character vectors that have a class of \sQuote{verbatim} will not be quoted in the output YAML document except when the YAML specification requires it. This means that you cannot do anything that would result in an invalid YAML document, but you can emit strings that would otherwise be quoted. This is useful for changing how logical vectors are emitted (see below for example). You can specify YAML tags for R objects by setting the \sQuote{tag} attribute to a character vector of length 1. If you set a tag for a vector, the tag will be applied to the YAML sequence as a whole, unless the vector has only 1 element. If you wish to tag individual elements, you must use a list of 1-length vectors, each with a tag attribute. Likewise, if you set a tag for an object that would be emitted as a YAML mapping (like a data frame or a named list), it will be applied to the mapping as a whole. Tags can be used in conjunction with YAML deserialization functions like \code{\link{yaml.load}} via custom handlers, however, if you set an internal tag on an incompatible data type (like \dQuote{!seq 1.0}), errors will occur when you try to deserialize the document. } \value{ Returns a YAML string which can be loaded using \code{\link{yaml.load}} or copied into a file for external use. } \references{ YAML: http://yaml.org YAML omap type: http://yaml.org/type/omap.html } \author{ Jeremy Stephens } \seealso{ \code{\link{yaml.load}} } \examples{ as.yaml(1:10) as.yaml(list(foo=1:10, bar=c("test1", "test2"))) as.yaml(list(foo=1:10, bar=c("test1", "test2")), indent=3) as.yaml(list(foo=1:10, bar=c("test1", "test2")), indent.mapping.sequence=TRUE) as.yaml(data.frame(a=1:10, b=letters[1:10], c=11:20)) as.yaml(list(a=1:2, b=3:4), omap=TRUE) as.yaml("multi\nline\nstring") as.yaml(function(x) x + 1) as.yaml(list(foo=list(list(x = 1, y = 2), list(x = 3, y = 4)))) # custom handler as.yaml(Sys.time(), handlers = list( POSIXct = function(x) format(Sys.time(), "\%Y-\%m-\%d") )) # custom handler with verbatim output to change how logical vectors are # emitted as.yaml(c(TRUE, FALSE), handlers = list( logical = function(x) { result <- ifelse(x, "true", "false") class(result) <- "verbatim" return(result) } )) # custom tag for scalar x <- "thing" attr(x, "tag") <- "!thing" as.yaml(x) # custom tag for sequence x <- 1:10 attr(x, "tag") <- "!thing" as.yaml(x) # custom tag for mapping x <- data.frame(a = letters[1:5], b = letters[6:10]) attr(x, "tag") <- "!thing" as.yaml(x) # custom tag for each element in a list x <- list(1, 2, 3) attr(x[[1]], "tag") <- "!a" attr(x[[2]], "tag") <- "!b" attr(x[[3]], "tag") <- "!c" as.yaml(x) } \keyword{ data } \keyword{ manip } yaml/man/read_yaml.Rd0000644000176200001440000000470113614635636014232 0ustar liggesusers\name{read_yaml} \alias{read_yaml} \title{Read a YAML file} \description{ Read a YAML document from a file and create an R object from it } \usage{ read_yaml(file, fileEncoding = "UTF-8", text, error.label, \dots) } \arguments{ \item{file}{either a character string naming a file or a \link{connection} open for writing} \item{fileEncoding}{character string: if non-empty declares the encoding used on a file (not a connection) so the character data can be re-encoded. See \code{\link{file}}.} \item{text}{character string: if \code{file} is not supplied and this is, then data are read from the value of \code{text} via a text connection. Notice that a literal string can be used to include (small) data sets within R code.} \item{error.label}{a label to prepend to error messages (see Details).} \item{...}{arguments to pass to \code{\link{yaml.load}}} } \details{ This function is a convenient wrapper for \code{\link{yaml.load}} and is a nicer alternative to \code{\link{yaml.load_file}}. You can specify a label to be prepended to error messages via the \code{error.label} argument. If \code{error.label} is missing, \code{read_yaml} will make an educated guess for the value of \code{error.label} by either using the specified filename (when \code{file} is a character vector) or using the description of the supplied connection object (via the \code{summary} function). If \code{text} is used, the default value of \code{error.label} will be \code{NULL}. } \value{ If the root YAML object is a map, a named list or list with an attribute of 'keys' is returned. If the root object is a sequence, a list or vector is returned, depending on the contents of the sequence. A vector of length 1 is returned for single objects. } \references{ YAML: http://yaml.org libyaml: http://pyyaml.org/wiki/LibYAML } \author{Jeremy Stephens } \seealso{\code{\link{yaml.load}}, \code{\link{write_yaml}}, \code{\link{yaml.load_file}}} \examples{ \dontrun{ # reading from a file connection filename <- tempfile() cat("test: data\n", file = filename) con <- file(filename, "r") read_yaml(con) close(con) # using a filename to specify input file read_yaml(filename) } # reading from a character vector read_yaml(text="- hey\n- hi\n- hello") } % Add one or more standard keywords, see file 'KEYWORDS' in the % R documentation directory. \keyword{programming} \keyword{data} \keyword{manip} yaml/man/yaml.load.Rd0000644000176200001440000001462713614635636014165 0ustar liggesusers\name{yaml.load} \alias{yaml.load} \alias{yaml.load_file} %- Also NEED an '\alias' for EACH other topic documented here. \title{ Convert a YAML string into R objects } \description{ Parse a YAML string and return R objects. } \usage{ yaml.load(string, as.named.list = TRUE, handlers = NULL, error.label = NULL, eval.expr = getOption("yaml.eval.expr", TRUE), merge.precedence = c("order", "override"), merge.warning = FALSE) yaml.load_file(input, error.label, ...) } %- maybe also 'usage' for other objects documented here. \arguments{ \item{string}{ the YAML string to be parsed } \item{as.named.list}{ whether or not to return a named list for maps (TRUE by default) } \item{handlers}{ named list of custom handler functions for YAML types (see Details) } \item{input}{ a filename or connection; if \code{input} is a filename, that file must be encoded in UTF-8 } \item{error.label}{ a label to prepend to error messages (see Details) } \item{eval.expr}{ whether or not to evaluate expressions found in the YAML document (see Details) } \item{merge.precedence}{ behavior of precedence during map merges (see Details) } \item{merge.warning}{ whether or not to warn about ignored key/value pairs during map merges } \item{...}{ arguments to pass to yaml.load } } \details{ Use \code{yaml.load} to load a YAML string. For files and connections, use \code{yaml.load_file}, which calls \code{yaml.load} with the contents of the specified file or connection. Sequences of uniform data (e.g. a sequence of integers) are converted into vectors. If the sequence is not uniform, it's returned as a list. Maps are converted into named lists by default, and all the keys in the map are converted to strings. If you don't want the keys to be coerced into strings, set \code{as.named.list} to FALSE. When it's FALSE, a list will be returned with an additional attribute named 'keys', which is a list of the un-coerced keys in the map (in the same order as the main list). You can specify custom handler functions via the \code{handlers} argument. This argument must be a named list of functions, where the names are the YAML types (i.e., 'int', 'float', 'seq', etc). The functions you provide will be passed one argument. Custom handler functions for string types (all types except sequence and map) will receive a character vector of length 1. Custom sequence functions will be passed a list of objects. Custom map functions will be passed the object that the internal map handler creates, which is either a named list or a list with a 'keys' attribute (depending on \code{as.named.list}). ALL functions you provide must return an object. See the examples for custom handler use. You can specify a label to be prepended to error messages via the \code{error.label} argument. When using \code{yaml.load_file}, you can either set the \code{error.label} argument explicitly or leave it missing. If missing, \code{yaml.load_file} will make an educated guess for the value of \code{error.label} by either using the specified filename (when \code{input} is a character vector) or using the description of the supplied connection object (via the \code{summary} function). You can explicity set \code{error.label} to \code{NULL} if you don't want to use this functionality. There is a built-in handler that will evaluate expressions that are tagged with the \sQuote{!expr} tag. Currently this handler is enabled by default, but the handler is being disabled by default in an upcoming version for safety and security reasons. If you do not explicity set the \code{eval.expr} argument to \code{TRUE}, you will get a warning if an expression is evaluated. Alternately, you can set the option named \sQuote{yaml.eval.expr} via the \code{options} function to turn off the warning. The \code{merge.precedence} parameter controls how merge keys are handled. The YAML merge key specification is not specific about how key/value conflicts are resolved during map merges. As a result, various YAML library implementations vary in merge key behavior (notably Python and Ruby). This package's default behavior (when \code{merge.precedence} is \sQuote{order}) is to give precedence to key/value pairs that appear first. If you set \code{merge.precedence} to \sQuote{override}, natural map key/value pairs will override any duplicate keys found in merged maps, regardless of order. This is the default behavior in Python's YAML library. This function uses the YAML parser provided by libyaml, which conforms to the YAML 1.1 specification. } \value{ If the root YAML object is a map, a named list or list with an attribute of 'keys' is returned. If the root object is a sequence, a list or vector is returned, depending on the contents of the sequence. A vector of length 1 is returned for single objects. } \references{ YAML: http://yaml.org libyaml: http://pyyaml.org/wiki/LibYAML YAML merge specification: http://yaml.org/type/merge.html } \author{ Jeremy Stephens } \seealso{ \code{\link{as.yaml}} } \examples{ yaml.load("- hey\n- hi\n- hello") yaml.load("foo: 123\nbar: 456") yaml.load("- foo\n- bar\n- 3.14") yaml.load("foo: bar\n123: 456", as.named.list = FALSE) \dontrun{ # reading from a file (uses readLines internally) filename <- tempfile() cat("foo: 123", file=filename, sep="\n") yaml.load_file(filename) } # custom scalar handler my.float.handler <- function(x) { as.numeric(x) + 123 } yaml.load("123.456", handlers=list("float#fix"=my.float.handler)) # custom sequence handler yaml.load("- 1\n- 2\n- 3", handlers=list(seq=function(x) { as.integer(x) + 3 })) # custom map handler yaml.load("foo: 123", handlers=list(map=function(x) { x$foo <- x$foo + 123; x })) # handling custom types yaml.load("!sqrt 555", handlers=list(sqrt=function(x) { sqrt(as.integer(x)) })) yaml.load("!foo\n- 1\n- 2", handlers=list(foo=function(x) { as.integer(x) + 1 })) yaml.load("!bar\none: 1\ntwo: 2", handlers=list(foo=function(x) { x$one <- "one"; x })) # loading R expressions # NOTE: this will not be done by default in the near future doc <- yaml.load("inc: !expr function(x) x + 1") doc$inc(1) # adding a label to error messages try(yaml.load("*", error.label = "foo")) } % Add one or more standard keywords, see file 'KEYWORDS' in the % R documentation directory. \keyword{ programming } \keyword{ data } \keyword{ manip } yaml/DESCRIPTION0000644000176200001440000000140513615210612012717 0ustar liggesusersPackage: yaml Type: Package Title: Methods to Convert R Data to YAML and Back Date: 2020-01-23 Version: 2.2.1 Suggests: RUnit Author: Jeremy Stephens [aut, cre], Kirill Simonov [aut], Yihui Xie [ctb], Zhuoer Dong [ctb], Hadley Wickham [ctb], Jeffrey Horner [ctb], reikoch [ctb], Will Beasley [ctb], Brendan O'Connor [ctb], Gregory R. Warnes [ctb] Maintainer: Jeremy Stephens License: BSD_3_clause + file LICENSE Description: Implements the 'libyaml' 'YAML' 1.1 parser and emitter () for R. URL: https://github.com/viking/r-yaml/ BugReports: https://github.com/viking/r-yaml/issues NeedsCompilation: yes Packaged: 2020-01-30 20:29:56 UTC; stephej1 Repository: CRAN Date/Publication: 2020-02-01 05:50:02 UTC yaml/tests/0000755000176200001440000000000013614635636012373 5ustar liggesusersyaml/tests/RUnit.R0000644000176200001440000000255613614635636013567 0ustar liggesusersstopifnot(require(RUnit, quietly=TRUE)) stopifnot(require(yaml, quietly=TRUE)) # Set RUnit options if (!getOption("yaml.verbose", FALSE)) { opts <- getOption("RUnit") opts$silent <- TRUE opts$verbose <- FALSE options("RUnit" = opts) } # Test helpers captureWarnings <- function(expr) { warnings <- character(0) suppressWarnings({ withCallingHandlers(expr, warning = function(w) { warnings <<- c(warnings, w$message) }) }) warnings } checkWarning <- function(expr) { warnings <- captureWarnings(expr) checkTrue(length(warnings) > 0) } checkNamedListEquals <- function(x, y) { checkEquals(class(x), class(y)) checkEquals(length(x), length(y)) ns <- sort(names(x)) checkEquals(ns, sort(names(y))) for (n in ns) { checkEquals(x[[n]], y[[n]]) } } # Define tests testSuite <- defineTestSuite(name = "yaml tests", dirs = system.file("tests", package = "yaml"), testFileRegexp = "^test_.+", testFuncRegexp = "^test_.+") tests <- runTestSuite(testSuite) # Print results printTextProtocol(tests, showDetails = FALSE) # Return success or failure to R CMD CHECK if (getErrors(tests)$nFail > 0) { stop("TEST FAILED!") } if (getErrors(tests)$nErr > 0) { stop("TEST HAD ERRORS!") } if (getErrors(tests)$nTestFunc < 1) { stop("NO TEST FUNCTIONS RUN!") } yaml/src/0000755000176200001440000000000013614636304012011 5ustar liggesusersyaml/src/yaml.h0000644000176200001440000015276713614635636013155 0ustar liggesusers/** * @file yaml.h * @brief Public interface for libyaml. * * Include the header file with the code: * @code * #include * @endcode */ #ifndef YAML_H #define YAML_H #ifdef __cplusplus extern "C" { #endif #include #include #include /** * @defgroup export Export Definitions * @{ */ /** The public API declaration. */ #if defined(__MINGW32__) # define YAML_DECLARE(type) type #elif defined(WIN32) # if defined(YAML_DECLARE_STATIC) # define YAML_DECLARE(type) type # elif defined(YAML_DECLARE_EXPORT) # define YAML_DECLARE(type) __declspec(dllexport) type # else # define YAML_DECLARE(type) __declspec(dllimport) type # endif #else # define YAML_DECLARE(type) type #endif /** @} */ /** * @defgroup version Version Information * @{ */ /** * Get the library version as a string. * * @returns The function returns the pointer to a static string of the form * @c "X.Y.Z", where @c X is the major version number, @c Y is a minor version * number, and @c Z is the patch version number. */ YAML_DECLARE(const char *) yaml_get_version_string(void); /** * Get the library version numbers. * * @param[out] major Major version number. * @param[out] minor Minor version number. * @param[out] patch Patch version number. */ YAML_DECLARE(void) yaml_get_version(int *major, int *minor, int *patch); /** @} */ /** * @defgroup basic Basic Types * @{ */ /** The character type (UTF-8 octet). */ typedef unsigned char yaml_char_t; /** The version directive data. */ typedef struct yaml_version_directive_s { /** The major version number. */ int major; /** The minor version number. */ int minor; } yaml_version_directive_t; /** The tag directive data. */ typedef struct yaml_tag_directive_s { /** The tag handle. */ yaml_char_t *handle; /** The tag prefix. */ yaml_char_t *prefix; } yaml_tag_directive_t; /** The stream encoding. */ typedef enum yaml_encoding_e { /** Let the parser choose the encoding. */ YAML_ANY_ENCODING, /** The default UTF-8 encoding. */ YAML_UTF8_ENCODING, /** The UTF-16-LE encoding with BOM. */ YAML_UTF16LE_ENCODING, /** The UTF-16-BE encoding with BOM. */ YAML_UTF16BE_ENCODING } yaml_encoding_t; /** Line break types. */ typedef enum yaml_break_e { /** Let the parser choose the break type. */ YAML_ANY_BREAK, /** Use CR for line breaks (Mac style). */ YAML_CR_BREAK, /** Use LN for line breaks (Unix style). */ YAML_LN_BREAK, /** Use CR LN for line breaks (DOS style). */ YAML_CRLN_BREAK } yaml_break_t; /** Many bad things could happen with the parser and emitter. */ typedef enum yaml_error_type_e { /** No error is produced. */ YAML_NO_ERROR, /** Cannot allocate or reallocate a block of memory. */ YAML_MEMORY_ERROR, /** Cannot read or decode the input stream. */ YAML_READER_ERROR, /** Cannot scan the input stream. */ YAML_SCANNER_ERROR, /** Cannot parse the input stream. */ YAML_PARSER_ERROR, /** Cannot compose a YAML document. */ YAML_COMPOSER_ERROR, /** Cannot write to the output stream. */ YAML_WRITER_ERROR, /** Cannot emit a YAML stream. */ YAML_EMITTER_ERROR } yaml_error_type_t; /** The pointer position. */ typedef struct yaml_mark_s { /** The position index. */ size_t index; /** The position line. */ size_t line; /** The position column. */ size_t column; } yaml_mark_t; /** @} */ /** * @defgroup styles Node Styles * @{ */ /** Scalar styles. */ typedef enum yaml_scalar_style_e { /** Let the emitter choose the style. */ YAML_ANY_SCALAR_STYLE, /** The plain scalar style. */ YAML_PLAIN_SCALAR_STYLE, /** The single-quoted scalar style. */ YAML_SINGLE_QUOTED_SCALAR_STYLE, /** The double-quoted scalar style. */ YAML_DOUBLE_QUOTED_SCALAR_STYLE, /** The literal scalar style. */ YAML_LITERAL_SCALAR_STYLE, /** The folded scalar style. */ YAML_FOLDED_SCALAR_STYLE } yaml_scalar_style_t; /** Sequence styles. */ typedef enum yaml_sequence_style_e { /** Let the emitter choose the style. */ YAML_ANY_SEQUENCE_STYLE, /** The block sequence style. */ YAML_BLOCK_SEQUENCE_STYLE, /** The flow sequence style. */ YAML_FLOW_SEQUENCE_STYLE } yaml_sequence_style_t; /** Mapping styles. */ typedef enum yaml_mapping_style_e { /** Let the emitter choose the style. */ YAML_ANY_MAPPING_STYLE, /** The block mapping style. */ YAML_BLOCK_MAPPING_STYLE, /** The flow mapping style. */ YAML_FLOW_MAPPING_STYLE /* YAML_FLOW_SET_MAPPING_STYLE */ } yaml_mapping_style_t; /** @} */ /** * @defgroup tokens Tokens * @{ */ /** Token types. */ typedef enum yaml_token_type_e { /** An empty token. */ YAML_NO_TOKEN, /** A STREAM-START token. */ YAML_STREAM_START_TOKEN, /** A STREAM-END token. */ YAML_STREAM_END_TOKEN, /** A VERSION-DIRECTIVE token. */ YAML_VERSION_DIRECTIVE_TOKEN, /** A TAG-DIRECTIVE token. */ YAML_TAG_DIRECTIVE_TOKEN, /** A DOCUMENT-START token. */ YAML_DOCUMENT_START_TOKEN, /** A DOCUMENT-END token. */ YAML_DOCUMENT_END_TOKEN, /** A BLOCK-SEQUENCE-START token. */ YAML_BLOCK_SEQUENCE_START_TOKEN, /** A BLOCK-MAPPING-START token. */ YAML_BLOCK_MAPPING_START_TOKEN, /** A BLOCK-END token. */ YAML_BLOCK_END_TOKEN, /** A FLOW-SEQUENCE-START token. */ YAML_FLOW_SEQUENCE_START_TOKEN, /** A FLOW-SEQUENCE-END token. */ YAML_FLOW_SEQUENCE_END_TOKEN, /** A FLOW-MAPPING-START token. */ YAML_FLOW_MAPPING_START_TOKEN, /** A FLOW-MAPPING-END token. */ YAML_FLOW_MAPPING_END_TOKEN, /** A BLOCK-ENTRY token. */ YAML_BLOCK_ENTRY_TOKEN, /** A FLOW-ENTRY token. */ YAML_FLOW_ENTRY_TOKEN, /** A KEY token. */ YAML_KEY_TOKEN, /** A VALUE token. */ YAML_VALUE_TOKEN, /** An ALIAS token. */ YAML_ALIAS_TOKEN, /** An ANCHOR token. */ YAML_ANCHOR_TOKEN, /** A TAG token. */ YAML_TAG_TOKEN, /** A SCALAR token. */ YAML_SCALAR_TOKEN } yaml_token_type_t; /** The token structure. */ typedef struct yaml_token_s { /** The token type. */ yaml_token_type_t type; /** The token data. */ union { /** The stream start (for @c YAML_STREAM_START_TOKEN). */ struct { /** The stream encoding. */ yaml_encoding_t encoding; } stream_start; /** The alias (for @c YAML_ALIAS_TOKEN). */ struct { /** The alias value. */ yaml_char_t *value; } alias; /** The anchor (for @c YAML_ANCHOR_TOKEN). */ struct { /** The anchor value. */ yaml_char_t *value; } anchor; /** The tag (for @c YAML_TAG_TOKEN). */ struct { /** The tag handle. */ yaml_char_t *handle; /** The tag suffix. */ yaml_char_t *suffix; } tag; /** The scalar value (for @c YAML_SCALAR_TOKEN). */ struct { /** The scalar value. */ yaml_char_t *value; /** The length of the scalar value. */ size_t length; /** The scalar style. */ yaml_scalar_style_t style; } scalar; /** The version directive (for @c YAML_VERSION_DIRECTIVE_TOKEN). */ struct { /** The major version number. */ int major; /** The minor version number. */ int minor; } version_directive; /** The tag directive (for @c YAML_TAG_DIRECTIVE_TOKEN). */ struct { /** The tag handle. */ yaml_char_t *handle; /** The tag prefix. */ yaml_char_t *prefix; } tag_directive; } data; /** The beginning of the token. */ yaml_mark_t start_mark; /** The end of the token. */ yaml_mark_t end_mark; } yaml_token_t; /** * Free any memory allocated for a token object. * * @param[in,out] token A token object. */ YAML_DECLARE(void) yaml_token_delete(yaml_token_t *token); /** @} */ /** * @defgroup events Events * @{ */ /** Event types. */ typedef enum yaml_event_type_e { /** An empty event. */ YAML_NO_EVENT, /** A STREAM-START event. */ YAML_STREAM_START_EVENT, /** A STREAM-END event. */ YAML_STREAM_END_EVENT, /** A DOCUMENT-START event. */ YAML_DOCUMENT_START_EVENT, /** A DOCUMENT-END event. */ YAML_DOCUMENT_END_EVENT, /** An ALIAS event. */ YAML_ALIAS_EVENT, /** A SCALAR event. */ YAML_SCALAR_EVENT, /** A SEQUENCE-START event. */ YAML_SEQUENCE_START_EVENT, /** A SEQUENCE-END event. */ YAML_SEQUENCE_END_EVENT, /** A MAPPING-START event. */ YAML_MAPPING_START_EVENT, /** A MAPPING-END event. */ YAML_MAPPING_END_EVENT } yaml_event_type_t; /** The event structure. */ typedef struct yaml_event_s { /** The event type. */ yaml_event_type_t type; /** The event data. */ union { /** The stream parameters (for @c YAML_STREAM_START_EVENT). */ struct { /** The document encoding. */ yaml_encoding_t encoding; } stream_start; /** The document parameters (for @c YAML_DOCUMENT_START_EVENT). */ struct { /** The version directive. */ yaml_version_directive_t *version_directive; /** The list of tag directives. */ struct { /** The beginning of the tag directives list. */ yaml_tag_directive_t *start; /** The end of the tag directives list. */ yaml_tag_directive_t *end; } tag_directives; /** Is the document indicator implicit? */ int implicit; } document_start; /** The document end parameters (for @c YAML_DOCUMENT_END_EVENT). */ struct { /** Is the document end indicator implicit? */ int implicit; } document_end; /** The alias parameters (for @c YAML_ALIAS_EVENT). */ struct { /** The anchor. */ yaml_char_t *anchor; } alias; /** The scalar parameters (for @c YAML_SCALAR_EVENT). */ struct { /** The anchor. */ yaml_char_t *anchor; /** The tag. */ yaml_char_t *tag; /** The scalar value. */ yaml_char_t *value; /** The length of the scalar value. */ size_t length; /** Is the tag optional for the plain style? */ int plain_implicit; /** Is the tag optional for any non-plain style? */ int quoted_implicit; /** The scalar style. */ yaml_scalar_style_t style; } scalar; /** The sequence parameters (for @c YAML_SEQUENCE_START_EVENT). */ struct { /** The anchor. */ yaml_char_t *anchor; /** The tag. */ yaml_char_t *tag; /** Is the tag optional? */ int implicit; /** The sequence style. */ yaml_sequence_style_t style; } sequence_start; /** The mapping parameters (for @c YAML_MAPPING_START_EVENT). */ struct { /** The anchor. */ yaml_char_t *anchor; /** The tag. */ yaml_char_t *tag; /** Is the tag optional? */ int implicit; /** The mapping style. */ yaml_mapping_style_t style; } mapping_start; } data; /** The beginning of the event. */ yaml_mark_t start_mark; /** The end of the event. */ yaml_mark_t end_mark; } yaml_event_t; /** * Create the STREAM-START event. * * @param[out] event An empty event object. * @param[in] encoding The stream encoding. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_stream_start_event_initialize(yaml_event_t *event, yaml_encoding_t encoding); /** * Create the STREAM-END event. * * @param[out] event An empty event object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_stream_end_event_initialize(yaml_event_t *event); /** * Create the DOCUMENT-START event. * * The @a implicit argument is considered as a stylistic parameter and may be * ignored by the emitter. * * @param[out] event An empty event object. * @param[in] version_directive The %YAML directive value or * @c NULL. * @param[in] tag_directives_start The beginning of the %TAG * directives list. * @param[in] tag_directives_end The end of the %TAG directives * list. * @param[in] implicit If the document start indicator is * implicit. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_document_start_event_initialize(yaml_event_t *event, yaml_version_directive_t *version_directive, yaml_tag_directive_t *tag_directives_start, yaml_tag_directive_t *tag_directives_end, int implicit); /** * Create the DOCUMENT-END event. * * The @a implicit argument is considered as a stylistic parameter and may be * ignored by the emitter. * * @param[out] event An empty event object. * @param[in] implicit If the document end indicator is implicit. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_document_end_event_initialize(yaml_event_t *event, int implicit); /** * Create an ALIAS event. * * @param[out] event An empty event object. * @param[in] anchor The anchor value. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_alias_event_initialize(yaml_event_t *event, yaml_char_t *anchor); /** * Create a SCALAR event. * * The @a style argument may be ignored by the emitter. * * Either the @a tag attribute or one of the @a plain_implicit and * @a quoted_implicit flags must be set. * * @param[out] event An empty event object. * @param[in] anchor The scalar anchor or @c NULL. * @param[in] tag The scalar tag or @c NULL. * @param[in] value The scalar value. * @param[in] length The length of the scalar value. * @param[in] plain_implicit If the tag may be omitted for the plain * style. * @param[in] quoted_implicit If the tag may be omitted for any * non-plain style. * @param[in] style The scalar style. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_scalar_event_initialize(yaml_event_t *event, yaml_char_t *anchor, yaml_char_t *tag, yaml_char_t *value, int length, int plain_implicit, int quoted_implicit, yaml_scalar_style_t style); /** * Create a SEQUENCE-START event. * * The @a style argument may be ignored by the emitter. * * Either the @a tag attribute or the @a implicit flag must be set. * * @param[out] event An empty event object. * @param[in] anchor The sequence anchor or @c NULL. * @param[in] tag The sequence tag or @c NULL. * @param[in] implicit If the tag may be omitted. * @param[in] style The sequence style. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_sequence_start_event_initialize(yaml_event_t *event, yaml_char_t *anchor, yaml_char_t *tag, int implicit, yaml_sequence_style_t style); /** * Create a SEQUENCE-END event. * * @param[out] event An empty event object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_sequence_end_event_initialize(yaml_event_t *event); /** * Create a MAPPING-START event. * * The @a style argument may be ignored by the emitter. * * Either the @a tag attribute or the @a implicit flag must be set. * * @param[out] event An empty event object. * @param[in] anchor The mapping anchor or @c NULL. * @param[in] tag The mapping tag or @c NULL. * @param[in] implicit If the tag may be omitted. * @param[in] style The mapping style. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_mapping_start_event_initialize(yaml_event_t *event, yaml_char_t *anchor, yaml_char_t *tag, int implicit, yaml_mapping_style_t style); /** * Create a MAPPING-END event. * * @param[out] event An empty event object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_mapping_end_event_initialize(yaml_event_t *event); /** * Free any memory allocated for an event object. * * @param[in,out] event An event object. */ YAML_DECLARE(void) yaml_event_delete(yaml_event_t *event); /** @} */ /** * @defgroup nodes Nodes * @{ */ /** The tag @c !!null with the only possible value: @c null. */ #define YAML_NULL_TAG "tag:yaml.org,2002:null" /** The tag @c !!bool with the values: @c true and @c false. */ #define YAML_BOOL_TAG "tag:yaml.org,2002:bool" /** The tag @c !!str for string values. */ #define YAML_STR_TAG "tag:yaml.org,2002:str" /** The tag @c !!int for integer values. */ #define YAML_INT_TAG "tag:yaml.org,2002:int" /** The tag @c !!float for float values. */ #define YAML_FLOAT_TAG "tag:yaml.org,2002:float" /** The tag @c !!timestamp for date and time values. */ #define YAML_TIMESTAMP_TAG "tag:yaml.org,2002:timestamp" /** The tag @c !!seq is used to denote sequences. */ #define YAML_SEQ_TAG "tag:yaml.org,2002:seq" /** The tag @c !!map is used to denote mapping. */ #define YAML_MAP_TAG "tag:yaml.org,2002:map" /** The default scalar tag is @c !!str. */ #define YAML_DEFAULT_SCALAR_TAG YAML_STR_TAG /** The default sequence tag is @c !!seq. */ #define YAML_DEFAULT_SEQUENCE_TAG YAML_SEQ_TAG /** The default mapping tag is @c !!map. */ #define YAML_DEFAULT_MAPPING_TAG YAML_MAP_TAG /** Node types. */ typedef enum yaml_node_type_e { /** An empty node. */ YAML_NO_NODE, /** A scalar node. */ YAML_SCALAR_NODE, /** A sequence node. */ YAML_SEQUENCE_NODE, /** A mapping node. */ YAML_MAPPING_NODE } yaml_node_type_t; /** The forward definition of a document node structure. */ typedef struct yaml_node_s yaml_node_t; /** An element of a sequence node. */ typedef int yaml_node_item_t; /** An element of a mapping node. */ typedef struct yaml_node_pair_s { /** The key of the element. */ int key; /** The value of the element. */ int value; } yaml_node_pair_t; /** The node structure. */ struct yaml_node_s { /** The node type. */ yaml_node_type_t type; /** The node tag. */ yaml_char_t *tag; /** The node data. */ union { /** The scalar parameters (for @c YAML_SCALAR_NODE). */ struct { /** The scalar value. */ yaml_char_t *value; /** The length of the scalar value. */ size_t length; /** The scalar style. */ yaml_scalar_style_t style; } scalar; /** The sequence parameters (for @c YAML_SEQUENCE_NODE). */ struct { /** The stack of sequence items. */ struct { /** The beginning of the stack. */ yaml_node_item_t *start; /** The end of the stack. */ yaml_node_item_t *end; /** The top of the stack. */ yaml_node_item_t *top; } items; /** The sequence style. */ yaml_sequence_style_t style; } sequence; /** The mapping parameters (for @c YAML_MAPPING_NODE). */ struct { /** The stack of mapping pairs (key, value). */ struct { /** The beginning of the stack. */ yaml_node_pair_t *start; /** The end of the stack. */ yaml_node_pair_t *end; /** The top of the stack. */ yaml_node_pair_t *top; } pairs; /** The mapping style. */ yaml_mapping_style_t style; } mapping; } data; /** The beginning of the node. */ yaml_mark_t start_mark; /** The end of the node. */ yaml_mark_t end_mark; }; /** The document structure. */ typedef struct yaml_document_s { /** The document nodes. */ struct { /** The beginning of the stack. */ yaml_node_t *start; /** The end of the stack. */ yaml_node_t *end; /** The top of the stack. */ yaml_node_t *top; } nodes; /** The version directive. */ yaml_version_directive_t *version_directive; /** The list of tag directives. */ struct { /** The beginning of the tag directives list. */ yaml_tag_directive_t *start; /** The end of the tag directives list. */ yaml_tag_directive_t *end; } tag_directives; /** Is the document start indicator implicit? */ int start_implicit; /** Is the document end indicator implicit? */ int end_implicit; /** The beginning of the document. */ yaml_mark_t start_mark; /** The end of the document. */ yaml_mark_t end_mark; } yaml_document_t; /** * Create a YAML document. * * @param[out] document An empty document object. * @param[in] version_directive The %YAML directive value or * @c NULL. * @param[in] tag_directives_start The beginning of the %TAG * directives list. * @param[in] tag_directives_end The end of the %TAG directives * list. * @param[in] start_implicit If the document start indicator is * implicit. * @param[in] end_implicit If the document end indicator is * implicit. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_document_initialize(yaml_document_t *document, yaml_version_directive_t *version_directive, yaml_tag_directive_t *tag_directives_start, yaml_tag_directive_t *tag_directives_end, int start_implicit, int end_implicit); /** * Delete a YAML document and all its nodes. * * @param[in,out] document A document object. */ YAML_DECLARE(void) yaml_document_delete(yaml_document_t *document); /** * Get a node of a YAML document. * * The pointer returned by this function is valid until any of the functions * modifying the documents are called. * * @param[in] document A document object. * @param[in] index The node id. * * @returns the node objct or @c NULL if @c node_id is out of range. */ YAML_DECLARE(yaml_node_t *) yaml_document_get_node(yaml_document_t *document, int index); /** * Get the root of a YAML document node. * * The root object is the first object added to the document. * * The pointer returned by this function is valid until any of the functions * modifying the documents are called. * * An empty document produced by the parser signifies the end of a YAML * stream. * * @param[in] document A document object. * * @returns the node object or @c NULL if the document is empty. */ YAML_DECLARE(yaml_node_t *) yaml_document_get_root_node(yaml_document_t *document); /** * Create a SCALAR node and attach it to the document. * * The @a style argument may be ignored by the emitter. * * @param[in,out] document A document object. * @param[in] tag The scalar tag. * @param[in] value The scalar value. * @param[in] length The length of the scalar value. * @param[in] style The scalar style. * * @returns the node id or @c 0 on error. */ YAML_DECLARE(int) yaml_document_add_scalar(yaml_document_t *document, yaml_char_t *tag, yaml_char_t *value, int length, yaml_scalar_style_t style); /** * Create a SEQUENCE node and attach it to the document. * * The @a style argument may be ignored by the emitter. * * @param[in,out] document A document object. * @param[in] tag The sequence tag. * @param[in] style The sequence style. * * @returns the node id or @c 0 on error. */ YAML_DECLARE(int) yaml_document_add_sequence(yaml_document_t *document, yaml_char_t *tag, yaml_sequence_style_t style); /** * Create a MAPPING node and attach it to the document. * * The @a style argument may be ignored by the emitter. * * @param[in,out] document A document object. * @param[in] tag The sequence tag. * @param[in] style The sequence style. * * @returns the node id or @c 0 on error. */ YAML_DECLARE(int) yaml_document_add_mapping(yaml_document_t *document, yaml_char_t *tag, yaml_mapping_style_t style); /** * Add an item to a SEQUENCE node. * * @param[in,out] document A document object. * @param[in] sequence The sequence node id. * @param[in] item The item node id. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_document_append_sequence_item(yaml_document_t *document, int sequence, int item); /** * Add a pair of a key and a value to a MAPPING node. * * @param[in,out] document A document object. * @param[in] mapping The mapping node id. * @param[in] key The key node id. * @param[in] value The value node id. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_document_append_mapping_pair(yaml_document_t *document, int mapping, int key, int value); /** @} */ /** * @defgroup parser Parser Definitions * @{ */ /** * The prototype of a read handler. * * The read handler is called when the parser needs to read more bytes from the * source. The handler should write not more than @a size bytes to the @a * buffer. The number of written bytes should be set to the @a length variable. * * @param[in,out] data A pointer to an application data specified by * yaml_parser_set_input(). * @param[out] buffer The buffer to write the data from the source. * @param[in] size The size of the buffer. * @param[out] size_read The actual number of bytes read from the source. * * @returns On success, the handler should return @c 1. If the handler failed, * the returned value should be @c 0. On EOF, the handler should set the * @a size_read to @c 0 and return @c 1. */ typedef int yaml_read_handler_t(void *data, unsigned char *buffer, size_t size, size_t *size_read); /** * This structure holds information about a potential simple key. */ typedef struct yaml_simple_key_s { /** Is a simple key possible? */ int possible; /** Is a simple key required? */ int required; /** The number of the token. */ size_t token_number; /** The position mark. */ yaml_mark_t mark; } yaml_simple_key_t; /** * The states of the parser. */ typedef enum yaml_parser_state_e { /** Expect STREAM-START. */ YAML_PARSE_STREAM_START_STATE, /** Expect the beginning of an implicit document. */ YAML_PARSE_IMPLICIT_DOCUMENT_START_STATE, /** Expect DOCUMENT-START. */ YAML_PARSE_DOCUMENT_START_STATE, /** Expect the content of a document. */ YAML_PARSE_DOCUMENT_CONTENT_STATE, /** Expect DOCUMENT-END. */ YAML_PARSE_DOCUMENT_END_STATE, /** Expect a block node. */ YAML_PARSE_BLOCK_NODE_STATE, /** Expect a block node or indentless sequence. */ YAML_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE, /** Expect a flow node. */ YAML_PARSE_FLOW_NODE_STATE, /** Expect the first entry of a block sequence. */ YAML_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE, /** Expect an entry of a block sequence. */ YAML_PARSE_BLOCK_SEQUENCE_ENTRY_STATE, /** Expect an entry of an indentless sequence. */ YAML_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE, /** Expect the first key of a block mapping. */ YAML_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE, /** Expect a block mapping key. */ YAML_PARSE_BLOCK_MAPPING_KEY_STATE, /** Expect a block mapping value. */ YAML_PARSE_BLOCK_MAPPING_VALUE_STATE, /** Expect the first entry of a flow sequence. */ YAML_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE, /** Expect an entry of a flow sequence. */ YAML_PARSE_FLOW_SEQUENCE_ENTRY_STATE, /** Expect a key of an ordered mapping. */ YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE, /** Expect a value of an ordered mapping. */ YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE, /** Expect the and of an ordered mapping entry. */ YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE, /** Expect the first key of a flow mapping. */ YAML_PARSE_FLOW_MAPPING_FIRST_KEY_STATE, /** Expect a key of a flow mapping. */ YAML_PARSE_FLOW_MAPPING_KEY_STATE, /** Expect a value of a flow mapping. */ YAML_PARSE_FLOW_MAPPING_VALUE_STATE, /** Expect an empty value of a flow mapping. */ YAML_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE, /** Expect nothing. */ YAML_PARSE_END_STATE } yaml_parser_state_t; /** * This structure holds aliases data. */ typedef struct yaml_alias_data_s { /** The anchor. */ yaml_char_t *anchor; /** The node id. */ int index; /** The anchor mark. */ yaml_mark_t mark; } yaml_alias_data_t; /** * The parser structure. * * All members are internal. Manage the structure using the @c yaml_parser_ * family of functions. */ typedef struct yaml_parser_s { /** * @name Error handling * @{ */ /** Error type. */ yaml_error_type_t error; /** Error description. */ const char *problem; /** The byte about which the problem occured. */ size_t problem_offset; /** The problematic value (@c -1 is none). */ int problem_value; /** The problem position. */ yaml_mark_t problem_mark; /** The error context. */ const char *context; /** The context position. */ yaml_mark_t context_mark; /** * @} */ /** * @name Reader stuff * @{ */ /** Read handler. */ yaml_read_handler_t *read_handler; /** A pointer for passing to the read handler. */ void *read_handler_data; /** Standard (string or file) input data. */ union { /** String input data. */ struct { /** The string start pointer. */ const unsigned char *start; /** The string end pointer. */ const unsigned char *end; /** The string current position. */ const unsigned char *current; } string; /** File input data. */ FILE *file; } input; /** EOF flag */ int eof; /** The working buffer. */ struct { /** The beginning of the buffer. */ yaml_char_t *start; /** The end of the buffer. */ yaml_char_t *end; /** The current position of the buffer. */ yaml_char_t *pointer; /** The last filled position of the buffer. */ yaml_char_t *last; } buffer; /* The number of unread characters in the buffer. */ size_t unread; /** The raw buffer. */ struct { /** The beginning of the buffer. */ unsigned char *start; /** The end of the buffer. */ unsigned char *end; /** The current position of the buffer. */ unsigned char *pointer; /** The last filled position of the buffer. */ unsigned char *last; } raw_buffer; /** The input encoding. */ yaml_encoding_t encoding; /** The offset of the current position (in bytes). */ size_t offset; /** The mark of the current position. */ yaml_mark_t mark; /** * @} */ /** * @name Scanner stuff * @{ */ /** Have we started to scan the input stream? */ int stream_start_produced; /** Have we reached the end of the input stream? */ int stream_end_produced; /** The number of unclosed '[' and '{' indicators. */ int flow_level; /** The tokens queue. */ struct { /** The beginning of the tokens queue. */ yaml_token_t *start; /** The end of the tokens queue. */ yaml_token_t *end; /** The head of the tokens queue. */ yaml_token_t *head; /** The tail of the tokens queue. */ yaml_token_t *tail; } tokens; /** The number of tokens fetched from the queue. */ size_t tokens_parsed; /* Does the tokens queue contain a token ready for dequeueing. */ int token_available; /** The indentation levels stack. */ struct { /** The beginning of the stack. */ int *start; /** The end of the stack. */ int *end; /** The top of the stack. */ int *top; } indents; /** The current indentation level. */ int indent; /** May a simple key occur at the current position? */ int simple_key_allowed; /** The stack of simple keys. */ struct { /** The beginning of the stack. */ yaml_simple_key_t *start; /** The end of the stack. */ yaml_simple_key_t *end; /** The top of the stack. */ yaml_simple_key_t *top; } simple_keys; /** * @} */ /** * @name Parser stuff * @{ */ /** The parser states stack. */ struct { /** The beginning of the stack. */ yaml_parser_state_t *start; /** The end of the stack. */ yaml_parser_state_t *end; /** The top of the stack. */ yaml_parser_state_t *top; } states; /** The current parser state. */ yaml_parser_state_t state; /** The stack of marks. */ struct { /** The beginning of the stack. */ yaml_mark_t *start; /** The end of the stack. */ yaml_mark_t *end; /** The top of the stack. */ yaml_mark_t *top; } marks; /** The list of TAG directives. */ struct { /** The beginning of the list. */ yaml_tag_directive_t *start; /** The end of the list. */ yaml_tag_directive_t *end; /** The top of the list. */ yaml_tag_directive_t *top; } tag_directives; /** * @} */ /** * @name Dumper stuff * @{ */ /** The alias data. */ struct { /** The beginning of the list. */ yaml_alias_data_t *start; /** The end of the list. */ yaml_alias_data_t *end; /** The top of the list. */ yaml_alias_data_t *top; } aliases; /** The currently parsed document. */ yaml_document_t *document; /** * @} */ } yaml_parser_t; /** * Initialize a parser. * * This function creates a new parser object. An application is responsible * for destroying the object using the yaml_parser_delete() function. * * @param[out] parser An empty parser object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_parser_initialize(yaml_parser_t *parser); /** * Destroy a parser. * * @param[in,out] parser A parser object. */ YAML_DECLARE(void) yaml_parser_delete(yaml_parser_t *parser); /** * Set a string input. * * Note that the @a input pointer must be valid while the @a parser object * exists. The application is responsible for destroing @a input after * destroying the @a parser. * * @param[in,out] parser A parser object. * @param[in] input A source data. * @param[in] size The length of the source data in bytes. */ YAML_DECLARE(void) yaml_parser_set_input_string(yaml_parser_t *parser, const unsigned char *input, size_t size); /** * Set a file input. * * @a file should be a file object open for reading. The application is * responsible for closing the @a file. * * @param[in,out] parser A parser object. * @param[in] file An open file. */ YAML_DECLARE(void) yaml_parser_set_input_file(yaml_parser_t *parser, FILE *file); /** * Set a generic input handler. * * @param[in,out] parser A parser object. * @param[in] handler A read handler. * @param[in] data Any application data for passing to the read * handler. */ YAML_DECLARE(void) yaml_parser_set_input(yaml_parser_t *parser, yaml_read_handler_t *handler, void *data); /** * Set the source encoding. * * @param[in,out] parser A parser object. * @param[in] encoding The source encoding. */ YAML_DECLARE(void) yaml_parser_set_encoding(yaml_parser_t *parser, yaml_encoding_t encoding); /** * Scan the input stream and produce the next token. * * Call the function subsequently to produce a sequence of tokens corresponding * to the input stream. The initial token has the type * @c YAML_STREAM_START_TOKEN while the ending token has the type * @c YAML_STREAM_END_TOKEN. * * An application is responsible for freeing any buffers associated with the * produced token object using the @c yaml_token_delete function. * * An application must not alternate the calls of yaml_parser_scan() with the * calls of yaml_parser_parse() or yaml_parser_load(). Doing this will break * the parser. * * @param[in,out] parser A parser object. * @param[out] token An empty token object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_parser_scan(yaml_parser_t *parser, yaml_token_t *token); /** * Parse the input stream and produce the next parsing event. * * Call the function subsequently to produce a sequence of events corresponding * to the input stream. The initial event has the type * @c YAML_STREAM_START_EVENT while the ending event has the type * @c YAML_STREAM_END_EVENT. * * An application is responsible for freeing any buffers associated with the * produced event object using the yaml_event_delete() function. * * An application must not alternate the calls of yaml_parser_parse() with the * calls of yaml_parser_scan() or yaml_parser_load(). Doing this will break the * parser. * * @param[in,out] parser A parser object. * @param[out] event An empty event object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_parser_parse(yaml_parser_t *parser, yaml_event_t *event); /** * Parse the input stream and produce the next YAML document. * * Call this function subsequently to produce a sequence of documents * constituting the input stream. * * If the produced document has no root node, it means that the document * end has been reached. * * An application is responsible for freeing any data associated with the * produced document object using the yaml_document_delete() function. * * An application must not alternate the calls of yaml_parser_load() with the * calls of yaml_parser_scan() or yaml_parser_parse(). Doing this will break * the parser. * * @param[in,out] parser A parser object. * @param[out] document An empty document object. * * @return @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_parser_load(yaml_parser_t *parser, yaml_document_t *document); /** @} */ /** * @defgroup emitter Emitter Definitions * @{ */ /** * The prototype of a write handler. * * The write handler is called when the emitter needs to flush the accumulated * characters to the output. The handler should write @a size bytes of the * @a buffer to the output. * * @param[in,out] data A pointer to an application data specified by * yaml_emitter_set_output(). * @param[in] buffer The buffer with bytes to be written. * @param[in] size The size of the buffer. * * @returns On success, the handler should return @c 1. If the handler failed, * the returned value should be @c 0. */ typedef int yaml_write_handler_t(void *data, unsigned char *buffer, size_t size); /** The emitter states. */ typedef enum yaml_emitter_state_e { /** Expect STREAM-START. */ YAML_EMIT_STREAM_START_STATE, /** Expect the first DOCUMENT-START or STREAM-END. */ YAML_EMIT_FIRST_DOCUMENT_START_STATE, /** Expect DOCUMENT-START or STREAM-END. */ YAML_EMIT_DOCUMENT_START_STATE, /** Expect the content of a document. */ YAML_EMIT_DOCUMENT_CONTENT_STATE, /** Expect DOCUMENT-END. */ YAML_EMIT_DOCUMENT_END_STATE, /** Expect the first item of a flow sequence. */ YAML_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE, /** Expect an item of a flow sequence. */ YAML_EMIT_FLOW_SEQUENCE_ITEM_STATE, /** Expect the first key of a flow mapping. */ YAML_EMIT_FLOW_MAPPING_FIRST_KEY_STATE, /** Expect a key of a flow mapping. */ YAML_EMIT_FLOW_MAPPING_KEY_STATE, /** Expect a value for a simple key of a flow mapping. */ YAML_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE, /** Expect a value of a flow mapping. */ YAML_EMIT_FLOW_MAPPING_VALUE_STATE, /** Expect the first item of a block sequence. */ YAML_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE, /** Expect an item of a block sequence. */ YAML_EMIT_BLOCK_SEQUENCE_ITEM_STATE, /** Expect the first key of a block mapping. */ YAML_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE, /** Expect the key of a block mapping. */ YAML_EMIT_BLOCK_MAPPING_KEY_STATE, /** Expect a value for a simple key of a block mapping. */ YAML_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE, /** Expect a value of a block mapping. */ YAML_EMIT_BLOCK_MAPPING_VALUE_STATE, /** Expect nothing. */ YAML_EMIT_END_STATE } yaml_emitter_state_t; /* This is needed for C++ */ typedef struct yaml_anchors_s { /** The number of references. */ int references; /** The anchor id. */ int anchor; /** If the node has been emitted? */ int serialized; } yaml_anchors_t; /** * The emitter structure. * * All members are internal. Manage the structure using the @c yaml_emitter_ * family of functions. */ typedef struct yaml_emitter_s { /** * @name Error handling * @{ */ /** Error type. */ yaml_error_type_t error; /** Error description. */ const char *problem; /** * @} */ /** * @name Writer stuff * @{ */ /** Write handler. */ yaml_write_handler_t *write_handler; /** A pointer for passing to the white handler. */ void *write_handler_data; /** Standard (string or file) output data. */ union { /** String output data. */ struct { /** The buffer pointer. */ unsigned char *buffer; /** The buffer size. */ size_t size; /** The number of written bytes. */ size_t *size_written; } string; /** File output data. */ FILE *file; } output; /** The working buffer. */ struct { /** The beginning of the buffer. */ yaml_char_t *start; /** The end of the buffer. */ yaml_char_t *end; /** The current position of the buffer. */ yaml_char_t *pointer; /** The last filled position of the buffer. */ yaml_char_t *last; } buffer; /** The raw buffer. */ struct { /** The beginning of the buffer. */ unsigned char *start; /** The end of the buffer. */ unsigned char *end; /** The current position of the buffer. */ unsigned char *pointer; /** The last filled position of the buffer. */ unsigned char *last; } raw_buffer; /** The stream encoding. */ yaml_encoding_t encoding; /** * @} */ /** * @name Emitter stuff * @{ */ /** If the output is in the canonical style? */ int canonical; /** The number of indentation spaces. */ int best_indent; /** Whether or not to indent block sequences in mapping context. */ int indent_mapping_sequence; /** The preferred width of the output lines. */ int best_width; /** Allow unescaped non-ASCII characters? */ int unicode; /** The preferred line break. */ yaml_break_t line_break; /** The stack of states. */ struct { /** The beginning of the stack. */ yaml_emitter_state_t *start; /** The end of the stack. */ yaml_emitter_state_t *end; /** The top of the stack. */ yaml_emitter_state_t *top; } states; /** The current emitter state. */ yaml_emitter_state_t state; /** The event queue. */ struct { /** The beginning of the event queue. */ yaml_event_t *start; /** The end of the event queue. */ yaml_event_t *end; /** The head of the event queue. */ yaml_event_t *head; /** The tail of the event queue. */ yaml_event_t *tail; } events; /** The stack of indentation levels. */ struct { /** The beginning of the stack. */ int *start; /** The end of the stack. */ int *end; /** The top of the stack. */ int *top; } indents; /** The list of tag directives. */ struct { /** The beginning of the list. */ yaml_tag_directive_t *start; /** The end of the list. */ yaml_tag_directive_t *end; /** The top of the list. */ yaml_tag_directive_t *top; } tag_directives; /** The current indentation level. */ int indent; /** The current flow level. */ int flow_level; /** Is it the document root context? */ int root_context; /** Is it a sequence context? */ int sequence_context; /** Is it a mapping context? */ int mapping_context; /** Is it a simple mapping key context? */ int simple_key_context; /** The current line. */ int line; /** The current column. */ int column; /** If the last character was a whitespace? */ int whitespace; /** If the last character was an indentation character (' ', '-', '?', ':')? */ int indention; /** If an explicit document end is required? */ int open_ended; /** Anchor analysis. */ struct { /** The anchor value. */ yaml_char_t *anchor; /** The anchor length. */ size_t anchor_length; /** Is it an alias? */ int alias; } anchor_data; /** Tag analysis. */ struct { /** The tag handle. */ yaml_char_t *handle; /** The tag handle length. */ size_t handle_length; /** The tag suffix. */ yaml_char_t *suffix; /** The tag suffix length. */ size_t suffix_length; } tag_data; /** Scalar analysis. */ struct { /** The scalar value. */ yaml_char_t *value; /** The scalar length. */ size_t length; /** Does the scalar contain line breaks? */ int multiline; /** Can the scalar be expessed in the flow plain style? */ int flow_plain_allowed; /** Can the scalar be expressed in the block plain style? */ int block_plain_allowed; /** Can the scalar be expressed in the single quoted style? */ int single_quoted_allowed; /** Can the scalar be expressed in the literal or folded styles? */ int block_allowed; /** The output style. */ yaml_scalar_style_t style; } scalar_data; /** * @} */ /** * @name Dumper stuff * @{ */ /** If the stream was already opened? */ int opened; /** If the stream was already closed? */ int closed; /** The information associated with the document nodes. */ yaml_anchors_t *anchors; /** The last assigned anchor id. */ int last_anchor_id; /** The currently emitted document. */ yaml_document_t *document; /** * @} */ } yaml_emitter_t; /** * Initialize an emitter. * * This function creates a new emitter object. An application is responsible * for destroying the object using the yaml_emitter_delete() function. * * @param[out] emitter An empty parser object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_emitter_initialize(yaml_emitter_t *emitter); /** * Destroy an emitter. * * @param[in,out] emitter An emitter object. */ YAML_DECLARE(void) yaml_emitter_delete(yaml_emitter_t *emitter); /** * Set a string output. * * The emitter will write the output characters to the @a output buffer of the * size @a size. The emitter will set @a size_written to the number of written * bytes. If the buffer is smaller than required, the emitter produces the * YAML_WRITE_ERROR error. * * @param[in,out] emitter An emitter object. * @param[in] output An output buffer. * @param[in] size The buffer size. * @param[in] size_written The pointer to save the number of written * bytes. */ YAML_DECLARE(void) yaml_emitter_set_output_string(yaml_emitter_t *emitter, unsigned char *output, size_t size, size_t *size_written); /** * Set a file output. * * @a file should be a file object open for writing. The application is * responsible for closing the @a file. * * @param[in,out] emitter An emitter object. * @param[in] file An open file. */ YAML_DECLARE(void) yaml_emitter_set_output_file(yaml_emitter_t *emitter, FILE *file); /** * Set a generic output handler. * * @param[in,out] emitter An emitter object. * @param[in] handler A write handler. * @param[in] data Any application data for passing to the write * handler. */ YAML_DECLARE(void) yaml_emitter_set_output(yaml_emitter_t *emitter, yaml_write_handler_t *handler, void *data); /** * Set the output encoding. * * @param[in,out] emitter An emitter object. * @param[in] encoding The output encoding. */ YAML_DECLARE(void) yaml_emitter_set_encoding(yaml_emitter_t *emitter, yaml_encoding_t encoding); /** * Set if the output should be in the "canonical" format as in the YAML * specification. * * @param[in,out] emitter An emitter object. * @param[in] canonical If the output is canonical. */ YAML_DECLARE(void) yaml_emitter_set_canonical(yaml_emitter_t *emitter, int canonical); /** * Set the intendation increment. * * @param[in,out] emitter An emitter object. * @param[in] indent The indentation increment (1 < . < 10). */ YAML_DECLARE(void) yaml_emitter_set_indent(yaml_emitter_t *emitter, int indent); /* * Set whether or not to indent block sequences in mapping context. * * @param[in,out] emitter An emitter object. * @param[in] indent_mapping_sequence Boolean. */ YAML_DECLARE(void) yaml_emitter_set_indent_mapping_sequence(yaml_emitter_t *emitter, int indent_mapping_sequence); /** * Set the preferred line width. @c -1 means unlimited. * * @param[in,out] emitter An emitter object. * @param[in] width The preferred line width. */ YAML_DECLARE(void) yaml_emitter_set_width(yaml_emitter_t *emitter, int width); /** * Set if unescaped non-ASCII characters are allowed. * * @param[in,out] emitter An emitter object. * @param[in] unicode If unescaped Unicode characters are allowed. */ YAML_DECLARE(void) yaml_emitter_set_unicode(yaml_emitter_t *emitter, int unicode); /** * Set the preferred line break. * * @param[in,out] emitter An emitter object. * @param[in] line_break The preferred line break. */ YAML_DECLARE(void) yaml_emitter_set_break(yaml_emitter_t *emitter, yaml_break_t line_break); /** * Emit an event. * * The event object may be generated using the yaml_parser_parse() function. * The emitter takes the responsibility for the event object and destroys its * content after it is emitted. The event object is destroyed even if the * function fails. * * @param[in,out] emitter An emitter object. * @param[in,out] event An event object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_emitter_emit(yaml_emitter_t *emitter, yaml_event_t *event); /** * Start a YAML stream. * * This function should be used before yaml_emitter_dump() is called. * * @param[in,out] emitter An emitter object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_emitter_open(yaml_emitter_t *emitter); /** * Finish a YAML stream. * * This function should be used after yaml_emitter_dump() is called. * * @param[in,out] emitter An emitter object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_emitter_close(yaml_emitter_t *emitter); /** * Emit a YAML document. * * The documen object may be generated using the yaml_parser_load() function * or the yaml_document_initialize() function. The emitter takes the * responsibility for the document object and destroys its content after * it is emitted. The document object is destroyed even if the function fails. * * @param[in,out] emitter An emitter object. * @param[in,out] document A document object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document); /** * Flush the accumulated characters to the output. * * @param[in,out] emitter An emitter object. * * @returns @c 1 if the function succeeded, @c 0 on error. */ YAML_DECLARE(int) yaml_emitter_flush(yaml_emitter_t *emitter); /** @} */ #ifdef __cplusplus } #endif #endif /* #ifndef YAML_H */ yaml/src/emitter.c0000644000176200001440000020020713614635636013636 0ustar liggesusers #include "yaml_private.h" /* * Flush the buffer if needed. */ #define FLUSH(emitter) \ ((emitter->buffer.pointer+5 < emitter->buffer.end) \ || yaml_emitter_flush(emitter)) /* * Put a character to the output buffer. */ #define PUT(emitter,value) \ (FLUSH(emitter) \ && (*(emitter->buffer.pointer++) = (yaml_char_t)(value), \ emitter->column++, \ 1)) /* * Put a line break to the output buffer. */ #define PUT_BREAK(emitter) \ (FLUSH(emitter) ? \ ((emitter->line_break == YAML_CR_BREAK ? \ (*(emitter->buffer.pointer++) = (yaml_char_t) '\r') : \ emitter->line_break == YAML_LN_BREAK ? \ (*(emitter->buffer.pointer++) = (yaml_char_t) '\n') : \ emitter->line_break == YAML_CRLN_BREAK ? \ (*(emitter->buffer.pointer++) = (yaml_char_t) '\r', \ *(emitter->buffer.pointer++) = (yaml_char_t) '\n') : 0), \ emitter->column = 0, \ emitter->line ++, \ 1) : 0) /* * Copy a character from a string into buffer. */ #define WRITE(emitter,string) \ (FLUSH(emitter) \ && (COPY(emitter->buffer,string), \ emitter->column ++, \ 1)) /* * Copy a line break character from a string into buffer. */ #define WRITE_BREAK(emitter,string) \ (FLUSH(emitter) \ && (CHECK(string,'\n') ? \ (PUT_BREAK(emitter), \ string.pointer ++, \ 1) : \ (COPY(emitter->buffer,string), \ emitter->column = 0, \ emitter->line ++, \ 1))) /* * API functions. */ YAML_DECLARE(int) yaml_emitter_emit(yaml_emitter_t *emitter, yaml_event_t *event); /* * Utility functions. */ static int yaml_emitter_set_emitter_error(yaml_emitter_t *emitter, const char *problem); static int yaml_emitter_need_more_events(yaml_emitter_t *emitter); static int yaml_emitter_append_tag_directive(yaml_emitter_t *emitter, yaml_tag_directive_t value, int allow_duplicates); static int yaml_emitter_increase_indent(yaml_emitter_t *emitter, int flow, int indentless); /* * State functions. */ static int yaml_emitter_state_machine(yaml_emitter_t *emitter, yaml_event_t *event); static int yaml_emitter_emit_stream_start(yaml_emitter_t *emitter, yaml_event_t *event); static int yaml_emitter_emit_document_start(yaml_emitter_t *emitter, yaml_event_t *event, int first); static int yaml_emitter_emit_document_content(yaml_emitter_t *emitter, yaml_event_t *event); static int yaml_emitter_emit_document_end(yaml_emitter_t *emitter, yaml_event_t *event); static int yaml_emitter_emit_flow_sequence_item(yaml_emitter_t *emitter, yaml_event_t *event, int first); static int yaml_emitter_emit_flow_mapping_key(yaml_emitter_t *emitter, yaml_event_t *event, int first); static int yaml_emitter_emit_flow_mapping_value(yaml_emitter_t *emitter, yaml_event_t *event, int simple); static int yaml_emitter_emit_block_sequence_item(yaml_emitter_t *emitter, yaml_event_t *event, int first); static int yaml_emitter_emit_block_mapping_key(yaml_emitter_t *emitter, yaml_event_t *event, int first); static int yaml_emitter_emit_block_mapping_value(yaml_emitter_t *emitter, yaml_event_t *event, int simple); static int yaml_emitter_emit_node(yaml_emitter_t *emitter, yaml_event_t *event, int root, int sequence, int mapping, int simple_key); static int yaml_emitter_emit_alias(yaml_emitter_t *emitter, yaml_event_t *event); static int yaml_emitter_emit_scalar(yaml_emitter_t *emitter, yaml_event_t *event); static int yaml_emitter_emit_sequence_start(yaml_emitter_t *emitter, yaml_event_t *event); static int yaml_emitter_emit_mapping_start(yaml_emitter_t *emitter, yaml_event_t *event); /* * Checkers. */ static int yaml_emitter_check_empty_document(yaml_emitter_t *emitter); static int yaml_emitter_check_empty_sequence(yaml_emitter_t *emitter); static int yaml_emitter_check_empty_mapping(yaml_emitter_t *emitter); static int yaml_emitter_check_simple_key(yaml_emitter_t *emitter); static int yaml_emitter_select_scalar_style(yaml_emitter_t *emitter, yaml_event_t *event); /* * Processors. */ static int yaml_emitter_process_anchor(yaml_emitter_t *emitter); static int yaml_emitter_process_tag(yaml_emitter_t *emitter); static int yaml_emitter_process_scalar(yaml_emitter_t *emitter); /* * Analyzers. */ static int yaml_emitter_analyze_version_directive(yaml_emitter_t *emitter, yaml_version_directive_t version_directive); static int yaml_emitter_analyze_tag_directive(yaml_emitter_t *emitter, yaml_tag_directive_t tag_directive); static int yaml_emitter_analyze_anchor(yaml_emitter_t *emitter, yaml_char_t *anchor, int alias); static int yaml_emitter_analyze_tag(yaml_emitter_t *emitter, yaml_char_t *tag); static int yaml_emitter_analyze_scalar(yaml_emitter_t *emitter, yaml_char_t *value, size_t length); static int yaml_emitter_analyze_event(yaml_emitter_t *emitter, yaml_event_t *event); /* * Writers. */ static int yaml_emitter_write_bom(yaml_emitter_t *emitter); static int yaml_emitter_write_indent(yaml_emitter_t *emitter); static int yaml_emitter_write_indicator(yaml_emitter_t *emitter, const char *indicator, int need_whitespace, int is_whitespace, int is_indention); static int yaml_emitter_write_anchor(yaml_emitter_t *emitter, yaml_char_t *value, size_t length); static int yaml_emitter_write_tag_handle(yaml_emitter_t *emitter, yaml_char_t *value, size_t length); static int yaml_emitter_write_tag_content(yaml_emitter_t *emitter, yaml_char_t *value, size_t length, int need_whitespace); static int yaml_emitter_write_plain_scalar(yaml_emitter_t *emitter, yaml_char_t *value, size_t length, int allow_breaks); static int yaml_emitter_write_single_quoted_scalar(yaml_emitter_t *emitter, yaml_char_t *value, size_t length, int allow_breaks); static int yaml_emitter_write_double_quoted_scalar(yaml_emitter_t *emitter, yaml_char_t *value, size_t length, int allow_breaks); static int yaml_emitter_write_block_scalar_hints(yaml_emitter_t *emitter, yaml_string_t string); static int yaml_emitter_write_literal_scalar(yaml_emitter_t *emitter, yaml_char_t *value, size_t length); static int yaml_emitter_write_folded_scalar(yaml_emitter_t *emitter, yaml_char_t *value, size_t length); /* * Set an emitter error and return 0. */ static int yaml_emitter_set_emitter_error(yaml_emitter_t *emitter, const char *problem) { emitter->error = YAML_EMITTER_ERROR; emitter->problem = problem; return 0; } /* * Emit an event. */ YAML_DECLARE(int) yaml_emitter_emit(yaml_emitter_t *emitter, yaml_event_t *event) { if (!ENQUEUE(emitter, emitter->events, *event)) { yaml_event_delete(event); return 0; } while (!yaml_emitter_need_more_events(emitter)) { if (!yaml_emitter_analyze_event(emitter, emitter->events.head)) return 0; if (!yaml_emitter_state_machine(emitter, emitter->events.head)) return 0; yaml_event_delete(&DEQUEUE(emitter, emitter->events)); } return 1; } /* * Check if we need to accumulate more events before emitting. * * We accumulate extra * - 1 event for DOCUMENT-START * - 2 events for SEQUENCE-START * - 3 events for MAPPING-START */ static int yaml_emitter_need_more_events(yaml_emitter_t *emitter) { int level = 0; int accumulate = 0; yaml_event_t *event; if (QUEUE_EMPTY(emitter, emitter->events)) return 1; switch (emitter->events.head->type) { case YAML_DOCUMENT_START_EVENT: accumulate = 1; break; case YAML_SEQUENCE_START_EVENT: accumulate = 2; break; case YAML_MAPPING_START_EVENT: accumulate = 3; break; default: return 0; } if (emitter->events.tail - emitter->events.head > accumulate) return 0; for (event = emitter->events.head; event != emitter->events.tail; event ++) { switch (event->type) { case YAML_STREAM_START_EVENT: case YAML_DOCUMENT_START_EVENT: case YAML_SEQUENCE_START_EVENT: case YAML_MAPPING_START_EVENT: level += 1; break; case YAML_STREAM_END_EVENT: case YAML_DOCUMENT_END_EVENT: case YAML_SEQUENCE_END_EVENT: case YAML_MAPPING_END_EVENT: level -= 1; break; default: break; } if (!level) return 0; } return 1; } /* * Append a directive to the directives stack. */ static int yaml_emitter_append_tag_directive(yaml_emitter_t *emitter, yaml_tag_directive_t value, int allow_duplicates) { yaml_tag_directive_t *tag_directive; yaml_tag_directive_t copy = { NULL, NULL }; for (tag_directive = emitter->tag_directives.start; tag_directive != emitter->tag_directives.top; tag_directive ++) { if (strcmp((char *)value.handle, (char *)tag_directive->handle) == 0) { if (allow_duplicates) return 1; return yaml_emitter_set_emitter_error(emitter, "duplicate %TAG directive"); } } copy.handle = yaml_strdup(value.handle); copy.prefix = yaml_strdup(value.prefix); if (!copy.handle || !copy.prefix) { emitter->error = YAML_MEMORY_ERROR; goto error; } if (!PUSH(emitter, emitter->tag_directives, copy)) goto error; return 1; error: yaml_free(copy.handle); yaml_free(copy.prefix); return 0; } /* * Increase the indentation level. */ static int yaml_emitter_increase_indent(yaml_emitter_t *emitter, int flow, int indentless) { if (!PUSH(emitter, emitter->indents, emitter->indent)) return 0; if (emitter->indent < 0) { emitter->indent = flow ? emitter->best_indent : 0; } else if (!indentless) { emitter->indent += emitter->best_indent; } return 1; } /* * State dispatcher. */ static int yaml_emitter_state_machine(yaml_emitter_t *emitter, yaml_event_t *event) { switch (emitter->state) { case YAML_EMIT_STREAM_START_STATE: return yaml_emitter_emit_stream_start(emitter, event); case YAML_EMIT_FIRST_DOCUMENT_START_STATE: return yaml_emitter_emit_document_start(emitter, event, 1); case YAML_EMIT_DOCUMENT_START_STATE: return yaml_emitter_emit_document_start(emitter, event, 0); case YAML_EMIT_DOCUMENT_CONTENT_STATE: return yaml_emitter_emit_document_content(emitter, event); case YAML_EMIT_DOCUMENT_END_STATE: return yaml_emitter_emit_document_end(emitter, event); case YAML_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE: return yaml_emitter_emit_flow_sequence_item(emitter, event, 1); case YAML_EMIT_FLOW_SEQUENCE_ITEM_STATE: return yaml_emitter_emit_flow_sequence_item(emitter, event, 0); case YAML_EMIT_FLOW_MAPPING_FIRST_KEY_STATE: return yaml_emitter_emit_flow_mapping_key(emitter, event, 1); case YAML_EMIT_FLOW_MAPPING_KEY_STATE: return yaml_emitter_emit_flow_mapping_key(emitter, event, 0); case YAML_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE: return yaml_emitter_emit_flow_mapping_value(emitter, event, 1); case YAML_EMIT_FLOW_MAPPING_VALUE_STATE: return yaml_emitter_emit_flow_mapping_value(emitter, event, 0); case YAML_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE: return yaml_emitter_emit_block_sequence_item(emitter, event, 1); case YAML_EMIT_BLOCK_SEQUENCE_ITEM_STATE: return yaml_emitter_emit_block_sequence_item(emitter, event, 0); case YAML_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE: return yaml_emitter_emit_block_mapping_key(emitter, event, 1); case YAML_EMIT_BLOCK_MAPPING_KEY_STATE: return yaml_emitter_emit_block_mapping_key(emitter, event, 0); case YAML_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE: return yaml_emitter_emit_block_mapping_value(emitter, event, 1); case YAML_EMIT_BLOCK_MAPPING_VALUE_STATE: return yaml_emitter_emit_block_mapping_value(emitter, event, 0); case YAML_EMIT_END_STATE: return yaml_emitter_set_emitter_error(emitter, "expected nothing after STREAM-END"); default: assert(1); /* Invalid state. */ } return 0; } /* * Expect STREAM-START. */ static int yaml_emitter_emit_stream_start(yaml_emitter_t *emitter, yaml_event_t *event) { if (event->type == YAML_STREAM_START_EVENT) { if (!emitter->encoding) { emitter->encoding = event->data.stream_start.encoding; } if (!emitter->encoding) { emitter->encoding = YAML_UTF8_ENCODING; } if (emitter->best_indent < 2 || emitter->best_indent > 9) { emitter->best_indent = 2; } if (emitter->best_width >= 0 && emitter->best_width <= emitter->best_indent*2) { emitter->best_width = 80; } if (emitter->best_width < 0) { emitter->best_width = INT_MAX; } if (!emitter->line_break) { emitter->line_break = YAML_LN_BREAK; } emitter->indent = -1; emitter->line = 0; emitter->column = 0; emitter->whitespace = 1; emitter->indention = 1; if (emitter->encoding != YAML_UTF8_ENCODING) { if (!yaml_emitter_write_bom(emitter)) return 0; } emitter->state = YAML_EMIT_FIRST_DOCUMENT_START_STATE; return 1; } return yaml_emitter_set_emitter_error(emitter, "expected STREAM-START"); } /* * Expect DOCUMENT-START or STREAM-END. */ static int yaml_emitter_emit_document_start(yaml_emitter_t *emitter, yaml_event_t *event, int first) { if (event->type == YAML_DOCUMENT_START_EVENT) { yaml_tag_directive_t default_tag_directives[] = { {(yaml_char_t *)"!", (yaml_char_t *)"!"}, {(yaml_char_t *)"!!", (yaml_char_t *)"tag:yaml.org,2002:"}, {NULL, NULL} }; yaml_tag_directive_t *tag_directive; int implicit; if (event->data.document_start.version_directive) { if (!yaml_emitter_analyze_version_directive(emitter, *event->data.document_start.version_directive)) return 0; } for (tag_directive = event->data.document_start.tag_directives.start; tag_directive != event->data.document_start.tag_directives.end; tag_directive ++) { if (!yaml_emitter_analyze_tag_directive(emitter, *tag_directive)) return 0; if (!yaml_emitter_append_tag_directive(emitter, *tag_directive, 0)) return 0; } for (tag_directive = default_tag_directives; tag_directive->handle; tag_directive ++) { if (!yaml_emitter_append_tag_directive(emitter, *tag_directive, 1)) return 0; } implicit = event->data.document_start.implicit; if (!first || emitter->canonical) { implicit = 0; } if ((event->data.document_start.version_directive || (event->data.document_start.tag_directives.start != event->data.document_start.tag_directives.end)) && emitter->open_ended) { if (!yaml_emitter_write_indicator(emitter, "...", 1, 0, 0)) return 0; if (!yaml_emitter_write_indent(emitter)) return 0; } if (event->data.document_start.version_directive) { implicit = 0; if (!yaml_emitter_write_indicator(emitter, "%YAML", 1, 0, 0)) return 0; if (!yaml_emitter_write_indicator(emitter, "1.1", 1, 0, 0)) return 0; if (!yaml_emitter_write_indent(emitter)) return 0; } if (event->data.document_start.tag_directives.start != event->data.document_start.tag_directives.end) { implicit = 0; for (tag_directive = event->data.document_start.tag_directives.start; tag_directive != event->data.document_start.tag_directives.end; tag_directive ++) { if (!yaml_emitter_write_indicator(emitter, "%TAG", 1, 0, 0)) return 0; if (!yaml_emitter_write_tag_handle(emitter, tag_directive->handle, strlen((char *)tag_directive->handle))) return 0; if (!yaml_emitter_write_tag_content(emitter, tag_directive->prefix, strlen((char *)tag_directive->prefix), 1)) return 0; if (!yaml_emitter_write_indent(emitter)) return 0; } } if (yaml_emitter_check_empty_document(emitter)) { implicit = 0; } if (!implicit) { if (!yaml_emitter_write_indent(emitter)) return 0; if (!yaml_emitter_write_indicator(emitter, "---", 1, 0, 0)) return 0; if (emitter->canonical) { if (!yaml_emitter_write_indent(emitter)) return 0; } } emitter->state = YAML_EMIT_DOCUMENT_CONTENT_STATE; return 1; } else if (event->type == YAML_STREAM_END_EVENT) { if (!yaml_emitter_flush(emitter)) return 0; emitter->state = YAML_EMIT_END_STATE; return 1; } return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END"); } /* * Expect the root node. */ static int yaml_emitter_emit_document_content(yaml_emitter_t *emitter, yaml_event_t *event) { if (!PUSH(emitter, emitter->states, YAML_EMIT_DOCUMENT_END_STATE)) return 0; return yaml_emitter_emit_node(emitter, event, 1, 0, 0, 0); } /* * Expect DOCUMENT-END. */ static int yaml_emitter_emit_document_end(yaml_emitter_t *emitter, yaml_event_t *event) { if (event->type == YAML_DOCUMENT_END_EVENT) { if (!yaml_emitter_write_indent(emitter)) return 0; if (!event->data.document_end.implicit) { if (!yaml_emitter_write_indicator(emitter, "...", 1, 0, 0)) return 0; if (!yaml_emitter_write_indent(emitter)) return 0; } if (!yaml_emitter_flush(emitter)) return 0; emitter->state = YAML_EMIT_DOCUMENT_START_STATE; while (!STACK_EMPTY(emitter, emitter->tag_directives)) { yaml_tag_directive_t tag_directive = POP(emitter, emitter->tag_directives); yaml_free(tag_directive.handle); yaml_free(tag_directive.prefix); } return 1; } return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END"); } /* * * Expect a flow item node. */ static int yaml_emitter_emit_flow_sequence_item(yaml_emitter_t *emitter, yaml_event_t *event, int first) { if (first) { if (!yaml_emitter_write_indicator(emitter, "[", 1, 1, 0)) return 0; if (!yaml_emitter_increase_indent(emitter, 1, 0)) return 0; emitter->flow_level ++; } if (event->type == YAML_SEQUENCE_END_EVENT) { emitter->flow_level --; emitter->indent = POP(emitter, emitter->indents); if (emitter->canonical && !first) { if (!yaml_emitter_write_indicator(emitter, ",", 0, 0, 0)) return 0; if (!yaml_emitter_write_indent(emitter)) return 0; } if (!yaml_emitter_write_indicator(emitter, "]", 0, 0, 0)) return 0; emitter->state = POP(emitter, emitter->states); return 1; } if (!first) { if (!yaml_emitter_write_indicator(emitter, ",", 0, 0, 0)) return 0; } if (emitter->canonical || emitter->column > emitter->best_width) { if (!yaml_emitter_write_indent(emitter)) return 0; } if (!PUSH(emitter, emitter->states, YAML_EMIT_FLOW_SEQUENCE_ITEM_STATE)) return 0; return yaml_emitter_emit_node(emitter, event, 0, 1, 0, 0); } /* * Expect a flow key node. */ static int yaml_emitter_emit_flow_mapping_key(yaml_emitter_t *emitter, yaml_event_t *event, int first) { if (first) { if (!yaml_emitter_write_indicator(emitter, "{", 1, 1, 0)) return 0; if (!yaml_emitter_increase_indent(emitter, 1, 0)) return 0; emitter->flow_level ++; } if (event->type == YAML_MAPPING_END_EVENT) { emitter->flow_level --; emitter->indent = POP(emitter, emitter->indents); if (emitter->canonical && !first) { if (!yaml_emitter_write_indicator(emitter, ",", 0, 0, 0)) return 0; if (!yaml_emitter_write_indent(emitter)) return 0; } if (!yaml_emitter_write_indicator(emitter, "}", 0, 0, 0)) return 0; emitter->state = POP(emitter, emitter->states); return 1; } if (!first) { if (!yaml_emitter_write_indicator(emitter, ",", 0, 0, 0)) return 0; } if (emitter->canonical || emitter->column > emitter->best_width) { if (!yaml_emitter_write_indent(emitter)) return 0; } if (!emitter->canonical && yaml_emitter_check_simple_key(emitter)) { if (!PUSH(emitter, emitter->states, YAML_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE)) return 0; return yaml_emitter_emit_node(emitter, event, 0, 0, 1, 1); } else { if (!yaml_emitter_write_indicator(emitter, "?", 1, 0, 0)) return 0; if (!PUSH(emitter, emitter->states, YAML_EMIT_FLOW_MAPPING_VALUE_STATE)) return 0; return yaml_emitter_emit_node(emitter, event, 0, 0, 1, 0); } } /* * Expect a flow value node. */ static int yaml_emitter_emit_flow_mapping_value(yaml_emitter_t *emitter, yaml_event_t *event, int simple) { if (simple) { if (!yaml_emitter_write_indicator(emitter, ":", 0, 0, 0)) return 0; } else { if (emitter->canonical || emitter->column > emitter->best_width) { if (!yaml_emitter_write_indent(emitter)) return 0; } if (!yaml_emitter_write_indicator(emitter, ":", 1, 0, 0)) return 0; } if (!PUSH(emitter, emitter->states, YAML_EMIT_FLOW_MAPPING_KEY_STATE)) return 0; return yaml_emitter_emit_node(emitter, event, 0, 0, 1, 0); } /* * Expect a block item node. */ static int yaml_emitter_emit_block_sequence_item(yaml_emitter_t *emitter, yaml_event_t *event, int first) { if (first) { if (!yaml_emitter_increase_indent(emitter, 0, (emitter->mapping_context && !emitter->indent_mapping_sequence && !emitter->indention))) return 0; } if (event->type == YAML_SEQUENCE_END_EVENT) { emitter->indent = POP(emitter, emitter->indents); emitter->state = POP(emitter, emitter->states); return 1; } if (!yaml_emitter_write_indent(emitter)) return 0; if (!yaml_emitter_write_indicator(emitter, "-", 1, 0, 1)) return 0; if (!PUSH(emitter, emitter->states, YAML_EMIT_BLOCK_SEQUENCE_ITEM_STATE)) return 0; return yaml_emitter_emit_node(emitter, event, 0, 1, 0, 0); } /* * Expect a block key node. */ static int yaml_emitter_emit_block_mapping_key(yaml_emitter_t *emitter, yaml_event_t *event, int first) { if (first) { if (!yaml_emitter_increase_indent(emitter, 0, 0)) return 0; } if (event->type == YAML_MAPPING_END_EVENT) { emitter->indent = POP(emitter, emitter->indents); emitter->state = POP(emitter, emitter->states); return 1; } if (!yaml_emitter_write_indent(emitter)) return 0; if (yaml_emitter_check_simple_key(emitter)) { if (!PUSH(emitter, emitter->states, YAML_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE)) return 0; return yaml_emitter_emit_node(emitter, event, 0, 0, 1, 1); } else { if (!yaml_emitter_write_indicator(emitter, "?", 1, 0, 1)) return 0; if (!PUSH(emitter, emitter->states, YAML_EMIT_BLOCK_MAPPING_VALUE_STATE)) return 0; return yaml_emitter_emit_node(emitter, event, 0, 0, 1, 0); } } /* * Expect a block value node. */ static int yaml_emitter_emit_block_mapping_value(yaml_emitter_t *emitter, yaml_event_t *event, int simple) { if (simple) { if (!yaml_emitter_write_indicator(emitter, ":", 0, 0, 0)) return 0; } else { if (!yaml_emitter_write_indent(emitter)) return 0; if (!yaml_emitter_write_indicator(emitter, ":", 1, 0, 1)) return 0; } if (!PUSH(emitter, emitter->states, YAML_EMIT_BLOCK_MAPPING_KEY_STATE)) return 0; return yaml_emitter_emit_node(emitter, event, 0, 0, 1, 0); } /* * Expect a node. */ static int yaml_emitter_emit_node(yaml_emitter_t *emitter, yaml_event_t *event, int root, int sequence, int mapping, int simple_key) { emitter->root_context = root; emitter->sequence_context = sequence; emitter->mapping_context = mapping; emitter->simple_key_context = simple_key; switch (event->type) { case YAML_ALIAS_EVENT: return yaml_emitter_emit_alias(emitter, event); case YAML_SCALAR_EVENT: return yaml_emitter_emit_scalar(emitter, event); case YAML_SEQUENCE_START_EVENT: return yaml_emitter_emit_sequence_start(emitter, event); case YAML_MAPPING_START_EVENT: return yaml_emitter_emit_mapping_start(emitter, event); default: return yaml_emitter_set_emitter_error(emitter, "expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS"); } return 0; } /* * Expect ALIAS. */ static int yaml_emitter_emit_alias(yaml_emitter_t *emitter, SHIM(yaml_event_t *event)) { if (!yaml_emitter_process_anchor(emitter)) return 0; emitter->state = POP(emitter, emitter->states); return 1; } /* * Expect SCALAR. */ static int yaml_emitter_emit_scalar(yaml_emitter_t *emitter, yaml_event_t *event) { if (!yaml_emitter_select_scalar_style(emitter, event)) return 0; if (!yaml_emitter_process_anchor(emitter)) return 0; if (!yaml_emitter_process_tag(emitter)) return 0; if (!yaml_emitter_increase_indent(emitter, 1, 0)) return 0; if (!yaml_emitter_process_scalar(emitter)) return 0; emitter->indent = POP(emitter, emitter->indents); emitter->state = POP(emitter, emitter->states); return 1; } /* * Expect SEQUENCE-START. */ static int yaml_emitter_emit_sequence_start(yaml_emitter_t *emitter, yaml_event_t *event) { if (!yaml_emitter_process_anchor(emitter)) return 0; if (!yaml_emitter_process_tag(emitter)) return 0; if (emitter->flow_level || emitter->canonical || event->data.sequence_start.style == YAML_FLOW_SEQUENCE_STYLE || yaml_emitter_check_empty_sequence(emitter)) { emitter->state = YAML_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE; } else { emitter->state = YAML_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE; } return 1; } /* * Expect MAPPING-START. */ static int yaml_emitter_emit_mapping_start(yaml_emitter_t *emitter, yaml_event_t *event) { if (!yaml_emitter_process_anchor(emitter)) return 0; if (!yaml_emitter_process_tag(emitter)) return 0; if (emitter->flow_level || emitter->canonical || event->data.mapping_start.style == YAML_FLOW_MAPPING_STYLE || yaml_emitter_check_empty_mapping(emitter)) { emitter->state = YAML_EMIT_FLOW_MAPPING_FIRST_KEY_STATE; } else { emitter->state = YAML_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE; } return 1; } /* * Check if the document content is an empty scalar. */ static int yaml_emitter_check_empty_document(SHIM(yaml_emitter_t *emitter)) { return 0; } /* * Check if the next events represent an empty sequence. */ static int yaml_emitter_check_empty_sequence(yaml_emitter_t *emitter) { if (emitter->events.tail - emitter->events.head < 2) return 0; return (emitter->events.head[0].type == YAML_SEQUENCE_START_EVENT && emitter->events.head[1].type == YAML_SEQUENCE_END_EVENT); } /* * Check if the next events represent an empty mapping. */ static int yaml_emitter_check_empty_mapping(yaml_emitter_t *emitter) { if (emitter->events.tail - emitter->events.head < 2) return 0; return (emitter->events.head[0].type == YAML_MAPPING_START_EVENT && emitter->events.head[1].type == YAML_MAPPING_END_EVENT); } /* * Check if the next node can be expressed as a simple key. */ static int yaml_emitter_check_simple_key(yaml_emitter_t *emitter) { yaml_event_t *event = emitter->events.head; size_t length = 0; switch (event->type) { case YAML_ALIAS_EVENT: length += emitter->anchor_data.anchor_length; break; case YAML_SCALAR_EVENT: if (emitter->scalar_data.multiline) return 0; length += emitter->anchor_data.anchor_length + emitter->tag_data.handle_length + emitter->tag_data.suffix_length + emitter->scalar_data.length; break; case YAML_SEQUENCE_START_EVENT: if (!yaml_emitter_check_empty_sequence(emitter)) return 0; length += emitter->anchor_data.anchor_length + emitter->tag_data.handle_length + emitter->tag_data.suffix_length; break; case YAML_MAPPING_START_EVENT: if (!yaml_emitter_check_empty_mapping(emitter)) return 0; length += emitter->anchor_data.anchor_length + emitter->tag_data.handle_length + emitter->tag_data.suffix_length; break; default: return 0; } if (length > 128) return 0; return 1; } /* * Determine an acceptable scalar style. */ static int yaml_emitter_select_scalar_style(yaml_emitter_t *emitter, yaml_event_t *event) { yaml_scalar_style_t style = event->data.scalar.style; int no_tag = (!emitter->tag_data.handle && !emitter->tag_data.suffix); if (no_tag && !event->data.scalar.plain_implicit && !event->data.scalar.quoted_implicit) { return yaml_emitter_set_emitter_error(emitter, "neither tag nor implicit flags are specified"); } if (style == YAML_ANY_SCALAR_STYLE) style = YAML_PLAIN_SCALAR_STYLE; if (emitter->canonical) style = YAML_DOUBLE_QUOTED_SCALAR_STYLE; if (emitter->simple_key_context && emitter->scalar_data.multiline) style = YAML_DOUBLE_QUOTED_SCALAR_STYLE; if (style == YAML_PLAIN_SCALAR_STYLE) { if ((emitter->flow_level && !emitter->scalar_data.flow_plain_allowed) || (!emitter->flow_level && !emitter->scalar_data.block_plain_allowed)) style = YAML_SINGLE_QUOTED_SCALAR_STYLE; if (!emitter->scalar_data.length && (emitter->flow_level || emitter->simple_key_context)) style = YAML_SINGLE_QUOTED_SCALAR_STYLE; if (no_tag && !event->data.scalar.plain_implicit) style = YAML_SINGLE_QUOTED_SCALAR_STYLE; } if (style == YAML_SINGLE_QUOTED_SCALAR_STYLE) { if (!emitter->scalar_data.single_quoted_allowed) style = YAML_DOUBLE_QUOTED_SCALAR_STYLE; } if (style == YAML_LITERAL_SCALAR_STYLE || style == YAML_FOLDED_SCALAR_STYLE) { if (!emitter->scalar_data.block_allowed || emitter->flow_level || emitter->simple_key_context) style = YAML_DOUBLE_QUOTED_SCALAR_STYLE; } if (no_tag && !event->data.scalar.quoted_implicit && style != YAML_PLAIN_SCALAR_STYLE) { emitter->tag_data.handle = (yaml_char_t *)"!"; emitter->tag_data.handle_length = 1; } emitter->scalar_data.style = style; return 1; } /* * Write an achor. */ static int yaml_emitter_process_anchor(yaml_emitter_t *emitter) { if (!emitter->anchor_data.anchor) return 1; if (!yaml_emitter_write_indicator(emitter, (emitter->anchor_data.alias ? "*" : "&"), 1, 0, 0)) return 0; return yaml_emitter_write_anchor(emitter, emitter->anchor_data.anchor, emitter->anchor_data.anchor_length); } /* * Write a tag. */ static int yaml_emitter_process_tag(yaml_emitter_t *emitter) { if (!emitter->tag_data.handle && !emitter->tag_data.suffix) return 1; if (emitter->tag_data.handle) { if (!yaml_emitter_write_tag_handle(emitter, emitter->tag_data.handle, emitter->tag_data.handle_length)) return 0; if (emitter->tag_data.suffix) { if (!yaml_emitter_write_tag_content(emitter, emitter->tag_data.suffix, emitter->tag_data.suffix_length, 0)) return 0; } } else { if (!yaml_emitter_write_indicator(emitter, "!<", 1, 0, 0)) return 0; if (!yaml_emitter_write_tag_content(emitter, emitter->tag_data.suffix, emitter->tag_data.suffix_length, 0)) return 0; if (!yaml_emitter_write_indicator(emitter, ">", 0, 0, 0)) return 0; } return 1; } /* * Write a scalar. */ static int yaml_emitter_process_scalar(yaml_emitter_t *emitter) { switch (emitter->scalar_data.style) { case YAML_PLAIN_SCALAR_STYLE: return yaml_emitter_write_plain_scalar(emitter, emitter->scalar_data.value, emitter->scalar_data.length, !emitter->simple_key_context); case YAML_SINGLE_QUOTED_SCALAR_STYLE: return yaml_emitter_write_single_quoted_scalar(emitter, emitter->scalar_data.value, emitter->scalar_data.length, !emitter->simple_key_context); case YAML_DOUBLE_QUOTED_SCALAR_STYLE: return yaml_emitter_write_double_quoted_scalar(emitter, emitter->scalar_data.value, emitter->scalar_data.length, !emitter->simple_key_context); case YAML_LITERAL_SCALAR_STYLE: return yaml_emitter_write_literal_scalar(emitter, emitter->scalar_data.value, emitter->scalar_data.length); case YAML_FOLDED_SCALAR_STYLE: return yaml_emitter_write_folded_scalar(emitter, emitter->scalar_data.value, emitter->scalar_data.length); default: assert(1); /* Impossible. */ } return 0; } /* * Check if a %YAML directive is valid. */ static int yaml_emitter_analyze_version_directive(yaml_emitter_t *emitter, yaml_version_directive_t version_directive) { if (version_directive.major != 1 || version_directive.minor != 1) { return yaml_emitter_set_emitter_error(emitter, "incompatible %YAML directive"); } return 1; } /* * Check if a %TAG directive is valid. */ static int yaml_emitter_analyze_tag_directive(yaml_emitter_t *emitter, yaml_tag_directive_t tag_directive) { yaml_string_t handle; yaml_string_t prefix; size_t handle_length; size_t prefix_length; handle_length = strlen((char *)tag_directive.handle); prefix_length = strlen((char *)tag_directive.prefix); STRING_ASSIGN(handle, tag_directive.handle, handle_length); STRING_ASSIGN(prefix, tag_directive.prefix, prefix_length); if (handle.start == handle.end) { return yaml_emitter_set_emitter_error(emitter, "tag handle must not be empty"); } if (handle.start[0] != '!') { return yaml_emitter_set_emitter_error(emitter, "tag handle must start with '!'"); } if (handle.end[-1] != '!') { return yaml_emitter_set_emitter_error(emitter, "tag handle must end with '!'"); } handle.pointer ++; while (handle.pointer < handle.end-1) { if (!IS_ALPHA(handle)) { return yaml_emitter_set_emitter_error(emitter, "tag handle must contain alphanumerical characters only"); } MOVE(handle); } if (prefix.start == prefix.end) { return yaml_emitter_set_emitter_error(emitter, "tag prefix must not be empty"); } return 1; } /* * Check if an anchor is valid. */ static int yaml_emitter_analyze_anchor(yaml_emitter_t *emitter, yaml_char_t *anchor, int alias) { size_t anchor_length; yaml_string_t string; anchor_length = strlen((char *)anchor); STRING_ASSIGN(string, anchor, anchor_length); if (string.start == string.end) { return yaml_emitter_set_emitter_error(emitter, alias ? "alias value must not be empty" : "anchor value must not be empty"); } while (string.pointer != string.end) { if (!IS_ALPHA(string)) { return yaml_emitter_set_emitter_error(emitter, alias ? "alias value must contain alphanumerical characters only" : "anchor value must contain alphanumerical characters only"); } MOVE(string); } emitter->anchor_data.anchor = string.start; emitter->anchor_data.anchor_length = string.end - string.start; emitter->anchor_data.alias = alias; return 1; } /* * Check if a tag is valid. */ static int yaml_emitter_analyze_tag(yaml_emitter_t *emitter, yaml_char_t *tag) { size_t tag_length; yaml_string_t string; yaml_tag_directive_t *tag_directive; tag_length = strlen((char *)tag); STRING_ASSIGN(string, tag, tag_length); if (string.start == string.end) { return yaml_emitter_set_emitter_error(emitter, "tag value must not be empty"); } for (tag_directive = emitter->tag_directives.start; tag_directive != emitter->tag_directives.top; tag_directive ++) { size_t prefix_length = strlen((char *)tag_directive->prefix); if (prefix_length < (size_t)(string.end - string.start) && strncmp((char *)tag_directive->prefix, (char *)string.start, prefix_length) == 0) { emitter->tag_data.handle = tag_directive->handle; emitter->tag_data.handle_length = strlen((char *)tag_directive->handle); emitter->tag_data.suffix = string.start + prefix_length; emitter->tag_data.suffix_length = (string.end - string.start) - prefix_length; return 1; } } emitter->tag_data.suffix = string.start; emitter->tag_data.suffix_length = string.end - string.start; return 1; } /* * Check if a scalar is valid. */ static int yaml_emitter_analyze_scalar(yaml_emitter_t *emitter, yaml_char_t *value, size_t length) { yaml_string_t string; int block_indicators = 0; int flow_indicators = 0; int line_breaks = 0; int special_characters = 0; int leading_space = 0; int leading_break = 0; int trailing_space = 0; int trailing_break = 0; int break_space = 0; int space_break = 0; int preceded_by_whitespace = 0; int followed_by_whitespace = 0; int previous_space = 0; int previous_break = 0; STRING_ASSIGN(string, value, length); emitter->scalar_data.value = value; emitter->scalar_data.length = length; if (string.start == string.end) { emitter->scalar_data.multiline = 0; emitter->scalar_data.flow_plain_allowed = 0; emitter->scalar_data.block_plain_allowed = 1; emitter->scalar_data.single_quoted_allowed = 1; emitter->scalar_data.block_allowed = 0; return 1; } if ((CHECK_AT(string, '-', 0) && CHECK_AT(string, '-', 1) && CHECK_AT(string, '-', 2)) || (CHECK_AT(string, '.', 0) && CHECK_AT(string, '.', 1) && CHECK_AT(string, '.', 2))) { block_indicators = 1; flow_indicators = 1; } preceded_by_whitespace = 1; followed_by_whitespace = IS_BLANKZ_AT(string, WIDTH(string)); while (string.pointer != string.end) { if (string.start == string.pointer) { if (CHECK(string, '#') || CHECK(string, ',') || CHECK(string, '[') || CHECK(string, ']') || CHECK(string, '{') || CHECK(string, '}') || CHECK(string, '&') || CHECK(string, '*') || CHECK(string, '!') || CHECK(string, '|') || CHECK(string, '>') || CHECK(string, '\'') || CHECK(string, '"') || CHECK(string, '%') || CHECK(string, '@') || CHECK(string, '`')) { flow_indicators = 1; block_indicators = 1; } if (CHECK(string, '?') || CHECK(string, ':')) { flow_indicators = 1; if (followed_by_whitespace) { block_indicators = 1; } } if (CHECK(string, '-') && followed_by_whitespace) { flow_indicators = 1; block_indicators = 1; } } else { if (CHECK(string, ',') || CHECK(string, '?') || CHECK(string, '[') || CHECK(string, ']') || CHECK(string, '{') || CHECK(string, '}')) { flow_indicators = 1; } if (CHECK(string, ':')) { flow_indicators = 1; if (followed_by_whitespace) { block_indicators = 1; } } if (CHECK(string, '#') && preceded_by_whitespace) { flow_indicators = 1; block_indicators = 1; } } if (!IS_PRINTABLE(string) || (!IS_ASCII(string) && !emitter->unicode)) { special_characters = 1; } if (IS_BREAK(string)) { line_breaks = 1; } if (IS_SPACE(string)) { if (string.start == string.pointer) { leading_space = 1; } if (string.pointer+WIDTH(string) == string.end) { trailing_space = 1; } if (previous_break) { break_space = 1; } previous_space = 1; previous_break = 0; } else if (IS_BREAK(string)) { if (string.start == string.pointer) { leading_break = 1; } if (string.pointer+WIDTH(string) == string.end) { trailing_break = 1; } if (previous_space) { space_break = 1; } previous_space = 0; previous_break = 1; } else { previous_space = 0; previous_break = 0; } preceded_by_whitespace = IS_BLANKZ(string); MOVE(string); if (string.pointer != string.end) { followed_by_whitespace = IS_BLANKZ_AT(string, WIDTH(string)); } } emitter->scalar_data.multiline = line_breaks; emitter->scalar_data.flow_plain_allowed = 1; emitter->scalar_data.block_plain_allowed = 1; emitter->scalar_data.single_quoted_allowed = 1; emitter->scalar_data.block_allowed = 1; if (leading_space || leading_break || trailing_space || trailing_break) { emitter->scalar_data.flow_plain_allowed = 0; emitter->scalar_data.block_plain_allowed = 0; } if (trailing_space) { emitter->scalar_data.block_allowed = 0; } if (break_space) { emitter->scalar_data.flow_plain_allowed = 0; emitter->scalar_data.block_plain_allowed = 0; emitter->scalar_data.single_quoted_allowed = 0; } if (space_break || special_characters) { emitter->scalar_data.flow_plain_allowed = 0; emitter->scalar_data.block_plain_allowed = 0; emitter->scalar_data.single_quoted_allowed = 0; emitter->scalar_data.block_allowed = 0; } if (line_breaks) { emitter->scalar_data.flow_plain_allowed = 0; emitter->scalar_data.block_plain_allowed = 0; } if (flow_indicators) { emitter->scalar_data.flow_plain_allowed = 0; } if (block_indicators) { emitter->scalar_data.block_plain_allowed = 0; } return 1; } /* * Check if the event data is valid. */ static int yaml_emitter_analyze_event(yaml_emitter_t *emitter, yaml_event_t *event) { emitter->anchor_data.anchor = NULL; emitter->anchor_data.anchor_length = 0; emitter->tag_data.handle = NULL; emitter->tag_data.handle_length = 0; emitter->tag_data.suffix = NULL; emitter->tag_data.suffix_length = 0; emitter->scalar_data.value = NULL; emitter->scalar_data.length = 0; switch (event->type) { case YAML_ALIAS_EVENT: if (!yaml_emitter_analyze_anchor(emitter, event->data.alias.anchor, 1)) return 0; return 1; case YAML_SCALAR_EVENT: if (event->data.scalar.anchor) { if (!yaml_emitter_analyze_anchor(emitter, event->data.scalar.anchor, 0)) return 0; } if (event->data.scalar.tag && (emitter->canonical || (!event->data.scalar.plain_implicit && !event->data.scalar.quoted_implicit))) { if (!yaml_emitter_analyze_tag(emitter, event->data.scalar.tag)) return 0; } if (!yaml_emitter_analyze_scalar(emitter, event->data.scalar.value, event->data.scalar.length)) return 0; return 1; case YAML_SEQUENCE_START_EVENT: if (event->data.sequence_start.anchor) { if (!yaml_emitter_analyze_anchor(emitter, event->data.sequence_start.anchor, 0)) return 0; } if (event->data.sequence_start.tag && (emitter->canonical || !event->data.sequence_start.implicit)) { if (!yaml_emitter_analyze_tag(emitter, event->data.sequence_start.tag)) return 0; } return 1; case YAML_MAPPING_START_EVENT: if (event->data.mapping_start.anchor) { if (!yaml_emitter_analyze_anchor(emitter, event->data.mapping_start.anchor, 0)) return 0; } if (event->data.mapping_start.tag && (emitter->canonical || !event->data.mapping_start.implicit)) { if (!yaml_emitter_analyze_tag(emitter, event->data.mapping_start.tag)) return 0; } return 1; default: return 1; } } /* * Write the BOM character. */ static int yaml_emitter_write_bom(yaml_emitter_t *emitter) { if (!FLUSH(emitter)) return 0; *(emitter->buffer.pointer++) = (yaml_char_t) '\xEF'; *(emitter->buffer.pointer++) = (yaml_char_t) '\xBB'; *(emitter->buffer.pointer++) = (yaml_char_t) '\xBF'; return 1; } static int yaml_emitter_write_indent(yaml_emitter_t *emitter) { int indent = (emitter->indent >= 0) ? emitter->indent : 0; if (!emitter->indention || emitter->column > indent || (emitter->column == indent && !emitter->whitespace)) { if (!PUT_BREAK(emitter)) return 0; } while (emitter->column < indent) { if (!PUT(emitter, ' ')) return 0; } emitter->whitespace = 1; emitter->indention = 1; return 1; } static int yaml_emitter_write_indicator(yaml_emitter_t *emitter, const char *indicator, int need_whitespace, int is_whitespace, int is_indention) { size_t indicator_length; yaml_string_t string; indicator_length = strlen(indicator); STRING_ASSIGN(string, (yaml_char_t *)indicator, indicator_length); if (need_whitespace && !emitter->whitespace) { if (!PUT(emitter, ' ')) return 0; } while (string.pointer != string.end) { if (!WRITE(emitter, string)) return 0; } emitter->whitespace = is_whitespace; emitter->indention = (emitter->indention && is_indention); emitter->open_ended = 0; return 1; } static int yaml_emitter_write_anchor(yaml_emitter_t *emitter, yaml_char_t *value, size_t length) { yaml_string_t string; STRING_ASSIGN(string, value, length); while (string.pointer != string.end) { if (!WRITE(emitter, string)) return 0; } emitter->whitespace = 0; emitter->indention = 0; return 1; } static int yaml_emitter_write_tag_handle(yaml_emitter_t *emitter, yaml_char_t *value, size_t length) { yaml_string_t string; STRING_ASSIGN(string, value, length); if (!emitter->whitespace) { if (!PUT(emitter, ' ')) return 0; } while (string.pointer != string.end) { if (!WRITE(emitter, string)) return 0; } emitter->whitespace = 0; emitter->indention = 0; return 1; } static int yaml_emitter_write_tag_content(yaml_emitter_t *emitter, yaml_char_t *value, size_t length, int need_whitespace) { yaml_string_t string; STRING_ASSIGN(string, value, length); if (need_whitespace && !emitter->whitespace) { if (!PUT(emitter, ' ')) return 0; } while (string.pointer != string.end) { if (IS_ALPHA(string) || CHECK(string, ';') || CHECK(string, '/') || CHECK(string, '?') || CHECK(string, ':') || CHECK(string, '@') || CHECK(string, '&') || CHECK(string, '=') || CHECK(string, '+') || CHECK(string, '$') || CHECK(string, ',') || CHECK(string, '_') || CHECK(string, '.') || CHECK(string, '~') || CHECK(string, '*') || CHECK(string, '\'') || CHECK(string, '(') || CHECK(string, ')') || CHECK(string, '[') || CHECK(string, ']')) { if (!WRITE(emitter, string)) return 0; } else { int width = WIDTH(string); unsigned int value; while (width --) { value = *(string.pointer++); if (!PUT(emitter, '%')) return 0; if (!PUT(emitter, (value >> 4) + ((value >> 4) < 10 ? '0' : 'A' - 10))) return 0; if (!PUT(emitter, (value & 0x0F) + ((value & 0x0F) < 10 ? '0' : 'A' - 10))) return 0; } } } emitter->whitespace = 0; emitter->indention = 0; return 1; } static int yaml_emitter_write_plain_scalar(yaml_emitter_t *emitter, yaml_char_t *value, size_t length, int allow_breaks) { yaml_string_t string; int spaces = 0; int breaks = 0; STRING_ASSIGN(string, value, length); if (!emitter->whitespace) { if (!PUT(emitter, ' ')) return 0; } while (string.pointer != string.end) { if (IS_SPACE(string)) { if (allow_breaks && !spaces && emitter->column > emitter->best_width && !IS_SPACE_AT(string, 1)) { if (!yaml_emitter_write_indent(emitter)) return 0; MOVE(string); } else { if (!WRITE(emitter, string)) return 0; } spaces = 1; } else if (IS_BREAK(string)) { if (!breaks && CHECK(string, '\n')) { if (!PUT_BREAK(emitter)) return 0; } if (!WRITE_BREAK(emitter, string)) return 0; emitter->indention = 1; breaks = 1; } else { if (breaks) { if (!yaml_emitter_write_indent(emitter)) return 0; } if (!WRITE(emitter, string)) return 0; emitter->indention = 0; spaces = 0; breaks = 0; } } emitter->whitespace = 0; emitter->indention = 0; if (emitter->root_context) { emitter->open_ended = 1; } return 1; } static int yaml_emitter_write_single_quoted_scalar(yaml_emitter_t *emitter, yaml_char_t *value, size_t length, int allow_breaks) { yaml_string_t string; int spaces = 0; int breaks = 0; STRING_ASSIGN(string, value, length); if (!yaml_emitter_write_indicator(emitter, "'", 1, 0, 0)) return 0; while (string.pointer != string.end) { if (IS_SPACE(string)) { if (allow_breaks && !spaces && emitter->column > emitter->best_width && string.pointer != string.start && string.pointer != string.end - 1 && !IS_SPACE_AT(string, 1)) { if (!yaml_emitter_write_indent(emitter)) return 0; MOVE(string); } else { if (!WRITE(emitter, string)) return 0; } spaces = 1; } else if (IS_BREAK(string)) { if (!breaks && CHECK(string, '\n')) { if (!PUT_BREAK(emitter)) return 0; } if (!WRITE_BREAK(emitter, string)) return 0; emitter->indention = 1; breaks = 1; } else { if (breaks) { if (!yaml_emitter_write_indent(emitter)) return 0; } if (CHECK(string, '\'')) { if (!PUT(emitter, '\'')) return 0; } if (!WRITE(emitter, string)) return 0; emitter->indention = 0; spaces = 0; breaks = 0; } } if (breaks) if (!yaml_emitter_write_indent(emitter)) return 0; if (!yaml_emitter_write_indicator(emitter, "'", 0, 0, 0)) return 0; emitter->whitespace = 0; emitter->indention = 0; return 1; } static int yaml_emitter_write_double_quoted_scalar(yaml_emitter_t *emitter, yaml_char_t *value, size_t length, int allow_breaks) { yaml_string_t string; int spaces = 0; STRING_ASSIGN(string, value, length); if (!yaml_emitter_write_indicator(emitter, "\"", 1, 0, 0)) return 0; while (string.pointer != string.end) { if (!IS_PRINTABLE(string) || (!emitter->unicode && !IS_ASCII(string)) || IS_BOM(string) || IS_BREAK(string) || CHECK(string, '"') || CHECK(string, '\\')) { unsigned char octet; unsigned int width; unsigned int value; int k; octet = string.pointer[0]; width = (octet & 0x80) == 0x00 ? 1 : (octet & 0xE0) == 0xC0 ? 2 : (octet & 0xF0) == 0xE0 ? 3 : (octet & 0xF8) == 0xF0 ? 4 : 0; value = (octet & 0x80) == 0x00 ? octet & 0x7F : (octet & 0xE0) == 0xC0 ? octet & 0x1F : (octet & 0xF0) == 0xE0 ? octet & 0x0F : (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; for (k = 1; k < (int)width; k ++) { octet = string.pointer[k]; value = (value << 6) + (octet & 0x3F); } string.pointer += width; if (!PUT(emitter, '\\')) return 0; switch (value) { case 0x00: if (!PUT(emitter, '0')) return 0; break; case 0x07: if (!PUT(emitter, 'a')) return 0; break; case 0x08: if (!PUT(emitter, 'b')) return 0; break; case 0x09: if (!PUT(emitter, 't')) return 0; break; case 0x0A: if (!PUT(emitter, 'n')) return 0; break; case 0x0B: if (!PUT(emitter, 'v')) return 0; break; case 0x0C: if (!PUT(emitter, 'f')) return 0; break; case 0x0D: if (!PUT(emitter, 'r')) return 0; break; case 0x1B: if (!PUT(emitter, 'e')) return 0; break; case 0x22: if (!PUT(emitter, '\"')) return 0; break; case 0x5C: if (!PUT(emitter, '\\')) return 0; break; case 0x85: if (!PUT(emitter, 'N')) return 0; break; case 0xA0: if (!PUT(emitter, '_')) return 0; break; case 0x2028: if (!PUT(emitter, 'L')) return 0; break; case 0x2029: if (!PUT(emitter, 'P')) return 0; break; default: if (value <= 0xFF) { if (!PUT(emitter, 'x')) return 0; width = 2; } else if (value <= 0xFFFF) { if (!PUT(emitter, 'u')) return 0; width = 4; } else { if (!PUT(emitter, 'U')) return 0; width = 8; } for (k = (width-1)*4; k >= 0; k -= 4) { int digit = (value >> k) & 0x0F; if (!PUT(emitter, digit + (digit < 10 ? '0' : 'A'-10))) return 0; } } spaces = 0; } else if (IS_SPACE(string)) { if (allow_breaks && !spaces && emitter->column > emitter->best_width && string.pointer != string.start && string.pointer != string.end - 1) { if (!yaml_emitter_write_indent(emitter)) return 0; if (IS_SPACE_AT(string, 1)) { if (!PUT(emitter, '\\')) return 0; } MOVE(string); } else { if (!WRITE(emitter, string)) return 0; } spaces = 1; } else { if (!WRITE(emitter, string)) return 0; spaces = 0; } } if (!yaml_emitter_write_indicator(emitter, "\"", 0, 0, 0)) return 0; emitter->whitespace = 0; emitter->indention = 0; return 1; } static int yaml_emitter_write_block_scalar_hints(yaml_emitter_t *emitter, yaml_string_t string) { char indent_hint[2]; const char *chomp_hint = NULL; if (IS_SPACE(string) || IS_BREAK(string)) { indent_hint[0] = '0' + (char)emitter->best_indent; indent_hint[1] = '\0'; if (!yaml_emitter_write_indicator(emitter, indent_hint, 0, 0, 0)) return 0; } emitter->open_ended = 0; string.pointer = string.end; if (string.start == string.pointer) { chomp_hint = "-"; } else { do { string.pointer --; } while ((*string.pointer & 0xC0) == 0x80); if (!IS_BREAK(string)) { chomp_hint = "-"; } else if (string.start == string.pointer) { chomp_hint = "+"; emitter->open_ended = 1; } else { do { string.pointer --; } while ((*string.pointer & 0xC0) == 0x80); if (IS_BREAK(string)) { chomp_hint = "+"; emitter->open_ended = 1; } } } if (chomp_hint) { if (!yaml_emitter_write_indicator(emitter, chomp_hint, 0, 0, 0)) return 0; } return 1; } static int yaml_emitter_write_literal_scalar(yaml_emitter_t *emitter, yaml_char_t *value, size_t length) { yaml_string_t string; int breaks = 1; STRING_ASSIGN(string, value, length); if (!yaml_emitter_write_indicator(emitter, "|", 1, 0, 0)) return 0; if (!yaml_emitter_write_block_scalar_hints(emitter, string)) return 0; if (!PUT_BREAK(emitter)) return 0; emitter->indention = 1; emitter->whitespace = 1; while (string.pointer != string.end) { if (IS_BREAK(string)) { if (!WRITE_BREAK(emitter, string)) return 0; emitter->indention = 1; breaks = 1; } else { if (breaks) { if (!yaml_emitter_write_indent(emitter)) return 0; } if (!WRITE(emitter, string)) return 0; emitter->indention = 0; breaks = 0; } } return 1; } static int yaml_emitter_write_folded_scalar(yaml_emitter_t *emitter, yaml_char_t *value, size_t length) { yaml_string_t string; int breaks = 1; int leading_spaces = 1; STRING_ASSIGN(string, value, length); if (!yaml_emitter_write_indicator(emitter, ">", 1, 0, 0)) return 0; if (!yaml_emitter_write_block_scalar_hints(emitter, string)) return 0; if (!PUT_BREAK(emitter)) return 0; emitter->indention = 1; emitter->whitespace = 1; while (string.pointer != string.end) { if (IS_BREAK(string)) { if (!breaks && !leading_spaces && CHECK(string, '\n')) { int k = 0; while (IS_BREAK_AT(string, k)) { k += WIDTH_AT(string, k); } if (!IS_BLANKZ_AT(string, k)) { if (!PUT_BREAK(emitter)) return 0; } } if (!WRITE_BREAK(emitter, string)) return 0; emitter->indention = 1; breaks = 1; } else { if (breaks) { if (!yaml_emitter_write_indent(emitter)) return 0; leading_spaces = IS_BLANK(string); } if (!breaks && IS_SPACE(string) && !IS_SPACE_AT(string, 1) && emitter->column > emitter->best_width) { if (!yaml_emitter_write_indent(emitter)) return 0; MOVE(string); } else { if (!WRITE(emitter, string)) return 0; } emitter->indention = 0; breaks = 0; } } return 1; } yaml/src/r_ext.h0000644000176200001440000000263013614635636013313 0ustar liggesusers#ifndef _R_EXT_H #define _R_EXT_H #ifdef __FAST_MATH__ #error The R yaml package will not work correctly with the -ffast-math option of GCC. #endif #include #include #include #include #include #include "R.h" #include "Rdefines.h" #include "R_ext/Rdynload.h" #include "R_ext/Parse.h" #include "R_ext/Print.h" #include "R_ext/PrtUtil.h" #include "yaml.h" #define REAL_BUF_SIZE 256 #define ERROR_MSG_SIZE 512 /* From implicit.c */ char *Ryaml_find_implicit_tag(const char *value, size_t size); /* Common functions */ int Ryaml_is_named_list(SEXP s_obj); SEXP Ryaml_collapse(SEXP s_obj, char *collapse); SEXP Ryaml_inspect(SEXP s_obj); SEXP Ryaml_get_classes(SEXP s_obj); int Ryaml_has_class(SEXP s_obj, char *name); void Ryaml_set_error_msg(const char *format, ...); SEXP Ryaml_sanitize_handlers(SEXP s_handlers); SEXP Ryaml_find_handler(SEXP s_handlers, const char *name); int Ryaml_run_handler(SEXP s_handler, SEXP s_arg, SEXP *s_result); /* Exported functions */ SEXP Ryaml_serialize_to_yaml(SEXP s_obj, SEXP s_line_sep, SEXP s_indent, SEXP s_omap, SEXP s_column_major, SEXP s_unicode, SEXP s_precision, SEXP s_indent_mapping_sequence, SEXP s_handlers); SEXP Ryaml_unserialize_from_yaml(SEXP s_string, SEXP s_as_named_list, SEXP s_handlers, SEXP s_error_label, SEXP s_eval_expr, SEXP s_eval_warning, SEXP s_merge_precedence, SEXP s_merge_warning); #endif yaml/src/scanner.c0000644000176200001440000030113413614635636013617 0ustar liggesusers /* * Introduction * ************ * * The following notes assume that you are familiar with the YAML specification * (http://yaml.org/spec/cvs/current.html). We mostly follow it, although in * some cases we are less restrictive that it requires. * * The process of transforming a YAML stream into a sequence of events is * divided on two steps: Scanning and Parsing. * * The Scanner transforms the input stream into a sequence of tokens, while the * parser transform the sequence of tokens produced by the Scanner into a * sequence of parsing events. * * The Scanner is rather clever and complicated. The Parser, on the contrary, * is a straightforward implementation of a recursive-descendant parser (or, * LL(1) parser, as it is usually called). * * Actually there are two issues of Scanning that might be called "clever", the * rest is quite straightforward. The issues are "block collection start" and * "simple keys". Both issues are explained below in details. * * Here the Scanning step is explained and implemented. We start with the list * of all the tokens produced by the Scanner together with short descriptions. * * Now, tokens: * * STREAM-START(encoding) # The stream start. * STREAM-END # The stream end. * VERSION-DIRECTIVE(major,minor) # The '%YAML' directive. * TAG-DIRECTIVE(handle,prefix) # The '%TAG' directive. * DOCUMENT-START # '---' * DOCUMENT-END # '...' * BLOCK-SEQUENCE-START # Indentation increase denoting a block * BLOCK-MAPPING-START # sequence or a block mapping. * BLOCK-END # Indentation decrease. * FLOW-SEQUENCE-START # '[' * FLOW-SEQUENCE-END # ']' * FLOW-MAPPING-START # '{' * FLOW-MAPPING-END # '}' * BLOCK-ENTRY # '-' * FLOW-ENTRY # ',' * KEY # '?' or nothing (simple keys). * VALUE # ':' * ALIAS(anchor) # '*anchor' * ANCHOR(anchor) # '&anchor' * TAG(handle,suffix) # '!handle!suffix' * SCALAR(value,style) # A scalar. * * The following two tokens are "virtual" tokens denoting the beginning and the * end of the stream: * * STREAM-START(encoding) * STREAM-END * * We pass the information about the input stream encoding with the * STREAM-START token. * * The next two tokens are responsible for tags: * * VERSION-DIRECTIVE(major,minor) * TAG-DIRECTIVE(handle,prefix) * * Example: * * %YAML 1.1 * %TAG ! !foo * %TAG !yaml! tag:yaml.org,2002: * --- * * The corresponding sequence of tokens: * * STREAM-START(utf-8) * VERSION-DIRECTIVE(1,1) * TAG-DIRECTIVE("!","!foo") * TAG-DIRECTIVE("!yaml","tag:yaml.org,2002:") * DOCUMENT-START * STREAM-END * * Note that the VERSION-DIRECTIVE and TAG-DIRECTIVE tokens occupy a whole * line. * * The document start and end indicators are represented by: * * DOCUMENT-START * DOCUMENT-END * * Note that if a YAML stream contains an implicit document (without '---' * and '...' indicators), no DOCUMENT-START and DOCUMENT-END tokens will be * produced. * * In the following examples, we present whole documents together with the * produced tokens. * * 1. An implicit document: * * 'a scalar' * * Tokens: * * STREAM-START(utf-8) * SCALAR("a scalar",single-quoted) * STREAM-END * * 2. An explicit document: * * --- * 'a scalar' * ... * * Tokens: * * STREAM-START(utf-8) * DOCUMENT-START * SCALAR("a scalar",single-quoted) * DOCUMENT-END * STREAM-END * * 3. Several documents in a stream: * * 'a scalar' * --- * 'another scalar' * --- * 'yet another scalar' * * Tokens: * * STREAM-START(utf-8) * SCALAR("a scalar",single-quoted) * DOCUMENT-START * SCALAR("another scalar",single-quoted) * DOCUMENT-START * SCALAR("yet another scalar",single-quoted) * STREAM-END * * We have already introduced the SCALAR token above. The following tokens are * used to describe aliases, anchors, tag, and scalars: * * ALIAS(anchor) * ANCHOR(anchor) * TAG(handle,suffix) * SCALAR(value,style) * * The following series of examples illustrate the usage of these tokens: * * 1. A recursive sequence: * * &A [ *A ] * * Tokens: * * STREAM-START(utf-8) * ANCHOR("A") * FLOW-SEQUENCE-START * ALIAS("A") * FLOW-SEQUENCE-END * STREAM-END * * 2. A tagged scalar: * * !!float "3.14" # A good approximation. * * Tokens: * * STREAM-START(utf-8) * TAG("!!","float") * SCALAR("3.14",double-quoted) * STREAM-END * * 3. Various scalar styles: * * --- # Implicit empty plain scalars do not produce tokens. * --- a plain scalar * --- 'a single-quoted scalar' * --- "a double-quoted scalar" * --- |- * a literal scalar * --- >- * a folded * scalar * * Tokens: * * STREAM-START(utf-8) * DOCUMENT-START * DOCUMENT-START * SCALAR("a plain scalar",plain) * DOCUMENT-START * SCALAR("a single-quoted scalar",single-quoted) * DOCUMENT-START * SCALAR("a double-quoted scalar",double-quoted) * DOCUMENT-START * SCALAR("a literal scalar",literal) * DOCUMENT-START * SCALAR("a folded scalar",folded) * STREAM-END * * Now it's time to review collection-related tokens. We will start with * flow collections: * * FLOW-SEQUENCE-START * FLOW-SEQUENCE-END * FLOW-MAPPING-START * FLOW-MAPPING-END * FLOW-ENTRY * KEY * VALUE * * The tokens FLOW-SEQUENCE-START, FLOW-SEQUENCE-END, FLOW-MAPPING-START, and * FLOW-MAPPING-END represent the indicators '[', ']', '{', and '}' * correspondingly. FLOW-ENTRY represent the ',' indicator. Finally the * indicators '?' and ':', which are used for denoting mapping keys and values, * are represented by the KEY and VALUE tokens. * * The following examples show flow collections: * * 1. A flow sequence: * * [item 1, item 2, item 3] * * Tokens: * * STREAM-START(utf-8) * FLOW-SEQUENCE-START * SCALAR("item 1",plain) * FLOW-ENTRY * SCALAR("item 2",plain) * FLOW-ENTRY * SCALAR("item 3",plain) * FLOW-SEQUENCE-END * STREAM-END * * 2. A flow mapping: * * { * a simple key: a value, # Note that the KEY token is produced. * ? a complex key: another value, * } * * Tokens: * * STREAM-START(utf-8) * FLOW-MAPPING-START * KEY * SCALAR("a simple key",plain) * VALUE * SCALAR("a value",plain) * FLOW-ENTRY * KEY * SCALAR("a complex key",plain) * VALUE * SCALAR("another value",plain) * FLOW-ENTRY * FLOW-MAPPING-END * STREAM-END * * A simple key is a key which is not denoted by the '?' indicator. Note that * the Scanner still produce the KEY token whenever it encounters a simple key. * * For scanning block collections, the following tokens are used (note that we * repeat KEY and VALUE here): * * BLOCK-SEQUENCE-START * BLOCK-MAPPING-START * BLOCK-END * BLOCK-ENTRY * KEY * VALUE * * The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation * increase that precedes a block collection (cf. the INDENT token in Python). * The token BLOCK-END denote indentation decrease that ends a block collection * (cf. the DEDENT token in Python). However YAML has some syntax pecularities * that makes detections of these tokens more complex. * * The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators * '-', '?', and ':' correspondingly. * * The following examples show how the tokens BLOCK-SEQUENCE-START, * BLOCK-MAPPING-START, and BLOCK-END are emitted by the Scanner: * * 1. Block sequences: * * - item 1 * - item 2 * - * - item 3.1 * - item 3.2 * - * key 1: value 1 * key 2: value 2 * * Tokens: * * STREAM-START(utf-8) * BLOCK-SEQUENCE-START * BLOCK-ENTRY * SCALAR("item 1",plain) * BLOCK-ENTRY * SCALAR("item 2",plain) * BLOCK-ENTRY * BLOCK-SEQUENCE-START * BLOCK-ENTRY * SCALAR("item 3.1",plain) * BLOCK-ENTRY * SCALAR("item 3.2",plain) * BLOCK-END * BLOCK-ENTRY * BLOCK-MAPPING-START * KEY * SCALAR("key 1",plain) * VALUE * SCALAR("value 1",plain) * KEY * SCALAR("key 2",plain) * VALUE * SCALAR("value 2",plain) * BLOCK-END * BLOCK-END * STREAM-END * * 2. Block mappings: * * a simple key: a value # The KEY token is produced here. * ? a complex key * : another value * a mapping: * key 1: value 1 * key 2: value 2 * a sequence: * - item 1 * - item 2 * * Tokens: * * STREAM-START(utf-8) * BLOCK-MAPPING-START * KEY * SCALAR("a simple key",plain) * VALUE * SCALAR("a value",plain) * KEY * SCALAR("a complex key",plain) * VALUE * SCALAR("another value",plain) * KEY * SCALAR("a mapping",plain) * BLOCK-MAPPING-START * KEY * SCALAR("key 1",plain) * VALUE * SCALAR("value 1",plain) * KEY * SCALAR("key 2",plain) * VALUE * SCALAR("value 2",plain) * BLOCK-END * KEY * SCALAR("a sequence",plain) * VALUE * BLOCK-SEQUENCE-START * BLOCK-ENTRY * SCALAR("item 1",plain) * BLOCK-ENTRY * SCALAR("item 2",plain) * BLOCK-END * BLOCK-END * STREAM-END * * YAML does not always require to start a new block collection from a new * line. If the current line contains only '-', '?', and ':' indicators, a new * block collection may start at the current line. The following examples * illustrate this case: * * 1. Collections in a sequence: * * - - item 1 * - item 2 * - key 1: value 1 * key 2: value 2 * - ? complex key * : complex value * * Tokens: * * STREAM-START(utf-8) * BLOCK-SEQUENCE-START * BLOCK-ENTRY * BLOCK-SEQUENCE-START * BLOCK-ENTRY * SCALAR("item 1",plain) * BLOCK-ENTRY * SCALAR("item 2",plain) * BLOCK-END * BLOCK-ENTRY * BLOCK-MAPPING-START * KEY * SCALAR("key 1",plain) * VALUE * SCALAR("value 1",plain) * KEY * SCALAR("key 2",plain) * VALUE * SCALAR("value 2",plain) * BLOCK-END * BLOCK-ENTRY * BLOCK-MAPPING-START * KEY * SCALAR("complex key") * VALUE * SCALAR("complex value") * BLOCK-END * BLOCK-END * STREAM-END * * 2. Collections in a mapping: * * ? a sequence * : - item 1 * - item 2 * ? a mapping * : key 1: value 1 * key 2: value 2 * * Tokens: * * STREAM-START(utf-8) * BLOCK-MAPPING-START * KEY * SCALAR("a sequence",plain) * VALUE * BLOCK-SEQUENCE-START * BLOCK-ENTRY * SCALAR("item 1",plain) * BLOCK-ENTRY * SCALAR("item 2",plain) * BLOCK-END * KEY * SCALAR("a mapping",plain) * VALUE * BLOCK-MAPPING-START * KEY * SCALAR("key 1",plain) * VALUE * SCALAR("value 1",plain) * KEY * SCALAR("key 2",plain) * VALUE * SCALAR("value 2",plain) * BLOCK-END * BLOCK-END * STREAM-END * * YAML also permits non-indented sequences if they are included into a block * mapping. In this case, the token BLOCK-SEQUENCE-START is not produced: * * key: * - item 1 # BLOCK-SEQUENCE-START is NOT produced here. * - item 2 * * Tokens: * * STREAM-START(utf-8) * BLOCK-MAPPING-START * KEY * SCALAR("key",plain) * VALUE * BLOCK-ENTRY * SCALAR("item 1",plain) * BLOCK-ENTRY * SCALAR("item 2",plain) * BLOCK-END */ #include "yaml_private.h" /* * Ensure that the buffer contains the required number of characters. * Return 1 on success, 0 on failure (reader error or memory error). */ #define CACHE(parser,length) \ (parser->unread >= (length) \ ? 1 \ : yaml_parser_update_buffer(parser, (length))) /* * Advance the buffer pointer. */ #define SKIP(parser) \ (parser->mark.index ++, \ parser->mark.column ++, \ parser->unread --, \ parser->buffer.pointer += WIDTH(parser->buffer)) #define SKIP_LINE(parser) \ (IS_CRLF(parser->buffer) ? \ (parser->mark.index += 2, \ parser->mark.column = 0, \ parser->mark.line ++, \ parser->unread -= 2, \ parser->buffer.pointer += 2) : \ IS_BREAK(parser->buffer) ? \ (parser->mark.index ++, \ parser->mark.column = 0, \ parser->mark.line ++, \ parser->unread --, \ parser->buffer.pointer += WIDTH(parser->buffer)) : 0) /* * Copy a character to a string buffer and advance pointers. */ #define READ(parser,string) \ (STRING_EXTEND(parser,string) ? \ (COPY(string,parser->buffer), \ parser->mark.index ++, \ parser->mark.column ++, \ parser->unread --, \ 1) : 0) /* * Copy a line break character to a string buffer and advance pointers. */ #define READ_LINE(parser,string) \ (STRING_EXTEND(parser,string) ? \ (((CHECK_AT(parser->buffer,'\r',0) \ && CHECK_AT(parser->buffer,'\n',1)) ? /* CR LF -> LF */ \ (*((string).pointer++) = (yaml_char_t) '\n', \ parser->buffer.pointer += 2, \ parser->mark.index += 2, \ parser->mark.column = 0, \ parser->mark.line ++, \ parser->unread -= 2) : \ (CHECK_AT(parser->buffer,'\r',0) \ || CHECK_AT(parser->buffer,'\n',0)) ? /* CR|LF -> LF */ \ (*((string).pointer++) = (yaml_char_t) '\n', \ parser->buffer.pointer ++, \ parser->mark.index ++, \ parser->mark.column = 0, \ parser->mark.line ++, \ parser->unread --) : \ (CHECK_AT(parser->buffer,'\xC2',0) \ && CHECK_AT(parser->buffer,'\x85',1)) ? /* NEL -> LF */ \ (*((string).pointer++) = (yaml_char_t) '\n', \ parser->buffer.pointer += 2, \ parser->mark.index ++, \ parser->mark.column = 0, \ parser->mark.line ++, \ parser->unread --) : \ (CHECK_AT(parser->buffer,'\xE2',0) && \ CHECK_AT(parser->buffer,'\x80',1) && \ (CHECK_AT(parser->buffer,'\xA8',2) || \ CHECK_AT(parser->buffer,'\xA9',2))) ? /* LS|PS -> LS|PS */ \ (*((string).pointer++) = *(parser->buffer.pointer++), \ *((string).pointer++) = *(parser->buffer.pointer++), \ *((string).pointer++) = *(parser->buffer.pointer++), \ parser->mark.index ++, \ parser->mark.column = 0, \ parser->mark.line ++, \ parser->unread --) : 0), \ 1) : 0) /* * Public API declarations. */ YAML_DECLARE(int) yaml_parser_scan(yaml_parser_t *parser, yaml_token_t *token); /* * Error handling. */ static int yaml_parser_set_scanner_error(yaml_parser_t *parser, const char *context, yaml_mark_t context_mark, const char *problem); /* * High-level token API. */ YAML_DECLARE(int) yaml_parser_fetch_more_tokens(yaml_parser_t *parser); static int yaml_parser_fetch_next_token(yaml_parser_t *parser); /* * Potential simple keys. */ static int yaml_parser_stale_simple_keys(yaml_parser_t *parser); static int yaml_parser_save_simple_key(yaml_parser_t *parser); static int yaml_parser_remove_simple_key(yaml_parser_t *parser); static int yaml_parser_increase_flow_level(yaml_parser_t *parser); static int yaml_parser_decrease_flow_level(yaml_parser_t *parser); /* * Indentation treatment. */ static int yaml_parser_roll_indent(yaml_parser_t *parser, ptrdiff_t column, ptrdiff_t number, yaml_token_type_t type, yaml_mark_t mark); static int yaml_parser_unroll_indent(yaml_parser_t *parser, ptrdiff_t column); /* * Token fetchers. */ static int yaml_parser_fetch_stream_start(yaml_parser_t *parser); static int yaml_parser_fetch_stream_end(yaml_parser_t *parser); static int yaml_parser_fetch_directive(yaml_parser_t *parser); static int yaml_parser_fetch_document_indicator(yaml_parser_t *parser, yaml_token_type_t type); static int yaml_parser_fetch_flow_collection_start(yaml_parser_t *parser, yaml_token_type_t type); static int yaml_parser_fetch_flow_collection_end(yaml_parser_t *parser, yaml_token_type_t type); static int yaml_parser_fetch_flow_entry(yaml_parser_t *parser); static int yaml_parser_fetch_block_entry(yaml_parser_t *parser); static int yaml_parser_fetch_key(yaml_parser_t *parser); static int yaml_parser_fetch_value(yaml_parser_t *parser); static int yaml_parser_fetch_anchor(yaml_parser_t *parser, yaml_token_type_t type); static int yaml_parser_fetch_tag(yaml_parser_t *parser); static int yaml_parser_fetch_block_scalar(yaml_parser_t *parser, int literal); static int yaml_parser_fetch_flow_scalar(yaml_parser_t *parser, int single); static int yaml_parser_fetch_plain_scalar(yaml_parser_t *parser); /* * Token scanners. */ static int yaml_parser_scan_to_next_token(yaml_parser_t *parser); static int yaml_parser_scan_directive(yaml_parser_t *parser, yaml_token_t *token); static int yaml_parser_scan_directive_name(yaml_parser_t *parser, yaml_mark_t start_mark, yaml_char_t **name); static int yaml_parser_scan_version_directive_value(yaml_parser_t *parser, yaml_mark_t start_mark, int *major, int *minor); static int yaml_parser_scan_version_directive_number(yaml_parser_t *parser, yaml_mark_t start_mark, int *number); static int yaml_parser_scan_tag_directive_value(yaml_parser_t *parser, yaml_mark_t mark, yaml_char_t **handle, yaml_char_t **prefix); static int yaml_parser_scan_anchor(yaml_parser_t *parser, yaml_token_t *token, yaml_token_type_t type); static int yaml_parser_scan_tag(yaml_parser_t *parser, yaml_token_t *token); static int yaml_parser_scan_tag_handle(yaml_parser_t *parser, int directive, yaml_mark_t start_mark, yaml_char_t **handle); static int yaml_parser_scan_tag_uri(yaml_parser_t *parser, int directive, yaml_char_t *head, yaml_mark_t start_mark, yaml_char_t **uri); static int yaml_parser_scan_uri_escapes(yaml_parser_t *parser, int directive, yaml_mark_t start_mark, yaml_string_t *string); static int yaml_parser_scan_block_scalar(yaml_parser_t *parser, yaml_token_t *token, int literal); static int yaml_parser_scan_block_scalar_breaks(yaml_parser_t *parser, int *indent, yaml_string_t *breaks, yaml_mark_t start_mark, yaml_mark_t *end_mark); static int yaml_parser_scan_flow_scalar(yaml_parser_t *parser, yaml_token_t *token, int single); static int yaml_parser_scan_plain_scalar(yaml_parser_t *parser, yaml_token_t *token); /* * Get the next token. */ YAML_DECLARE(int) yaml_parser_scan(yaml_parser_t *parser, yaml_token_t *token) { assert(parser); /* Non-NULL parser object is expected. */ assert(token); /* Non-NULL token object is expected. */ /* Erase the token object. */ memset(token, 0, sizeof(yaml_token_t)); /* No tokens after STREAM-END or error. */ if (parser->stream_end_produced || parser->error) { return 1; } /* Ensure that the tokens queue contains enough tokens. */ if (!parser->token_available) { if (!yaml_parser_fetch_more_tokens(parser)) return 0; } /* Fetch the next token from the queue. */ *token = DEQUEUE(parser, parser->tokens); parser->token_available = 0; parser->tokens_parsed ++; if (token->type == YAML_STREAM_END_TOKEN) { parser->stream_end_produced = 1; } return 1; } /* * Set the scanner error and return 0. */ static int yaml_parser_set_scanner_error(yaml_parser_t *parser, const char *context, yaml_mark_t context_mark, const char *problem) { parser->error = YAML_SCANNER_ERROR; parser->context = context; parser->context_mark = context_mark; parser->problem = problem; parser->problem_mark = parser->mark; return 0; } /* * Ensure that the tokens queue contains at least one token which can be * returned to the Parser. */ YAML_DECLARE(int) yaml_parser_fetch_more_tokens(yaml_parser_t *parser) { int need_more_tokens; /* While we need more tokens to fetch, do it. */ while (1) { /* * Check if we really need to fetch more tokens. */ need_more_tokens = 0; if (parser->tokens.head == parser->tokens.tail) { /* Queue is empty. */ need_more_tokens = 1; } else { yaml_simple_key_t *simple_key; /* Check if any potential simple key may occupy the head position. */ if (!yaml_parser_stale_simple_keys(parser)) return 0; for (simple_key = parser->simple_keys.start; simple_key != parser->simple_keys.top; simple_key++) { if (simple_key->possible && simple_key->token_number == parser->tokens_parsed) { need_more_tokens = 1; break; } } } /* We are finished. */ if (!need_more_tokens) break; /* Fetch the next token. */ if (!yaml_parser_fetch_next_token(parser)) return 0; } parser->token_available = 1; return 1; } /* * The dispatcher for token fetchers. */ static int yaml_parser_fetch_next_token(yaml_parser_t *parser) { /* Ensure that the buffer is initialized. */ if (!CACHE(parser, 1)) return 0; /* Check if we just started scanning. Fetch STREAM-START then. */ if (!parser->stream_start_produced) return yaml_parser_fetch_stream_start(parser); /* Eat whitespaces and comments until we reach the next token. */ if (!yaml_parser_scan_to_next_token(parser)) return 0; /* Remove obsolete potential simple keys. */ if (!yaml_parser_stale_simple_keys(parser)) return 0; /* Check the indentation level against the current column. */ if (!yaml_parser_unroll_indent(parser, parser->mark.column)) return 0; /* * Ensure that the buffer contains at least 4 characters. 4 is the length * of the longest indicators ('--- ' and '... '). */ if (!CACHE(parser, 4)) return 0; /* Is it the end of the stream? */ if (IS_Z(parser->buffer)) return yaml_parser_fetch_stream_end(parser); /* Is it a directive? */ if (parser->mark.column == 0 && CHECK(parser->buffer, '%')) return yaml_parser_fetch_directive(parser); /* Is it the document start indicator? */ if (parser->mark.column == 0 && CHECK_AT(parser->buffer, '-', 0) && CHECK_AT(parser->buffer, '-', 1) && CHECK_AT(parser->buffer, '-', 2) && IS_BLANKZ_AT(parser->buffer, 3)) return yaml_parser_fetch_document_indicator(parser, YAML_DOCUMENT_START_TOKEN); /* Is it the document end indicator? */ if (parser->mark.column == 0 && CHECK_AT(parser->buffer, '.', 0) && CHECK_AT(parser->buffer, '.', 1) && CHECK_AT(parser->buffer, '.', 2) && IS_BLANKZ_AT(parser->buffer, 3)) return yaml_parser_fetch_document_indicator(parser, YAML_DOCUMENT_END_TOKEN); /* Is it the flow sequence start indicator? */ if (CHECK(parser->buffer, '[')) return yaml_parser_fetch_flow_collection_start(parser, YAML_FLOW_SEQUENCE_START_TOKEN); /* Is it the flow mapping start indicator? */ if (CHECK(parser->buffer, '{')) return yaml_parser_fetch_flow_collection_start(parser, YAML_FLOW_MAPPING_START_TOKEN); /* Is it the flow sequence end indicator? */ if (CHECK(parser->buffer, ']')) return yaml_parser_fetch_flow_collection_end(parser, YAML_FLOW_SEQUENCE_END_TOKEN); /* Is it the flow mapping end indicator? */ if (CHECK(parser->buffer, '}')) return yaml_parser_fetch_flow_collection_end(parser, YAML_FLOW_MAPPING_END_TOKEN); /* Is it the flow entry indicator? */ if (CHECK(parser->buffer, ',')) return yaml_parser_fetch_flow_entry(parser); /* Is it the block entry indicator? */ if (CHECK(parser->buffer, '-') && IS_BLANKZ_AT(parser->buffer, 1)) return yaml_parser_fetch_block_entry(parser); /* Is it the key indicator? */ if (CHECK(parser->buffer, '?') && (parser->flow_level || IS_BLANKZ_AT(parser->buffer, 1))) return yaml_parser_fetch_key(parser); /* Is it the value indicator? */ if (CHECK(parser->buffer, ':') && (parser->flow_level || IS_BLANKZ_AT(parser->buffer, 1))) return yaml_parser_fetch_value(parser); /* Is it an alias? */ if (CHECK(parser->buffer, '*')) return yaml_parser_fetch_anchor(parser, YAML_ALIAS_TOKEN); /* Is it an anchor? */ if (CHECK(parser->buffer, '&')) return yaml_parser_fetch_anchor(parser, YAML_ANCHOR_TOKEN); /* Is it a tag? */ if (CHECK(parser->buffer, '!')) return yaml_parser_fetch_tag(parser); /* Is it a literal scalar? */ if (CHECK(parser->buffer, '|') && !parser->flow_level) return yaml_parser_fetch_block_scalar(parser, 1); /* Is it a folded scalar? */ if (CHECK(parser->buffer, '>') && !parser->flow_level) return yaml_parser_fetch_block_scalar(parser, 0); /* Is it a single-quoted scalar? */ if (CHECK(parser->buffer, '\'')) return yaml_parser_fetch_flow_scalar(parser, 1); /* Is it a double-quoted scalar? */ if (CHECK(parser->buffer, '"')) return yaml_parser_fetch_flow_scalar(parser, 0); /* * Is it a plain scalar? * * A plain scalar may start with any non-blank characters except * * '-', '?', ':', ',', '[', ']', '{', '}', * '#', '&', '*', '!', '|', '>', '\'', '\"', * '%', '@', '`'. * * In the block context (and, for the '-' indicator, in the flow context * too), it may also start with the characters * * '-', '?', ':' * * if it is followed by a non-space character. * * The last rule is more restrictive than the specification requires. */ if (!(IS_BLANKZ(parser->buffer) || CHECK(parser->buffer, '-') || CHECK(parser->buffer, '?') || CHECK(parser->buffer, ':') || CHECK(parser->buffer, ',') || CHECK(parser->buffer, '[') || CHECK(parser->buffer, ']') || CHECK(parser->buffer, '{') || CHECK(parser->buffer, '}') || CHECK(parser->buffer, '#') || CHECK(parser->buffer, '&') || CHECK(parser->buffer, '*') || CHECK(parser->buffer, '!') || CHECK(parser->buffer, '|') || CHECK(parser->buffer, '>') || CHECK(parser->buffer, '\'') || CHECK(parser->buffer, '"') || CHECK(parser->buffer, '%') || CHECK(parser->buffer, '@') || CHECK(parser->buffer, '`')) || (CHECK(parser->buffer, '-') && !IS_BLANK_AT(parser->buffer, 1)) || (!parser->flow_level && (CHECK(parser->buffer, '?') || CHECK(parser->buffer, ':')) && !IS_BLANKZ_AT(parser->buffer, 1))) return yaml_parser_fetch_plain_scalar(parser); /* * If we don't determine the token type so far, it is an error. */ return yaml_parser_set_scanner_error(parser, "while scanning for the next token", parser->mark, "found character that cannot start any token"); } /* * Check the list of potential simple keys and remove the positions that * cannot contain simple keys anymore. */ static int yaml_parser_stale_simple_keys(yaml_parser_t *parser) { yaml_simple_key_t *simple_key; /* Check for a potential simple key for each flow level. */ for (simple_key = parser->simple_keys.start; simple_key != parser->simple_keys.top; simple_key ++) { /* * The specification requires that a simple key * * - is limited to a single line, * - is shorter than 1024 characters. */ if (simple_key->possible && (simple_key->mark.line < parser->mark.line || simple_key->mark.index+1024 < parser->mark.index)) { /* Check if the potential simple key to be removed is required. */ if (simple_key->required) { return yaml_parser_set_scanner_error(parser, "while scanning a simple key", simple_key->mark, "could not find expected ':'"); } simple_key->possible = 0; } } return 1; } /* * Check if a simple key may start at the current position and add it if * needed. */ static int yaml_parser_save_simple_key(yaml_parser_t *parser) { /* * A simple key is required at the current position if the scanner is in * the block context and the current column coincides with the indentation * level. */ int required = (!parser->flow_level && parser->indent == (ptrdiff_t)parser->mark.column); /* * If the current position may start a simple key, save it. */ if (parser->simple_key_allowed) { yaml_simple_key_t simple_key; simple_key.possible = 1; simple_key.required = required; simple_key.token_number = parser->tokens_parsed + (parser->tokens.tail - parser->tokens.head); simple_key.mark = parser->mark; if (!yaml_parser_remove_simple_key(parser)) return 0; *(parser->simple_keys.top-1) = simple_key; } return 1; } /* * Remove a potential simple key at the current flow level. */ static int yaml_parser_remove_simple_key(yaml_parser_t *parser) { yaml_simple_key_t *simple_key = parser->simple_keys.top-1; if (simple_key->possible) { /* If the key is required, it is an error. */ if (simple_key->required) { return yaml_parser_set_scanner_error(parser, "while scanning a simple key", simple_key->mark, "could not find expected ':'"); } } /* Remove the key from the stack. */ simple_key->possible = 0; return 1; } /* * Increase the flow level and resize the simple key list if needed. */ static int yaml_parser_increase_flow_level(yaml_parser_t *parser) { yaml_simple_key_t empty_simple_key = { 0, 0, 0, { 0, 0, 0 } }; /* Reset the simple key on the next level. */ if (!PUSH(parser, parser->simple_keys, empty_simple_key)) return 0; /* Increase the flow level. */ if (parser->flow_level == INT_MAX) { parser->error = YAML_MEMORY_ERROR; return 0; } parser->flow_level++; return 1; } /* * Decrease the flow level. */ static int yaml_parser_decrease_flow_level(yaml_parser_t *parser) { if (parser->flow_level) { parser->flow_level --; (void)POP(parser, parser->simple_keys); } return 1; } /* * Push the current indentation level to the stack and set the new level * the current column is greater than the indentation level. In this case, * append or insert the specified token into the token queue. * */ static int yaml_parser_roll_indent(yaml_parser_t *parser, ptrdiff_t column, ptrdiff_t number, yaml_token_type_t type, yaml_mark_t mark) { yaml_token_t token; /* In the flow context, do nothing. */ if (parser->flow_level) return 1; if (parser->indent < column) { /* * Push the current indentation level to the stack and set the new * indentation level. */ if (!PUSH(parser, parser->indents, parser->indent)) return 0; if (column > INT_MAX) { parser->error = YAML_MEMORY_ERROR; return 0; } parser->indent = column; /* Create a token and insert it into the queue. */ TOKEN_INIT(token, type, mark, mark); if (number == -1) { if (!ENQUEUE(parser, parser->tokens, token)) return 0; } else { if (!QUEUE_INSERT(parser, parser->tokens, number - parser->tokens_parsed, token)) return 0; } } return 1; } /* * Pop indentation levels from the indents stack until the current level * becomes less or equal to the column. For each indentation level, append * the BLOCK-END token. */ static int yaml_parser_unroll_indent(yaml_parser_t *parser, ptrdiff_t column) { yaml_token_t token; /* In the flow context, do nothing. */ if (parser->flow_level) return 1; /* Loop through the indentation levels in the stack. */ while (parser->indent > column) { /* Create a token and append it to the queue. */ TOKEN_INIT(token, YAML_BLOCK_END_TOKEN, parser->mark, parser->mark); if (!ENQUEUE(parser, parser->tokens, token)) return 0; /* Pop the indentation level. */ parser->indent = POP(parser, parser->indents); } return 1; } /* * Initialize the scanner and produce the STREAM-START token. */ static int yaml_parser_fetch_stream_start(yaml_parser_t *parser) { yaml_simple_key_t simple_key = { 0, 0, 0, { 0, 0, 0 } }; yaml_token_t token; /* Set the initial indentation. */ parser->indent = -1; /* Initialize the simple key stack. */ if (!PUSH(parser, parser->simple_keys, simple_key)) return 0; /* A simple key is allowed at the beginning of the stream. */ parser->simple_key_allowed = 1; /* We have started. */ parser->stream_start_produced = 1; /* Create the STREAM-START token and append it to the queue. */ STREAM_START_TOKEN_INIT(token, parser->encoding, parser->mark, parser->mark); if (!ENQUEUE(parser, parser->tokens, token)) return 0; return 1; } /* * Produce the STREAM-END token and shut down the scanner. */ static int yaml_parser_fetch_stream_end(yaml_parser_t *parser) { yaml_token_t token; /* Force new line. */ if (parser->mark.column != 0) { parser->mark.column = 0; parser->mark.line ++; } /* Reset the indentation level. */ if (!yaml_parser_unroll_indent(parser, -1)) return 0; /* Reset simple keys. */ if (!yaml_parser_remove_simple_key(parser)) return 0; parser->simple_key_allowed = 0; /* Create the STREAM-END token and append it to the queue. */ STREAM_END_TOKEN_INIT(token, parser->mark, parser->mark); if (!ENQUEUE(parser, parser->tokens, token)) return 0; return 1; } /* * Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token. */ static int yaml_parser_fetch_directive(yaml_parser_t *parser) { yaml_token_t token; /* Reset the indentation level. */ if (!yaml_parser_unroll_indent(parser, -1)) return 0; /* Reset simple keys. */ if (!yaml_parser_remove_simple_key(parser)) return 0; parser->simple_key_allowed = 0; /* Create the YAML-DIRECTIVE or TAG-DIRECTIVE token. */ if (!yaml_parser_scan_directive(parser, &token)) return 0; /* Append the token to the queue. */ if (!ENQUEUE(parser, parser->tokens, token)) { yaml_token_delete(&token); return 0; } return 1; } /* * Produce the DOCUMENT-START or DOCUMENT-END token. */ static int yaml_parser_fetch_document_indicator(yaml_parser_t *parser, yaml_token_type_t type) { yaml_mark_t start_mark, end_mark; yaml_token_t token; /* Reset the indentation level. */ if (!yaml_parser_unroll_indent(parser, -1)) return 0; /* Reset simple keys. */ if (!yaml_parser_remove_simple_key(parser)) return 0; parser->simple_key_allowed = 0; /* Consume the token. */ start_mark = parser->mark; SKIP(parser); SKIP(parser); SKIP(parser); end_mark = parser->mark; /* Create the DOCUMENT-START or DOCUMENT-END token. */ TOKEN_INIT(token, type, start_mark, end_mark); /* Append the token to the queue. */ if (!ENQUEUE(parser, parser->tokens, token)) return 0; return 1; } /* * Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token. */ static int yaml_parser_fetch_flow_collection_start(yaml_parser_t *parser, yaml_token_type_t type) { yaml_mark_t start_mark, end_mark; yaml_token_t token; /* The indicators '[' and '{' may start a simple key. */ if (!yaml_parser_save_simple_key(parser)) return 0; /* Increase the flow level. */ if (!yaml_parser_increase_flow_level(parser)) return 0; /* A simple key may follow the indicators '[' and '{'. */ parser->simple_key_allowed = 1; /* Consume the token. */ start_mark = parser->mark; SKIP(parser); end_mark = parser->mark; /* Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token. */ TOKEN_INIT(token, type, start_mark, end_mark); /* Append the token to the queue. */ if (!ENQUEUE(parser, parser->tokens, token)) return 0; return 1; } /* * Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token. */ static int yaml_parser_fetch_flow_collection_end(yaml_parser_t *parser, yaml_token_type_t type) { yaml_mark_t start_mark, end_mark; yaml_token_t token; /* Reset any potential simple key on the current flow level. */ if (!yaml_parser_remove_simple_key(parser)) return 0; /* Decrease the flow level. */ if (!yaml_parser_decrease_flow_level(parser)) return 0; /* No simple keys after the indicators ']' and '}'. */ parser->simple_key_allowed = 0; /* Consume the token. */ start_mark = parser->mark; SKIP(parser); end_mark = parser->mark; /* Create the FLOW-SEQUENCE-END of FLOW-MAPPING-END token. */ TOKEN_INIT(token, type, start_mark, end_mark); /* Append the token to the queue. */ if (!ENQUEUE(parser, parser->tokens, token)) return 0; return 1; } /* * Produce the FLOW-ENTRY token. */ static int yaml_parser_fetch_flow_entry(yaml_parser_t *parser) { yaml_mark_t start_mark, end_mark; yaml_token_t token; /* Reset any potential simple keys on the current flow level. */ if (!yaml_parser_remove_simple_key(parser)) return 0; /* Simple keys are allowed after ','. */ parser->simple_key_allowed = 1; /* Consume the token. */ start_mark = parser->mark; SKIP(parser); end_mark = parser->mark; /* Create the FLOW-ENTRY token and append it to the queue. */ TOKEN_INIT(token, YAML_FLOW_ENTRY_TOKEN, start_mark, end_mark); if (!ENQUEUE(parser, parser->tokens, token)) return 0; return 1; } /* * Produce the BLOCK-ENTRY token. */ static int yaml_parser_fetch_block_entry(yaml_parser_t *parser) { yaml_mark_t start_mark, end_mark; yaml_token_t token; /* Check if the scanner is in the block context. */ if (!parser->flow_level) { /* Check if we are allowed to start a new entry. */ if (!parser->simple_key_allowed) { return yaml_parser_set_scanner_error(parser, NULL, parser->mark, "block sequence entries are not allowed in this context"); } /* Add the BLOCK-SEQUENCE-START token if needed. */ if (!yaml_parser_roll_indent(parser, parser->mark.column, -1, YAML_BLOCK_SEQUENCE_START_TOKEN, parser->mark)) return 0; } else { /* * It is an error for the '-' indicator to occur in the flow context, * but we let the Parser detect and report about it because the Parser * is able to point to the context. */ } /* Reset any potential simple keys on the current flow level. */ if (!yaml_parser_remove_simple_key(parser)) return 0; /* Simple keys are allowed after '-'. */ parser->simple_key_allowed = 1; /* Consume the token. */ start_mark = parser->mark; SKIP(parser); end_mark = parser->mark; /* Create the BLOCK-ENTRY token and append it to the queue. */ TOKEN_INIT(token, YAML_BLOCK_ENTRY_TOKEN, start_mark, end_mark); if (!ENQUEUE(parser, parser->tokens, token)) return 0; return 1; } /* * Produce the KEY token. */ static int yaml_parser_fetch_key(yaml_parser_t *parser) { yaml_mark_t start_mark, end_mark; yaml_token_t token; /* In the block context, additional checks are required. */ if (!parser->flow_level) { /* Check if we are allowed to start a new key (not necessary simple). */ if (!parser->simple_key_allowed) { return yaml_parser_set_scanner_error(parser, NULL, parser->mark, "mapping keys are not allowed in this context"); } /* Add the BLOCK-MAPPING-START token if needed. */ if (!yaml_parser_roll_indent(parser, parser->mark.column, -1, YAML_BLOCK_MAPPING_START_TOKEN, parser->mark)) return 0; } /* Reset any potential simple keys on the current flow level. */ if (!yaml_parser_remove_simple_key(parser)) return 0; /* Simple keys are allowed after '?' in the block context. */ parser->simple_key_allowed = (!parser->flow_level); /* Consume the token. */ start_mark = parser->mark; SKIP(parser); end_mark = parser->mark; /* Create the KEY token and append it to the queue. */ TOKEN_INIT(token, YAML_KEY_TOKEN, start_mark, end_mark); if (!ENQUEUE(parser, parser->tokens, token)) return 0; return 1; } /* * Produce the VALUE token. */ static int yaml_parser_fetch_value(yaml_parser_t *parser) { yaml_mark_t start_mark, end_mark; yaml_token_t token; yaml_simple_key_t *simple_key = parser->simple_keys.top-1; /* Have we found a simple key? */ if (simple_key->possible) { /* Create the KEY token and insert it into the queue. */ TOKEN_INIT(token, YAML_KEY_TOKEN, simple_key->mark, simple_key->mark); if (!QUEUE_INSERT(parser, parser->tokens, simple_key->token_number - parser->tokens_parsed, token)) return 0; /* In the block context, we may need to add the BLOCK-MAPPING-START token. */ if (!yaml_parser_roll_indent(parser, simple_key->mark.column, simple_key->token_number, YAML_BLOCK_MAPPING_START_TOKEN, simple_key->mark)) return 0; /* Remove the simple key. */ simple_key->possible = 0; /* A simple key cannot follow another simple key. */ parser->simple_key_allowed = 0; } else { /* The ':' indicator follows a complex key. */ /* In the block context, extra checks are required. */ if (!parser->flow_level) { /* Check if we are allowed to start a complex value. */ if (!parser->simple_key_allowed) { return yaml_parser_set_scanner_error(parser, NULL, parser->mark, "mapping values are not allowed in this context"); } /* Add the BLOCK-MAPPING-START token if needed. */ if (!yaml_parser_roll_indent(parser, parser->mark.column, -1, YAML_BLOCK_MAPPING_START_TOKEN, parser->mark)) return 0; } /* Simple keys after ':' are allowed in the block context. */ parser->simple_key_allowed = (!parser->flow_level); } /* Consume the token. */ start_mark = parser->mark; SKIP(parser); end_mark = parser->mark; /* Create the VALUE token and append it to the queue. */ TOKEN_INIT(token, YAML_VALUE_TOKEN, start_mark, end_mark); if (!ENQUEUE(parser, parser->tokens, token)) return 0; return 1; } /* * Produce the ALIAS or ANCHOR token. */ static int yaml_parser_fetch_anchor(yaml_parser_t *parser, yaml_token_type_t type) { yaml_token_t token; /* An anchor or an alias could be a simple key. */ if (!yaml_parser_save_simple_key(parser)) return 0; /* A simple key cannot follow an anchor or an alias. */ parser->simple_key_allowed = 0; /* Create the ALIAS or ANCHOR token and append it to the queue. */ if (!yaml_parser_scan_anchor(parser, &token, type)) return 0; if (!ENQUEUE(parser, parser->tokens, token)) { yaml_token_delete(&token); return 0; } return 1; } /* * Produce the TAG token. */ static int yaml_parser_fetch_tag(yaml_parser_t *parser) { yaml_token_t token; /* A tag could be a simple key. */ if (!yaml_parser_save_simple_key(parser)) return 0; /* A simple key cannot follow a tag. */ parser->simple_key_allowed = 0; /* Create the TAG token and append it to the queue. */ if (!yaml_parser_scan_tag(parser, &token)) return 0; if (!ENQUEUE(parser, parser->tokens, token)) { yaml_token_delete(&token); return 0; } return 1; } /* * Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens. */ static int yaml_parser_fetch_block_scalar(yaml_parser_t *parser, int literal) { yaml_token_t token; /* Remove any potential simple keys. */ if (!yaml_parser_remove_simple_key(parser)) return 0; /* A simple key may follow a block scalar. */ parser->simple_key_allowed = 1; /* Create the SCALAR token and append it to the queue. */ if (!yaml_parser_scan_block_scalar(parser, &token, literal)) return 0; if (!ENQUEUE(parser, parser->tokens, token)) { yaml_token_delete(&token); return 0; } return 1; } /* * Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens. */ static int yaml_parser_fetch_flow_scalar(yaml_parser_t *parser, int single) { yaml_token_t token; /* A plain scalar could be a simple key. */ if (!yaml_parser_save_simple_key(parser)) return 0; /* A simple key cannot follow a flow scalar. */ parser->simple_key_allowed = 0; /* Create the SCALAR token and append it to the queue. */ if (!yaml_parser_scan_flow_scalar(parser, &token, single)) return 0; if (!ENQUEUE(parser, parser->tokens, token)) { yaml_token_delete(&token); return 0; } return 1; } /* * Produce the SCALAR(...,plain) token. */ static int yaml_parser_fetch_plain_scalar(yaml_parser_t *parser) { yaml_token_t token; /* A plain scalar could be a simple key. */ if (!yaml_parser_save_simple_key(parser)) return 0; /* A simple key cannot follow a flow scalar. */ parser->simple_key_allowed = 0; /* Create the SCALAR token and append it to the queue. */ if (!yaml_parser_scan_plain_scalar(parser, &token)) return 0; if (!ENQUEUE(parser, parser->tokens, token)) { yaml_token_delete(&token); return 0; } return 1; } /* * Eat whitespaces and comments until the next token is found. */ static int yaml_parser_scan_to_next_token(yaml_parser_t *parser) { /* Until the next token is not found. */ while (1) { /* Allow the BOM mark to start a line. */ if (!CACHE(parser, 1)) return 0; if (parser->mark.column == 0 && IS_BOM(parser->buffer)) SKIP(parser); /* * Eat whitespaces. * * Tabs are allowed: * * - in the flow context; * - in the block context, but not at the beginning of the line or * after '-', '?', or ':' (complex value). */ if (!CACHE(parser, 1)) return 0; while (CHECK(parser->buffer,' ') || ((parser->flow_level || !parser->simple_key_allowed) && CHECK(parser->buffer, '\t'))) { SKIP(parser); if (!CACHE(parser, 1)) return 0; } /* Eat a comment until a line break. */ if (CHECK(parser->buffer, '#')) { while (!IS_BREAKZ(parser->buffer)) { SKIP(parser); if (!CACHE(parser, 1)) return 0; } } /* If it is a line break, eat it. */ if (IS_BREAK(parser->buffer)) { if (!CACHE(parser, 2)) return 0; SKIP_LINE(parser); /* In the block context, a new line may start a simple key. */ if (!parser->flow_level) { parser->simple_key_allowed = 1; } } else { /* We have found a token. */ break; } } return 1; } /* * Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. * * Scope: * %YAML 1.1 # a comment \n * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * %TAG !yaml! tag:yaml.org,2002: \n * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ int yaml_parser_scan_directive(yaml_parser_t *parser, yaml_token_t *token) { yaml_mark_t start_mark, end_mark; yaml_char_t *name = NULL; int major, minor; yaml_char_t *handle = NULL, *prefix = NULL; /* Eat '%'. */ start_mark = parser->mark; SKIP(parser); /* Scan the directive name. */ if (!yaml_parser_scan_directive_name(parser, start_mark, &name)) goto error; /* Is it a YAML directive? */ if (strcmp((char *)name, "YAML") == 0) { /* Scan the VERSION directive value. */ if (!yaml_parser_scan_version_directive_value(parser, start_mark, &major, &minor)) goto error; end_mark = parser->mark; /* Create a VERSION-DIRECTIVE token. */ VERSION_DIRECTIVE_TOKEN_INIT(*token, major, minor, start_mark, end_mark); } /* Is it a TAG directive? */ else if (strcmp((char *)name, "TAG") == 0) { /* Scan the TAG directive value. */ if (!yaml_parser_scan_tag_directive_value(parser, start_mark, &handle, &prefix)) goto error; end_mark = parser->mark; /* Create a TAG-DIRECTIVE token. */ TAG_DIRECTIVE_TOKEN_INIT(*token, handle, prefix, start_mark, end_mark); } /* Unknown directive. */ else { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "found unknown directive name"); goto error; } /* Eat the rest of the line including any comments. */ if (!CACHE(parser, 1)) goto error; while (IS_BLANK(parser->buffer)) { SKIP(parser); if (!CACHE(parser, 1)) goto error; } if (CHECK(parser->buffer, '#')) { while (!IS_BREAKZ(parser->buffer)) { SKIP(parser); if (!CACHE(parser, 1)) goto error; } } /* Check if we are at the end of the line. */ if (!IS_BREAKZ(parser->buffer)) { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "did not find expected comment or line break"); goto error; } /* Eat a line break. */ if (IS_BREAK(parser->buffer)) { if (!CACHE(parser, 2)) goto error; SKIP_LINE(parser); } yaml_free(name); return 1; error: yaml_free(prefix); yaml_free(handle); yaml_free(name); return 0; } /* * Scan the directive name. * * Scope: * %YAML 1.1 # a comment \n * ^^^^ * %TAG !yaml! tag:yaml.org,2002: \n * ^^^ */ static int yaml_parser_scan_directive_name(yaml_parser_t *parser, yaml_mark_t start_mark, yaml_char_t **name) { yaml_string_t string = NULL_STRING; if (!STRING_INIT(parser, string, INITIAL_STRING_SIZE)) goto error; /* Consume the directive name. */ if (!CACHE(parser, 1)) goto error; while (IS_ALPHA(parser->buffer)) { if (!READ(parser, string)) goto error; if (!CACHE(parser, 1)) goto error; } /* Check if the name is empty. */ if (string.start == string.pointer) { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "could not find expected directive name"); goto error; } /* Check for an blank character after the name. */ if (!IS_BLANKZ(parser->buffer)) { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "found unexpected non-alphabetical character"); goto error; } *name = string.start; return 1; error: STRING_DEL(parser, string); return 0; } /* * Scan the value of VERSION-DIRECTIVE. * * Scope: * %YAML 1.1 # a comment \n * ^^^^^^ */ static int yaml_parser_scan_version_directive_value(yaml_parser_t *parser, yaml_mark_t start_mark, int *major, int *minor) { /* Eat whitespaces. */ if (!CACHE(parser, 1)) return 0; while (IS_BLANK(parser->buffer)) { SKIP(parser); if (!CACHE(parser, 1)) return 0; } /* Consume the major version number. */ if (!yaml_parser_scan_version_directive_number(parser, start_mark, major)) return 0; /* Eat '.'. */ if (!CHECK(parser->buffer, '.')) { return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", start_mark, "did not find expected digit or '.' character"); } SKIP(parser); /* Consume the minor version number. */ if (!yaml_parser_scan_version_directive_number(parser, start_mark, minor)) return 0; return 1; } #define MAX_NUMBER_LENGTH 9 /* * Scan the version number of VERSION-DIRECTIVE. * * Scope: * %YAML 1.1 # a comment \n * ^ * %YAML 1.1 # a comment \n * ^ */ static int yaml_parser_scan_version_directive_number(yaml_parser_t *parser, yaml_mark_t start_mark, int *number) { int value = 0; size_t length = 0; /* Repeat while the next character is digit. */ if (!CACHE(parser, 1)) return 0; while (IS_DIGIT(parser->buffer)) { /* Check if the number is too long. */ if (++length > MAX_NUMBER_LENGTH) { return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", start_mark, "found extremely long version number"); } value = value*10 + AS_DIGIT(parser->buffer); SKIP(parser); if (!CACHE(parser, 1)) return 0; } /* Check if the number was present. */ if (!length) { return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", start_mark, "did not find expected version number"); } *number = value; return 1; } /* * Scan the value of a TAG-DIRECTIVE token. * * Scope: * %TAG !yaml! tag:yaml.org,2002: \n * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ static int yaml_parser_scan_tag_directive_value(yaml_parser_t *parser, yaml_mark_t start_mark, yaml_char_t **handle, yaml_char_t **prefix) { yaml_char_t *handle_value = NULL; yaml_char_t *prefix_value = NULL; /* Eat whitespaces. */ if (!CACHE(parser, 1)) goto error; while (IS_BLANK(parser->buffer)) { SKIP(parser); if (!CACHE(parser, 1)) goto error; } /* Scan a handle. */ if (!yaml_parser_scan_tag_handle(parser, 1, start_mark, &handle_value)) goto error; /* Expect a whitespace. */ if (!CACHE(parser, 1)) goto error; if (!IS_BLANK(parser->buffer)) { yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", start_mark, "did not find expected whitespace"); goto error; } /* Eat whitespaces. */ while (IS_BLANK(parser->buffer)) { SKIP(parser); if (!CACHE(parser, 1)) goto error; } /* Scan a prefix. */ if (!yaml_parser_scan_tag_uri(parser, 1, NULL, start_mark, &prefix_value)) goto error; /* Expect a whitespace or line break. */ if (!CACHE(parser, 1)) goto error; if (!IS_BLANKZ(parser->buffer)) { yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", start_mark, "did not find expected whitespace or line break"); goto error; } *handle = handle_value; *prefix = prefix_value; return 1; error: yaml_free(handle_value); yaml_free(prefix_value); return 0; } static int yaml_parser_scan_anchor(yaml_parser_t *parser, yaml_token_t *token, yaml_token_type_t type) { int length = 0; yaml_mark_t start_mark, end_mark; yaml_string_t string = NULL_STRING; if (!STRING_INIT(parser, string, INITIAL_STRING_SIZE)) goto error; /* Eat the indicator character. */ start_mark = parser->mark; SKIP(parser); /* Consume the value. */ if (!CACHE(parser, 1)) goto error; while (IS_ALPHA(parser->buffer)) { if (!READ(parser, string)) goto error; if (!CACHE(parser, 1)) goto error; length ++; } end_mark = parser->mark; /* * Check if length of the anchor is greater than 0 and it is followed by * a whitespace character or one of the indicators: * * '?', ':', ',', ']', '}', '%', '@', '`'. */ if (!length || !(IS_BLANKZ(parser->buffer) || CHECK(parser->buffer, '?') || CHECK(parser->buffer, ':') || CHECK(parser->buffer, ',') || CHECK(parser->buffer, ']') || CHECK(parser->buffer, '}') || CHECK(parser->buffer, '%') || CHECK(parser->buffer, '@') || CHECK(parser->buffer, '`'))) { yaml_parser_set_scanner_error(parser, type == YAML_ANCHOR_TOKEN ? "while scanning an anchor" : "while scanning an alias", start_mark, "did not find expected alphabetic or numeric character"); goto error; } /* Create a token. */ if (type == YAML_ANCHOR_TOKEN) { ANCHOR_TOKEN_INIT(*token, string.start, start_mark, end_mark); } else { ALIAS_TOKEN_INIT(*token, string.start, start_mark, end_mark); } return 1; error: STRING_DEL(parser, string); return 0; } /* * Scan a TAG token. */ static int yaml_parser_scan_tag(yaml_parser_t *parser, yaml_token_t *token) { yaml_char_t *handle = NULL; yaml_char_t *suffix = NULL; yaml_mark_t start_mark, end_mark; start_mark = parser->mark; /* Check if the tag is in the canonical form. */ if (!CACHE(parser, 2)) goto error; if (CHECK_AT(parser->buffer, '<', 1)) { /* Set the handle to '' */ handle = YAML_MALLOC(1); if (!handle) goto error; handle[0] = '\0'; /* Eat '!<' */ SKIP(parser); SKIP(parser); /* Consume the tag value. */ if (!yaml_parser_scan_tag_uri(parser, 0, NULL, start_mark, &suffix)) goto error; /* Check for '>' and eat it. */ if (!CHECK(parser->buffer, '>')) { yaml_parser_set_scanner_error(parser, "while scanning a tag", start_mark, "did not find the expected '>'"); goto error; } SKIP(parser); } else { /* The tag has either the '!suffix' or the '!handle!suffix' form. */ /* First, try to scan a handle. */ if (!yaml_parser_scan_tag_handle(parser, 0, start_mark, &handle)) goto error; /* Check if it is, indeed, handle. */ if (handle[0] == '!' && handle[1] != '\0' && handle[strlen((char *)handle)-1] == '!') { /* Scan the suffix now. */ if (!yaml_parser_scan_tag_uri(parser, 0, NULL, start_mark, &suffix)) goto error; } else { /* It wasn't a handle after all. Scan the rest of the tag. */ if (!yaml_parser_scan_tag_uri(parser, 0, handle, start_mark, &suffix)) goto error; /* Set the handle to '!'. */ yaml_free(handle); handle = YAML_MALLOC(2); if (!handle) goto error; handle[0] = '!'; handle[1] = '\0'; /* * A special case: the '!' tag. Set the handle to '' and the * suffix to '!'. */ if (suffix[0] == '\0') { yaml_char_t *tmp = handle; handle = suffix; suffix = tmp; } } } /* Check the character which ends the tag. */ if (!CACHE(parser, 1)) goto error; if (!IS_BLANKZ(parser->buffer)) { yaml_parser_set_scanner_error(parser, "while scanning a tag", start_mark, "did not find expected whitespace or line break"); goto error; } end_mark = parser->mark; /* Create a token. */ TAG_TOKEN_INIT(*token, handle, suffix, start_mark, end_mark); return 1; error: yaml_free(handle); yaml_free(suffix); return 0; } /* * Scan a tag handle. */ static int yaml_parser_scan_tag_handle(yaml_parser_t *parser, int directive, yaml_mark_t start_mark, yaml_char_t **handle) { yaml_string_t string = NULL_STRING; if (!STRING_INIT(parser, string, INITIAL_STRING_SIZE)) goto error; /* Check the initial '!' character. */ if (!CACHE(parser, 1)) goto error; if (!CHECK(parser->buffer, '!')) { yaml_parser_set_scanner_error(parser, directive ? "while scanning a tag directive" : "while scanning a tag", start_mark, "did not find expected '!'"); goto error; } /* Copy the '!' character. */ if (!READ(parser, string)) goto error; /* Copy all subsequent alphabetical and numerical characters. */ if (!CACHE(parser, 1)) goto error; while (IS_ALPHA(parser->buffer)) { if (!READ(parser, string)) goto error; if (!CACHE(parser, 1)) goto error; } /* Check if the trailing character is '!' and copy it. */ if (CHECK(parser->buffer, '!')) { if (!READ(parser, string)) goto error; } else { /* * It's either the '!' tag or not really a tag handle. If it's a %TAG * directive, it's an error. If it's a tag token, it must be a part of * URI. */ if (directive && !(string.start[0] == '!' && string.start[1] == '\0')) { yaml_parser_set_scanner_error(parser, "while parsing a tag directive", start_mark, "did not find expected '!'"); goto error; } } *handle = string.start; return 1; error: STRING_DEL(parser, string); return 0; } /* * Scan a tag. */ static int yaml_parser_scan_tag_uri(yaml_parser_t *parser, int directive, yaml_char_t *head, yaml_mark_t start_mark, yaml_char_t **uri) { size_t length = head ? strlen((char *)head) : 0; yaml_string_t string = NULL_STRING; if (!STRING_INIT(parser, string, INITIAL_STRING_SIZE)) goto error; /* Resize the string to include the head. */ while ((size_t)(string.end - string.start) <= length) { if (!yaml_string_extend(&string.start, &string.pointer, &string.end)) { parser->error = YAML_MEMORY_ERROR; goto error; } } /* * Copy the head if needed. * * Note that we don't copy the leading '!' character. */ if (length > 1) { memcpy(string.start, head+1, length-1); string.pointer += length-1; } /* Scan the tag. */ if (!CACHE(parser, 1)) goto error; /* * The set of characters that may appear in URI is as follows: * * '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', * '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', * '%'. */ while (IS_ALPHA(parser->buffer) || CHECK(parser->buffer, ';') || CHECK(parser->buffer, '/') || CHECK(parser->buffer, '?') || CHECK(parser->buffer, ':') || CHECK(parser->buffer, '@') || CHECK(parser->buffer, '&') || CHECK(parser->buffer, '=') || CHECK(parser->buffer, '+') || CHECK(parser->buffer, '$') || CHECK(parser->buffer, ',') || CHECK(parser->buffer, '.') || CHECK(parser->buffer, '!') || CHECK(parser->buffer, '~') || CHECK(parser->buffer, '*') || CHECK(parser->buffer, '\'') || CHECK(parser->buffer, '(') || CHECK(parser->buffer, ')') || CHECK(parser->buffer, '[') || CHECK(parser->buffer, ']') || CHECK(parser->buffer, '%')) { /* Check if it is a URI-escape sequence. */ if (CHECK(parser->buffer, '%')) { if (!STRING_EXTEND(parser, string)) goto error; if (!yaml_parser_scan_uri_escapes(parser, directive, start_mark, &string)) goto error; } else { if (!READ(parser, string)) goto error; } length ++; if (!CACHE(parser, 1)) goto error; } /* Check if the tag is non-empty. */ if (!length) { if (!STRING_EXTEND(parser, string)) goto error; yaml_parser_set_scanner_error(parser, directive ? "while parsing a %TAG directive" : "while parsing a tag", start_mark, "did not find expected tag URI"); goto error; } *uri = string.start; return 1; error: STRING_DEL(parser, string); return 0; } /* * Decode an URI-escape sequence corresponding to a single UTF-8 character. */ static int yaml_parser_scan_uri_escapes(yaml_parser_t *parser, int directive, yaml_mark_t start_mark, yaml_string_t *string) { int width = 0; /* Decode the required number of characters. */ do { unsigned char octet = 0; /* Check for a URI-escaped octet. */ if (!CACHE(parser, 3)) return 0; if (!(CHECK(parser->buffer, '%') && IS_HEX_AT(parser->buffer, 1) && IS_HEX_AT(parser->buffer, 2))) { return yaml_parser_set_scanner_error(parser, directive ? "while parsing a %TAG directive" : "while parsing a tag", start_mark, "did not find URI escaped octet"); } /* Get the octet. */ octet = (AS_HEX_AT(parser->buffer, 1) << 4) + AS_HEX_AT(parser->buffer, 2); /* If it is the leading octet, determine the length of the UTF-8 sequence. */ if (!width) { width = (octet & 0x80) == 0x00 ? 1 : (octet & 0xE0) == 0xC0 ? 2 : (octet & 0xF0) == 0xE0 ? 3 : (octet & 0xF8) == 0xF0 ? 4 : 0; if (!width) { return yaml_parser_set_scanner_error(parser, directive ? "while parsing a %TAG directive" : "while parsing a tag", start_mark, "found an incorrect leading UTF-8 octet"); } } else { /* Check if the trailing octet is correct. */ if ((octet & 0xC0) != 0x80) { return yaml_parser_set_scanner_error(parser, directive ? "while parsing a %TAG directive" : "while parsing a tag", start_mark, "found an incorrect trailing UTF-8 octet"); } } /* Copy the octet and move the pointers. */ *(string->pointer++) = octet; SKIP(parser); SKIP(parser); SKIP(parser); } while (--width); return 1; } /* * Scan a block scalar. */ static int yaml_parser_scan_block_scalar(yaml_parser_t *parser, yaml_token_t *token, int literal) { yaml_mark_t start_mark; yaml_mark_t end_mark; yaml_string_t string = NULL_STRING; yaml_string_t leading_break = NULL_STRING; yaml_string_t trailing_breaks = NULL_STRING; int chomping = 0; int increment = 0; int indent = 0; int leading_blank = 0; int trailing_blank = 0; if (!STRING_INIT(parser, string, INITIAL_STRING_SIZE)) goto error; if (!STRING_INIT(parser, leading_break, INITIAL_STRING_SIZE)) goto error; if (!STRING_INIT(parser, trailing_breaks, INITIAL_STRING_SIZE)) goto error; /* Eat the indicator '|' or '>'. */ start_mark = parser->mark; SKIP(parser); /* Scan the additional block scalar indicators. */ if (!CACHE(parser, 1)) goto error; /* Check for a chomping indicator. */ if (CHECK(parser->buffer, '+') || CHECK(parser->buffer, '-')) { /* Set the chomping method and eat the indicator. */ chomping = CHECK(parser->buffer, '+') ? +1 : -1; SKIP(parser); /* Check for an indentation indicator. */ if (!CACHE(parser, 1)) goto error; if (IS_DIGIT(parser->buffer)) { /* Check that the indentation is greater than 0. */ if (CHECK(parser->buffer, '0')) { yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "found an indentation indicator equal to 0"); goto error; } /* Get the indentation level and eat the indicator. */ increment = AS_DIGIT(parser->buffer); SKIP(parser); } } /* Do the same as above, but in the opposite order. */ else if (IS_DIGIT(parser->buffer)) { if (CHECK(parser->buffer, '0')) { yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "found an indentation indicator equal to 0"); goto error; } increment = AS_DIGIT(parser->buffer); SKIP(parser); if (!CACHE(parser, 1)) goto error; if (CHECK(parser->buffer, '+') || CHECK(parser->buffer, '-')) { chomping = CHECK(parser->buffer, '+') ? +1 : -1; SKIP(parser); } } /* Eat whitespaces and comments to the end of the line. */ if (!CACHE(parser, 1)) goto error; while (IS_BLANK(parser->buffer)) { SKIP(parser); if (!CACHE(parser, 1)) goto error; } if (CHECK(parser->buffer, '#')) { while (!IS_BREAKZ(parser->buffer)) { SKIP(parser); if (!CACHE(parser, 1)) goto error; } } /* Check if we are at the end of the line. */ if (!IS_BREAKZ(parser->buffer)) { yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "did not find expected comment or line break"); goto error; } /* Eat a line break. */ if (IS_BREAK(parser->buffer)) { if (!CACHE(parser, 2)) goto error; SKIP_LINE(parser); } end_mark = parser->mark; /* Set the indentation level if it was specified. */ if (increment) { indent = parser->indent >= 0 ? parser->indent+increment : increment; } /* Scan the leading line breaks and determine the indentation level if needed. */ if (!yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark)) goto error; /* Scan the block scalar content. */ if (!CACHE(parser, 1)) goto error; while ((int)parser->mark.column == indent && !(IS_Z(parser->buffer))) { /* * We are at the beginning of a non-empty line. */ /* Is it a trailing whitespace? */ trailing_blank = IS_BLANK(parser->buffer); /* Check if we need to fold the leading line break. */ if (!literal && (*leading_break.start == '\n') && !leading_blank && !trailing_blank) { /* Do we need to join the lines by space? */ if (*trailing_breaks.start == '\0') { if (!STRING_EXTEND(parser, string)) goto error; *(string.pointer ++) = ' '; } CLEAR(parser, leading_break); } else { if (!JOIN(parser, string, leading_break)) goto error; CLEAR(parser, leading_break); } /* Append the remaining line breaks. */ if (!JOIN(parser, string, trailing_breaks)) goto error; CLEAR(parser, trailing_breaks); /* Is it a leading whitespace? */ leading_blank = IS_BLANK(parser->buffer); /* Consume the current line. */ while (!IS_BREAKZ(parser->buffer)) { if (!READ(parser, string)) goto error; if (!CACHE(parser, 1)) goto error; } /* Consume the line break. */ if (!CACHE(parser, 2)) goto error; if (!READ_LINE(parser, leading_break)) goto error; /* Eat the following indentation spaces and line breaks. */ if (!yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark)) goto error; } /* Chomp the tail. */ if (chomping != -1) { if (!JOIN(parser, string, leading_break)) goto error; } if (chomping == 1) { if (!JOIN(parser, string, trailing_breaks)) goto error; } /* Create a token. */ SCALAR_TOKEN_INIT(*token, string.start, string.pointer-string.start, literal ? YAML_LITERAL_SCALAR_STYLE : YAML_FOLDED_SCALAR_STYLE, start_mark, end_mark); STRING_DEL(parser, leading_break); STRING_DEL(parser, trailing_breaks); return 1; error: STRING_DEL(parser, string); STRING_DEL(parser, leading_break); STRING_DEL(parser, trailing_breaks); return 0; } /* * Scan indentation spaces and line breaks for a block scalar. Determine the * indentation level if needed. */ static int yaml_parser_scan_block_scalar_breaks(yaml_parser_t *parser, int *indent, yaml_string_t *breaks, yaml_mark_t start_mark, yaml_mark_t *end_mark) { int max_indent = 0; *end_mark = parser->mark; /* Eat the indentation spaces and line breaks. */ while (1) { /* Eat the indentation spaces. */ if (!CACHE(parser, 1)) return 0; while ((!*indent || (int)parser->mark.column < *indent) && IS_SPACE(parser->buffer)) { SKIP(parser); if (!CACHE(parser, 1)) return 0; } if ((int)parser->mark.column > max_indent) max_indent = (int)parser->mark.column; /* Check for a tab character messing the indentation. */ if ((!*indent || (int)parser->mark.column < *indent) && IS_TAB(parser->buffer)) { return yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "found a tab character where an indentation space is expected"); } /* Have we found a non-empty line? */ if (!IS_BREAK(parser->buffer)) break; /* Consume the line break. */ if (!CACHE(parser, 2)) return 0; if (!READ_LINE(parser, *breaks)) return 0; *end_mark = parser->mark; } /* Determine the indentation level if needed. */ if (!*indent) { *indent = max_indent; if (*indent < parser->indent + 1) *indent = parser->indent + 1; if (*indent < 1) *indent = 1; } return 1; } /* * Scan a quoted scalar. */ static int yaml_parser_scan_flow_scalar(yaml_parser_t *parser, yaml_token_t *token, int single) { yaml_mark_t start_mark; yaml_mark_t end_mark; yaml_string_t string = NULL_STRING; yaml_string_t leading_break = NULL_STRING; yaml_string_t trailing_breaks = NULL_STRING; yaml_string_t whitespaces = NULL_STRING; int leading_blanks; if (!STRING_INIT(parser, string, INITIAL_STRING_SIZE)) goto error; if (!STRING_INIT(parser, leading_break, INITIAL_STRING_SIZE)) goto error; if (!STRING_INIT(parser, trailing_breaks, INITIAL_STRING_SIZE)) goto error; if (!STRING_INIT(parser, whitespaces, INITIAL_STRING_SIZE)) goto error; /* Eat the left quote. */ start_mark = parser->mark; SKIP(parser); /* Consume the content of the quoted scalar. */ while (1) { /* Check that there are no document indicators at the beginning of the line. */ if (!CACHE(parser, 4)) goto error; if (parser->mark.column == 0 && ((CHECK_AT(parser->buffer, '-', 0) && CHECK_AT(parser->buffer, '-', 1) && CHECK_AT(parser->buffer, '-', 2)) || (CHECK_AT(parser->buffer, '.', 0) && CHECK_AT(parser->buffer, '.', 1) && CHECK_AT(parser->buffer, '.', 2))) && IS_BLANKZ_AT(parser->buffer, 3)) { yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", start_mark, "found unexpected document indicator"); goto error; } /* Check for EOF. */ if (IS_Z(parser->buffer)) { yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", start_mark, "found unexpected end of stream"); goto error; } /* Consume non-blank characters. */ if (!CACHE(parser, 2)) goto error; leading_blanks = 0; while (!IS_BLANKZ(parser->buffer)) { /* Check for an escaped single quote. */ if (single && CHECK_AT(parser->buffer, '\'', 0) && CHECK_AT(parser->buffer, '\'', 1)) { if (!STRING_EXTEND(parser, string)) goto error; *(string.pointer++) = '\''; SKIP(parser); SKIP(parser); } /* Check for the right quote. */ else if (CHECK(parser->buffer, single ? '\'' : '"')) { break; } /* Check for an escaped line break. */ else if (!single && CHECK(parser->buffer, '\\') && IS_BREAK_AT(parser->buffer, 1)) { if (!CACHE(parser, 3)) goto error; SKIP(parser); SKIP_LINE(parser); leading_blanks = 1; break; } /* Check for an escape sequence. */ else if (!single && CHECK(parser->buffer, '\\')) { size_t code_length = 0; if (!STRING_EXTEND(parser, string)) goto error; /* Check the escape character. */ switch (parser->buffer.pointer[1]) { case '0': *(string.pointer++) = '\0'; break; case 'a': *(string.pointer++) = '\x07'; break; case 'b': *(string.pointer++) = '\x08'; break; case 't': case '\t': *(string.pointer++) = '\x09'; break; case 'n': *(string.pointer++) = '\x0A'; break; case 'v': *(string.pointer++) = '\x0B'; break; case 'f': *(string.pointer++) = '\x0C'; break; case 'r': *(string.pointer++) = '\x0D'; break; case 'e': *(string.pointer++) = '\x1B'; break; case ' ': *(string.pointer++) = '\x20'; break; case '"': *(string.pointer++) = '"'; break; case '/': *(string.pointer++) = '/'; break; case '\\': *(string.pointer++) = '\\'; break; case 'N': /* NEL (#x85) */ *(string.pointer++) = '\xC2'; *(string.pointer++) = '\x85'; break; case '_': /* #xA0 */ *(string.pointer++) = '\xC2'; *(string.pointer++) = '\xA0'; break; case 'L': /* LS (#x2028) */ *(string.pointer++) = '\xE2'; *(string.pointer++) = '\x80'; *(string.pointer++) = '\xA8'; break; case 'P': /* PS (#x2029) */ *(string.pointer++) = '\xE2'; *(string.pointer++) = '\x80'; *(string.pointer++) = '\xA9'; break; case 'x': code_length = 2; break; case 'u': code_length = 4; break; case 'U': code_length = 8; break; default: yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", start_mark, "found unknown escape character"); goto error; } SKIP(parser); SKIP(parser); /* Consume an arbitrary escape code. */ if (code_length) { unsigned int value = 0; size_t k; /* Scan the character value. */ if (!CACHE(parser, code_length)) goto error; for (k = 0; k < code_length; k ++) { if (!IS_HEX_AT(parser->buffer, k)) { yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", start_mark, "did not find expected hexdecimal number"); goto error; } value = (value << 4) + AS_HEX_AT(parser->buffer, k); } /* Check the value and write the character. */ if ((value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF) { yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", start_mark, "found invalid Unicode character escape code"); goto error; } if (value <= 0x7F) { *(string.pointer++) = value; } else if (value <= 0x7FF) { *(string.pointer++) = 0xC0 + (value >> 6); *(string.pointer++) = 0x80 + (value & 0x3F); } else if (value <= 0xFFFF) { *(string.pointer++) = 0xE0 + (value >> 12); *(string.pointer++) = 0x80 + ((value >> 6) & 0x3F); *(string.pointer++) = 0x80 + (value & 0x3F); } else { *(string.pointer++) = 0xF0 + (value >> 18); *(string.pointer++) = 0x80 + ((value >> 12) & 0x3F); *(string.pointer++) = 0x80 + ((value >> 6) & 0x3F); *(string.pointer++) = 0x80 + (value & 0x3F); } /* Advance the pointer. */ for (k = 0; k < code_length; k ++) { SKIP(parser); } } } else { /* It is a non-escaped non-blank character. */ if (!READ(parser, string)) goto error; } if (!CACHE(parser, 2)) goto error; } /* Check if we are at the end of the scalar. */ /* Fix for crash unitialized value crash * Credit for the bug and input is to OSS Fuzz * Credit for the fix to Alex Gaynor */ if (!CACHE(parser, 1)) goto error; if (CHECK(parser->buffer, single ? '\'' : '"')) break; /* Consume blank characters. */ if (!CACHE(parser, 1)) goto error; while (IS_BLANK(parser->buffer) || IS_BREAK(parser->buffer)) { if (IS_BLANK(parser->buffer)) { /* Consume a space or a tab character. */ if (!leading_blanks) { if (!READ(parser, whitespaces)) goto error; } else { SKIP(parser); } } else { if (!CACHE(parser, 2)) goto error; /* Check if it is a first line break. */ if (!leading_blanks) { CLEAR(parser, whitespaces); if (!READ_LINE(parser, leading_break)) goto error; leading_blanks = 1; } else { if (!READ_LINE(parser, trailing_breaks)) goto error; } } if (!CACHE(parser, 1)) goto error; } /* Join the whitespaces or fold line breaks. */ if (leading_blanks) { /* Do we need to fold line breaks? */ if (leading_break.start[0] == '\n') { if (trailing_breaks.start[0] == '\0') { if (!STRING_EXTEND(parser, string)) goto error; *(string.pointer++) = ' '; } else { if (!JOIN(parser, string, trailing_breaks)) goto error; CLEAR(parser, trailing_breaks); } CLEAR(parser, leading_break); } else { if (!JOIN(parser, string, leading_break)) goto error; if (!JOIN(parser, string, trailing_breaks)) goto error; CLEAR(parser, leading_break); CLEAR(parser, trailing_breaks); } } else { if (!JOIN(parser, string, whitespaces)) goto error; CLEAR(parser, whitespaces); } } /* Eat the right quote. */ SKIP(parser); end_mark = parser->mark; /* Create a token. */ SCALAR_TOKEN_INIT(*token, string.start, string.pointer-string.start, single ? YAML_SINGLE_QUOTED_SCALAR_STYLE : YAML_DOUBLE_QUOTED_SCALAR_STYLE, start_mark, end_mark); STRING_DEL(parser, leading_break); STRING_DEL(parser, trailing_breaks); STRING_DEL(parser, whitespaces); return 1; error: STRING_DEL(parser, string); STRING_DEL(parser, leading_break); STRING_DEL(parser, trailing_breaks); STRING_DEL(parser, whitespaces); return 0; } /* * Scan a plain scalar. */ static int yaml_parser_scan_plain_scalar(yaml_parser_t *parser, yaml_token_t *token) { yaml_mark_t start_mark; yaml_mark_t end_mark; yaml_string_t string = NULL_STRING; yaml_string_t leading_break = NULL_STRING; yaml_string_t trailing_breaks = NULL_STRING; yaml_string_t whitespaces = NULL_STRING; int leading_blanks = 0; int indent = parser->indent+1; if (!STRING_INIT(parser, string, INITIAL_STRING_SIZE)) goto error; if (!STRING_INIT(parser, leading_break, INITIAL_STRING_SIZE)) goto error; if (!STRING_INIT(parser, trailing_breaks, INITIAL_STRING_SIZE)) goto error; if (!STRING_INIT(parser, whitespaces, INITIAL_STRING_SIZE)) goto error; start_mark = end_mark = parser->mark; /* Consume the content of the plain scalar. */ while (1) { /* Check for a document indicator. */ if (!CACHE(parser, 4)) goto error; if (parser->mark.column == 0 && ((CHECK_AT(parser->buffer, '-', 0) && CHECK_AT(parser->buffer, '-', 1) && CHECK_AT(parser->buffer, '-', 2)) || (CHECK_AT(parser->buffer, '.', 0) && CHECK_AT(parser->buffer, '.', 1) && CHECK_AT(parser->buffer, '.', 2))) && IS_BLANKZ_AT(parser->buffer, 3)) break; /* Check for a comment. */ if (CHECK(parser->buffer, '#')) break; /* Consume non-blank characters. */ while (!IS_BLANKZ(parser->buffer)) { /* Check for "x:" + one of ',?[]{}' in the flow context. TODO: Fix the test "spec-08-13". * This is not completely according to the spec * See http://yaml.org/spec/1.1/#id907281 9.1.3. Plain */ if (parser->flow_level && CHECK(parser->buffer, ':') && ( CHECK_AT(parser->buffer, ',', 1) || CHECK_AT(parser->buffer, '?', 1) || CHECK_AT(parser->buffer, '[', 1) || CHECK_AT(parser->buffer, ']', 1) || CHECK_AT(parser->buffer, '{', 1) || CHECK_AT(parser->buffer, '}', 1) ) ) { yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", start_mark, "found unexpected ':'"); goto error; } /* Check for indicators that may end a plain scalar. */ if ((CHECK(parser->buffer, ':') && IS_BLANKZ_AT(parser->buffer, 1)) || (parser->flow_level && (CHECK(parser->buffer, ',') || CHECK(parser->buffer, '?') || CHECK(parser->buffer, '[') || CHECK(parser->buffer, ']') || CHECK(parser->buffer, '{') || CHECK(parser->buffer, '}')))) break; /* Check if we need to join whitespaces and breaks. */ if (leading_blanks || whitespaces.start != whitespaces.pointer) { if (leading_blanks) { /* Do we need to fold line breaks? */ if (leading_break.start[0] == '\n') { if (trailing_breaks.start[0] == '\0') { if (!STRING_EXTEND(parser, string)) goto error; *(string.pointer++) = ' '; } else { if (!JOIN(parser, string, trailing_breaks)) goto error; CLEAR(parser, trailing_breaks); } CLEAR(parser, leading_break); } else { if (!JOIN(parser, string, leading_break)) goto error; if (!JOIN(parser, string, trailing_breaks)) goto error; CLEAR(parser, leading_break); CLEAR(parser, trailing_breaks); } leading_blanks = 0; } else { if (!JOIN(parser, string, whitespaces)) goto error; CLEAR(parser, whitespaces); } } /* Copy the character. */ if (!READ(parser, string)) goto error; end_mark = parser->mark; if (!CACHE(parser, 2)) goto error; } /* Is it the end? */ if (!(IS_BLANK(parser->buffer) || IS_BREAK(parser->buffer))) break; /* Consume blank characters. */ if (!CACHE(parser, 1)) goto error; while (IS_BLANK(parser->buffer) || IS_BREAK(parser->buffer)) { if (IS_BLANK(parser->buffer)) { /* Check for tab character that abuse indentation. */ if (leading_blanks && (int)parser->mark.column < indent && IS_TAB(parser->buffer)) { yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", start_mark, "found a tab character that violate indentation"); goto error; } /* Consume a space or a tab character. */ if (!leading_blanks) { if (!READ(parser, whitespaces)) goto error; } else { SKIP(parser); } } else { if (!CACHE(parser, 2)) goto error; /* Check if it is a first line break. */ if (!leading_blanks) { CLEAR(parser, whitespaces); if (!READ_LINE(parser, leading_break)) goto error; leading_blanks = 1; } else { if (!READ_LINE(parser, trailing_breaks)) goto error; } } if (!CACHE(parser, 1)) goto error; } /* Check indentation level. */ if (!parser->flow_level && (int)parser->mark.column < indent) break; } /* Create a token. */ SCALAR_TOKEN_INIT(*token, string.start, string.pointer-string.start, YAML_PLAIN_SCALAR_STYLE, start_mark, end_mark); /* Note that we change the 'simple_key_allowed' flag. */ if (leading_blanks) { parser->simple_key_allowed = 1; } STRING_DEL(parser, leading_break); STRING_DEL(parser, trailing_breaks); STRING_DEL(parser, whitespaces); return 1; error: STRING_DEL(parser, string); STRING_DEL(parser, leading_break); STRING_DEL(parser, trailing_breaks); STRING_DEL(parser, whitespaces); return 0; } yaml/src/reader.c0000644000176200001440000004045013614635636013431 0ustar liggesusers #include "yaml_private.h" /* * Declarations. */ static int yaml_parser_set_reader_error(yaml_parser_t *parser, const char *problem, size_t offset, int value); static int yaml_parser_update_raw_buffer(yaml_parser_t *parser); static int yaml_parser_determine_encoding(yaml_parser_t *parser); YAML_DECLARE(int) yaml_parser_update_buffer(yaml_parser_t *parser, size_t length); /* * Set the reader error and return 0. */ static int yaml_parser_set_reader_error(yaml_parser_t *parser, const char *problem, size_t offset, int value) { parser->error = YAML_READER_ERROR; parser->problem = problem; parser->problem_offset = offset; parser->problem_value = value; return 0; } /* * Byte order marks. */ #define BOM_UTF8 "\xef\xbb\xbf" #define BOM_UTF16LE "\xff\xfe" #define BOM_UTF16BE "\xfe\xff" /* * Determine the input stream encoding by checking the BOM symbol. If no BOM is * found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. */ static int yaml_parser_determine_encoding(yaml_parser_t *parser) { /* Ensure that we had enough bytes in the raw buffer. */ while (!parser->eof && parser->raw_buffer.last - parser->raw_buffer.pointer < 3) { if (!yaml_parser_update_raw_buffer(parser)) { return 0; } } /* Determine the encoding. */ if (parser->raw_buffer.last - parser->raw_buffer.pointer >= 2 && !memcmp(parser->raw_buffer.pointer, BOM_UTF16LE, 2)) { parser->encoding = YAML_UTF16LE_ENCODING; parser->raw_buffer.pointer += 2; parser->offset += 2; } else if (parser->raw_buffer.last - parser->raw_buffer.pointer >= 2 && !memcmp(parser->raw_buffer.pointer, BOM_UTF16BE, 2)) { parser->encoding = YAML_UTF16BE_ENCODING; parser->raw_buffer.pointer += 2; parser->offset += 2; } else if (parser->raw_buffer.last - parser->raw_buffer.pointer >= 3 && !memcmp(parser->raw_buffer.pointer, BOM_UTF8, 3)) { parser->encoding = YAML_UTF8_ENCODING; parser->raw_buffer.pointer += 3; parser->offset += 3; } else { parser->encoding = YAML_UTF8_ENCODING; } return 1; } /* * Update the raw buffer. */ static int yaml_parser_update_raw_buffer(yaml_parser_t *parser) { size_t size_read = 0; /* Return if the raw buffer is full. */ if (parser->raw_buffer.start == parser->raw_buffer.pointer && parser->raw_buffer.last == parser->raw_buffer.end) return 1; /* Return on EOF. */ if (parser->eof) return 1; /* Move the remaining bytes in the raw buffer to the beginning. */ if (parser->raw_buffer.start < parser->raw_buffer.pointer && parser->raw_buffer.pointer < parser->raw_buffer.last) { memmove(parser->raw_buffer.start, parser->raw_buffer.pointer, parser->raw_buffer.last - parser->raw_buffer.pointer); } parser->raw_buffer.last -= parser->raw_buffer.pointer - parser->raw_buffer.start; parser->raw_buffer.pointer = parser->raw_buffer.start; /* Call the read handler to fill the buffer. */ if (!parser->read_handler(parser->read_handler_data, parser->raw_buffer.last, parser->raw_buffer.end - parser->raw_buffer.last, &size_read)) { return yaml_parser_set_reader_error(parser, "input error", parser->offset, -1); } parser->raw_buffer.last += size_read; if (!size_read) { parser->eof = 1; } return 1; } /* * Ensure that the buffer contains at least `length` characters. * Return 1 on success, 0 on failure. * * The length is supposed to be significantly less that the buffer size. */ YAML_DECLARE(int) yaml_parser_update_buffer(yaml_parser_t *parser, size_t length) { int first = 1; assert(parser->read_handler); /* Read handler must be set. */ /* If the EOF flag is set and the raw buffer is empty, do nothing. */ if (parser->eof && parser->raw_buffer.pointer == parser->raw_buffer.last) return 1; /* Return if the buffer contains enough characters. */ if (parser->unread >= length) return 1; /* Determine the input encoding if it is not known yet. */ if (!parser->encoding) { if (!yaml_parser_determine_encoding(parser)) return 0; } /* Move the unread characters to the beginning of the buffer. */ if (parser->buffer.start < parser->buffer.pointer && parser->buffer.pointer < parser->buffer.last) { size_t size = parser->buffer.last - parser->buffer.pointer; memmove(parser->buffer.start, parser->buffer.pointer, size); parser->buffer.pointer = parser->buffer.start; parser->buffer.last = parser->buffer.start + size; } else if (parser->buffer.pointer == parser->buffer.last) { parser->buffer.pointer = parser->buffer.start; parser->buffer.last = parser->buffer.start; } /* Fill the buffer until it has enough characters. */ while (parser->unread < length) { /* Fill the raw buffer if necessary. */ if (!first || parser->raw_buffer.pointer == parser->raw_buffer.last) { if (!yaml_parser_update_raw_buffer(parser)) return 0; } first = 0; /* Decode the raw buffer. */ while (parser->raw_buffer.pointer != parser->raw_buffer.last) { unsigned int value = 0, value2 = 0; int incomplete = 0; unsigned char octet; unsigned int width = 0; int low, high; size_t k; size_t raw_unread = parser->raw_buffer.last - parser->raw_buffer.pointer; /* Decode the next character. */ switch (parser->encoding) { case YAML_UTF8_ENCODING: /* * Decode a UTF-8 character. Check RFC 3629 * (http://www.ietf.org/rfc/rfc3629.txt) for more details. * * The following table (taken from the RFC) is used for * decoding. * * Char. number range | UTF-8 octet sequence * (hexadecimal) | (binary) * --------------------+------------------------------------ * 0000 0000-0000 007F | 0xxxxxxx * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx * * Additionally, the characters in the range 0xD800-0xDFFF * are prohibited as they are reserved for use with UTF-16 * surrogate pairs. */ /* Determine the length of the UTF-8 sequence. */ octet = parser->raw_buffer.pointer[0]; width = (octet & 0x80) == 0x00 ? 1 : (octet & 0xE0) == 0xC0 ? 2 : (octet & 0xF0) == 0xE0 ? 3 : (octet & 0xF8) == 0xF0 ? 4 : 0; /* Check if the leading octet is valid. */ if (!width) return yaml_parser_set_reader_error(parser, "invalid leading UTF-8 octet", parser->offset, octet); /* Check if the raw buffer contains an incomplete character. */ if (width > raw_unread) { if (parser->eof) { return yaml_parser_set_reader_error(parser, "incomplete UTF-8 octet sequence", parser->offset, -1); } incomplete = 1; break; } /* Decode the leading octet. */ value = (octet & 0x80) == 0x00 ? octet & 0x7F : (octet & 0xE0) == 0xC0 ? octet & 0x1F : (octet & 0xF0) == 0xE0 ? octet & 0x0F : (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; /* Check and decode the trailing octets. */ for (k = 1; k < width; k ++) { octet = parser->raw_buffer.pointer[k]; /* Check if the octet is valid. */ if ((octet & 0xC0) != 0x80) return yaml_parser_set_reader_error(parser, "invalid trailing UTF-8 octet", parser->offset+k, octet); /* Decode the octet. */ value = (value << 6) + (octet & 0x3F); } /* Check the length of the sequence against the value. */ if (!((width == 1) || (width == 2 && value >= 0x80) || (width == 3 && value >= 0x800) || (width == 4 && value >= 0x10000))) return yaml_parser_set_reader_error(parser, "invalid length of a UTF-8 sequence", parser->offset, -1); /* Check the range of the value. */ if ((value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF) return yaml_parser_set_reader_error(parser, "invalid Unicode character", parser->offset, value); break; case YAML_UTF16LE_ENCODING: case YAML_UTF16BE_ENCODING: low = (parser->encoding == YAML_UTF16LE_ENCODING ? 0 : 1); high = (parser->encoding == YAML_UTF16LE_ENCODING ? 1 : 0); /* * The UTF-16 encoding is not as simple as one might * naively think. Check RFC 2781 * (http://www.ietf.org/rfc/rfc2781.txt). * * Normally, two subsequent bytes describe a Unicode * character. However a special technique (called a * surrogate pair) is used for specifying character * values larger than 0xFFFF. * * A surrogate pair consists of two pseudo-characters: * high surrogate area (0xD800-0xDBFF) * low surrogate area (0xDC00-0xDFFF) * * The following formulas are used for decoding * and encoding characters using surrogate pairs: * * U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) * U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) * W1 = 110110yyyyyyyyyy * W2 = 110111xxxxxxxxxx * * where U is the character value, W1 is the high surrogate * area, W2 is the low surrogate area. */ /* Check for incomplete UTF-16 character. */ if (raw_unread < 2) { if (parser->eof) { return yaml_parser_set_reader_error(parser, "incomplete UTF-16 character", parser->offset, -1); } incomplete = 1; break; } /* Get the character. */ value = parser->raw_buffer.pointer[low] + (parser->raw_buffer.pointer[high] << 8); /* Check for unexpected low surrogate area. */ if ((value & 0xFC00) == 0xDC00) return yaml_parser_set_reader_error(parser, "unexpected low surrogate area", parser->offset, value); /* Check for a high surrogate area. */ if ((value & 0xFC00) == 0xD800) { width = 4; /* Check for incomplete surrogate pair. */ if (raw_unread < 4) { if (parser->eof) { return yaml_parser_set_reader_error(parser, "incomplete UTF-16 surrogate pair", parser->offset, -1); } incomplete = 1; break; } /* Get the next character. */ value2 = parser->raw_buffer.pointer[low+2] + (parser->raw_buffer.pointer[high+2] << 8); /* Check for a low surrogate area. */ if ((value2 & 0xFC00) != 0xDC00) return yaml_parser_set_reader_error(parser, "expected low surrogate area", parser->offset+2, value2); /* Generate the value of the surrogate pair. */ value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF); } else { width = 2; } break; default: assert(1); /* Impossible. */ } /* Check if the raw buffer contains enough bytes to form a character. */ if (incomplete) break; /* * Check if the character is in the allowed range: * #x9 | #xA | #xD | [#x20-#x7E] (8 bit) * | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) * | [#x10000-#x10FFFF] (32 bit) */ if (! (value == 0x09 || value == 0x0A || value == 0x0D || (value >= 0x20 && value <= 0x7E) || (value == 0x85) || (value >= 0xA0 && value <= 0xD7FF) || (value >= 0xE000 && value <= 0xFFFD) || (value >= 0x10000 && value <= 0x10FFFF))) return yaml_parser_set_reader_error(parser, "control characters are not allowed", parser->offset, value); /* Move the raw pointers. */ parser->raw_buffer.pointer += width; parser->offset += width; /* Finally put the character into the buffer. */ /* 0000 0000-0000 007F -> 0xxxxxxx */ if (value <= 0x7F) { *(parser->buffer.last++) = value; } /* 0000 0080-0000 07FF -> 110xxxxx 10xxxxxx */ else if (value <= 0x7FF) { *(parser->buffer.last++) = 0xC0 + (value >> 6); *(parser->buffer.last++) = 0x80 + (value & 0x3F); } /* 0000 0800-0000 FFFF -> 1110xxxx 10xxxxxx 10xxxxxx */ else if (value <= 0xFFFF) { *(parser->buffer.last++) = 0xE0 + (value >> 12); *(parser->buffer.last++) = 0x80 + ((value >> 6) & 0x3F); *(parser->buffer.last++) = 0x80 + (value & 0x3F); } /* 0001 0000-0010 FFFF -> 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ else { *(parser->buffer.last++) = 0xF0 + (value >> 18); *(parser->buffer.last++) = 0x80 + ((value >> 12) & 0x3F); *(parser->buffer.last++) = 0x80 + ((value >> 6) & 0x3F); *(parser->buffer.last++) = 0x80 + (value & 0x3F); } parser->unread ++; } /* On EOF, put NUL into the buffer and return. */ if (parser->eof) { *(parser->buffer.last++) = '\0'; parser->unread ++; return 1; } } if (parser->offset >= MAX_FILE_SIZE) { return yaml_parser_set_reader_error(parser, "input is too long", parser->offset, -1); } return 1; } yaml/src/yaml_private.h0000644000176200001440000007356613614635636014706 0ustar liggesusers#define YAML_VERSION_MAJOR 0 #define YAML_VERSION_MINOR 2 #define YAML_VERSION_PATCH 2 #define YAML_VERSION_STRING "0.2.2" #include #include #include #include /* * Memory management. */ YAML_DECLARE(void *) yaml_malloc(size_t size); YAML_DECLARE(void *) yaml_realloc(void *ptr, size_t size); YAML_DECLARE(void) yaml_free(void *ptr); YAML_DECLARE(yaml_char_t *) yaml_strdup(const yaml_char_t *); /* * Reader: Ensure that the buffer contains at least `length` characters. */ YAML_DECLARE(int) yaml_parser_update_buffer(yaml_parser_t *parser, size_t length); /* * Scanner: Ensure that the token stack contains at least one token ready. */ YAML_DECLARE(int) yaml_parser_fetch_more_tokens(yaml_parser_t *parser); /* * The size of the input raw buffer. */ #define INPUT_RAW_BUFFER_SIZE 16384 /* * The size of the input buffer. * * It should be possible to decode the whole raw buffer. */ #define INPUT_BUFFER_SIZE (INPUT_RAW_BUFFER_SIZE*3) /* * The size of the output buffer. */ #define OUTPUT_BUFFER_SIZE 16384 /* * The size of the output raw buffer. * * It should be possible to encode the whole output buffer. */ #define OUTPUT_RAW_BUFFER_SIZE (OUTPUT_BUFFER_SIZE*2+2) /* * The maximum size of a YAML input file. * This used to be PTRDIFF_MAX, but that's not entirely portable * because stdint.h isn't available on all platforms. * It is not entirely clear why this isn't the maximum value * that can fit into the parser->offset field. */ #define MAX_FILE_SIZE (~(size_t)0 / 2) /* * The size of other stacks and queues. */ #define INITIAL_STACK_SIZE 16 #define INITIAL_QUEUE_SIZE 16 #define INITIAL_STRING_SIZE 16 /* * Buffer management. */ #define BUFFER_INIT(context,buffer,size) \ (((buffer).start = (yaml_char_t *)yaml_malloc(size)) ? \ ((buffer).last = (buffer).pointer = (buffer).start, \ (buffer).end = (buffer).start+(size), \ 1) : \ ((context)->error = YAML_MEMORY_ERROR, \ 0)) #define BUFFER_DEL(context,buffer) \ (yaml_free((buffer).start), \ (buffer).start = (buffer).pointer = (buffer).end = 0) /* * String management. */ typedef struct { yaml_char_t *start; yaml_char_t *end; yaml_char_t *pointer; } yaml_string_t; YAML_DECLARE(int) yaml_string_extend(yaml_char_t **start, yaml_char_t **pointer, yaml_char_t **end); YAML_DECLARE(int) yaml_string_join( yaml_char_t **a_start, yaml_char_t **a_pointer, yaml_char_t **a_end, yaml_char_t **b_start, yaml_char_t **b_pointer, yaml_char_t **b_end); #define NULL_STRING { NULL, NULL, NULL } #define STRING(string,length) { (string), (string)+(length), (string) } #define STRING_ASSIGN(value,string,length) \ ((value).start = (string), \ (value).end = (string)+(length), \ (value).pointer = (string)) #define STRING_INIT(context,string,size) \ (((string).start = YAML_MALLOC(size)) ? \ ((string).pointer = (string).start, \ (string).end = (string).start+(size), \ memset((string).start, 0, (size)), \ 1) : \ ((context)->error = YAML_MEMORY_ERROR, \ 0)) #define STRING_DEL(context,string) \ (yaml_free((string).start), \ (string).start = (string).pointer = (string).end = 0) #define STRING_EXTEND(context,string) \ ((((string).pointer+5 < (string).end) \ || yaml_string_extend(&(string).start, \ &(string).pointer, &(string).end)) ? \ 1 : \ ((context)->error = YAML_MEMORY_ERROR, \ 0)) #define CLEAR(context,string) \ ((string).pointer = (string).start, \ memset((string).start, 0, (string).end-(string).start)) #define JOIN(context,string_a,string_b) \ ((yaml_string_join(&(string_a).start, &(string_a).pointer, \ &(string_a).end, &(string_b).start, \ &(string_b).pointer, &(string_b).end)) ? \ ((string_b).pointer = (string_b).start, \ 1) : \ ((context)->error = YAML_MEMORY_ERROR, \ 0)) /* * String check operations. */ /* * Check the octet at the specified position. */ #define CHECK_AT(string,octet,offset) \ ((string).pointer[offset] == (yaml_char_t)(octet)) /* * Check the current octet in the buffer. */ #define CHECK(string,octet) (CHECK_AT((string),(octet),0)) /* * Check if the character at the specified position is an alphabetical * character, a digit, '_', or '-'. */ #define IS_ALPHA_AT(string,offset) \ (((string).pointer[offset] >= (yaml_char_t) '0' && \ (string).pointer[offset] <= (yaml_char_t) '9') || \ ((string).pointer[offset] >= (yaml_char_t) 'A' && \ (string).pointer[offset] <= (yaml_char_t) 'Z') || \ ((string).pointer[offset] >= (yaml_char_t) 'a' && \ (string).pointer[offset] <= (yaml_char_t) 'z') || \ (string).pointer[offset] == '_' || \ (string).pointer[offset] == '-') #define IS_ALPHA(string) IS_ALPHA_AT((string),0) /* * Check if the character at the specified position is a digit. */ #define IS_DIGIT_AT(string,offset) \ (((string).pointer[offset] >= (yaml_char_t) '0' && \ (string).pointer[offset] <= (yaml_char_t) '9')) #define IS_DIGIT(string) IS_DIGIT_AT((string),0) /* * Get the value of a digit. */ #define AS_DIGIT_AT(string,offset) \ ((string).pointer[offset] - (yaml_char_t) '0') #define AS_DIGIT(string) AS_DIGIT_AT((string),0) /* * Check if the character at the specified position is a hex-digit. */ #define IS_HEX_AT(string,offset) \ (((string).pointer[offset] >= (yaml_char_t) '0' && \ (string).pointer[offset] <= (yaml_char_t) '9') || \ ((string).pointer[offset] >= (yaml_char_t) 'A' && \ (string).pointer[offset] <= (yaml_char_t) 'F') || \ ((string).pointer[offset] >= (yaml_char_t) 'a' && \ (string).pointer[offset] <= (yaml_char_t) 'f')) #define IS_HEX(string) IS_HEX_AT((string),0) /* * Get the value of a hex-digit. */ #define AS_HEX_AT(string,offset) \ (((string).pointer[offset] >= (yaml_char_t) 'A' && \ (string).pointer[offset] <= (yaml_char_t) 'F') ? \ ((string).pointer[offset] - (yaml_char_t) 'A' + 10) : \ ((string).pointer[offset] >= (yaml_char_t) 'a' && \ (string).pointer[offset] <= (yaml_char_t) 'f') ? \ ((string).pointer[offset] - (yaml_char_t) 'a' + 10) : \ ((string).pointer[offset] - (yaml_char_t) '0')) #define AS_HEX(string) AS_HEX_AT((string),0) /* * Check if the character is ASCII. */ #define IS_ASCII_AT(string,offset) \ ((string).pointer[offset] <= (yaml_char_t) '\x7F') #define IS_ASCII(string) IS_ASCII_AT((string),0) /* * Check if the character can be printed unescaped. */ #define IS_PRINTABLE_AT(string,offset) \ (((string).pointer[offset] == 0x0A) /* . == #x0A */ \ || ((string).pointer[offset] >= 0x20 /* #x20 <= . <= #x7E */ \ && (string).pointer[offset] <= 0x7E) \ || ((string).pointer[offset] == 0xC2 /* #0xA0 <= . <= #xD7FF */ \ && (string).pointer[offset+1] >= 0xA0) \ || ((string).pointer[offset] > 0xC2 \ && (string).pointer[offset] < 0xED) \ || ((string).pointer[offset] == 0xED \ && (string).pointer[offset+1] < 0xA0) \ || ((string).pointer[offset] == 0xEE) \ || ((string).pointer[offset] == 0xEF /* #xE000 <= . <= #xFFFD */ \ && !((string).pointer[offset+1] == 0xBB /* && . != #xFEFF */ \ && (string).pointer[offset+2] == 0xBF) \ && !((string).pointer[offset+1] == 0xBF \ && ((string).pointer[offset+2] == 0xBE \ || (string).pointer[offset+2] == 0xBF)))) #define IS_PRINTABLE(string) IS_PRINTABLE_AT((string),0) /* * Check if the character at the specified position is NUL. */ #define IS_Z_AT(string,offset) CHECK_AT((string),'\0',(offset)) #define IS_Z(string) IS_Z_AT((string),0) /* * Check if the character at the specified position is BOM. */ #define IS_BOM_AT(string,offset) \ (CHECK_AT((string),'\xEF',(offset)) \ && CHECK_AT((string),'\xBB',(offset)+1) \ && CHECK_AT((string),'\xBF',(offset)+2)) /* BOM (#xFEFF) */ #define IS_BOM(string) IS_BOM_AT(string,0) /* * Check if the character at the specified position is space. */ #define IS_SPACE_AT(string,offset) CHECK_AT((string),' ',(offset)) #define IS_SPACE(string) IS_SPACE_AT((string),0) /* * Check if the character at the specified position is tab. */ #define IS_TAB_AT(string,offset) CHECK_AT((string),'\t',(offset)) #define IS_TAB(string) IS_TAB_AT((string),0) /* * Check if the character at the specified position is blank (space or tab). */ #define IS_BLANK_AT(string,offset) \ (IS_SPACE_AT((string),(offset)) || IS_TAB_AT((string),(offset))) #define IS_BLANK(string) IS_BLANK_AT((string),0) /* * Check if the character at the specified position is a line break. */ #define IS_BREAK_AT(string,offset) \ (CHECK_AT((string),'\r',(offset)) /* CR (#xD)*/ \ || CHECK_AT((string),'\n',(offset)) /* LF (#xA) */ \ || (CHECK_AT((string),'\xC2',(offset)) \ && CHECK_AT((string),'\x85',(offset)+1)) /* NEL (#x85) */ \ || (CHECK_AT((string),'\xE2',(offset)) \ && CHECK_AT((string),'\x80',(offset)+1) \ && CHECK_AT((string),'\xA8',(offset)+2)) /* LS (#x2028) */ \ || (CHECK_AT((string),'\xE2',(offset)) \ && CHECK_AT((string),'\x80',(offset)+1) \ && CHECK_AT((string),'\xA9',(offset)+2))) /* PS (#x2029) */ #define IS_BREAK(string) IS_BREAK_AT((string),0) #define IS_CRLF_AT(string,offset) \ (CHECK_AT((string),'\r',(offset)) && CHECK_AT((string),'\n',(offset)+1)) #define IS_CRLF(string) IS_CRLF_AT((string),0) /* * Check if the character is a line break or NUL. */ #define IS_BREAKZ_AT(string,offset) \ (IS_BREAK_AT((string),(offset)) || IS_Z_AT((string),(offset))) #define IS_BREAKZ(string) IS_BREAKZ_AT((string),0) /* * Check if the character is a line break, space, or NUL. */ #define IS_SPACEZ_AT(string,offset) \ (IS_SPACE_AT((string),(offset)) || IS_BREAKZ_AT((string),(offset))) #define IS_SPACEZ(string) IS_SPACEZ_AT((string),0) /* * Check if the character is a line break, space, tab, or NUL. */ #define IS_BLANKZ_AT(string,offset) \ (IS_BLANK_AT((string),(offset)) || IS_BREAKZ_AT((string),(offset))) #define IS_BLANKZ(string) IS_BLANKZ_AT((string),0) /* * Determine the width of the character. */ #define WIDTH_AT(string,offset) \ (((string).pointer[offset] & 0x80) == 0x00 ? 1 : \ ((string).pointer[offset] & 0xE0) == 0xC0 ? 2 : \ ((string).pointer[offset] & 0xF0) == 0xE0 ? 3 : \ ((string).pointer[offset] & 0xF8) == 0xF0 ? 4 : 0) #define WIDTH(string) WIDTH_AT((string),0) /* * Move the string pointer to the next character. */ #define MOVE(string) ((string).pointer += WIDTH((string))) /* * Copy a character and move the pointers of both strings. */ #define COPY(string_a,string_b) \ ((*(string_b).pointer & 0x80) == 0x00 ? \ (*((string_a).pointer++) = *((string_b).pointer++)) : \ (*(string_b).pointer & 0xE0) == 0xC0 ? \ (*((string_a).pointer++) = *((string_b).pointer++), \ *((string_a).pointer++) = *((string_b).pointer++)) : \ (*(string_b).pointer & 0xF0) == 0xE0 ? \ (*((string_a).pointer++) = *((string_b).pointer++), \ *((string_a).pointer++) = *((string_b).pointer++), \ *((string_a).pointer++) = *((string_b).pointer++)) : \ (*(string_b).pointer & 0xF8) == 0xF0 ? \ (*((string_a).pointer++) = *((string_b).pointer++), \ *((string_a).pointer++) = *((string_b).pointer++), \ *((string_a).pointer++) = *((string_b).pointer++), \ *((string_a).pointer++) = *((string_b).pointer++)) : 0) /* * Stack and queue management. */ YAML_DECLARE(int) yaml_stack_extend(void **start, void **top, void **end); YAML_DECLARE(int) yaml_queue_extend(void **start, void **head, void **tail, void **end); #define STACK_INIT(context,stack,type) \ (((stack).start = (type)yaml_malloc(INITIAL_STACK_SIZE*sizeof(*(stack).start))) ? \ ((stack).top = (stack).start, \ (stack).end = (stack).start+INITIAL_STACK_SIZE, \ 1) : \ ((context)->error = YAML_MEMORY_ERROR, \ 0)) #define STACK_DEL(context,stack) \ (yaml_free((stack).start), \ (stack).start = (stack).top = (stack).end = 0) #define STACK_EMPTY(context,stack) \ ((stack).start == (stack).top) #define STACK_LIMIT(context,stack,size) \ ((stack).top - (stack).start < (size) ? \ 1 : \ ((context)->error = YAML_MEMORY_ERROR, \ 0)) #define PUSH(context,stack,value) \ (((stack).top != (stack).end \ || yaml_stack_extend((void **)&(stack).start, \ (void **)&(stack).top, (void **)&(stack).end)) ? \ (*((stack).top++) = value, \ 1) : \ ((context)->error = YAML_MEMORY_ERROR, \ 0)) #define POP(context,stack) \ (*(--(stack).top)) #define QUEUE_INIT(context,queue,size,type) \ (((queue).start = (type)yaml_malloc((size)*sizeof(*(queue).start))) ? \ ((queue).head = (queue).tail = (queue).start, \ (queue).end = (queue).start+(size), \ 1) : \ ((context)->error = YAML_MEMORY_ERROR, \ 0)) #define QUEUE_DEL(context,queue) \ (yaml_free((queue).start), \ (queue).start = (queue).head = (queue).tail = (queue).end = 0) #define QUEUE_EMPTY(context,queue) \ ((queue).head == (queue).tail) #define ENQUEUE(context,queue,value) \ (((queue).tail != (queue).end \ || yaml_queue_extend((void **)&(queue).start, (void **)&(queue).head, \ (void **)&(queue).tail, (void **)&(queue).end)) ? \ (*((queue).tail++) = value, \ 1) : \ ((context)->error = YAML_MEMORY_ERROR, \ 0)) #define DEQUEUE(context,queue) \ (*((queue).head++)) #define QUEUE_INSERT(context,queue,index,value) \ (((queue).tail != (queue).end \ || yaml_queue_extend((void **)&(queue).start, (void **)&(queue).head, \ (void **)&(queue).tail, (void **)&(queue).end)) ? \ (memmove((queue).head+(index)+1,(queue).head+(index), \ ((queue).tail-(queue).head-(index))*sizeof(*(queue).start)), \ *((queue).head+(index)) = value, \ (queue).tail++, \ 1) : \ ((context)->error = YAML_MEMORY_ERROR, \ 0)) /* * Token initializers. */ #define TOKEN_INIT(token,token_type,token_start_mark,token_end_mark) \ (memset(&(token), 0, sizeof(yaml_token_t)), \ (token).type = (token_type), \ (token).start_mark = (token_start_mark), \ (token).end_mark = (token_end_mark)) #define STREAM_START_TOKEN_INIT(token,token_encoding,start_mark,end_mark) \ (TOKEN_INIT((token),YAML_STREAM_START_TOKEN,(start_mark),(end_mark)), \ (token).data.stream_start.encoding = (token_encoding)) #define STREAM_END_TOKEN_INIT(token,start_mark,end_mark) \ (TOKEN_INIT((token),YAML_STREAM_END_TOKEN,(start_mark),(end_mark))) #define ALIAS_TOKEN_INIT(token,token_value,start_mark,end_mark) \ (TOKEN_INIT((token),YAML_ALIAS_TOKEN,(start_mark),(end_mark)), \ (token).data.alias.value = (token_value)) #define ANCHOR_TOKEN_INIT(token,token_value,start_mark,end_mark) \ (TOKEN_INIT((token),YAML_ANCHOR_TOKEN,(start_mark),(end_mark)), \ (token).data.anchor.value = (token_value)) #define TAG_TOKEN_INIT(token,token_handle,token_suffix,start_mark,end_mark) \ (TOKEN_INIT((token),YAML_TAG_TOKEN,(start_mark),(end_mark)), \ (token).data.tag.handle = (token_handle), \ (token).data.tag.suffix = (token_suffix)) #define SCALAR_TOKEN_INIT(token,token_value,token_length,token_style,start_mark,end_mark) \ (TOKEN_INIT((token),YAML_SCALAR_TOKEN,(start_mark),(end_mark)), \ (token).data.scalar.value = (token_value), \ (token).data.scalar.length = (token_length), \ (token).data.scalar.style = (token_style)) #define VERSION_DIRECTIVE_TOKEN_INIT(token,token_major,token_minor,start_mark,end_mark) \ (TOKEN_INIT((token),YAML_VERSION_DIRECTIVE_TOKEN,(start_mark),(end_mark)), \ (token).data.version_directive.major = (token_major), \ (token).data.version_directive.minor = (token_minor)) #define TAG_DIRECTIVE_TOKEN_INIT(token,token_handle,token_prefix,start_mark,end_mark) \ (TOKEN_INIT((token),YAML_TAG_DIRECTIVE_TOKEN,(start_mark),(end_mark)), \ (token).data.tag_directive.handle = (token_handle), \ (token).data.tag_directive.prefix = (token_prefix)) /* * Event initializers. */ #define EVENT_INIT(event,event_type,event_start_mark,event_end_mark) \ (memset(&(event), 0, sizeof(yaml_event_t)), \ (event).type = (event_type), \ (event).start_mark = (event_start_mark), \ (event).end_mark = (event_end_mark)) #define STREAM_START_EVENT_INIT(event,event_encoding,start_mark,end_mark) \ (EVENT_INIT((event),YAML_STREAM_START_EVENT,(start_mark),(end_mark)), \ (event).data.stream_start.encoding = (event_encoding)) #define STREAM_END_EVENT_INIT(event,start_mark,end_mark) \ (EVENT_INIT((event),YAML_STREAM_END_EVENT,(start_mark),(end_mark))) #define DOCUMENT_START_EVENT_INIT(event,event_version_directive, \ event_tag_directives_start,event_tag_directives_end,event_implicit,start_mark,end_mark) \ (EVENT_INIT((event),YAML_DOCUMENT_START_EVENT,(start_mark),(end_mark)), \ (event).data.document_start.version_directive = (event_version_directive), \ (event).data.document_start.tag_directives.start = (event_tag_directives_start), \ (event).data.document_start.tag_directives.end = (event_tag_directives_end), \ (event).data.document_start.implicit = (event_implicit)) #define DOCUMENT_END_EVENT_INIT(event,event_implicit,start_mark,end_mark) \ (EVENT_INIT((event),YAML_DOCUMENT_END_EVENT,(start_mark),(end_mark)), \ (event).data.document_end.implicit = (event_implicit)) #define ALIAS_EVENT_INIT(event,event_anchor,start_mark,end_mark) \ (EVENT_INIT((event),YAML_ALIAS_EVENT,(start_mark),(end_mark)), \ (event).data.alias.anchor = (event_anchor)) #define SCALAR_EVENT_INIT(event,event_anchor,event_tag,event_value,event_length, \ event_plain_implicit, event_quoted_implicit,event_style,start_mark,end_mark) \ (EVENT_INIT((event),YAML_SCALAR_EVENT,(start_mark),(end_mark)), \ (event).data.scalar.anchor = (event_anchor), \ (event).data.scalar.tag = (event_tag), \ (event).data.scalar.value = (event_value), \ (event).data.scalar.length = (event_length), \ (event).data.scalar.plain_implicit = (event_plain_implicit), \ (event).data.scalar.quoted_implicit = (event_quoted_implicit), \ (event).data.scalar.style = (event_style)) #define SEQUENCE_START_EVENT_INIT(event,event_anchor,event_tag, \ event_implicit,event_style,start_mark,end_mark) \ (EVENT_INIT((event),YAML_SEQUENCE_START_EVENT,(start_mark),(end_mark)), \ (event).data.sequence_start.anchor = (event_anchor), \ (event).data.sequence_start.tag = (event_tag), \ (event).data.sequence_start.implicit = (event_implicit), \ (event).data.sequence_start.style = (event_style)) #define SEQUENCE_END_EVENT_INIT(event,start_mark,end_mark) \ (EVENT_INIT((event),YAML_SEQUENCE_END_EVENT,(start_mark),(end_mark))) #define MAPPING_START_EVENT_INIT(event,event_anchor,event_tag, \ event_implicit,event_style,start_mark,end_mark) \ (EVENT_INIT((event),YAML_MAPPING_START_EVENT,(start_mark),(end_mark)), \ (event).data.mapping_start.anchor = (event_anchor), \ (event).data.mapping_start.tag = (event_tag), \ (event).data.mapping_start.implicit = (event_implicit), \ (event).data.mapping_start.style = (event_style)) #define MAPPING_END_EVENT_INIT(event,start_mark,end_mark) \ (EVENT_INIT((event),YAML_MAPPING_END_EVENT,(start_mark),(end_mark))) /* * Document initializer. */ #define DOCUMENT_INIT(document,document_nodes_start,document_nodes_end, \ document_version_directive,document_tag_directives_start, \ document_tag_directives_end,document_start_implicit, \ document_end_implicit,document_start_mark,document_end_mark) \ (memset(&(document), 0, sizeof(yaml_document_t)), \ (document).nodes.start = (document_nodes_start), \ (document).nodes.end = (document_nodes_end), \ (document).nodes.top = (document_nodes_start), \ (document).version_directive = (document_version_directive), \ (document).tag_directives.start = (document_tag_directives_start), \ (document).tag_directives.end = (document_tag_directives_end), \ (document).start_implicit = (document_start_implicit), \ (document).end_implicit = (document_end_implicit), \ (document).start_mark = (document_start_mark), \ (document).end_mark = (document_end_mark)) /* * Node initializers. */ #define NODE_INIT(node,node_type,node_tag,node_start_mark,node_end_mark) \ (memset(&(node), 0, sizeof(yaml_node_t)), \ (node).type = (node_type), \ (node).tag = (node_tag), \ (node).start_mark = (node_start_mark), \ (node).end_mark = (node_end_mark)) #define SCALAR_NODE_INIT(node,node_tag,node_value,node_length, \ node_style,start_mark,end_mark) \ (NODE_INIT((node),YAML_SCALAR_NODE,(node_tag),(start_mark),(end_mark)), \ (node).data.scalar.value = (node_value), \ (node).data.scalar.length = (node_length), \ (node).data.scalar.style = (node_style)) #define SEQUENCE_NODE_INIT(node,node_tag,node_items_start,node_items_end, \ node_style,start_mark,end_mark) \ (NODE_INIT((node),YAML_SEQUENCE_NODE,(node_tag),(start_mark),(end_mark)), \ (node).data.sequence.items.start = (node_items_start), \ (node).data.sequence.items.end = (node_items_end), \ (node).data.sequence.items.top = (node_items_start), \ (node).data.sequence.style = (node_style)) #define MAPPING_NODE_INIT(node,node_tag,node_pairs_start,node_pairs_end, \ node_style,start_mark,end_mark) \ (NODE_INIT((node),YAML_MAPPING_NODE,(node_tag),(start_mark),(end_mark)), \ (node).data.mapping.pairs.start = (node_pairs_start), \ (node).data.mapping.pairs.end = (node_pairs_end), \ (node).data.mapping.pairs.top = (node_pairs_start), \ (node).data.mapping.style = (node_style)) /* Strict C compiler warning helpers */ #if defined(__clang__) || defined(__GNUC__) # define HASATTRIBUTE_UNUSED #endif #ifdef HASATTRIBUTE_UNUSED # define __attribute__unused__ __attribute__((__unused__)) #else # define __attribute__unused__ #endif /* Shim arguments are arguments that must be included in your function, * but serve no purpose inside. Silence compiler warnings. */ #define SHIM(a) /*@unused@*/ a __attribute__unused__ /* UNUSED_PARAM() marks a shim argument in the body to silence compiler warnings */ #ifdef __clang__ # define UNUSED_PARAM(a) (void)(a); #else # define UNUSED_PARAM(a) /*@-noeffect*/if (0) (void)(a)/*@=noeffect*/; #endif #define YAML_MALLOC_STATIC(type) (type*)yaml_malloc(sizeof(type)) #define YAML_MALLOC(size) (yaml_char_t *)yaml_malloc(size) yaml/src/r_emit.c0000644000176200001440000006276413614635636013462 0ustar liggesusers#include "r_ext.h" extern SEXP Ryaml_TagSymbol; extern SEXP Ryaml_DeparseFunc; extern char Ryaml_error_msg[ERROR_MSG_SIZE]; typedef struct { char *buffer; size_t size; size_t capa; } s_emitter_output; static SEXP Ryaml_deparse_function(s_obj) SEXP s_obj; { SEXP s_new_obj = NULL, s_call = NULL, s_result = NULL, s_chr = NULL; int i = 0, j = 0, res_len = 0, chr_len = 0, str_len = 0, str_end = 0; char *str = NULL; /* Copy function without any attributes */ if (TYPEOF(s_obj) == CLOSXP) { PROTECT(s_obj); PROTECT(s_new_obj = allocSExp(CLOSXP)); SET_FORMALS(s_new_obj, FORMALS(s_obj)); SET_BODY(s_new_obj, BODY(s_obj)); SET_CLOENV(s_new_obj, CLOENV(s_obj)); SET_OBJECT(s_new_obj, OBJECT(s_obj)); UNPROTECT(2); s_obj = s_new_obj; } /* first get R's deparsed character vector */ PROTECT(s_obj); PROTECT(s_call = lang2(Ryaml_DeparseFunc, s_obj)); s_result = eval(s_call, R_GlobalEnv); UNPROTECT(2); PROTECT(s_result); str_len = 0; res_len = length(s_result); for (i = 0; i < res_len; i++) { PROTECT(s_chr = STRING_ELT(s_result, i)); str_len += length(s_chr); UNPROTECT(1); } str_len += length(s_result); /* for newlines */ /* The point of this is to collapse the deparsed function whilst * eliminating trailing spaces. LibYAML's emitter won't output * a string with trailing spaces as a multiline scalar. */ str = (char *)malloc(sizeof(char) * (str_len + 1)); str_end = 0; for (i = 0; i < res_len; i++) { PROTECT(s_chr = STRING_ELT(s_result, i)); chr_len = length(s_chr); memcpy((void *)(str + str_end), (void *)CHAR(s_chr), chr_len); str_end += chr_len; UNPROTECT(1); /* find place to terminate line */ for (j = str_end - 1; j > 0; j--) { if (str[j] != ' ') { break; } } str[j + 1] = '\n'; str_end = j + 2; } /* null terminate string */ str[str_end] = 0; UNPROTECT(1); /* s_result */ PROTECT(s_result = allocVector(STRSXP, 1)); SET_STRING_ELT(s_result, 0, mkCharCE(str, CE_UTF8)); UNPROTECT(1); free(str); return s_result; } /* Format a vector of reals for emitting */ static SEXP Ryaml_format_real(s_obj, precision) SEXP s_obj; int precision; { SEXP s_retval = NULL; int i = 0, j = 0, k = 0, n = 0, suffix_len = 0; double x = 0, e = 0; char str[REAL_BUF_SIZE], format[5] = "%.*f", *strp = NULL; PROTECT(s_retval = allocVector(STRSXP, length(s_obj))); for (i = 0; i < length(s_obj); i++) { x = REAL(s_obj)[i]; if (x == R_PosInf) { SET_STRING_ELT(s_retval, i, mkCharCE(".inf", CE_UTF8)); } else if (x == R_NegInf) { SET_STRING_ELT(s_retval, i, mkCharCE("-.inf", CE_UTF8)); } else if (R_IsNA(x)) { SET_STRING_ELT(s_retval, i, mkCharCE(".na.real", CE_UTF8)); } else if (R_IsNaN(x)) { SET_STRING_ELT(s_retval, i, mkCharCE(".nan", CE_UTF8)); } else { if (x != 0) { e = log10(fabs(x)); if (e < -4 || e >= precision) { format[3] = 'e'; } } n = snprintf(str, REAL_BUF_SIZE, format, precision, x); if (n >= REAL_BUF_SIZE) { warning("string representation of numeric was truncated because it was more than %d characters", REAL_BUF_SIZE); } else if (n < 0) { error("couldn't format numeric value"); } else { /* tweak the string a little */ strp = str + n; /* end of the string */ j = n - 1; if (format[3] == 'e') { /* find 'e' first */ for (k = 0; j >= 0; j--, k++) { if (str[j] == 'e') { break; } } if (k == 4 && str[j+2] == '0') { /* windows sprintf likes to add an extra 0 to the exp part */ /* ex: 1.000e+007 */ str[j+2] = str[j+3]; str[j+3] = str[j+4]; str[j+4] = str[j+5]; /* null */ n -= 1; } strp = str + j; j -= 1; } suffix_len = n - j; /* remove trailing zeros */ for (k = 0; j >= 0; j--, k++) { if (str[j] != '0' || str[j-1] == '.') { break; } } if (k > 0) { memmove(str + j + 1, strp, suffix_len); } } SET_STRING_ELT(s_retval, i, mkCharCE(str, CE_UTF8)); } } UNPROTECT(1); return s_retval; } /* Format a vector of ints for emitting. Handle NAs. */ static SEXP Ryaml_format_int(s_obj) SEXP s_obj; { SEXP s_retval = NULL; int i = 0; PROTECT(s_retval = coerceVector(s_obj, STRSXP)); for (i = 0; i < length(s_obj); i++) { if (INTEGER(s_obj)[i] == NA_INTEGER) { SET_STRING_ELT(s_retval, i, mkCharCE(".na.integer", CE_UTF8)); } } UNPROTECT(1); return s_retval; } /* Format a vector of logicals for emitting. Handle NAs. */ static SEXP Ryaml_format_logical(s_obj) SEXP s_obj; { SEXP s_retval = NULL; int i = 0, val = 0; PROTECT(s_retval = allocVector(STRSXP, length(s_obj))); for (i = 0; i < length(s_obj); i++) { val = LOGICAL(s_obj)[i]; if (val == NA_LOGICAL) { SET_STRING_ELT(s_retval, i, mkCharCE(".na", CE_UTF8)); } else if (val == 0) { SET_STRING_ELT(s_retval, i, mkCharCE("no", CE_UTF8)); } else { SET_STRING_ELT(s_retval, i, mkCharCE("yes", CE_UTF8)); } } UNPROTECT(1); return s_retval; } /* Format a vector of strings for emitting. Handle NAs. */ static SEXP Ryaml_format_string(s_obj) SEXP s_obj; { SEXP s_retval = NULL; int i = 0; PROTECT(s_retval = duplicate(s_obj)); for (i = 0; i < length(s_obj); i++) { if (STRING_ELT(s_obj, i) == NA_STRING) { SET_STRING_ELT(s_retval, i, mkCharCE(".na.character", CE_UTF8)); } } UNPROTECT(1); return s_retval; } /* Take a CHARSXP, return a scalar style (for emitting) */ static yaml_scalar_style_t Ryaml_string_style(s_obj) SEXP s_obj; { char *tag = NULL; const char *chr = CHAR(s_obj); int len = 0, j = 0; PROTECT(s_obj); len = length(s_obj); UNPROTECT(1); tag = Ryaml_find_implicit_tag(chr, len); if (strcmp((char *) tag, "str#na") == 0) { return YAML_ANY_SCALAR_STYLE; } if (strcmp((char *) tag, "str") != 0) { /* If this element has an implicit tag, it needs to be quoted */ return YAML_SINGLE_QUOTED_SCALAR_STYLE; } /* Change to literal if there's a newline in this string */ for (j = 0; j < len; j++) { if (chr[j] == '\n') { return YAML_LITERAL_SCALAR_STYLE; } } return YAML_ANY_SCALAR_STYLE; } /* Take a vector and an index and return another vector of size 1 */ static SEXP Ryaml_yoink(s_vec, index) SEXP s_vec; int index; { SEXP s_tmp = NULL, s_levels = NULL; int type = 0, factor = 0, level_idx = 0; type = TYPEOF(s_vec); factor = type == INTSXP && Ryaml_has_class(s_vec, "factor"); PROTECT(s_tmp = allocVector(factor ? STRSXP : type, 1)); switch(type) { case LGLSXP: LOGICAL(s_tmp)[0] = LOGICAL(s_vec)[index]; break; case INTSXP: if (factor) { s_levels = GET_LEVELS(s_vec); level_idx = INTEGER(s_vec)[index]; if (level_idx == NA_INTEGER || level_idx < 1 || level_idx > LENGTH(s_levels)) { SET_STRING_ELT(s_tmp, 0, NA_STRING); } else { SET_STRING_ELT(s_tmp, 0, STRING_ELT(s_levels, level_idx - 1)); } } else { INTEGER(s_tmp)[0] = INTEGER(s_vec)[index]; } break; case REALSXP: REAL(s_tmp)[0] = REAL(s_vec)[index]; break; case CPLXSXP: COMPLEX(s_tmp)[0] = COMPLEX(s_vec)[index]; break; case STRSXP: SET_STRING_ELT(s_tmp, 0, STRING_ELT(s_vec, index)); break; case RAWSXP: RAW(s_tmp)[0] = RAW(s_vec)[index]; break; } UNPROTECT(1); return s_tmp; } static int Ryaml_serialize_to_yaml_write_handler(data, buffer, size) void *data; unsigned char *buffer; size_t size; { s_emitter_output *output = (s_emitter_output *)data; if (output->size + size > output->capa) { output->capa = (output->capa + size) * 2; output->buffer = (char *)realloc(output->buffer, output->capa * sizeof(char)); if (output->buffer == NULL) { return 0; } } memmove((void *)(output->buffer + output->size), (void *)buffer, size); output->size += size; return 1; } static int emit_char(emitter, event, s_obj, tag, implicit_tag, scalar_style) yaml_emitter_t *emitter; yaml_event_t *event; SEXP s_obj; char *tag; int implicit_tag; yaml_scalar_style_t scalar_style; { yaml_scalar_event_initialize(event, NULL, (yaml_char_t *)tag, (yaml_char_t *)CHAR(s_obj), LENGTH(s_obj), implicit_tag, implicit_tag, scalar_style); return yaml_emitter_emit(emitter, event); } static int emit_string(emitter, event, s_obj, tag, implicit_tag) yaml_emitter_t *emitter; yaml_event_t *event; SEXP s_obj; char *tag; int implicit_tag; { SEXP s_new_obj = NULL, s_chr = NULL; int result = 0, i = 0, verbatim = 0; verbatim = Ryaml_has_class(s_obj, "verbatim"); if (!verbatim) { PROTECT(s_obj); s_new_obj = Ryaml_format_string(s_obj); UNPROTECT(1); s_obj = s_new_obj; } PROTECT(s_obj); result = 0; for (i = 0; i < length(s_obj); i++) { PROTECT(s_chr = STRING_ELT(s_obj, i)); result = emit_char(emitter, event, s_chr, tag, implicit_tag, verbatim ? YAML_PLAIN_SCALAR_STYLE : Ryaml_string_style(s_chr)); UNPROTECT(1); /* s_chr */ if (!result) { break; } } UNPROTECT(1); /* s_obj */ return result; } static int emit_factor(emitter, event, s_obj, tag, implicit_tag) yaml_emitter_t *emitter; yaml_event_t *event; SEXP s_obj; char *tag; int implicit_tag; { SEXP s_levels = NULL, s_level_chr = NULL; yaml_scalar_style_t *scalar_styles = NULL, scalar_style; int i = 0, len = 0, level_idx = 0, result = 0, *scalar_style_is_set = NULL; s_levels = GET_LEVELS(s_obj); len = length(s_levels); scalar_styles = (yaml_scalar_style_t *)malloc(sizeof(yaml_scalar_style_t) * len); scalar_style_is_set = (int *)calloc(len, sizeof(int)); result = 1; for (i = 0; i < length(s_obj); i++) { level_idx = INTEGER(s_obj)[i]; if (level_idx == NA_INTEGER || level_idx < 1 || level_idx > len) { s_level_chr = mkCharCE(".na.character", CE_UTF8); scalar_style = YAML_ANY_SCALAR_STYLE; } else { s_level_chr = STRING_ELT(s_levels, level_idx - 1); if (!scalar_style_is_set[level_idx - 1]) { scalar_styles[level_idx - 1] = Ryaml_string_style(s_level_chr); } scalar_style = scalar_styles[level_idx - 1]; } result = emit_char(emitter, event, s_level_chr, tag, implicit_tag, scalar_style); if (!result) { break; } } free(scalar_styles); free(scalar_style_is_set); return result; } static int emit_nil(emitter, event, s_obj) yaml_emitter_t *emitter; yaml_event_t *event; SEXP s_obj; { yaml_scalar_event_initialize(event, NULL, NULL, (yaml_char_t *)"~", 1, 1, 1, YAML_ANY_SCALAR_STYLE); return yaml_emitter_emit(emitter, event); } static int emit_object(emitter, event, s_obj, omap, column_major, precision, s_handlers) yaml_emitter_t *emitter; yaml_event_t *event; SEXP s_obj; int omap; int column_major; int precision; SEXP s_handlers; { SEXP s_chr = NULL, s_names = NULL, s_elt = NULL, s_type = NULL, s_classes = NULL, s_class = NULL, s_tmp = NULL, s_inspect = NULL, s_handler = NULL, s_new_obj = NULL, s_tag = NULL; const char *inspect = NULL, *klass = NULL, *tag = NULL; int implicit_tag = 0, tag_applied = 0, rows = 0, cols = 0, i = 0, j = 0, result = 0, err = 0, len = 0, handled = 0; #if DEBUG Rprintf("=== Emitting ===\n"); PrintValue(s_obj); #endif /* Look for custom handler by class */ PROTECT(s_classes = Ryaml_get_classes(s_obj)); for (i = 0; i < length(s_classes); i++) { PROTECT(s_class = STRING_ELT(s_classes, i)); klass = CHAR(s_class); PROTECT(s_handler = Ryaml_find_handler(s_handlers, klass)); if (s_handler != R_NilValue) { err = Ryaml_run_handler(s_handler, s_obj, &s_new_obj); if (err != 0) { warning("an error occurred when handling object of class '%s'; using default handler", klass); } else { handled = 1; #if DEBUG PROTECT(s_new_obj); Rprintf("Result from custom handler:\n"); PrintValue(s_new_obj); UNPROTECT(1); #endif } } UNPROTECT(2); /* s_handler, s_class */ if (handled) { break; } } UNPROTECT(1); /* s_classes */ if (handled) { s_obj = s_new_obj; } /* Deparse function objects first, since this operation creates a new object. * Doing this here prevents having to reprotect an object later. */ if ((TYPEOF(s_obj) == CLOSXP || TYPEOF(s_obj) == SPECIALSXP || TYPEOF(s_obj) == BUILTINSXP)) { /* Deparse function into a string */ PROTECT(s_new_obj = Ryaml_deparse_function(s_obj)); /* Add '!expr' tag if not already tagged, otherwise reuse tag */ s_tag = getAttrib(s_obj, Ryaml_TagSymbol); if (s_tag == R_NilValue) { PROTECT(s_tag = ScalarString(mkCharCE("!expr", CE_UTF8))); setAttrib(s_new_obj, Ryaml_TagSymbol, s_tag); UNPROTECT(1); } else { /* Reuse tag for deparsed function string */ setAttrib(s_new_obj, Ryaml_TagSymbol, s_tag); } UNPROTECT(1); /* s_new_obj */ s_obj = s_new_obj; } /* If a custom handler transformed the object, it needs protecting. * Protecting an unchanged object is not harmful. */ PROTECT(s_obj); /* Check for custom tag */ implicit_tag = 1; tag = NULL; PROTECT(s_tag = getAttrib(s_obj, Ryaml_TagSymbol)); if (s_tag != R_NilValue) { if (TYPEOF(s_tag) != STRSXP || length(s_tag) != 1) { PROTECT(s_inspect = Ryaml_inspect(s_tag)); inspect = CHAR(STRING_ELT(s_inspect, 0)); warning("Invalid 'tag' attribute: %s", inspect); UNPROTECT(1); /* s_inspect */ } else { tag = CHAR(STRING_ELT(s_tag, 0)); implicit_tag = 0; } } switch (TYPEOF(s_obj)) { case NILSXP: /* NOTE: There is no way to tag NILSXP */ result = emit_nil(emitter, event, s_obj); break; /* atomic vector types */ case LGLSXP: case REALSXP: case INTSXP: case STRSXP: /* TODO: add complex and raw */ len = length(s_obj); if (len != 1) { /* Apply tag to sequence */ tag_applied = 1; yaml_sequence_start_event_initialize(event, NULL, (yaml_char_t *)tag, implicit_tag, YAML_ANY_SEQUENCE_STYLE); result = yaml_emitter_emit(emitter, event); if (!result) { break; } } if (len >= 1) { if (Ryaml_has_class(s_obj, "factor")) { if (tag_applied) { result = emit_factor(emitter, event, s_obj, NULL, 1); } else { result = emit_factor(emitter, event, s_obj, tag, implicit_tag); } if (!result) { break; } } else if (TYPEOF(s_obj) == STRSXP) { if (tag_applied) { result = emit_string(emitter, event, s_obj, NULL, 1); } else { result = emit_string(emitter, event, s_obj, tag, implicit_tag); } if (!result) { break; } } else { switch(TYPEOF(s_obj)) { case REALSXP: s_new_obj = Ryaml_format_real(s_obj, precision); break; case INTSXP: s_new_obj = Ryaml_format_int(s_obj); break; case LGLSXP: s_new_obj = Ryaml_format_logical(s_obj); break; } PROTECT(s_new_obj); for (i = 0; i < length(s_new_obj); i++) { PROTECT(s_chr = STRING_ELT(s_new_obj, i)); if (tag_applied) { result = emit_char(emitter, event, s_chr, NULL, 1, YAML_ANY_SCALAR_STYLE); } else { result = emit_char(emitter, event, s_chr, tag, implicit_tag, YAML_ANY_SCALAR_STYLE); } UNPROTECT(1); /* s_chr */ if (!result) { break; } } UNPROTECT(1); /* s_new_obj */ if (!result) { break; } } } if (length(s_obj) != 1) { yaml_sequence_end_event_initialize(event); result = yaml_emitter_emit(emitter, event); } break; case VECSXP: if (Ryaml_has_class(s_obj, "data.frame") && length(s_obj) > 0 && !column_major) { rows = length(VECTOR_ELT(s_obj, 0)); cols = length(s_obj); PROTECT(s_names = GET_NAMES(s_obj)); yaml_sequence_start_event_initialize(event, NULL, (yaml_char_t *)tag, implicit_tag, YAML_ANY_SEQUENCE_STYLE); result = yaml_emitter_emit(emitter, event); if (!result) { UNPROTECT(1); /* s_names */ break; } for (i = 0; i < rows; i++) { yaml_mapping_start_event_initialize(event, NULL, NULL, 1, YAML_ANY_MAPPING_STYLE); result = yaml_emitter_emit(emitter, event); if (!result) { break; } for (j = 0; j < cols; j++) { PROTECT(s_chr = STRING_ELT(s_names, j)); result = emit_char(emitter, event, s_chr, NULL, 1, Ryaml_string_style(s_chr)); UNPROTECT(1); if (!result) { break; } /* Need to create a vector of size one, then emit it */ PROTECT(s_elt = VECTOR_ELT(s_obj, j)); PROTECT(s_tmp = Ryaml_yoink(s_elt, i)); result = emit_object(emitter, event, s_tmp, omap, column_major, precision, s_handlers); UNPROTECT(2); if (!result) { break; } } if (result) { yaml_mapping_end_event_initialize(event); result = yaml_emitter_emit(emitter, event); } if (!result) { break; } } if (result) { yaml_sequence_end_event_initialize(event); result = yaml_emitter_emit(emitter, event); } UNPROTECT(1); /* s_names */ } else if (Ryaml_is_named_list(s_obj)) { if (omap) { if (tag == NULL) { tag = "!omap"; implicit_tag = 0; } yaml_sequence_start_event_initialize(event, NULL, (yaml_char_t *)tag, implicit_tag, YAML_ANY_SEQUENCE_STYLE); result = yaml_emitter_emit(emitter, event); } else { yaml_mapping_start_event_initialize(event, NULL, (yaml_char_t *)tag, implicit_tag, YAML_ANY_MAPPING_STYLE); result = yaml_emitter_emit(emitter, event); } if (!result) { break; } PROTECT(s_names = GET_NAMES(s_obj)); for (i = 0; i < length(s_obj); i++) { if (omap) { yaml_mapping_start_event_initialize(event, NULL, NULL, 1, YAML_ANY_MAPPING_STYLE); result = yaml_emitter_emit(emitter, event); if (!result) { break; } } PROTECT(s_chr = STRING_ELT(s_names, i)); result = emit_char(emitter, event, s_chr, NULL, 1, Ryaml_string_style(s_chr)); UNPROTECT(1); if (!result) { break; } PROTECT(s_elt = VECTOR_ELT(s_obj, i)); result = emit_object(emitter, event, s_elt, omap, column_major, precision, s_handlers); UNPROTECT(1); if (result && omap) { yaml_mapping_end_event_initialize(event); result = yaml_emitter_emit(emitter, event); } if (!result) { break; } } if (result) { if (omap) { yaml_sequence_end_event_initialize(event); } else { yaml_mapping_end_event_initialize(event); } result = yaml_emitter_emit(emitter, event); } UNPROTECT(1); /* s_names */ } else { yaml_sequence_start_event_initialize(event, NULL, (yaml_char_t *)tag, implicit_tag, YAML_ANY_SEQUENCE_STYLE); result = yaml_emitter_emit(emitter, event); if (!result) { break; } for (i = 0; i < length(s_obj); i++) { PROTECT(s_elt = VECTOR_ELT(s_obj, i)); result = emit_object(emitter, event, s_elt, omap, column_major, precision, s_handlers); UNPROTECT(1); if (!result) { break; } } yaml_sequence_end_event_initialize(event); result = yaml_emitter_emit(emitter, event); } break; default: PROTECT(s_type = type2str(TYPEOF(s_obj))); PROTECT(s_classes = GET_CLASS(s_obj)); if (TYPEOF(s_classes) != STRSXP || LENGTH(s_classes) == 0) { Ryaml_set_error_msg("don't know how to emit object of type: '%s'\n", CHAR(s_type)); } else { PROTECT(s_inspect = Ryaml_inspect(s_classes)); inspect = CHAR(STRING_ELT(s_inspect, 0)); Ryaml_set_error_msg("don't know how to emit object of type: '%s', class: %s\n", CHAR(s_type), inspect); UNPROTECT(1); /* s_inspect */ } UNPROTECT(2); /* s_type, s_classes */ result = 0; } UNPROTECT(2); /* s_obj, s_tag */ return result; } SEXP Ryaml_serialize_to_yaml(s_obj, s_line_sep, s_indent, s_omap, s_column_major, s_unicode, s_precision, s_indent_mapping_sequence, s_handlers) SEXP s_obj; SEXP s_line_sep; SEXP s_indent; SEXP s_omap; SEXP s_column_major; SEXP s_unicode; SEXP s_precision; SEXP s_indent_mapping_sequence; SEXP s_handlers; { SEXP s_retval = NULL; yaml_emitter_t emitter; yaml_event_t event; s_emitter_output output; int status = 0, line_sep = 0, indent = 0, omap = 0, column_major = 0, unicode = 0, precision = 0, indent_mapping_sequence = 0; const char *c_line_sep = NULL; c_line_sep = CHAR(STRING_ELT(s_line_sep, 0)); if (c_line_sep[0] == '\n') { line_sep = YAML_LN_BREAK; } else if (c_line_sep[0] == '\r') { if (c_line_sep[1] == '\n') { line_sep = YAML_CRLN_BREAK; } else { line_sep = YAML_CR_BREAK; } } else { error("argument `line.sep` must be a either '\n', '\r\n', or '\r'"); return R_NilValue; } if (isNumeric(s_indent) && length(s_indent) == 1) { s_indent = coerceVector(s_indent, INTSXP); indent = INTEGER(s_indent)[0]; } else if (isInteger(s_indent) && length(s_indent) == 1) { indent = INTEGER(s_indent)[0]; } else { error("argument `indent` must be a numeric or integer vector of length 1"); return R_NilValue; } if (indent <= 0) { error("argument `indent` must be greater than 0"); return R_NilValue; } if (!isLogical(s_omap) || length(s_omap) != 1) { error("argument `omap` must be either TRUE or FALSE"); return R_NilValue; } omap = LOGICAL(s_omap)[0]; if (!isLogical(s_column_major) || length(s_column_major) != 1) { error("argument `column.major` must be either TRUE or FALSE"); return R_NilValue; } column_major = LOGICAL(s_column_major)[0]; if (!isLogical(s_unicode) || length(s_unicode) != 1) { error("argument `unicode` must be either TRUE or FALSE"); return R_NilValue; } unicode = LOGICAL(s_unicode)[0]; if (isNumeric(s_precision) && length(s_precision) == 1) { s_precision = coerceVector(s_precision, INTSXP); precision = INTEGER(s_precision)[0]; } else if (isInteger(s_precision) && length(s_precision) == 1) { precision = INTEGER(s_precision)[0]; } else { error("argument `precision` must be a numeric or integer vector of length 1"); return R_NilValue; } if (precision < 1 || precision > 22) { error("argument `precision` must be in the range 1..22"); } if (!isLogical(s_indent_mapping_sequence) || length(s_indent_mapping_sequence) != 1) { error("argument `indent.mapping.sequence` must be either TRUE or FALSE"); return R_NilValue; } indent_mapping_sequence = LOGICAL(s_indent_mapping_sequence)[0]; PROTECT(s_handlers = Ryaml_sanitize_handlers(s_handlers)); yaml_emitter_initialize(&emitter); yaml_emitter_set_unicode(&emitter, unicode); yaml_emitter_set_break(&emitter, line_sep); yaml_emitter_set_indent(&emitter, indent); yaml_emitter_set_indent_mapping_sequence(&emitter, indent_mapping_sequence); output.buffer = NULL; output.size = output.capa = 0; yaml_emitter_set_output(&emitter, Ryaml_serialize_to_yaml_write_handler, &output); yaml_stream_start_event_initialize(&event, YAML_ANY_ENCODING); status = yaml_emitter_emit(&emitter, &event); if (!status) goto done; yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 1); status = yaml_emitter_emit(&emitter, &event); if (!status) goto done; status = emit_object(&emitter, &event, s_obj, omap, column_major, precision, s_handlers); if (!status) goto done; yaml_document_end_event_initialize(&event, 1); status = yaml_emitter_emit(&emitter, &event); if (!status) goto done; yaml_stream_end_event_initialize(&event); status = yaml_emitter_emit(&emitter, &event); done: UNPROTECT(1); /* s_handlers */ if (status) { PROTECT(s_retval = allocVector(STRSXP, 1)); SET_STRING_ELT(s_retval, 0, mkCharLen(output.buffer, output.size)); UNPROTECT(1); } else { if (emitter.problem != NULL) { Ryaml_set_error_msg("Emitter error: %s", emitter.problem); } else { Ryaml_set_error_msg("Unknown emitter error"); } s_retval = R_NilValue; } yaml_emitter_delete(&emitter); if (status) { free(output.buffer); } else { error(Ryaml_error_msg); } return s_retval; } yaml/src/api.c0000644000176200001440000010775313614635636012752 0ustar liggesusers #include "yaml_private.h" /* * Get the library version. */ YAML_DECLARE(const char *) yaml_get_version_string(void) { return YAML_VERSION_STRING; } /* * Get the library version numbers. */ YAML_DECLARE(void) yaml_get_version(int *major, int *minor, int *patch) { *major = YAML_VERSION_MAJOR; *minor = YAML_VERSION_MINOR; *patch = YAML_VERSION_PATCH; } /* * Allocate a dynamic memory block. */ YAML_DECLARE(void *) yaml_malloc(size_t size) { return malloc(size ? size : 1); } /* * Reallocate a dynamic memory block. */ YAML_DECLARE(void *) yaml_realloc(void *ptr, size_t size) { return ptr ? realloc(ptr, size ? size : 1) : malloc(size ? size : 1); } /* * Free a dynamic memory block. */ YAML_DECLARE(void) yaml_free(void *ptr) { if (ptr) free(ptr); } /* * Duplicate a string. */ YAML_DECLARE(yaml_char_t *) yaml_strdup(const yaml_char_t *str) { if (!str) return NULL; return (yaml_char_t *)strdup((char *)str); } /* * Extend a string. */ YAML_DECLARE(int) yaml_string_extend(yaml_char_t **start, yaml_char_t **pointer, yaml_char_t **end) { yaml_char_t *new_start = (yaml_char_t *)yaml_realloc((void*)*start, (*end - *start)*2); if (!new_start) return 0; memset(new_start + (*end - *start), 0, *end - *start); *pointer = new_start + (*pointer - *start); *end = new_start + (*end - *start)*2; *start = new_start; return 1; } /* * Append a string B to a string A. */ YAML_DECLARE(int) yaml_string_join( yaml_char_t **a_start, yaml_char_t **a_pointer, yaml_char_t **a_end, yaml_char_t **b_start, yaml_char_t **b_pointer, SHIM(yaml_char_t **b_end)) { UNUSED_PARAM(b_end) if (*b_start == *b_pointer) return 1; while (*a_end - *a_pointer <= *b_pointer - *b_start) { if (!yaml_string_extend(a_start, a_pointer, a_end)) return 0; } memcpy(*a_pointer, *b_start, *b_pointer - *b_start); *a_pointer += *b_pointer - *b_start; return 1; } /* * Extend a stack. */ YAML_DECLARE(int) yaml_stack_extend(void **start, void **top, void **end) { void *new_start; if ((char *)*end - (char *)*start >= INT_MAX / 2) return 0; new_start = yaml_realloc(*start, ((char *)*end - (char *)*start)*2); if (!new_start) return 0; *top = (char *)new_start + ((char *)*top - (char *)*start); *end = (char *)new_start + ((char *)*end - (char *)*start)*2; *start = new_start; return 1; } /* * Extend or move a queue. */ YAML_DECLARE(int) yaml_queue_extend(void **start, void **head, void **tail, void **end) { /* Check if we need to resize the queue. */ if (*start == *head && *tail == *end) { void *new_start = yaml_realloc(*start, ((char *)*end - (char *)*start)*2); if (!new_start) return 0; *head = (char *)new_start + ((char *)*head - (char *)*start); *tail = (char *)new_start + ((char *)*tail - (char *)*start); *end = (char *)new_start + ((char *)*end - (char *)*start)*2; *start = new_start; } /* Check if we need to move the queue at the beginning of the buffer. */ if (*tail == *end) { if (*head != *tail) { memmove(*start, *head, (char *)*tail - (char *)*head); } *tail = (char *)*tail - (char *)*head + (char *)*start; *head = *start; } return 1; } /* * Create a new parser object. */ YAML_DECLARE(int) yaml_parser_initialize(yaml_parser_t *parser) { assert(parser); /* Non-NULL parser object expected. */ memset(parser, 0, sizeof(yaml_parser_t)); if (!BUFFER_INIT(parser, parser->raw_buffer, INPUT_RAW_BUFFER_SIZE)) goto error; if (!BUFFER_INIT(parser, parser->buffer, INPUT_BUFFER_SIZE)) goto error; if (!QUEUE_INIT(parser, parser->tokens, INITIAL_QUEUE_SIZE, yaml_token_t*)) goto error; if (!STACK_INIT(parser, parser->indents, int*)) goto error; if (!STACK_INIT(parser, parser->simple_keys, yaml_simple_key_t*)) goto error; if (!STACK_INIT(parser, parser->states, yaml_parser_state_t*)) goto error; if (!STACK_INIT(parser, parser->marks, yaml_mark_t*)) goto error; if (!STACK_INIT(parser, parser->tag_directives, yaml_tag_directive_t*)) goto error; return 1; error: BUFFER_DEL(parser, parser->raw_buffer); BUFFER_DEL(parser, parser->buffer); QUEUE_DEL(parser, parser->tokens); STACK_DEL(parser, parser->indents); STACK_DEL(parser, parser->simple_keys); STACK_DEL(parser, parser->states); STACK_DEL(parser, parser->marks); STACK_DEL(parser, parser->tag_directives); return 0; } /* * Destroy a parser object. */ YAML_DECLARE(void) yaml_parser_delete(yaml_parser_t *parser) { assert(parser); /* Non-NULL parser object expected. */ BUFFER_DEL(parser, parser->raw_buffer); BUFFER_DEL(parser, parser->buffer); while (!QUEUE_EMPTY(parser, parser->tokens)) { yaml_token_delete(&DEQUEUE(parser, parser->tokens)); } QUEUE_DEL(parser, parser->tokens); STACK_DEL(parser, parser->indents); STACK_DEL(parser, parser->simple_keys); STACK_DEL(parser, parser->states); STACK_DEL(parser, parser->marks); while (!STACK_EMPTY(parser, parser->tag_directives)) { yaml_tag_directive_t tag_directive = POP(parser, parser->tag_directives); yaml_free(tag_directive.handle); yaml_free(tag_directive.prefix); } STACK_DEL(parser, parser->tag_directives); memset(parser, 0, sizeof(yaml_parser_t)); } /* * String read handler. */ static int yaml_string_read_handler(void *data, unsigned char *buffer, size_t size, size_t *size_read) { yaml_parser_t *parser = (yaml_parser_t *)data; if (parser->input.string.current == parser->input.string.end) { *size_read = 0; return 1; } if (size > (size_t)(parser->input.string.end - parser->input.string.current)) { size = parser->input.string.end - parser->input.string.current; } memcpy(buffer, parser->input.string.current, size); parser->input.string.current += size; *size_read = size; return 1; } /* * File read handler. */ static int yaml_file_read_handler(void *data, unsigned char *buffer, size_t size, size_t *size_read) { yaml_parser_t *parser = (yaml_parser_t *)data; *size_read = fread(buffer, 1, size, parser->input.file); return !ferror(parser->input.file); } /* * Set a string input. */ YAML_DECLARE(void) yaml_parser_set_input_string(yaml_parser_t *parser, const unsigned char *input, size_t size) { assert(parser); /* Non-NULL parser object expected. */ assert(!parser->read_handler); /* You can set the source only once. */ assert(input); /* Non-NULL input string expected. */ parser->read_handler = yaml_string_read_handler; parser->read_handler_data = parser; parser->input.string.start = input; parser->input.string.current = input; parser->input.string.end = input+size; } /* * Set a file input. */ YAML_DECLARE(void) yaml_parser_set_input_file(yaml_parser_t *parser, FILE *file) { assert(parser); /* Non-NULL parser object expected. */ assert(!parser->read_handler); /* You can set the source only once. */ assert(file); /* Non-NULL file object expected. */ parser->read_handler = yaml_file_read_handler; parser->read_handler_data = parser; parser->input.file = file; } /* * Set a generic input. */ YAML_DECLARE(void) yaml_parser_set_input(yaml_parser_t *parser, yaml_read_handler_t *handler, void *data) { assert(parser); /* Non-NULL parser object expected. */ assert(!parser->read_handler); /* You can set the source only once. */ assert(handler); /* Non-NULL read handler expected. */ parser->read_handler = handler; parser->read_handler_data = data; } /* * Set the source encoding. */ YAML_DECLARE(void) yaml_parser_set_encoding(yaml_parser_t *parser, yaml_encoding_t encoding) { assert(parser); /* Non-NULL parser object expected. */ assert(!parser->encoding); /* Encoding is already set or detected. */ parser->encoding = encoding; } /* * Create a new emitter object. */ YAML_DECLARE(int) yaml_emitter_initialize(yaml_emitter_t *emitter) { assert(emitter); /* Non-NULL emitter object expected. */ memset(emitter, 0, sizeof(yaml_emitter_t)); if (!BUFFER_INIT(emitter, emitter->buffer, OUTPUT_BUFFER_SIZE)) goto error; if (!BUFFER_INIT(emitter, emitter->raw_buffer, OUTPUT_RAW_BUFFER_SIZE)) goto error; if (!STACK_INIT(emitter, emitter->states, yaml_emitter_state_t*)) goto error; if (!QUEUE_INIT(emitter, emitter->events, INITIAL_QUEUE_SIZE, yaml_event_t*)) goto error; if (!STACK_INIT(emitter, emitter->indents, int*)) goto error; if (!STACK_INIT(emitter, emitter->tag_directives, yaml_tag_directive_t*)) goto error; return 1; error: BUFFER_DEL(emitter, emitter->buffer); BUFFER_DEL(emitter, emitter->raw_buffer); STACK_DEL(emitter, emitter->states); QUEUE_DEL(emitter, emitter->events); STACK_DEL(emitter, emitter->indents); STACK_DEL(emitter, emitter->tag_directives); return 0; } /* * Destroy an emitter object. */ YAML_DECLARE(void) yaml_emitter_delete(yaml_emitter_t *emitter) { assert(emitter); /* Non-NULL emitter object expected. */ BUFFER_DEL(emitter, emitter->buffer); BUFFER_DEL(emitter, emitter->raw_buffer); STACK_DEL(emitter, emitter->states); while (!QUEUE_EMPTY(emitter, emitter->events)) { yaml_event_delete(&DEQUEUE(emitter, emitter->events)); } QUEUE_DEL(emitter, emitter->events); STACK_DEL(emitter, emitter->indents); while (!STACK_EMPTY(empty, emitter->tag_directives)) { yaml_tag_directive_t tag_directive = POP(emitter, emitter->tag_directives); yaml_free(tag_directive.handle); yaml_free(tag_directive.prefix); } STACK_DEL(emitter, emitter->tag_directives); yaml_free(emitter->anchors); memset(emitter, 0, sizeof(yaml_emitter_t)); } /* * String write handler. */ static int yaml_string_write_handler(void *data, unsigned char *buffer, size_t size) { yaml_emitter_t *emitter = (yaml_emitter_t *)data; if (emitter->output.string.size - *emitter->output.string.size_written < size) { memcpy(emitter->output.string.buffer + *emitter->output.string.size_written, buffer, emitter->output.string.size - *emitter->output.string.size_written); *emitter->output.string.size_written = emitter->output.string.size; return 0; } memcpy(emitter->output.string.buffer + *emitter->output.string.size_written, buffer, size); *emitter->output.string.size_written += size; return 1; } /* * File write handler. */ static int yaml_file_write_handler(void *data, unsigned char *buffer, size_t size) { yaml_emitter_t *emitter = (yaml_emitter_t *)data; return (fwrite(buffer, 1, size, emitter->output.file) == size); } /* * Set a string output. */ YAML_DECLARE(void) yaml_emitter_set_output_string(yaml_emitter_t *emitter, unsigned char *output, size_t size, size_t *size_written) { assert(emitter); /* Non-NULL emitter object expected. */ assert(!emitter->write_handler); /* You can set the output only once. */ assert(output); /* Non-NULL output string expected. */ emitter->write_handler = yaml_string_write_handler; emitter->write_handler_data = emitter; emitter->output.string.buffer = output; emitter->output.string.size = size; emitter->output.string.size_written = size_written; *size_written = 0; } /* * Set a file output. */ YAML_DECLARE(void) yaml_emitter_set_output_file(yaml_emitter_t *emitter, FILE *file) { assert(emitter); /* Non-NULL emitter object expected. */ assert(!emitter->write_handler); /* You can set the output only once. */ assert(file); /* Non-NULL file object expected. */ emitter->write_handler = yaml_file_write_handler; emitter->write_handler_data = emitter; emitter->output.file = file; } /* * Set a generic output handler. */ YAML_DECLARE(void) yaml_emitter_set_output(yaml_emitter_t *emitter, yaml_write_handler_t *handler, void *data) { assert(emitter); /* Non-NULL emitter object expected. */ assert(!emitter->write_handler); /* You can set the output only once. */ assert(handler); /* Non-NULL handler object expected. */ emitter->write_handler = handler; emitter->write_handler_data = data; } /* * Set the output encoding. */ YAML_DECLARE(void) yaml_emitter_set_encoding(yaml_emitter_t *emitter, yaml_encoding_t encoding) { assert(emitter); /* Non-NULL emitter object expected. */ assert(!emitter->encoding); /* You can set encoding only once. */ emitter->encoding = encoding; } /* * Set the canonical output style. */ YAML_DECLARE(void) yaml_emitter_set_canonical(yaml_emitter_t *emitter, int canonical) { assert(emitter); /* Non-NULL emitter object expected. */ emitter->canonical = (canonical != 0); } /* * Set the indentation increment. */ YAML_DECLARE(void) yaml_emitter_set_indent(yaml_emitter_t *emitter, int indent) { assert(emitter); /* Non-NULL emitter object expected. */ emitter->best_indent = (1 < indent && indent < 10) ? indent : 2; } /* * Set whether or not to indent block sequences in mapping context. */ YAML_DECLARE(void) yaml_emitter_set_indent_mapping_sequence(yaml_emitter_t *emitter, int indent_mapping_sequence) { assert(emitter); /* Non-NULL emitter object expected. */ emitter->indent_mapping_sequence = indent_mapping_sequence; } /* * Set the preferred line width. */ YAML_DECLARE(void) yaml_emitter_set_width(yaml_emitter_t *emitter, int width) { assert(emitter); /* Non-NULL emitter object expected. */ emitter->best_width = (width >= 0) ? width : -1; } /* * Set if unescaped non-ASCII characters are allowed. */ YAML_DECLARE(void) yaml_emitter_set_unicode(yaml_emitter_t *emitter, int unicode) { assert(emitter); /* Non-NULL emitter object expected. */ emitter->unicode = (unicode != 0); } /* * Set the preferred line break character. */ YAML_DECLARE(void) yaml_emitter_set_break(yaml_emitter_t *emitter, yaml_break_t line_break) { assert(emitter); /* Non-NULL emitter object expected. */ emitter->line_break = line_break; } /* * Destroy a token object. */ YAML_DECLARE(void) yaml_token_delete(yaml_token_t *token) { assert(token); /* Non-NULL token object expected. */ switch (token->type) { case YAML_TAG_DIRECTIVE_TOKEN: yaml_free(token->data.tag_directive.handle); yaml_free(token->data.tag_directive.prefix); break; case YAML_ALIAS_TOKEN: yaml_free(token->data.alias.value); break; case YAML_ANCHOR_TOKEN: yaml_free(token->data.anchor.value); break; case YAML_TAG_TOKEN: yaml_free(token->data.tag.handle); yaml_free(token->data.tag.suffix); break; case YAML_SCALAR_TOKEN: yaml_free(token->data.scalar.value); break; default: break; } memset(token, 0, sizeof(yaml_token_t)); } /* * Check if a string is a valid UTF-8 sequence. * * Check 'reader.c' for more details on UTF-8 encoding. */ static int yaml_check_utf8(yaml_char_t *start, size_t length) { yaml_char_t *end = start+length; yaml_char_t *pointer = start; while (pointer < end) { unsigned char octet; unsigned int width; unsigned int value; size_t k; octet = pointer[0]; width = (octet & 0x80) == 0x00 ? 1 : (octet & 0xE0) == 0xC0 ? 2 : (octet & 0xF0) == 0xE0 ? 3 : (octet & 0xF8) == 0xF0 ? 4 : 0; value = (octet & 0x80) == 0x00 ? octet & 0x7F : (octet & 0xE0) == 0xC0 ? octet & 0x1F : (octet & 0xF0) == 0xE0 ? octet & 0x0F : (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; if (!width) return 0; if (pointer+width > end) return 0; for (k = 1; k < width; k ++) { octet = pointer[k]; if ((octet & 0xC0) != 0x80) return 0; value = (value << 6) + (octet & 0x3F); } if (!((width == 1) || (width == 2 && value >= 0x80) || (width == 3 && value >= 0x800) || (width == 4 && value >= 0x10000))) return 0; pointer += width; } return 1; } /* * Create STREAM-START. */ YAML_DECLARE(int) yaml_stream_start_event_initialize(yaml_event_t *event, yaml_encoding_t encoding) { yaml_mark_t mark = { 0, 0, 0 }; assert(event); /* Non-NULL event object is expected. */ STREAM_START_EVENT_INIT(*event, encoding, mark, mark); return 1; } /* * Create STREAM-END. */ YAML_DECLARE(int) yaml_stream_end_event_initialize(yaml_event_t *event) { yaml_mark_t mark = { 0, 0, 0 }; assert(event); /* Non-NULL event object is expected. */ STREAM_END_EVENT_INIT(*event, mark, mark); return 1; } /* * Create DOCUMENT-START. */ YAML_DECLARE(int) yaml_document_start_event_initialize(yaml_event_t *event, yaml_version_directive_t *version_directive, yaml_tag_directive_t *tag_directives_start, yaml_tag_directive_t *tag_directives_end, int implicit) { struct { yaml_error_type_t error; } context; yaml_mark_t mark = { 0, 0, 0 }; yaml_version_directive_t *version_directive_copy = NULL; struct { yaml_tag_directive_t *start; yaml_tag_directive_t *end; yaml_tag_directive_t *top; } tag_directives_copy = { NULL, NULL, NULL }; yaml_tag_directive_t value = { NULL, NULL }; assert(event); /* Non-NULL event object is expected. */ assert((tag_directives_start && tag_directives_end) || (tag_directives_start == tag_directives_end)); /* Valid tag directives are expected. */ if (version_directive) { version_directive_copy = YAML_MALLOC_STATIC(yaml_version_directive_t); if (!version_directive_copy) goto error; version_directive_copy->major = version_directive->major; version_directive_copy->minor = version_directive->minor; } if (tag_directives_start != tag_directives_end) { yaml_tag_directive_t *tag_directive; if (!STACK_INIT(&context, tag_directives_copy, yaml_tag_directive_t*)) goto error; for (tag_directive = tag_directives_start; tag_directive != tag_directives_end; tag_directive ++) { assert(tag_directive->handle); assert(tag_directive->prefix); if (!yaml_check_utf8(tag_directive->handle, strlen((char *)tag_directive->handle))) goto error; if (!yaml_check_utf8(tag_directive->prefix, strlen((char *)tag_directive->prefix))) goto error; value.handle = yaml_strdup(tag_directive->handle); value.prefix = yaml_strdup(tag_directive->prefix); if (!value.handle || !value.prefix) goto error; if (!PUSH(&context, tag_directives_copy, value)) goto error; value.handle = NULL; value.prefix = NULL; } } DOCUMENT_START_EVENT_INIT(*event, version_directive_copy, tag_directives_copy.start, tag_directives_copy.top, implicit, mark, mark); return 1; error: yaml_free(version_directive_copy); while (!STACK_EMPTY(context, tag_directives_copy)) { yaml_tag_directive_t value = POP(context, tag_directives_copy); yaml_free(value.handle); yaml_free(value.prefix); } STACK_DEL(context, tag_directives_copy); yaml_free(value.handle); yaml_free(value.prefix); return 0; } /* * Create DOCUMENT-END. */ YAML_DECLARE(int) yaml_document_end_event_initialize(yaml_event_t *event, int implicit) { yaml_mark_t mark = { 0, 0, 0 }; assert(event); /* Non-NULL emitter object is expected. */ DOCUMENT_END_EVENT_INIT(*event, implicit, mark, mark); return 1; } /* * Create ALIAS. */ YAML_DECLARE(int) yaml_alias_event_initialize(yaml_event_t *event, yaml_char_t *anchor) { yaml_mark_t mark = { 0, 0, 0 }; yaml_char_t *anchor_copy = NULL; assert(event); /* Non-NULL event object is expected. */ assert(anchor); /* Non-NULL anchor is expected. */ if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0; anchor_copy = yaml_strdup(anchor); if (!anchor_copy) return 0; ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark); return 1; } /* * Create SCALAR. */ YAML_DECLARE(int) yaml_scalar_event_initialize(yaml_event_t *event, yaml_char_t *anchor, yaml_char_t *tag, yaml_char_t *value, int length, int plain_implicit, int quoted_implicit, yaml_scalar_style_t style) { yaml_mark_t mark = { 0, 0, 0 }; yaml_char_t *anchor_copy = NULL; yaml_char_t *tag_copy = NULL; yaml_char_t *value_copy = NULL; assert(event); /* Non-NULL event object is expected. */ assert(value); /* Non-NULL anchor is expected. */ if (anchor) { if (!yaml_check_utf8(anchor, strlen((char *)anchor))) goto error; anchor_copy = yaml_strdup(anchor); if (!anchor_copy) goto error; } if (tag) { if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error; tag_copy = yaml_strdup(tag); if (!tag_copy) goto error; } if (length < 0) { length = strlen((char *)value); } if (!yaml_check_utf8(value, length)) goto error; value_copy = YAML_MALLOC(length+1); if (!value_copy) goto error; memcpy(value_copy, value, length); value_copy[length] = '\0'; SCALAR_EVENT_INIT(*event, anchor_copy, tag_copy, value_copy, length, plain_implicit, quoted_implicit, style, mark, mark); return 1; error: yaml_free(anchor_copy); yaml_free(tag_copy); yaml_free(value_copy); return 0; } /* * Create SEQUENCE-START. */ YAML_DECLARE(int) yaml_sequence_start_event_initialize(yaml_event_t *event, yaml_char_t *anchor, yaml_char_t *tag, int implicit, yaml_sequence_style_t style) { yaml_mark_t mark = { 0, 0, 0 }; yaml_char_t *anchor_copy = NULL; yaml_char_t *tag_copy = NULL; assert(event); /* Non-NULL event object is expected. */ if (anchor) { if (!yaml_check_utf8(anchor, strlen((char *)anchor))) goto error; anchor_copy = yaml_strdup(anchor); if (!anchor_copy) goto error; } if (tag) { if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error; tag_copy = yaml_strdup(tag); if (!tag_copy) goto error; } SEQUENCE_START_EVENT_INIT(*event, anchor_copy, tag_copy, implicit, style, mark, mark); return 1; error: yaml_free(anchor_copy); yaml_free(tag_copy); return 0; } /* * Create SEQUENCE-END. */ YAML_DECLARE(int) yaml_sequence_end_event_initialize(yaml_event_t *event) { yaml_mark_t mark = { 0, 0, 0 }; assert(event); /* Non-NULL event object is expected. */ SEQUENCE_END_EVENT_INIT(*event, mark, mark); return 1; } /* * Create MAPPING-START. */ YAML_DECLARE(int) yaml_mapping_start_event_initialize(yaml_event_t *event, yaml_char_t *anchor, yaml_char_t *tag, int implicit, yaml_mapping_style_t style) { yaml_mark_t mark = { 0, 0, 0 }; yaml_char_t *anchor_copy = NULL; yaml_char_t *tag_copy = NULL; assert(event); /* Non-NULL event object is expected. */ if (anchor) { if (!yaml_check_utf8(anchor, strlen((char *)anchor))) goto error; anchor_copy = yaml_strdup(anchor); if (!anchor_copy) goto error; } if (tag) { if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error; tag_copy = yaml_strdup(tag); if (!tag_copy) goto error; } MAPPING_START_EVENT_INIT(*event, anchor_copy, tag_copy, implicit, style, mark, mark); return 1; error: yaml_free(anchor_copy); yaml_free(tag_copy); return 0; } /* * Create MAPPING-END. */ YAML_DECLARE(int) yaml_mapping_end_event_initialize(yaml_event_t *event) { yaml_mark_t mark = { 0, 0, 0 }; assert(event); /* Non-NULL event object is expected. */ MAPPING_END_EVENT_INIT(*event, mark, mark); return 1; } /* * Destroy an event object. */ YAML_DECLARE(void) yaml_event_delete(yaml_event_t *event) { yaml_tag_directive_t *tag_directive; assert(event); /* Non-NULL event object expected. */ switch (event->type) { case YAML_DOCUMENT_START_EVENT: yaml_free(event->data.document_start.version_directive); for (tag_directive = event->data.document_start.tag_directives.start; tag_directive != event->data.document_start.tag_directives.end; tag_directive++) { yaml_free(tag_directive->handle); yaml_free(tag_directive->prefix); } yaml_free(event->data.document_start.tag_directives.start); break; case YAML_ALIAS_EVENT: yaml_free(event->data.alias.anchor); break; case YAML_SCALAR_EVENT: yaml_free(event->data.scalar.anchor); yaml_free(event->data.scalar.tag); yaml_free(event->data.scalar.value); break; case YAML_SEQUENCE_START_EVENT: yaml_free(event->data.sequence_start.anchor); yaml_free(event->data.sequence_start.tag); break; case YAML_MAPPING_START_EVENT: yaml_free(event->data.mapping_start.anchor); yaml_free(event->data.mapping_start.tag); break; default: break; } memset(event, 0, sizeof(yaml_event_t)); } /* * Create a document object. */ YAML_DECLARE(int) yaml_document_initialize(yaml_document_t *document, yaml_version_directive_t *version_directive, yaml_tag_directive_t *tag_directives_start, yaml_tag_directive_t *tag_directives_end, int start_implicit, int end_implicit) { struct { yaml_error_type_t error; } context; struct { yaml_node_t *start; yaml_node_t *end; yaml_node_t *top; } nodes = { NULL, NULL, NULL }; yaml_version_directive_t *version_directive_copy = NULL; struct { yaml_tag_directive_t *start; yaml_tag_directive_t *end; yaml_tag_directive_t *top; } tag_directives_copy = { NULL, NULL, NULL }; yaml_tag_directive_t value = { NULL, NULL }; yaml_mark_t mark = { 0, 0, 0 }; assert(document); /* Non-NULL document object is expected. */ assert((tag_directives_start && tag_directives_end) || (tag_directives_start == tag_directives_end)); /* Valid tag directives are expected. */ if (!STACK_INIT(&context, nodes, yaml_node_t*)) goto error; if (version_directive) { version_directive_copy = YAML_MALLOC_STATIC(yaml_version_directive_t); if (!version_directive_copy) goto error; version_directive_copy->major = version_directive->major; version_directive_copy->minor = version_directive->minor; } if (tag_directives_start != tag_directives_end) { yaml_tag_directive_t *tag_directive; if (!STACK_INIT(&context, tag_directives_copy, yaml_tag_directive_t*)) goto error; for (tag_directive = tag_directives_start; tag_directive != tag_directives_end; tag_directive ++) { assert(tag_directive->handle); assert(tag_directive->prefix); if (!yaml_check_utf8(tag_directive->handle, strlen((char *)tag_directive->handle))) goto error; if (!yaml_check_utf8(tag_directive->prefix, strlen((char *)tag_directive->prefix))) goto error; value.handle = yaml_strdup(tag_directive->handle); value.prefix = yaml_strdup(tag_directive->prefix); if (!value.handle || !value.prefix) goto error; if (!PUSH(&context, tag_directives_copy, value)) goto error; value.handle = NULL; value.prefix = NULL; } } DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, tag_directives_copy.start, tag_directives_copy.top, start_implicit, end_implicit, mark, mark); return 1; error: STACK_DEL(&context, nodes); yaml_free(version_directive_copy); while (!STACK_EMPTY(&context, tag_directives_copy)) { yaml_tag_directive_t value = POP(&context, tag_directives_copy); yaml_free(value.handle); yaml_free(value.prefix); } STACK_DEL(&context, tag_directives_copy); yaml_free(value.handle); yaml_free(value.prefix); return 0; } /* * Destroy a document object. */ YAML_DECLARE(void) yaml_document_delete(yaml_document_t *document) { yaml_tag_directive_t *tag_directive; assert(document); /* Non-NULL document object is expected. */ while (!STACK_EMPTY(&context, document->nodes)) { yaml_node_t node = POP(&context, document->nodes); yaml_free(node.tag); switch (node.type) { case YAML_SCALAR_NODE: yaml_free(node.data.scalar.value); break; case YAML_SEQUENCE_NODE: STACK_DEL(&context, node.data.sequence.items); break; case YAML_MAPPING_NODE: STACK_DEL(&context, node.data.mapping.pairs); break; default: assert(0); /* Should not happen. */ } } STACK_DEL(&context, document->nodes); yaml_free(document->version_directive); for (tag_directive = document->tag_directives.start; tag_directive != document->tag_directives.end; tag_directive++) { yaml_free(tag_directive->handle); yaml_free(tag_directive->prefix); } yaml_free(document->tag_directives.start); memset(document, 0, sizeof(yaml_document_t)); } /** * Get a document node. */ YAML_DECLARE(yaml_node_t *) yaml_document_get_node(yaml_document_t *document, int index) { assert(document); /* Non-NULL document object is expected. */ if (index > 0 && document->nodes.start + index <= document->nodes.top) { return document->nodes.start + index - 1; } return NULL; } /** * Get the root object. */ YAML_DECLARE(yaml_node_t *) yaml_document_get_root_node(yaml_document_t *document) { assert(document); /* Non-NULL document object is expected. */ if (document->nodes.top != document->nodes.start) { return document->nodes.start; } return NULL; } /* * Add a scalar node to a document. */ YAML_DECLARE(int) yaml_document_add_scalar(yaml_document_t *document, yaml_char_t *tag, yaml_char_t *value, int length, yaml_scalar_style_t style) { struct { yaml_error_type_t error; } context; yaml_mark_t mark = { 0, 0, 0 }; yaml_char_t *tag_copy = NULL; yaml_char_t *value_copy = NULL; yaml_node_t node; assert(document); /* Non-NULL document object is expected. */ assert(value); /* Non-NULL value is expected. */ if (!tag) { tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG; } if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error; tag_copy = yaml_strdup(tag); if (!tag_copy) goto error; if (length < 0) { length = strlen((char *)value); } if (!yaml_check_utf8(value, length)) goto error; value_copy = YAML_MALLOC(length+1); if (!value_copy) goto error; memcpy(value_copy, value, length); value_copy[length] = '\0'; SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark); if (!PUSH(&context, document->nodes, node)) goto error; return document->nodes.top - document->nodes.start; error: yaml_free(tag_copy); yaml_free(value_copy); return 0; } /* * Add a sequence node to a document. */ YAML_DECLARE(int) yaml_document_add_sequence(yaml_document_t *document, yaml_char_t *tag, yaml_sequence_style_t style) { struct { yaml_error_type_t error; } context; yaml_mark_t mark = { 0, 0, 0 }; yaml_char_t *tag_copy = NULL; struct { yaml_node_item_t *start; yaml_node_item_t *end; yaml_node_item_t *top; } items = { NULL, NULL, NULL }; yaml_node_t node; assert(document); /* Non-NULL document object is expected. */ if (!tag) { tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG; } if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error; tag_copy = yaml_strdup(tag); if (!tag_copy) goto error; if (!STACK_INIT(&context, items, yaml_node_item_t*)) goto error; SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, style, mark, mark); if (!PUSH(&context, document->nodes, node)) goto error; return document->nodes.top - document->nodes.start; error: STACK_DEL(&context, items); yaml_free(tag_copy); return 0; } /* * Add a mapping node to a document. */ YAML_DECLARE(int) yaml_document_add_mapping(yaml_document_t *document, yaml_char_t *tag, yaml_mapping_style_t style) { struct { yaml_error_type_t error; } context; yaml_mark_t mark = { 0, 0, 0 }; yaml_char_t *tag_copy = NULL; struct { yaml_node_pair_t *start; yaml_node_pair_t *end; yaml_node_pair_t *top; } pairs = { NULL, NULL, NULL }; yaml_node_t node; assert(document); /* Non-NULL document object is expected. */ if (!tag) { tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG; } if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error; tag_copy = yaml_strdup(tag); if (!tag_copy) goto error; if (!STACK_INIT(&context, pairs, yaml_node_pair_t*)) goto error; MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, style, mark, mark); if (!PUSH(&context, document->nodes, node)) goto error; return document->nodes.top - document->nodes.start; error: STACK_DEL(&context, pairs); yaml_free(tag_copy); return 0; } /* * Append an item to a sequence node. */ YAML_DECLARE(int) yaml_document_append_sequence_item(yaml_document_t *document, int sequence, int item) { struct { yaml_error_type_t error; } context; assert(document); /* Non-NULL document is required. */ assert(sequence > 0 && document->nodes.start + sequence <= document->nodes.top); /* Valid sequence id is required. */ assert(document->nodes.start[sequence-1].type == YAML_SEQUENCE_NODE); /* A sequence node is required. */ assert(item > 0 && document->nodes.start + item <= document->nodes.top); /* Valid item id is required. */ if (!PUSH(&context, document->nodes.start[sequence-1].data.sequence.items, item)) return 0; return 1; } /* * Append a pair of a key and a value to a mapping node. */ YAML_DECLARE(int) yaml_document_append_mapping_pair(yaml_document_t *document, int mapping, int key, int value) { struct { yaml_error_type_t error; } context; yaml_node_pair_t pair; assert(document); /* Non-NULL document is required. */ assert(mapping > 0 && document->nodes.start + mapping <= document->nodes.top); /* Valid mapping id is required. */ assert(document->nodes.start[mapping-1].type == YAML_MAPPING_NODE); /* A mapping node is required. */ assert(key > 0 && document->nodes.start + key <= document->nodes.top); /* Valid key id is required. */ assert(value > 0 && document->nodes.start + value <= document->nodes.top); /* Valid value id is required. */ pair.key = key; pair.value = value; if (!PUSH(&context, document->nodes.start[mapping-1].data.mapping.pairs, pair)) return 0; return 1; } yaml/src/loader.c0000644000176200001440000002716113614635636013441 0ustar liggesusers #include "yaml_private.h" /* * API functions. */ YAML_DECLARE(int) yaml_parser_load(yaml_parser_t *parser, yaml_document_t *document); /* * Error handling. */ static int yaml_parser_set_composer_error(yaml_parser_t *parser, const char *problem, yaml_mark_t problem_mark); static int yaml_parser_set_composer_error_context(yaml_parser_t *parser, const char *context, yaml_mark_t context_mark, const char *problem, yaml_mark_t problem_mark); /* * Alias handling. */ static int yaml_parser_register_anchor(yaml_parser_t *parser, int index, yaml_char_t *anchor); /* * Clean up functions. */ static void yaml_parser_delete_aliases(yaml_parser_t *parser); /* * Composer functions. */ static int yaml_parser_load_document(yaml_parser_t *parser, yaml_event_t *first_event); static int yaml_parser_load_node(yaml_parser_t *parser, yaml_event_t *first_event); static int yaml_parser_load_alias(yaml_parser_t *parser, yaml_event_t *first_event); static int yaml_parser_load_scalar(yaml_parser_t *parser, yaml_event_t *first_event); static int yaml_parser_load_sequence(yaml_parser_t *parser, yaml_event_t *first_event); static int yaml_parser_load_mapping(yaml_parser_t *parser, yaml_event_t *first_event); /* * Load the next document of the stream. */ YAML_DECLARE(int) yaml_parser_load(yaml_parser_t *parser, yaml_document_t *document) { yaml_event_t event; assert(parser); /* Non-NULL parser object is expected. */ assert(document); /* Non-NULL document object is expected. */ memset(document, 0, sizeof(yaml_document_t)); if (!STACK_INIT(parser, document->nodes, yaml_node_t*)) goto error; if (!parser->stream_start_produced) { if (!yaml_parser_parse(parser, &event)) goto error; assert(event.type == YAML_STREAM_START_EVENT); /* STREAM-START is expected. */ } if (parser->stream_end_produced) { return 1; } if (!yaml_parser_parse(parser, &event)) goto error; if (event.type == YAML_STREAM_END_EVENT) { return 1; } if (!STACK_INIT(parser, parser->aliases, yaml_alias_data_t*)) goto error; parser->document = document; if (!yaml_parser_load_document(parser, &event)) goto error; yaml_parser_delete_aliases(parser); parser->document = NULL; return 1; error: yaml_parser_delete_aliases(parser); yaml_document_delete(document); parser->document = NULL; return 0; } /* * Set composer error. */ static int yaml_parser_set_composer_error(yaml_parser_t *parser, const char *problem, yaml_mark_t problem_mark) { parser->error = YAML_COMPOSER_ERROR; parser->problem = problem; parser->problem_mark = problem_mark; return 0; } /* * Set composer error with context. */ static int yaml_parser_set_composer_error_context(yaml_parser_t *parser, const char *context, yaml_mark_t context_mark, const char *problem, yaml_mark_t problem_mark) { parser->error = YAML_COMPOSER_ERROR; parser->context = context; parser->context_mark = context_mark; parser->problem = problem; parser->problem_mark = problem_mark; return 0; } /* * Delete the stack of aliases. */ static void yaml_parser_delete_aliases(yaml_parser_t *parser) { while (!STACK_EMPTY(parser, parser->aliases)) { yaml_free(POP(parser, parser->aliases).anchor); } STACK_DEL(parser, parser->aliases); } /* * Compose a document object. */ static int yaml_parser_load_document(yaml_parser_t *parser, yaml_event_t *first_event) { yaml_event_t event; assert(first_event->type == YAML_DOCUMENT_START_EVENT); /* DOCUMENT-START is expected. */ parser->document->version_directive = first_event->data.document_start.version_directive; parser->document->tag_directives.start = first_event->data.document_start.tag_directives.start; parser->document->tag_directives.end = first_event->data.document_start.tag_directives.end; parser->document->start_implicit = first_event->data.document_start.implicit; parser->document->start_mark = first_event->start_mark; if (!yaml_parser_parse(parser, &event)) return 0; if (!yaml_parser_load_node(parser, &event)) return 0; if (!yaml_parser_parse(parser, &event)) return 0; assert(event.type == YAML_DOCUMENT_END_EVENT); /* DOCUMENT-END is expected. */ parser->document->end_implicit = event.data.document_end.implicit; parser->document->end_mark = event.end_mark; return 1; } /* * Compose a node. */ static int yaml_parser_load_node(yaml_parser_t *parser, yaml_event_t *first_event) { switch (first_event->type) { case YAML_ALIAS_EVENT: return yaml_parser_load_alias(parser, first_event); case YAML_SCALAR_EVENT: return yaml_parser_load_scalar(parser, first_event); case YAML_SEQUENCE_START_EVENT: return yaml_parser_load_sequence(parser, first_event); case YAML_MAPPING_START_EVENT: return yaml_parser_load_mapping(parser, first_event); default: assert(0); /* Could not happen. */ return 0; } return 0; } /* * Add an anchor. */ static int yaml_parser_register_anchor(yaml_parser_t *parser, int index, yaml_char_t *anchor) { yaml_alias_data_t data; yaml_alias_data_t *alias_data; if (!anchor) return 1; data.anchor = anchor; data.index = index; data.mark = parser->document->nodes.start[index-1].start_mark; for (alias_data = parser->aliases.start; alias_data != parser->aliases.top; alias_data ++) { if (strcmp((char *)alias_data->anchor, (char *)anchor) == 0) { yaml_free(anchor); return yaml_parser_set_composer_error_context(parser, "found duplicate anchor; first occurrence", alias_data->mark, "second occurrence", data.mark); } } if (!PUSH(parser, parser->aliases, data)) { yaml_free(anchor); return 0; } return 1; } /* * Compose a node corresponding to an alias. */ static int yaml_parser_load_alias(yaml_parser_t *parser, yaml_event_t *first_event) { yaml_char_t *anchor = first_event->data.alias.anchor; yaml_alias_data_t *alias_data; for (alias_data = parser->aliases.start; alias_data != parser->aliases.top; alias_data ++) { if (strcmp((char *)alias_data->anchor, (char *)anchor) == 0) { yaml_free(anchor); return alias_data->index; } } yaml_free(anchor); return yaml_parser_set_composer_error(parser, "found undefined alias", first_event->start_mark); } /* * Compose a scalar node. */ static int yaml_parser_load_scalar(yaml_parser_t *parser, yaml_event_t *first_event) { yaml_node_t node; int index; yaml_char_t *tag = first_event->data.scalar.tag; if (!STACK_LIMIT(parser, parser->document->nodes, INT_MAX-1)) goto error; if (!tag || strcmp((char *)tag, "!") == 0) { yaml_free(tag); tag = yaml_strdup((yaml_char_t *)YAML_DEFAULT_SCALAR_TAG); if (!tag) goto error; } SCALAR_NODE_INIT(node, tag, first_event->data.scalar.value, first_event->data.scalar.length, first_event->data.scalar.style, first_event->start_mark, first_event->end_mark); if (!PUSH(parser, parser->document->nodes, node)) goto error; index = parser->document->nodes.top - parser->document->nodes.start; if (!yaml_parser_register_anchor(parser, index, first_event->data.scalar.anchor)) return 0; return index; error: yaml_free(tag); yaml_free(first_event->data.scalar.anchor); yaml_free(first_event->data.scalar.value); return 0; } /* * Compose a sequence node. */ static int yaml_parser_load_sequence(yaml_parser_t *parser, yaml_event_t *first_event) { yaml_event_t event; yaml_node_t node; struct { yaml_node_item_t *start; yaml_node_item_t *end; yaml_node_item_t *top; } items = { NULL, NULL, NULL }; int index, item_index; yaml_char_t *tag = first_event->data.sequence_start.tag; if (!STACK_LIMIT(parser, parser->document->nodes, INT_MAX-1)) goto error; if (!tag || strcmp((char *)tag, "!") == 0) { yaml_free(tag); tag = yaml_strdup((yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG); if (!tag) goto error; } if (!STACK_INIT(parser, items, yaml_node_item_t*)) goto error; SEQUENCE_NODE_INIT(node, tag, items.start, items.end, first_event->data.sequence_start.style, first_event->start_mark, first_event->end_mark); if (!PUSH(parser, parser->document->nodes, node)) goto error; index = parser->document->nodes.top - parser->document->nodes.start; if (!yaml_parser_register_anchor(parser, index, first_event->data.sequence_start.anchor)) return 0; if (!yaml_parser_parse(parser, &event)) return 0; while (event.type != YAML_SEQUENCE_END_EVENT) { if (!STACK_LIMIT(parser, parser->document->nodes.start[index-1].data.sequence.items, INT_MAX-1)) return 0; item_index = yaml_parser_load_node(parser, &event); if (!item_index) return 0; if (!PUSH(parser, parser->document->nodes.start[index-1].data.sequence.items, item_index)) return 0; if (!yaml_parser_parse(parser, &event)) return 0; } parser->document->nodes.start[index-1].end_mark = event.end_mark; return index; error: yaml_free(tag); yaml_free(first_event->data.sequence_start.anchor); return 0; } /* * Compose a mapping node. */ static int yaml_parser_load_mapping(yaml_parser_t *parser, yaml_event_t *first_event) { yaml_event_t event; yaml_node_t node; struct { yaml_node_pair_t *start; yaml_node_pair_t *end; yaml_node_pair_t *top; } pairs = { NULL, NULL, NULL }; int index; yaml_node_pair_t pair; yaml_char_t *tag = first_event->data.mapping_start.tag; if (!STACK_LIMIT(parser, parser->document->nodes, INT_MAX-1)) goto error; if (!tag || strcmp((char *)tag, "!") == 0) { yaml_free(tag); tag = yaml_strdup((yaml_char_t *)YAML_DEFAULT_MAPPING_TAG); if (!tag) goto error; } if (!STACK_INIT(parser, pairs, yaml_node_pair_t*)) goto error; MAPPING_NODE_INIT(node, tag, pairs.start, pairs.end, first_event->data.mapping_start.style, first_event->start_mark, first_event->end_mark); if (!PUSH(parser, parser->document->nodes, node)) goto error; index = parser->document->nodes.top - parser->document->nodes.start; if (!yaml_parser_register_anchor(parser, index, first_event->data.mapping_start.anchor)) return 0; if (!yaml_parser_parse(parser, &event)) return 0; while (event.type != YAML_MAPPING_END_EVENT) { if (!STACK_LIMIT(parser, parser->document->nodes.start[index-1].data.mapping.pairs, INT_MAX-1)) return 0; pair.key = yaml_parser_load_node(parser, &event); if (!pair.key) return 0; if (!yaml_parser_parse(parser, &event)) return 0; pair.value = yaml_parser_load_node(parser, &event); if (!pair.value) return 0; if (!PUSH(parser, parser->document->nodes.start[index-1].data.mapping.pairs, pair)) return 0; if (!yaml_parser_parse(parser, &event)) return 0; } parser->document->nodes.start[index-1].end_mark = event.end_mark; return index; error: yaml_free(tag); yaml_free(first_event->data.mapping_start.anchor); return 0; } yaml/src/r_parse.c0000644000176200001440000011316213614635636013623 0ustar liggesusers#include "r_ext.h" extern SEXP Ryaml_KeysSymbol; extern SEXP Ryaml_IdenticalFunc; extern SEXP Ryaml_Sentinel; extern SEXP Ryaml_SequenceStart; extern SEXP Ryaml_MappingStart; extern SEXP Ryaml_MappingEnd; extern char Ryaml_error_msg[ERROR_MSG_SIZE]; /* Compare two R objects (with the R identical function). * Returns 0 or 1 */ static int Ryaml_cmp(s_first, s_second) SEXP s_first; SEXP s_second; { int i = 0, retval = 0, *arr = NULL; SEXP s_call = NULL, s_result = NULL, s_bool = NULL; PROTECT(s_bool = allocVector(LGLSXP, 1)); LOGICAL(s_bool)[0] = 1; PROTECT(s_call = LCONS(Ryaml_IdenticalFunc, list4(s_first, s_second, s_bool, s_bool))); PROTECT(s_result = eval(s_call, R_GlobalEnv)); arr = LOGICAL(s_result); for(i = 0; i < length(s_result); i++) { if (!arr[i]) { retval = 1; break; } } UNPROTECT(3); return retval; } /* Returns the index of the first instance of needle in haystack */ static int Ryaml_index(s_haystack, s_needle, character, upper_bound) SEXP s_haystack; SEXP s_needle; int character; int upper_bound; { int i = 0; if (character) { for (i = 0; i < upper_bound; i++) { if (strcmp(CHAR(s_needle), CHAR(STRING_ELT(s_haystack, i))) == 0) { return i; } } } else { for (i = 0; i < upper_bound; i++) { if (Ryaml_cmp(s_needle, VECTOR_ELT(s_haystack, i)) == 0) { return i; } } } return -1; } /* Returns true if obj is a list with a keys attribute */ static int Ryaml_is_pseudo_hash(s_obj) SEXP s_obj; { SEXP s_keys = NULL; if (TYPEOF(s_obj) != VECSXP) return 0; s_keys = getAttrib(s_obj, Ryaml_KeysSymbol); return (s_keys != R_NilValue && TYPEOF(s_keys) == VECSXP); } /* Set a character attribute on an R object */ static void Ryaml_set_str_attrib(s_obj, s_sym, str) SEXP s_obj; SEXP s_sym; char *str; { SEXP s_val = NULL; PROTECT(s_val = ScalarString(mkCharCE(str, CE_UTF8))); setAttrib(s_obj, s_sym, s_val); UNPROTECT(1); } /* Set the R object's class attribute */ static void Ryaml_set_class(s_obj, name) SEXP s_obj; char *name; { Ryaml_set_str_attrib(s_obj, R_ClassSymbol, name); } /* Get the type part of the tag, throw away any !'s */ static char * process_tag(tag) char *tag; { char *retval = tag; if (strncmp(retval, "tag:yaml.org,2002:", 18) == 0) { retval = retval + 18; } else { while (*retval == '!') { retval++; } } return retval; } static int handle_alias(event, s_stack_tail, s_aliases_head) yaml_event_t *event; SEXP *s_stack_tail; SEXP s_aliases_head; { SEXP s_curr = NULL, s_obj = NULL; int handled = 0; const char *name = NULL, *anchor = NULL; /* Try to find object with the supplied anchor */ anchor = (const char *)event->data.alias.anchor; s_curr = CDR(s_aliases_head); while (s_curr != R_NilValue) { s_obj = CAR(s_curr); name = CHAR(TAG(s_curr)); if (strcmp(name, anchor) == 0) { /* Found object, push onto stack */ SETCDR(*s_stack_tail, list1(s_obj)); *s_stack_tail = CDR(*s_stack_tail); MARK_NOT_MUTABLE(s_obj); handled = 1; break; } s_curr = CDR(s_curr); } if (!handled) { warning("Unknown anchor: %s", (char *)event->data.alias.anchor); PROTECT(s_obj = ScalarString(mkCharCE("_yaml.bad-anchor_", CE_UTF8))); Ryaml_set_class(s_obj, "_yaml.bad-anchor_"); UNPROTECT(1); SETCDR(*s_stack_tail, list1(s_obj)); *s_stack_tail = CDR(*s_stack_tail); } return 0; } static int handle_scalar(event, s_stack_tail, s_handlers, eval_expr, eval_warning) yaml_event_t *event; SEXP *s_stack_tail; SEXP s_handlers; int eval_expr; int eval_warning; { SEXP s_obj = NULL, s_handler = NULL, s_new_obj = NULL, s_expr = NULL; const char *value = NULL, *tag = NULL, *nptr = NULL; char *endptr = NULL; size_t len = 0; int handled = 0, coercion_err = 0, base = 0, n = 0; long int long_n = 0; double f = 0.0f; ParseStatus parse_status; tag = (const char *)event->data.scalar.tag; value = (const char *)event->data.scalar.value; len = event->data.scalar.length; if (tag == NULL || strcmp(tag, "!") == 0) { /* There's no tag! */ /* If this is a quoted string, leave it as a string */ switch (event->data.scalar.style) { case YAML_SINGLE_QUOTED_SCALAR_STYLE: case YAML_DOUBLE_QUOTED_SCALAR_STYLE: tag = "str"; break; default: /* Try to tag it */ tag = Ryaml_find_implicit_tag(value, len); } } else { tag = process_tag(tag); } #if DEBUG Rprintf("Value: (%s), Tag: (%s)\n", value, tag); #endif /* 'Vanilla' object */ PROTECT(s_obj = ScalarString(mkCharCE(value, CE_UTF8))); /* Look for a custom R handler */ PROTECT(s_handler = Ryaml_find_handler(s_handlers, (const char *)tag)); if (s_handler != R_NilValue) { if (Ryaml_run_handler(s_handler, s_obj, &s_new_obj) != 0) { warning("an error occurred when handling type '%s'; using default handler", tag); } else { handled = 1; } } UNPROTECT(1); /* s_handler */ if (!handled) { /* Default handlers */ if (strcmp(tag, "str") == 0) { /* already a string */ } else if (strcmp(tag, "seq") == 0) { coercion_err = 1; } else if (strcmp(tag, "int#na") == 0) { s_new_obj = ScalarInteger(NA_INTEGER); } else if (strcmp(tag, "int") == 0 || strncmp(tag, "int#", 4) == 0) { base = -1; if (strcmp(tag, "int") == 0) { base = 10; } else if (strcmp(tag, "int#hex") == 0) { base = 16; } else if (strcmp(tag, "int#oct") == 0) { base = 8; } if (base >= 0) { errno = 0; nptr = CHAR(STRING_ELT(s_obj, 0)); long_n = strtol(nptr, &endptr, base); if (*endptr != 0) { /* strtol is perfectly happy converting partial strings to * integers, but R isn't. If you call as.integer() on a * string that isn't completely an integer, you get back * an NA. So I'm reproducing that behavior here. */ warning("NAs introduced by coercion: %s is not an integer", nptr); n = NA_INTEGER; } else if (errno == ERANGE) { warning("NAs introduced by coercion: %s is out of integer range", nptr); n = NA_INTEGER; } else if (long_n < INT_MIN || long_n > INT_MAX || (int)long_n == NA_INTEGER) { warning("NAs introduced by coercion: %s is out of integer range", nptr); n = NA_INTEGER; } else { n = (int)long_n; } s_new_obj = ScalarInteger(n); } else { /* unknown integer base; no-op */ } } else if (strcmp(tag, "float") == 0 || strcmp(tag, "float#fix") == 0 || strcmp(tag, "float#exp") == 0) { errno = 0; nptr = CHAR(STRING_ELT(s_obj, 0)); f = strtod(nptr, &endptr); if (*endptr != 0) { /* No valid floats found (see note above about integers) */ warning("NAs introduced by coercion: %s is not a real", nptr); f = NA_REAL; } else if (errno == ERANGE || f == NA_REAL) { warning("NAs introduced by coercion: %s is out of real range", nptr); f = NA_REAL; } s_new_obj = ScalarReal(f); } else if (strcmp(tag, "bool") == 0) { /* This would happen if someone explicitly specified a tag of 'bool' */ tag = Ryaml_find_implicit_tag(value, len); if (strcmp(tag, "bool#yes") == 0) { s_new_obj = ScalarLogical(TRUE); } else if (strcmp(tag, "bool#no") == 0) { s_new_obj = ScalarLogical(FALSE); } else if (strcmp(tag, "bool#na") == 0) { s_new_obj = ScalarLogical(NA_LOGICAL); } else { warning("NAs introduced by coercion: %s is not a recognized boolean value", value); s_new_obj = ScalarLogical(NA_LOGICAL); } } else if (strcmp(tag, "bool#yes") == 0) { s_new_obj = ScalarLogical(TRUE); } else if (strcmp(tag, "bool#no") == 0) { s_new_obj = ScalarLogical(FALSE); } else if (strcmp(tag, "bool#na") == 0) { s_new_obj = ScalarLogical(NA_LOGICAL); } else if (strcmp(tag, "omap") == 0) { coercion_err = 1; } else if (strcmp(tag, "merge") == 0) { /* see http://yaml.org/type/merge.html */ PROTECT(s_new_obj = ScalarString(mkCharCE("_yaml.merge_", CE_UTF8))); Ryaml_set_class(s_new_obj, "_yaml.merge_"); UNPROTECT(1); } else if (strcmp(tag, "float#na") == 0) { s_new_obj = ScalarReal(NA_REAL); } else if (strcmp(tag, "float#nan") == 0) { s_new_obj = ScalarReal(R_NaN); } else if (strcmp(tag, "float#inf") == 0) { s_new_obj = ScalarReal(R_PosInf); } else if (strcmp(tag, "float#neginf") == 0) { s_new_obj = ScalarReal(R_NegInf); } else if (strcmp(tag, "str#na") == 0) { s_new_obj = ScalarString(NA_STRING); } else if (strcmp(tag, "null") == 0) { s_new_obj = R_NilValue; } else if (strcmp(tag, "expr") == 0) { if (eval_expr) { PROTECT(s_obj); s_expr = R_ParseVector(s_obj, 1, &parse_status, R_NilValue); UNPROTECT(1); if (parse_status != PARSE_OK) { coercion_err = 1; Ryaml_set_error_msg("Could not parse expression: %s", CHAR(STRING_ELT(s_obj, 0))); } else { /* NOTE: R_tryEval will not return if R_Interactive is FALSE. */ PROTECT(s_expr); PROTECT(s_new_obj = R_tryEval(VECTOR_ELT(s_expr, 0), R_GlobalEnv, &coercion_err)); if (coercion_err) { Ryaml_set_error_msg("Could not evaluate expression: %s", CHAR(STRING_ELT(s_obj, 0))); } else if (eval_warning) { warning("Evaluating R expressions (!expr) will soon require explicit `eval.expr` option (see yaml.load help)"); } UNPROTECT(2); /* s_expr, s_new_obj */ } } } } UNPROTECT(1); /* s_obj */ if (coercion_err == 1) { if (Ryaml_error_msg[0] == 0) { Ryaml_set_error_msg("Invalid tag for scalar: %s", tag); } return 1; } SETCDR(*s_stack_tail, list1(s_new_obj == NULL ? s_obj : s_new_obj)); *s_stack_tail = CDR(*s_stack_tail); return 0; } static void handle_structure_start(event, s_stack_tail, is_map) yaml_event_t *event; SEXP *s_stack_tail; int is_map; { SEXP s_sym = NULL, s_tag_obj = NULL, s_anchor_obj = NULL, s_tag = NULL; yaml_char_t *tag = NULL, *anchor = NULL; if (is_map) { s_sym = Ryaml_MappingStart; tag = event->data.mapping_start.tag; anchor = event->data.mapping_start.anchor; } else { s_sym = Ryaml_SequenceStart; tag = event->data.sequence_start.tag; anchor = event->data.sequence_start.anchor; } SETCDR(*s_stack_tail, list1(s_sym)); *s_stack_tail = CDR(*s_stack_tail); /* Create pairlist tag */ if (tag == NULL) { s_tag_obj = R_NilValue; } else { s_tag_obj = mkCharCE((const char *)tag, CE_UTF8); } if (anchor == NULL) { s_anchor_obj = R_NilValue; } else { PROTECT(s_tag_obj); s_anchor_obj = mkCharCE((const char *)anchor, CE_UTF8); UNPROTECT(1); } s_tag = list2(s_tag_obj, s_anchor_obj); SET_TAG(*s_stack_tail, s_tag); } static int handle_sequence(event, s_stack_head, s_stack_tail, s_handlers, coerce_keys) yaml_event_t *event; SEXP s_stack_head; SEXP *s_stack_tail; SEXP s_handlers; int coerce_keys; { SEXP s_curr = NULL, s_obj = NULL, s_sequence_start = NULL, s_list = NULL, s_handler = NULL, s_new_obj = NULL, s_keys = NULL, s_key = NULL, s_tag = NULL, s_inspect = NULL; int count = 0, i = 0, j = 0, type = 0, child_type = 0, handled = 0, coercion_err = 0, len = 0, total_len = 0, dup_key = 0, idx = 0, obj_len = 0; const char *tag = NULL, *inspect = NULL; /* Find start of sequence and count elements */ s_curr = CDR(s_stack_head); count = 0; while (s_curr != R_NilValue) { if (CAR(s_curr) == Ryaml_SequenceStart) { s_sequence_start = s_curr; count = 0; } else if (s_sequence_start != NULL) { count++; } s_curr = CDR(s_curr); } if (s_sequence_start == NULL) { Ryaml_set_error_msg("Internal error: couldn't find start of sequence!"); return 1; } s_tag = CAR(TAG(s_sequence_start)); tag = s_tag == R_NilValue ? NULL : CHAR(s_tag); /* Initialize list */ PROTECT(s_list = allocVector(VECSXP, count)); /* Populate the list, popping items off the stack as we go */ type = -2; s_curr = CDR(s_sequence_start); for (i = 0; i < count; i++) { s_obj = CAR(s_curr); s_curr = CDR(s_curr); SET_VECTOR_ELT(s_list, i, s_obj); /* Treat primitive vectors with more than one element as a list for * coercion purposes. */ child_type = TYPEOF(s_obj); switch (child_type) { case LGLSXP: case INTSXP: case REALSXP: case STRSXP: if (length(s_obj) != 1) { child_type = VECSXP; } break; } if (type == -2) { type = child_type; } else if (type != -1 && child_type != type) { type = -1; } } /* Tags! */ if (tag == NULL) { tag = "seq"; } else { tag = process_tag(tag); } /* Look for a custom R handler */ s_handler = Ryaml_find_handler(s_handlers, (const char *)tag); if (s_handler != R_NilValue) { if (Ryaml_run_handler(s_handler, s_list, &s_new_obj) != 0) { warning("an error occurred when handling type '%s'; using default handler", tag); } else { handled = 1; } } if (!handled) { /* default handlers, ordered by most-used */ if (strcmp(tag, "seq") == 0) { /* Let's try to coerce this list! */ switch (type) { case LGLSXP: case INTSXP: case REALSXP: case STRSXP: s_new_obj = coerceVector(s_list, type); break; } } else if (strcmp(tag, "str") == 0) { coercion_err = 1; } else if (strcmp(tag, "int#na") == 0) { coercion_err = 1; } else if (strcmp(tag, "int") == 0 || strncmp(tag, "int#", 4) == 0) { coercion_err = 1; } else if (strcmp(tag, "float") == 0 || strcmp(tag, "float#fix") == 0 || strcmp(tag, "float#exp") == 0) { coercion_err = 1; } else if (strcmp(tag, "bool#yes") == 0) { coercion_err = 1; } else if (strcmp(tag, "bool#no") == 0) { coercion_err = 1; } else if (strcmp(tag, "bool#na") == 0) { coercion_err = 1; } else if (strcmp(tag, "omap") == 0) { /* NOTE: This is here mostly because of backwards compatibility * with R yaml 1.x package. All maps are ordered in 2.x, so there's * no real need to use omap */ len = length(s_list); total_len = 0; for (i = 0; i < len; i++) { s_obj = VECTOR_ELT(s_list, i); if ((coerce_keys && !Ryaml_is_named_list(s_obj)) || (!coerce_keys && !Ryaml_is_pseudo_hash(s_obj))) { Ryaml_set_error_msg("omap must be a sequence of maps"); coercion_err = 1; break; } total_len += length(s_obj); } /* Construct the list! */ if (!coercion_err) { PROTECT(s_new_obj = allocVector(VECSXP, total_len)); if (coerce_keys) { s_keys = allocVector(STRSXP, total_len); SET_NAMES(s_new_obj, s_keys); } else { s_keys = allocVector(VECSXP, total_len); setAttrib(s_new_obj, Ryaml_KeysSymbol, s_keys); } for (i = 0, idx = 0; i < len && dup_key == 0; i++) { s_obj = VECTOR_ELT(s_list, i); obj_len = length(s_obj); for (j = 0; j < obj_len && dup_key == 0; j++) { SET_VECTOR_ELT(s_new_obj, idx, VECTOR_ELT(s_obj, j)); if (coerce_keys) { PROTECT(s_key = STRING_ELT(GET_NAMES(s_obj), j)); SET_STRING_ELT(s_keys, idx, s_key); if (Ryaml_index(s_keys, s_key, 1, idx) >= 0) { dup_key = 1; Ryaml_set_error_msg("Duplicate omap key: '%s'", CHAR(s_key)); } UNPROTECT(1); /* s_key */ } else { s_key = VECTOR_ELT(getAttrib(s_obj, Ryaml_KeysSymbol), j); SET_VECTOR_ELT(s_keys, idx, s_key); if (Ryaml_index(s_keys, s_key, 0, idx) >= 0) { dup_key = 1; PROTECT(s_inspect = Ryaml_inspect(s_key)); inspect = CHAR(STRING_ELT(s_inspect, 0)); Ryaml_set_error_msg("Duplicate omap key: %s", inspect); UNPROTECT(1); } } idx++; } } UNPROTECT(1); /* s_new_obj */ if (dup_key == 1) { coercion_err = 1; } } } else if (strcmp(tag, "merge") == 0) { coercion_err = 1; } else if (strcmp(tag, "float#na") == 0) { coercion_err = 1; } else if (strcmp(tag, "float#nan") == 0) { coercion_err = 1; } else if (strcmp(tag, "float#inf") == 0) { coercion_err = 1; } else if (strcmp(tag, "float#neginf") == 0) { coercion_err = 1; } else if (strcmp(tag, "str#na") == 0) { coercion_err = 1; } else if (strcmp(tag, "null") == 0) { s_new_obj = R_NilValue; } else if (strcmp(tag, "expr") == 0) { coercion_err = 1; } } UNPROTECT(1); /* s_list */ if (coercion_err == 1) { if (Ryaml_error_msg[0] == 0) { Ryaml_set_error_msg("Invalid tag: %s for sequence", tag); } return 1; } SETCAR(s_sequence_start, s_new_obj == NULL ? s_list : s_new_obj); SETCDR(s_sequence_start, R_NilValue); *s_stack_tail = s_sequence_start; return 0; } static SEXP find_map_entry(s_map_head, s_key, character) SEXP s_map_head; SEXP s_key; int character; { SEXP s_curr = NULL; s_curr = CDR(s_map_head); if (character) { while (s_curr != R_NilValue) { if (strcmp(CHAR(s_key), CHAR(CAR(TAG(s_curr)))) == 0) { return s_curr; } s_curr = CDR(s_curr); } } else { while (CAR(s_curr) != R_NilValue) { if (Ryaml_cmp(s_key, CAR(TAG(s_curr))) == 0) { return s_curr; } s_curr = CDR(s_curr); } } return NULL; } static int expand_merge(s_merge_list, s_map_head, s_map_tail, coerce_keys, merge_warning) SEXP s_merge_list; SEXP s_map_head; SEXP *s_map_tail; int coerce_keys; int merge_warning; { SEXP s_merge_keys = NULL, s_value = NULL, s_key = NULL, s_result = NULL, s_inspect = NULL; int i = 0, count = 0; const char *inspect = NULL; s_merge_keys = coerce_keys ? GET_NAMES(s_merge_list) : getAttrib(s_merge_list, Ryaml_KeysSymbol); for (i = 0; i < length(s_merge_list); i++) { s_value = VECTOR_ELT(s_merge_list, i); if (coerce_keys) { s_key = STRING_ELT(s_merge_keys, i); } else { s_key = VECTOR_ELT(s_merge_keys, i); } PROTECT(s_key); s_result = find_map_entry(s_map_head, s_key, coerce_keys); if (s_result != NULL) { /* A matching key is already in the map, so ignore this one. */ if (merge_warning) { if (coerce_keys) { inspect = CHAR(s_key); } else { PROTECT(s_inspect = Ryaml_inspect(s_key)); inspect = CHAR(STRING_ELT(s_inspect, 0)); } warning("Duplicate map key ignored during merge: '%s'", inspect); if (!coerce_keys) { UNPROTECT(1); /* s_inspect */ } } } else { SETCDR(*s_map_tail, list1(s_value)); *s_map_tail = CDR(*s_map_tail); SET_TAG(*s_map_tail, list2(s_key, ScalarLogical(TRUE))); count++; } UNPROTECT(1); /* s_key */ } return count; } static int is_mergeable(s_merge_list, coerce_keys) SEXP s_merge_list; int coerce_keys; { return (coerce_keys && Ryaml_is_named_list(s_merge_list)) || (!coerce_keys && Ryaml_is_pseudo_hash(s_merge_list)); } /* Return -1 on error or number of entries added to map. */ static int handle_map_entry(s_key, s_value, s_map_head, s_map_tail, coerce_keys, merge_warning) SEXP s_key; SEXP s_value; SEXP s_map_head; SEXP *s_map_tail; int coerce_keys; int merge_warning; { SEXP s_result = NULL, s_tag = NULL, s_inspect = NULL; const char *inspect = NULL; int len = 0, count = 0; if (coerce_keys) { /* (Possibly) convert this key to a character vector, and then save * the first element in the vector (CHARSXP element). Throw away * the containing vector, since it's not needed anymore. */ PROTECT(s_key = AS_CHARACTER(s_key)); len = length(s_key); if (len == 0) { warning("Empty character vector used as a list name"); s_key = mkCharCE("", CE_UTF8); } else { if (len > 1) { warning("Character vector of length greater than 1 used as a list name"); } s_key = STRING_ELT(s_key, 0); } UNPROTECT(1); } PROTECT(s_key); s_result = find_map_entry(s_map_head, s_key, coerce_keys); if (s_result != NULL) { /* A matching key is already in the map. If the existing key is from a * merge, it's okay to ignore it. If not, it's a duplicate key error. */ s_tag = TAG(s_result); if (coerce_keys) { inspect = CHAR(s_key); } else { PROTECT(s_inspect = Ryaml_inspect(s_key)); inspect = CHAR(STRING_ELT(s_inspect, 0)); } if (LOGICAL(CADR(s_tag))[0] == FALSE) { Ryaml_set_error_msg("Duplicate map key: '%s'", inspect); count = -1; } else if (merge_warning) { warning("Duplicate map key ignored after merge: '%s'", inspect); } if (!coerce_keys) { UNPROTECT(1); /* s_inspect */ } } else { SETCDR(*s_map_tail, list1(s_value)); *s_map_tail = CDR(*s_map_tail); SET_TAG(*s_map_tail, list2(s_key, ScalarLogical(FALSE))); count = 1; } UNPROTECT(1); /* s_key */ return count; } /* Return -1 on error or number of entries added to map. */ static int handle_merge(s_value, s_map_head, s_map_tail, coerce_keys, merge_warning) SEXP s_value; SEXP s_map_head; SEXP *s_map_tail; int coerce_keys; int merge_warning; { SEXP s_obj = NULL, s_inspect = NULL; const char *inspect = NULL; int i = 0, count = 0, len = 0; if (is_mergeable(s_value, coerce_keys)) { /* i.e. * - &bar { hey: dude } * - foo: * hello: friend * <<: *bar */ count = expand_merge(s_value, s_map_head, s_map_tail, coerce_keys, merge_warning); } else if (TYPEOF(s_value) == VECSXP) { /* i.e. * - &bar { hey: dude } * - &baz { hi: buddy } * - foo: * hello: friend * <<: [*bar, *baz] */ for (i = 0; i < length(s_value); i++) { s_obj = VECTOR_ELT(s_value, i); if (is_mergeable(s_obj, coerce_keys)) { len = expand_merge(s_obj, s_map_head, s_map_tail, coerce_keys, merge_warning); if (len >= 0) { count += len; } else { count = -1; break; } } else { /* Illegal merge */ PROTECT(s_inspect = Ryaml_inspect(s_value)); inspect = CHAR(STRING_ELT(s_inspect, 0)); Ryaml_set_error_msg("Illegal merge: %s", inspect); UNPROTECT(1); count = -1; break; } } } else { /* Illegal merge */ PROTECT(s_inspect = Ryaml_inspect(s_value)); inspect = CHAR(STRING_ELT(s_inspect, 0)); Ryaml_set_error_msg("Illegal merge: %s", inspect); UNPROTECT(1); count = -1; } return count; } static int handle_map(event, s_stack_head, s_stack_tail, s_handlers, coerce_keys, merge_override, merge_warning) yaml_event_t *event; SEXP s_stack_head; SEXP *s_stack_tail; SEXP s_handlers; int coerce_keys; int merge_override; int merge_warning; { SEXP s_list = NULL, s_keys = NULL, s_key = NULL, s_value = NULL, s_prev = NULL, s_curr = NULL, s_mapping_start = NULL, s_interim_map_head = NULL, s_interim_map_tail = NULL, s_new_obj = NULL, s_handler = NULL, s_tag = NULL; int count = 0, i = 0, map_err = 0, handled = 0, coercion_err = 0, len = 0; const char *tag = NULL, *original_tag = NULL; /* Find beginning of last map */ s_curr = CDR(s_stack_head); while (s_curr != R_NilValue) { if (CAR(s_curr) == Ryaml_MappingStart) { s_mapping_start = s_curr; } s_curr = CDR(s_curr); } if (s_mapping_start == NULL) { Ryaml_set_error_msg("Internal error: couldn't find start of mapping!"); return 1; } /* Set up interim map */ PROTECT(s_interim_map_head = s_interim_map_tail = list1(Ryaml_MappingStart)); if (merge_override) { /* If merge override is turned on, then normal map entries always take * precedence over any merged map entries. Therefore, go through and look * for any normal entries and place them in the interim map first. */ s_prev = s_mapping_start; s_curr = CDR(s_mapping_start); while (!map_err && s_curr != R_NilValue) { s_key = CAR(s_curr); s_value = CADR(s_curr); if (!Ryaml_has_class(s_key, "_yaml.merge_")) { len = handle_map_entry(s_key, s_value, s_interim_map_head, &s_interim_map_tail, coerce_keys, merge_warning); if (len >= 0) { count += len; /* Remove key/value from stack to prevent double processing */ SETCDR(s_prev, CDDR(s_curr)); s_curr = CDDR(s_curr); } else { map_err = 1; } } else { /* Skip over merge key/value */ s_prev = CDR(s_curr); s_curr = CDDR(s_curr); } } } /* Iterate keys and values */ s_curr = CDR(s_mapping_start); while (!map_err && s_curr != R_NilValue) { s_key = CAR(s_curr); s_value = CADR(s_curr); s_curr = CDDR(s_curr); if (Ryaml_has_class(s_key, "_yaml.merge_")) { len = handle_merge(s_value, s_interim_map_head, &s_interim_map_tail, coerce_keys, merge_warning); } else { if (merge_override) { /* If merge override is turned on, merges should have already been processed. */ Ryaml_set_error_msg("Merge override failed"); map_err = 1; break; } len = handle_map_entry(s_key, s_value, s_interim_map_head, &s_interim_map_tail, coerce_keys, merge_warning); } if (len >= 0) { count += len; } else { map_err = 1; } } if (map_err) { UNPROTECT(1); /* s_map */ return 1; } /* Initialize value list */ PROTECT(s_list = allocVector(VECSXP, count)); /* Initialize key list/vector */ if (coerce_keys) { s_keys = NEW_STRING(count); SET_NAMES(s_list, s_keys); } else { s_keys = allocVector(VECSXP, count); setAttrib(s_list, Ryaml_KeysSymbol, s_keys); } /* Iterate map entries */ s_curr = CDR(s_interim_map_head); for (i = 0; i < count; i++) { s_value = CAR(s_curr); s_key = CAR(TAG(s_curr)); s_curr = CDR(s_curr); SET_VECTOR_ELT(s_list, i, s_value); /* map key */ if (coerce_keys) { SET_STRING_ELT(s_keys, i, s_key); } else { SET_VECTOR_ELT(s_keys, i, s_key); } } UNPROTECT(2); /* s_interim_map_head, s_list */ /* Tags! */ s_tag = CAR(TAG(s_mapping_start)); original_tag = tag = (s_tag == R_NilValue ? NULL : CHAR(s_tag)); if (tag == NULL) { tag = "map"; } else { tag = process_tag(tag); } /* Look for a custom R handler */ PROTECT(s_list); s_handler = Ryaml_find_handler(s_handlers, (const char *) tag); if (s_handler != R_NilValue) { if (Ryaml_run_handler(s_handler, s_list, &s_new_obj) != 0) { warning("an error occurred when handling type '%s'; using default handler", tag); } else { handled = 1; } } UNPROTECT(1); /* s_list */ if (!handled) { /* default handlers, ordered by most-used */ if (strcmp(tag, "map") == 0) { /* already a map */ } else if (strcmp(tag, "str") == 0) { coercion_err = 1; } else if (strcmp(tag, "seq") == 0) { coercion_err = 1; } else if (strcmp(tag, "int#na") == 0) { coercion_err = 1; } else if (strcmp(tag, "int") == 0 || strncmp(tag, "int#", 4) == 0) { coercion_err = 1; } else if (strcmp(tag, "float") == 0 || strcmp(tag, "float#fix") == 0 || strcmp(tag, "float#exp") == 0) { coercion_err = 1; } else if (strcmp(tag, "bool#yes") == 0) { coercion_err = 1; } else if (strcmp(tag, "bool#no") == 0) { coercion_err = 1; } else if (strcmp(tag, "bool#na") == 0) { coercion_err = 1; } else if (strcmp(tag, "omap") == 0) { coercion_err = 1; } else if (strcmp(tag, "merge") == 0) { coercion_err = 1; } else if (strcmp(tag, "float#na") == 0) { coercion_err = 1; } else if (strcmp(tag, "float#nan") == 0) { coercion_err = 1; } else if (strcmp(tag, "float#inf") == 0) { coercion_err = 1; } else if (strcmp(tag, "float#neginf") == 0) { coercion_err = 1; } else if (strcmp(tag, "str#na") == 0) { coercion_err = 1; } else if (strcmp(tag, "null") == 0) { s_new_obj = R_NilValue; } else if (strcmp(tag, "expr") == 0) { coercion_err = 1; } } if (coercion_err == 1) { if (Ryaml_error_msg[0] == 0) { Ryaml_set_error_msg("Invalid tag: %s for map", original_tag); } return 1; } SETCAR(s_mapping_start, s_new_obj == NULL ? s_list : s_new_obj); SETCDR(s_mapping_start, R_NilValue); *s_stack_tail = s_mapping_start; return 0; } static void possibly_record_alias(s_anchor, s_aliases_tail, s_obj) SEXP s_anchor; SEXP *s_aliases_tail; SEXP s_obj; { if (s_anchor == NULL || TYPEOF(s_anchor) != CHARSXP) return; SETCDR(*s_aliases_tail, list1(s_obj)); *s_aliases_tail = CDR(*s_aliases_tail); SET_TAG(*s_aliases_tail, s_anchor); } SEXP Ryaml_unserialize_from_yaml(s_string, s_as_named_list, s_handlers, s_error_label, s_eval_expr, s_eval_warning, s_merge_precedence, s_merge_warning) SEXP s_string; SEXP s_as_named_list; SEXP s_handlers; SEXP s_error_label; SEXP s_eval_expr; SEXP s_eval_warning; SEXP s_merge_precedence; SEXP s_merge_warning; { SEXP s_retval = NULL, s_stack_head = NULL, s_stack_tail = NULL, s_aliases_head = NULL, s_aliases_tail = NULL, s_anchor = NULL; yaml_parser_t parser; yaml_event_t event; const char *string = NULL, *error_label = NULL, *merge_precedence = NULL; char *error_msg_copy = NULL; long len = 0; int as_named_list = 0, done = 0, err = 0, eval_expr = 0, eval_warning = 0, merge_override = 0, merge_warning = 0; if (!isString(s_string) || length(s_string) != 1) { error("string argument must be a character vector of length 1"); return R_NilValue; } if (!isLogical(s_as_named_list) || length(s_as_named_list) != 1) { error("as.named.list argument must be a logical vector of length 1"); return R_NilValue; } if (s_error_label == R_NilValue) { error_label = NULL; } else if (!isString(s_error_label) || length(s_error_label) != 1) { error("error.label argument must be either NULL or a character vector of length 1"); return R_NilValue; } else { error_label = CHAR(STRING_ELT(s_error_label, 0)); } if (!isLogical(s_eval_expr) || length(s_eval_expr) != 1) { error("eval.expr argument must be a logical vector of length 1"); return R_NilValue; } if (!isLogical(s_eval_warning) || length(s_eval_warning) != 1) { error("eval.warning argument must be a logical vector of length 1"); return R_NilValue; } if (!isString(s_merge_precedence) || length(s_merge_precedence) != 1) { error("merge.precedence argument must be a character vector of length 1"); return R_NilValue; } else { merge_precedence = CHAR(STRING_ELT(s_merge_precedence, 0)); if (strcmp(merge_precedence, "order") == 0) { merge_override = 0; } else if (strcmp(merge_precedence, "override") == 0) { merge_override = 1; } else { error("merge.precedence must be either 'ordered' or 'override'"); return R_NilValue; } } if (!isLogical(s_merge_warning) || length(s_merge_warning) != 1) { error("merge.warning argument must be a logical vector of length 1"); return R_NilValue; } PROTECT(s_handlers = Ryaml_sanitize_handlers(s_handlers)); string = CHAR(STRING_ELT(s_string, 0)); len = length(STRING_ELT(s_string, 0)); as_named_list = LOGICAL(s_as_named_list)[0]; eval_expr = LOGICAL(s_eval_expr)[0]; eval_warning = LOGICAL(s_eval_warning)[0]; merge_warning = LOGICAL(s_merge_warning)[0]; yaml_parser_initialize(&parser); yaml_parser_set_input_string(&parser, (const unsigned char *)string, len); PROTECT(s_stack_head = s_stack_tail = list1(Ryaml_Sentinel)); PROTECT(s_aliases_head = s_aliases_tail = list1(Ryaml_Sentinel)); Ryaml_error_msg[0] = 0; while (!done) { if (yaml_parser_parse(&parser, &event)) { err = 0; switch (event.type) { case YAML_NO_EVENT: case YAML_STREAM_START_EVENT: case YAML_DOCUMENT_START_EVENT: case YAML_DOCUMENT_END_EVENT: break; case YAML_ALIAS_EVENT: #if DEBUG Rprintf("ALIAS: %s\n", event.data.alias.anchor); #endif handle_alias(&event, &s_stack_tail, s_aliases_head); break; case YAML_SCALAR_EVENT: #if DEBUG Rprintf("SCALAR: %s (%s) [%s]\n", event.data.scalar.value, event.data.scalar.tag, event.data.scalar.anchor); #endif err = handle_scalar(&event, &s_stack_tail, s_handlers, eval_expr, eval_warning); if (!err && event.data.scalar.anchor != NULL) { PROTECT(s_anchor = mkCharCE((char *)event.data.scalar.anchor, CE_UTF8)); possibly_record_alias(s_anchor, &s_aliases_tail, CAR(s_stack_tail)); UNPROTECT(1); } break; case YAML_SEQUENCE_START_EVENT: #if DEBUG Rprintf("SEQUENCE START: (%s) [%s]\n", event.data.sequence_start.tag, event.data.sequence_start.anchor); #endif handle_structure_start(&event, &s_stack_tail, 0); break; case YAML_SEQUENCE_END_EVENT: #if DEBUG Rprintf("SEQUENCE END\n"); #endif err = handle_sequence(&event, s_stack_head, &s_stack_tail, s_handlers, as_named_list); if (!err) { s_anchor = CADR(TAG(s_stack_tail)); possibly_record_alias(s_anchor, &s_aliases_tail, CAR(s_stack_tail)); SET_TAG(s_stack_tail, R_NilValue); } break; case YAML_MAPPING_START_EVENT: #if DEBUG Rprintf("MAPPING START: (%s) [%s]\n", event.data.mapping_start.tag, event.data.mapping_start.anchor); #endif handle_structure_start(&event, &s_stack_tail, 1); break; case YAML_MAPPING_END_EVENT: #if DEBUG Rprintf("MAPPING END\n"); #endif err = handle_map(&event, s_stack_head, &s_stack_tail, s_handlers, as_named_list, merge_override, merge_warning); if (!err) { s_anchor = CADR(TAG(s_stack_tail)); possibly_record_alias(s_anchor, &s_aliases_tail, CAR(s_stack_tail)); SET_TAG(s_stack_tail, R_NilValue); } break; case YAML_STREAM_END_EVENT: if (CADR(s_stack_head) != Ryaml_Sentinel) { s_retval = CADR(s_stack_head); } else { s_retval = R_NilValue; } done = 1; break; } if (err) { s_retval = R_NilValue; done = 1; } } else { s_retval = R_NilValue; /* Parser error */ switch (parser.error) { case YAML_MEMORY_ERROR: Ryaml_set_error_msg("Memory error: Not enough memory for parsing"); break; case YAML_READER_ERROR: if (parser.problem_value != -1) { Ryaml_set_error_msg("Reader error: %s: #%X at %d", parser.problem, parser.problem_value, (int)parser.problem_offset); } else { Ryaml_set_error_msg("Reader error: %s at %d", parser.problem, (int)parser.problem_offset); } break; case YAML_SCANNER_ERROR: if (parser.context) { Ryaml_set_error_msg("Scanner error: %s at line %d, column %d " "%s at line %d, column %d\n", parser.context, (int)parser.context_mark.line+1, (int)parser.context_mark.column+1, parser.problem, (int)parser.problem_mark.line+1, (int)parser.problem_mark.column+1); } else { Ryaml_set_error_msg("Scanner error: %s at line %d, column %d", parser.problem, (int)parser.problem_mark.line+1, (int)parser.problem_mark.column+1); } break; case YAML_PARSER_ERROR: if (parser.context) { Ryaml_set_error_msg("Parser error: %s at line %d, column %d " "%s at line %d, column %d", parser.context, (int)parser.context_mark.line+1, (int)parser.context_mark.column+1, parser.problem, (int)parser.problem_mark.line+1, (int)parser.problem_mark.column+1); } else { Ryaml_set_error_msg("Parser error: %s at line %d, column %d", parser.problem, (int)parser.problem_mark.line+1, (int)parser.problem_mark.column+1); } break; default: /* Couldn't happen unless there is an undocumented/unhandled error * from LibYAML. */ Ryaml_set_error_msg("Internal error: unknown parser error"); break; } done = 1; } yaml_event_delete(&event); } yaml_parser_delete(&parser); if (Ryaml_error_msg[0] != 0) { /* Prepend label to error message if specified */ if (error_label != NULL) { error_msg_copy = (char *)malloc(sizeof(char) * ERROR_MSG_SIZE); if (error_msg_copy == NULL) { Ryaml_set_error_msg("Ran out of memory!"); } else { memcpy(error_msg_copy, Ryaml_error_msg, ERROR_MSG_SIZE); Ryaml_set_error_msg("(%s) %s", error_label, error_msg_copy); free(error_msg_copy); } } error(Ryaml_error_msg); } UNPROTECT(3); /* s_stack_head, s_aliases_head, s_handlers */ return s_retval; } yaml/src/implicit.c0000644000176200001440000005510713614635636014006 0ustar liggesusers/* Generated by re2c 1.2.1 */ #line 1 "implicit.re" #include "yaml.h" char * Ryaml_find_implicit_tag(str, len) const char *str; size_t len; { /* This bit was taken from implicit.re, which is in the Syck library. * * Copyright (C) 2003 why the lucky stiff */ const char *cursor, *marker; cursor = str; #line 19 "implicit.c" { char yych; yych = *cursor; switch (yych) { case 0x00: goto yy2; case '+': goto yy6; case '-': goto yy7; case '.': goto yy8; case '0': goto yy9; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy10; case '<': goto yy11; case '=': goto yy12; case 'F': goto yy13; case 'N': goto yy14; case 'O': goto yy15; case 'T': goto yy16; case 'Y': goto yy17; case 'f': goto yy18; case 'n': goto yy19; case 'o': goto yy20; case 't': goto yy21; case 'y': goto yy22; case '~': goto yy23; default: goto yy4; } yy2: ++cursor; #line 58 "implicit.re" { return "null"; } #line 57 "implicit.c" yy4: ++cursor; yy5: #line 104 "implicit.re" { return "str"; } #line 63 "implicit.c" yy6: yych = *(marker = ++cursor); switch (yych) { case '.': goto yy24; case '0': goto yy26; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy27; default: goto yy5; } yy7: yych = *(marker = ++cursor); switch (yych) { case '.': goto yy29; case '0': goto yy26; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy27; default: goto yy5; } yy8: yych = *(marker = ++cursor); switch (yych) { case 0x00: goto yy30; case ',': goto yy32; case '.': goto yy34; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy36; case 'E': case 'e': goto yy38; case 'I': goto yy39; case 'N': goto yy40; case 'i': goto yy41; case 'n': goto yy42; default: goto yy5; } yy9: yych = *(marker = ++cursor); switch (yych) { case 0x00: goto yy43; case ',': goto yy45; case '.': goto yy36; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': goto yy47; case '8': case '9': goto yy48; case ':': goto yy49; case 'x': goto yy50; default: goto yy5; } yy10: yych = *(marker = ++cursor); switch (yych) { case 0x00: goto yy43; case ',': goto yy27; case '.': goto yy36; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy51; case ':': goto yy49; default: goto yy5; } yy11: yych = *(marker = ++cursor); switch (yych) { case '<': goto yy52; default: goto yy5; } yy12: yych = *++cursor; if (yych <= 0x00) goto yy53; goto yy5; yy13: yych = *(marker = ++cursor); switch (yych) { case 'A': goto yy55; case 'a': goto yy56; default: goto yy5; } yy14: yych = *(marker = ++cursor); switch (yych) { case 0x00: goto yy57; case 'O': case 'o': goto yy59; case 'U': goto yy60; case 'u': goto yy61; default: goto yy5; } yy15: yych = *(marker = ++cursor); switch (yych) { case 'F': goto yy62; case 'N': case 'n': goto yy63; case 'f': goto yy64; default: goto yy5; } yy16: yych = *(marker = ++cursor); switch (yych) { case 'R': goto yy65; case 'r': goto yy66; default: goto yy5; } yy17: yych = *(marker = ++cursor); switch (yych) { case 0x00: goto yy67; case 'E': goto yy69; case 'e': goto yy70; default: goto yy5; } yy18: yych = *(marker = ++cursor); switch (yych) { case 'a': goto yy56; default: goto yy5; } yy19: yych = *(marker = ++cursor); switch (yych) { case 0x00: goto yy57; case 'o': goto yy59; case 'u': goto yy61; default: goto yy5; } yy20: yych = *(marker = ++cursor); switch (yych) { case 'f': goto yy64; case 'n': goto yy63; default: goto yy5; } yy21: yych = *(marker = ++cursor); switch (yych) { case 'r': goto yy66; default: goto yy5; } yy22: yych = *(marker = ++cursor); switch (yych) { case 0x00: goto yy67; case 'e': goto yy70; default: goto yy5; } yy23: yych = *++cursor; if (yych <= 0x00) goto yy2; goto yy5; yy24: yych = *++cursor; switch (yych) { case '.': goto yy34; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy36; case 'E': case 'e': goto yy38; case 'I': goto yy39; case 'i': goto yy41; default: goto yy33; } yy25: cursor = marker; goto yy5; yy26: yych = *++cursor; switch (yych) { case 0x00: goto yy43; case 'x': goto yy50; default: goto yy46; } yy27: yych = *++cursor; yy28: switch (yych) { case 0x00: goto yy43; case ',': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy27; case '.': goto yy36; case ':': goto yy49; default: goto yy25; } yy29: yych = *++cursor; switch (yych) { case '.': goto yy34; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy36; case 'E': case 'e': goto yy38; case 'I': goto yy73; case 'i': goto yy74; default: goto yy33; } yy30: ++cursor; #line 76 "implicit.re" { return "float#fix"; } #line 322 "implicit.c" yy32: yych = *++cursor; yy33: switch (yych) { case 0x00: goto yy30; case ',': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy32; default: goto yy25; } yy34: yych = *++cursor; switch (yych) { case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy34; case 'E': case 'e': goto yy38; default: goto yy25; } yy36: yych = *++cursor; switch (yych) { case 0x00: goto yy30; case ',': goto yy32; case '.': goto yy34; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy36; case 'E': case 'e': goto yy38; default: goto yy25; } yy38: yych = *++cursor; switch (yych) { case '+': case '-': goto yy75; default: goto yy25; } yy39: yych = *++cursor; switch (yych) { case 'N': goto yy76; case 'n': goto yy77; default: goto yy25; } yy40: yych = *++cursor; switch (yych) { case 'A': case 'a': goto yy78; default: goto yy25; } yy41: yych = *++cursor; switch (yych) { case 'n': goto yy77; default: goto yy25; } yy42: yych = *++cursor; switch (yych) { case 'a': goto yy79; default: goto yy25; } yy43: ++cursor; #line 74 "implicit.re" { return "int"; } #line 416 "implicit.c" yy45: yych = *++cursor; yy46: switch (yych) { case 0x00: goto yy80; case ',': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': goto yy45; case '.': goto yy36; case '8': case '9': goto yy71; case ':': goto yy49; default: goto yy25; } yy47: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': goto yy82; case '8': case '9': goto yy83; default: goto yy46; } yy48: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy83; default: goto yy72; } yy49: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': goto yy84; case '6': case '7': case '8': case '9': goto yy85; default: goto yy25; } yy50: yych = *++cursor; if (yych <= 0x00) goto yy25; goto yy87; yy51: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy88; default: goto yy28; } yy52: yych = *++cursor; if (yych <= 0x00) goto yy89; goto yy25; yy53: ++cursor; #line 100 "implicit.re" { return "default"; } #line 509 "implicit.c" yy55: yych = *++cursor; switch (yych) { case 'L': goto yy91; default: goto yy25; } yy56: yych = *++cursor; switch (yych) { case 'l': goto yy92; default: goto yy25; } yy57: ++cursor; #line 62 "implicit.re" { return "bool#no"; } #line 526 "implicit.c" yy59: yych = *++cursor; if (yych <= 0x00) goto yy57; goto yy25; yy60: yych = *++cursor; switch (yych) { case 'L': goto yy93; default: goto yy25; } yy61: yych = *++cursor; switch (yych) { case 'l': goto yy94; default: goto yy25; } yy62: yych = *++cursor; switch (yych) { case 'F': goto yy59; default: goto yy25; } yy63: yych = *++cursor; if (yych <= 0x00) goto yy67; goto yy25; yy64: yych = *++cursor; switch (yych) { case 'f': goto yy59; default: goto yy25; } yy65: yych = *++cursor; switch (yych) { case 'U': goto yy95; default: goto yy25; } yy66: yych = *++cursor; switch (yych) { case 'u': goto yy96; default: goto yy25; } yy67: ++cursor; #line 60 "implicit.re" { return "bool#yes"; } #line 575 "implicit.c" yy69: yych = *++cursor; switch (yych) { case 'S': goto yy63; default: goto yy25; } yy70: yych = *++cursor; switch (yych) { case 's': goto yy63; default: goto yy25; } yy71: yych = *++cursor; yy72: switch (yych) { case ',': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy71; case '.': goto yy36; case ':': goto yy49; default: goto yy25; } yy73: yych = *++cursor; switch (yych) { case 'N': goto yy97; case 'n': goto yy98; default: goto yy25; } yy74: yych = *++cursor; switch (yych) { case 'n': goto yy98; default: goto yy25; } yy75: yych = *++cursor; if (yych <= 0x00) goto yy25; goto yy100; yy76: yych = *++cursor; switch (yych) { case 'F': goto yy101; default: goto yy25; } yy77: yych = *++cursor; switch (yych) { case 'f': goto yy101; default: goto yy25; } yy78: yych = *++cursor; switch (yych) { case 'N': goto yy102; default: goto yy25; } yy79: yych = *++cursor; switch (yych) { case 0x00: goto yy103; case '.': goto yy105; case 'n': goto yy102; default: goto yy25; } yy80: ++cursor; #line 68 "implicit.re" { return "int#oct"; } #line 654 "implicit.c" yy82: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': goto yy106; case '8': case '9': goto yy107; default: goto yy46; } yy83: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy107; default: goto yy72; } yy84: yych = *++cursor; switch (yych) { case 0x00: goto yy108; case '.': goto yy110; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy85; case ':': goto yy49; default: goto yy25; } yy85: yych = *++cursor; switch (yych) { case 0x00: goto yy108; case '.': goto yy110; case ':': goto yy49; default: goto yy25; } yy86: yych = *++cursor; yy87: switch (yych) { case 0x00: goto yy112; case ',': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': goto yy86; default: goto yy25; } yy88: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy114; default: goto yy28; } yy89: ++cursor; #line 102 "implicit.re" { return "merge"; } #line 760 "implicit.c" yy91: yych = *++cursor; switch (yych) { case 'S': goto yy115; default: goto yy25; } yy92: yych = *++cursor; switch (yych) { case 's': goto yy116; default: goto yy25; } yy93: yych = *++cursor; switch (yych) { case 'L': goto yy117; default: goto yy25; } yy94: yych = *++cursor; switch (yych) { case 'l': goto yy117; default: goto yy25; } yy95: yych = *++cursor; switch (yych) { case 'E': goto yy63; default: goto yy25; } yy96: yych = *++cursor; switch (yych) { case 'e': goto yy63; default: goto yy25; } yy97: yych = *++cursor; switch (yych) { case 'F': goto yy118; default: goto yy25; } yy98: yych = *++cursor; switch (yych) { case 'f': goto yy118; default: goto yy25; } yy99: yych = *++cursor; yy100: switch (yych) { case 0x00: goto yy119; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy99; default: goto yy25; } yy101: yych = *++cursor; if (yych <= 0x00) goto yy121; goto yy25; yy102: yych = *++cursor; if (yych <= 0x00) goto yy123; goto yy25; yy103: ++cursor; #line 64 "implicit.re" { return "bool#na"; } #line 838 "implicit.c" yy105: yych = *++cursor; switch (yych) { case 'c': goto yy125; case 'i': goto yy126; case 'r': goto yy127; default: goto yy25; } yy106: yych = *++cursor; switch (yych) { case '-': goto yy128; default: goto yy46; } yy107: yych = *++cursor; switch (yych) { case '-': goto yy128; default: goto yy72; } yy108: ++cursor; #line 70 "implicit.re" { return "int#base60"; } #line 863 "implicit.c" yy110: yych = *++cursor; switch (yych) { case 0x00: goto yy129; case ',': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy110; default: goto yy25; } yy112: ++cursor; #line 66 "implicit.re" { return "int#hex"; } #line 885 "implicit.c" yy114: yych = *++cursor; switch (yych) { case '-': goto yy128; default: goto yy28; } yy115: yych = *++cursor; switch (yych) { case 'E': goto yy59; default: goto yy25; } yy116: yych = *++cursor; switch (yych) { case 'e': goto yy59; default: goto yy25; } yy117: yych = *++cursor; if (yych <= 0x00) goto yy2; goto yy25; yy118: yych = *++cursor; if (yych <= 0x00) goto yy131; goto yy25; yy119: ++cursor; #line 78 "implicit.re" { return "float#exp"; } #line 916 "implicit.c" yy121: ++cursor; #line 82 "implicit.re" { return "float#inf"; } #line 921 "implicit.c" yy123: ++cursor; #line 86 "implicit.re" { return "float#nan"; } #line 926 "implicit.c" yy125: yych = *++cursor; switch (yych) { case 'h': goto yy133; default: goto yy25; } yy126: yych = *++cursor; switch (yych) { case 'n': goto yy134; default: goto yy25; } yy127: yych = *++cursor; switch (yych) { case 'e': goto yy135; default: goto yy25; } yy128: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy136; default: goto yy25; } yy129: ++cursor; #line 80 "implicit.re" { return "float#base60"; } #line 964 "implicit.c" yy131: ++cursor; #line 84 "implicit.re" { return "float#neginf"; } #line 969 "implicit.c" yy133: yych = *++cursor; switch (yych) { case 'a': goto yy137; default: goto yy25; } yy134: yych = *++cursor; switch (yych) { case 't': goto yy138; default: goto yy25; } yy135: yych = *++cursor; switch (yych) { case 'a': goto yy139; default: goto yy25; } yy136: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy140; default: goto yy25; } yy137: yych = *++cursor; switch (yych) { case 'r': goto yy141; default: goto yy25; } yy138: yych = *++cursor; switch (yych) { case 'e': goto yy142; default: goto yy25; } yy139: yych = *++cursor; switch (yych) { case 'l': goto yy143; default: goto yy25; } yy140: yych = *++cursor; switch (yych) { case '-': goto yy144; default: goto yy25; } yy141: yych = *++cursor; switch (yych) { case 'a': goto yy145; default: goto yy25; } yy142: yych = *++cursor; switch (yych) { case 'g': goto yy146; default: goto yy25; } yy143: yych = *++cursor; if (yych <= 0x00) goto yy147; goto yy25; yy144: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy149; default: goto yy25; } yy145: yych = *++cursor; switch (yych) { case 'c': goto yy150; default: goto yy25; } yy146: yych = *++cursor; switch (yych) { case 'e': goto yy151; default: goto yy25; } yy147: ++cursor; #line 88 "implicit.re" { return "float#na"; } #line 1074 "implicit.c" yy149: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy152; default: goto yy25; } yy150: yych = *++cursor; switch (yych) { case 't': goto yy153; default: goto yy25; } yy151: yych = *++cursor; switch (yych) { case 'r': goto yy154; default: goto yy25; } yy152: yych = *++cursor; switch (yych) { case 0x00: goto yy155; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy25; case 'T': case 't': goto yy159; default: goto yy158; } yy153: yych = *++cursor; switch (yych) { case 'e': goto yy160; default: goto yy25; } yy154: yych = *++cursor; if (yych <= 0x00) goto yy161; goto yy25; yy155: ++cursor; #line 90 "implicit.re" { return "timestamp#ymd"; } #line 1134 "implicit.c" yy157: yych = *++cursor; yy158: switch (yych) { case '\t': case ' ': goto yy157; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy163; default: goto yy25; } yy159: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy164; default: goto yy25; } yy160: yych = *++cursor; switch (yych) { case 'r': goto yy165; default: goto yy25; } yy161: ++cursor; #line 72 "implicit.re" { return "int#na"; } #line 1178 "implicit.c" yy163: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy166; default: goto yy25; } yy164: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy167; default: goto yy25; } yy165: yych = *++cursor; if (yych <= 0x00) goto yy168; goto yy25; yy166: yych = *++cursor; switch (yych) { case ':': goto yy170; default: goto yy25; } yy167: yych = *++cursor; switch (yych) { case ':': goto yy171; default: goto yy25; } yy168: ++cursor; #line 98 "implicit.re" { return "str#na"; } #line 1229 "implicit.c" yy170: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy172; default: goto yy25; } yy171: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy173; default: goto yy25; } yy172: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy174; default: goto yy25; } yy173: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy175; default: goto yy25; } yy174: yych = *++cursor; switch (yych) { case ':': goto yy176; default: goto yy25; } yy175: yych = *++cursor; switch (yych) { case ':': goto yy177; default: goto yy25; } yy176: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy178; default: goto yy25; } yy177: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy179; default: goto yy25; } yy178: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy180; default: goto yy25; } yy179: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy181; default: goto yy25; } yy180: yych = *++cursor; switch (yych) { case '.': goto yy184; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy25; default: goto yy185; } yy181: yych = *++cursor; switch (yych) { case '.': goto yy187; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy25; default: goto yy188; } yy182: yych = *++cursor; switch (yych) { case '\t': case ' ': goto yy182; case '+': case '-': goto yy190; case 'Z': goto yy191; default: goto yy25; } yy184: yych = *++cursor; yy185: switch (yych) { case '\t': case ' ': goto yy182; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy184; default: goto yy25; } yy186: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy192; default: goto yy25; } yy187: yych = *++cursor; yy188: switch (yych) { case '+': case '-': goto yy186; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy187; case 'Z': goto yy189; default: goto yy25; } yy189: yych = *++cursor; if (yych <= 0x00) goto yy193; goto yy25; yy190: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy195; default: goto yy25; } yy191: yych = *++cursor; if (yych <= 0x00) goto yy196; goto yy25; yy192: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy198; default: goto yy25; } yy193: ++cursor; #line 92 "implicit.re" { return "timestamp#iso8601"; } #line 1498 "implicit.c" yy195: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy199; default: goto yy25; } yy196: ++cursor; #line 94 "implicit.re" { return "timestamp#spaced"; } #line 1518 "implicit.c" yy198: yych = *++cursor; switch (yych) { case 0x00: goto yy193; case ':': goto yy200; default: goto yy25; } yy199: yych = *++cursor; switch (yych) { case 0x00: goto yy196; case ':': goto yy201; default: goto yy25; } yy200: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy202; default: goto yy25; } yy201: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy203; default: goto yy25; } yy202: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy189; default: goto yy25; } yy203: yych = *++cursor; switch (yych) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto yy191; default: goto yy25; } } #line 106 "implicit.re" } yaml/src/parser.c0000644000176200001440000012757313614635636013477 0ustar liggesusers /* * The parser implements the following grammar: * * stream ::= STREAM-START implicit_document? explicit_document* STREAM-END * implicit_document ::= block_node DOCUMENT-END* * explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* * block_node_or_indentless_sequence ::= * ALIAS * | properties (block_content | indentless_block_sequence)? * | block_content * | indentless_block_sequence * block_node ::= ALIAS * | properties block_content? * | block_content * flow_node ::= ALIAS * | properties flow_content? * | flow_content * properties ::= TAG ANCHOR? | ANCHOR TAG? * block_content ::= block_collection | flow_collection | SCALAR * flow_content ::= flow_collection | SCALAR * block_collection ::= block_sequence | block_mapping * flow_collection ::= flow_sequence | flow_mapping * block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END * indentless_sequence ::= (BLOCK-ENTRY block_node?)+ * block_mapping ::= BLOCK-MAPPING_START * ((KEY block_node_or_indentless_sequence?)? * (VALUE block_node_or_indentless_sequence?)?)* * BLOCK-END * flow_sequence ::= FLOW-SEQUENCE-START * (flow_sequence_entry FLOW-ENTRY)* * flow_sequence_entry? * FLOW-SEQUENCE-END * flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? * flow_mapping ::= FLOW-MAPPING-START * (flow_mapping_entry FLOW-ENTRY)* * flow_mapping_entry? * FLOW-MAPPING-END * flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? */ #include "yaml_private.h" /* * Peek the next token in the token queue. */ #define PEEK_TOKEN(parser) \ ((parser->token_available || yaml_parser_fetch_more_tokens(parser)) ? \ parser->tokens.head : NULL) /* * Remove the next token from the queue (must be called after PEEK_TOKEN). */ #define SKIP_TOKEN(parser) \ (parser->token_available = 0, \ parser->tokens_parsed ++, \ parser->stream_end_produced = \ (parser->tokens.head->type == YAML_STREAM_END_TOKEN), \ parser->tokens.head ++) /* * Public API declarations. */ YAML_DECLARE(int) yaml_parser_parse(yaml_parser_t *parser, yaml_event_t *event); /* * Error handling. */ static int yaml_parser_set_parser_error(yaml_parser_t *parser, const char *problem, yaml_mark_t problem_mark); static int yaml_parser_set_parser_error_context(yaml_parser_t *parser, const char *context, yaml_mark_t context_mark, const char *problem, yaml_mark_t problem_mark); /* * State functions. */ static int yaml_parser_state_machine(yaml_parser_t *parser, yaml_event_t *event); static int yaml_parser_parse_stream_start(yaml_parser_t *parser, yaml_event_t *event); static int yaml_parser_parse_document_start(yaml_parser_t *parser, yaml_event_t *event, int implicit); static int yaml_parser_parse_document_content(yaml_parser_t *parser, yaml_event_t *event); static int yaml_parser_parse_document_end(yaml_parser_t *parser, yaml_event_t *event); static int yaml_parser_parse_node(yaml_parser_t *parser, yaml_event_t *event, int block, int indentless_sequence); static int yaml_parser_parse_block_sequence_entry(yaml_parser_t *parser, yaml_event_t *event, int first); static int yaml_parser_parse_indentless_sequence_entry(yaml_parser_t *parser, yaml_event_t *event); static int yaml_parser_parse_block_mapping_key(yaml_parser_t *parser, yaml_event_t *event, int first); static int yaml_parser_parse_block_mapping_value(yaml_parser_t *parser, yaml_event_t *event); static int yaml_parser_parse_flow_sequence_entry(yaml_parser_t *parser, yaml_event_t *event, int first); static int yaml_parser_parse_flow_sequence_entry_mapping_key(yaml_parser_t *parser, yaml_event_t *event); static int yaml_parser_parse_flow_sequence_entry_mapping_value(yaml_parser_t *parser, yaml_event_t *event); static int yaml_parser_parse_flow_sequence_entry_mapping_end(yaml_parser_t *parser, yaml_event_t *event); static int yaml_parser_parse_flow_mapping_key(yaml_parser_t *parser, yaml_event_t *event, int first); static int yaml_parser_parse_flow_mapping_value(yaml_parser_t *parser, yaml_event_t *event, int empty); /* * Utility functions. */ static int yaml_parser_process_empty_scalar(yaml_parser_t *parser, yaml_event_t *event, yaml_mark_t mark); static int yaml_parser_process_directives(yaml_parser_t *parser, yaml_version_directive_t **version_directive_ref, yaml_tag_directive_t **tag_directives_start_ref, yaml_tag_directive_t **tag_directives_end_ref); static int yaml_parser_append_tag_directive(yaml_parser_t *parser, yaml_tag_directive_t value, int allow_duplicates, yaml_mark_t mark); /* * Get the next event. */ YAML_DECLARE(int) yaml_parser_parse(yaml_parser_t *parser, yaml_event_t *event) { assert(parser); /* Non-NULL parser object is expected. */ assert(event); /* Non-NULL event object is expected. */ /* Erase the event object. */ memset(event, 0, sizeof(yaml_event_t)); /* No events after the end of the stream or error. */ if (parser->stream_end_produced || parser->error || parser->state == YAML_PARSE_END_STATE) { return 1; } /* Generate the next event. */ return yaml_parser_state_machine(parser, event); } /* * Set parser error. */ static int yaml_parser_set_parser_error(yaml_parser_t *parser, const char *problem, yaml_mark_t problem_mark) { parser->error = YAML_PARSER_ERROR; parser->problem = problem; parser->problem_mark = problem_mark; return 0; } static int yaml_parser_set_parser_error_context(yaml_parser_t *parser, const char *context, yaml_mark_t context_mark, const char *problem, yaml_mark_t problem_mark) { parser->error = YAML_PARSER_ERROR; parser->context = context; parser->context_mark = context_mark; parser->problem = problem; parser->problem_mark = problem_mark; return 0; } /* * State dispatcher. */ static int yaml_parser_state_machine(yaml_parser_t *parser, yaml_event_t *event) { switch (parser->state) { case YAML_PARSE_STREAM_START_STATE: return yaml_parser_parse_stream_start(parser, event); case YAML_PARSE_IMPLICIT_DOCUMENT_START_STATE: return yaml_parser_parse_document_start(parser, event, 1); case YAML_PARSE_DOCUMENT_START_STATE: return yaml_parser_parse_document_start(parser, event, 0); case YAML_PARSE_DOCUMENT_CONTENT_STATE: return yaml_parser_parse_document_content(parser, event); case YAML_PARSE_DOCUMENT_END_STATE: return yaml_parser_parse_document_end(parser, event); case YAML_PARSE_BLOCK_NODE_STATE: return yaml_parser_parse_node(parser, event, 1, 0); case YAML_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: return yaml_parser_parse_node(parser, event, 1, 1); case YAML_PARSE_FLOW_NODE_STATE: return yaml_parser_parse_node(parser, event, 0, 0); case YAML_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: return yaml_parser_parse_block_sequence_entry(parser, event, 1); case YAML_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: return yaml_parser_parse_block_sequence_entry(parser, event, 0); case YAML_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: return yaml_parser_parse_indentless_sequence_entry(parser, event); case YAML_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: return yaml_parser_parse_block_mapping_key(parser, event, 1); case YAML_PARSE_BLOCK_MAPPING_KEY_STATE: return yaml_parser_parse_block_mapping_key(parser, event, 0); case YAML_PARSE_BLOCK_MAPPING_VALUE_STATE: return yaml_parser_parse_block_mapping_value(parser, event); case YAML_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: return yaml_parser_parse_flow_sequence_entry(parser, event, 1); case YAML_PARSE_FLOW_SEQUENCE_ENTRY_STATE: return yaml_parser_parse_flow_sequence_entry(parser, event, 0); case YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: return yaml_parser_parse_flow_sequence_entry_mapping_key(parser, event); case YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: return yaml_parser_parse_flow_sequence_entry_mapping_value(parser, event); case YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: return yaml_parser_parse_flow_sequence_entry_mapping_end(parser, event); case YAML_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: return yaml_parser_parse_flow_mapping_key(parser, event, 1); case YAML_PARSE_FLOW_MAPPING_KEY_STATE: return yaml_parser_parse_flow_mapping_key(parser, event, 0); case YAML_PARSE_FLOW_MAPPING_VALUE_STATE: return yaml_parser_parse_flow_mapping_value(parser, event, 0); case YAML_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: return yaml_parser_parse_flow_mapping_value(parser, event, 1); default: assert(1); /* Invalid state. */ } return 0; } /* * Parse the production: * stream ::= STREAM-START implicit_document? explicit_document* STREAM-END * ************ */ static int yaml_parser_parse_stream_start(yaml_parser_t *parser, yaml_event_t *event) { yaml_token_t *token; token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type != YAML_STREAM_START_TOKEN) { return yaml_parser_set_parser_error(parser, "did not find expected ", token->start_mark); } parser->state = YAML_PARSE_IMPLICIT_DOCUMENT_START_STATE; STREAM_START_EVENT_INIT(*event, token->data.stream_start.encoding, token->start_mark, token->start_mark); SKIP_TOKEN(parser); return 1; } /* * Parse the productions: * implicit_document ::= block_node DOCUMENT-END* * * * explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* * ************************* */ static int yaml_parser_parse_document_start(yaml_parser_t *parser, yaml_event_t *event, int implicit) { yaml_token_t *token; yaml_version_directive_t *version_directive = NULL; struct { yaml_tag_directive_t *start; yaml_tag_directive_t *end; } tag_directives = { NULL, NULL }; token = PEEK_TOKEN(parser); if (!token) return 0; /* Parse extra document end indicators. */ if (!implicit) { while (token->type == YAML_DOCUMENT_END_TOKEN) { SKIP_TOKEN(parser); token = PEEK_TOKEN(parser); if (!token) return 0; } } /* Parse an implicit document. */ if (implicit && token->type != YAML_VERSION_DIRECTIVE_TOKEN && token->type != YAML_TAG_DIRECTIVE_TOKEN && token->type != YAML_DOCUMENT_START_TOKEN && token->type != YAML_STREAM_END_TOKEN) { if (!yaml_parser_process_directives(parser, NULL, NULL, NULL)) return 0; if (!PUSH(parser, parser->states, YAML_PARSE_DOCUMENT_END_STATE)) return 0; parser->state = YAML_PARSE_BLOCK_NODE_STATE; DOCUMENT_START_EVENT_INIT(*event, NULL, NULL, NULL, 1, token->start_mark, token->start_mark); return 1; } /* Parse an explicit document. */ else if (token->type != YAML_STREAM_END_TOKEN) { yaml_mark_t start_mark, end_mark; start_mark = token->start_mark; if (!yaml_parser_process_directives(parser, &version_directive, &tag_directives.start, &tag_directives.end)) return 0; token = PEEK_TOKEN(parser); if (!token) goto error; if (token->type != YAML_DOCUMENT_START_TOKEN) { yaml_parser_set_parser_error(parser, "did not find expected ", token->start_mark); goto error; } if (!PUSH(parser, parser->states, YAML_PARSE_DOCUMENT_END_STATE)) goto error; parser->state = YAML_PARSE_DOCUMENT_CONTENT_STATE; end_mark = token->end_mark; DOCUMENT_START_EVENT_INIT(*event, version_directive, tag_directives.start, tag_directives.end, 0, start_mark, end_mark); SKIP_TOKEN(parser); version_directive = NULL; tag_directives.start = tag_directives.end = NULL; return 1; } /* Parse the stream end. */ else { parser->state = YAML_PARSE_END_STATE; STREAM_END_EVENT_INIT(*event, token->start_mark, token->end_mark); SKIP_TOKEN(parser); return 1; } error: yaml_free(version_directive); while (tag_directives.start != tag_directives.end) { yaml_free(tag_directives.end[-1].handle); yaml_free(tag_directives.end[-1].prefix); tag_directives.end --; } yaml_free(tag_directives.start); return 0; } /* * Parse the productions: * explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* * *********** */ static int yaml_parser_parse_document_content(yaml_parser_t *parser, yaml_event_t *event) { yaml_token_t *token; token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type == YAML_VERSION_DIRECTIVE_TOKEN || token->type == YAML_TAG_DIRECTIVE_TOKEN || token->type == YAML_DOCUMENT_START_TOKEN || token->type == YAML_DOCUMENT_END_TOKEN || token->type == YAML_STREAM_END_TOKEN) { parser->state = POP(parser, parser->states); return yaml_parser_process_empty_scalar(parser, event, token->start_mark); } else { return yaml_parser_parse_node(parser, event, 1, 0); } } /* * Parse the productions: * implicit_document ::= block_node DOCUMENT-END* * ************* * explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* * ************* */ static int yaml_parser_parse_document_end(yaml_parser_t *parser, yaml_event_t *event) { yaml_token_t *token; yaml_mark_t start_mark, end_mark; int implicit = 1; token = PEEK_TOKEN(parser); if (!token) return 0; start_mark = end_mark = token->start_mark; if (token->type == YAML_DOCUMENT_END_TOKEN) { end_mark = token->end_mark; SKIP_TOKEN(parser); implicit = 0; } while (!STACK_EMPTY(parser, parser->tag_directives)) { yaml_tag_directive_t tag_directive = POP(parser, parser->tag_directives); yaml_free(tag_directive.handle); yaml_free(tag_directive.prefix); } parser->state = YAML_PARSE_DOCUMENT_START_STATE; DOCUMENT_END_EVENT_INIT(*event, implicit, start_mark, end_mark); return 1; } /* * Parse the productions: * block_node_or_indentless_sequence ::= * ALIAS * ***** * | properties (block_content | indentless_block_sequence)? * ********** * * | block_content | indentless_block_sequence * * * block_node ::= ALIAS * ***** * | properties block_content? * ********** * * | block_content * * * flow_node ::= ALIAS * ***** * | properties flow_content? * ********** * * | flow_content * * * properties ::= TAG ANCHOR? | ANCHOR TAG? * ************************* * block_content ::= block_collection | flow_collection | SCALAR * ****** * flow_content ::= flow_collection | SCALAR * ****** */ static int yaml_parser_parse_node(yaml_parser_t *parser, yaml_event_t *event, int block, int indentless_sequence) { yaml_token_t *token; yaml_char_t *anchor = NULL; yaml_char_t *tag_handle = NULL; yaml_char_t *tag_suffix = NULL; yaml_char_t *tag = NULL; yaml_mark_t start_mark, end_mark, tag_mark; int implicit; token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type == YAML_ALIAS_TOKEN) { parser->state = POP(parser, parser->states); ALIAS_EVENT_INIT(*event, token->data.alias.value, token->start_mark, token->end_mark); SKIP_TOKEN(parser); return 1; } else { start_mark = end_mark = token->start_mark; if (token->type == YAML_ANCHOR_TOKEN) { anchor = token->data.anchor.value; start_mark = token->start_mark; end_mark = token->end_mark; SKIP_TOKEN(parser); token = PEEK_TOKEN(parser); if (!token) goto error; if (token->type == YAML_TAG_TOKEN) { tag_handle = token->data.tag.handle; tag_suffix = token->data.tag.suffix; tag_mark = token->start_mark; end_mark = token->end_mark; SKIP_TOKEN(parser); token = PEEK_TOKEN(parser); if (!token) goto error; } } else if (token->type == YAML_TAG_TOKEN) { tag_handle = token->data.tag.handle; tag_suffix = token->data.tag.suffix; start_mark = tag_mark = token->start_mark; end_mark = token->end_mark; SKIP_TOKEN(parser); token = PEEK_TOKEN(parser); if (!token) goto error; if (token->type == YAML_ANCHOR_TOKEN) { anchor = token->data.anchor.value; end_mark = token->end_mark; SKIP_TOKEN(parser); token = PEEK_TOKEN(parser); if (!token) goto error; } } if (tag_handle) { if (!*tag_handle) { tag = tag_suffix; yaml_free(tag_handle); tag_handle = tag_suffix = NULL; } else { yaml_tag_directive_t *tag_directive; for (tag_directive = parser->tag_directives.start; tag_directive != parser->tag_directives.top; tag_directive ++) { if (strcmp((char *)tag_directive->handle, (char *)tag_handle) == 0) { size_t prefix_len = strlen((char *)tag_directive->prefix); size_t suffix_len = strlen((char *)tag_suffix); tag = YAML_MALLOC(prefix_len+suffix_len+1); if (!tag) { parser->error = YAML_MEMORY_ERROR; goto error; } memcpy(tag, tag_directive->prefix, prefix_len); memcpy(tag+prefix_len, tag_suffix, suffix_len); tag[prefix_len+suffix_len] = '\0'; yaml_free(tag_handle); yaml_free(tag_suffix); tag_handle = tag_suffix = NULL; break; } } if (!tag) { yaml_parser_set_parser_error_context(parser, "while parsing a node", start_mark, "found undefined tag handle", tag_mark); goto error; } } } implicit = (!tag || !*tag); if (indentless_sequence && token->type == YAML_BLOCK_ENTRY_TOKEN) { end_mark = token->end_mark; parser->state = YAML_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE; SEQUENCE_START_EVENT_INIT(*event, anchor, tag, implicit, YAML_BLOCK_SEQUENCE_STYLE, start_mark, end_mark); return 1; } else { if (token->type == YAML_SCALAR_TOKEN) { int plain_implicit = 0; int quoted_implicit = 0; end_mark = token->end_mark; if ((token->data.scalar.style == YAML_PLAIN_SCALAR_STYLE && !tag) || (tag && strcmp((char *)tag, "!") == 0)) { plain_implicit = 1; } else if (!tag) { quoted_implicit = 1; } parser->state = POP(parser, parser->states); SCALAR_EVENT_INIT(*event, anchor, tag, token->data.scalar.value, token->data.scalar.length, plain_implicit, quoted_implicit, token->data.scalar.style, start_mark, end_mark); SKIP_TOKEN(parser); return 1; } else if (token->type == YAML_FLOW_SEQUENCE_START_TOKEN) { end_mark = token->end_mark; parser->state = YAML_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE; SEQUENCE_START_EVENT_INIT(*event, anchor, tag, implicit, YAML_FLOW_SEQUENCE_STYLE, start_mark, end_mark); return 1; } else if (token->type == YAML_FLOW_MAPPING_START_TOKEN) { end_mark = token->end_mark; parser->state = YAML_PARSE_FLOW_MAPPING_FIRST_KEY_STATE; MAPPING_START_EVENT_INIT(*event, anchor, tag, implicit, YAML_FLOW_MAPPING_STYLE, start_mark, end_mark); return 1; } else if (block && token->type == YAML_BLOCK_SEQUENCE_START_TOKEN) { end_mark = token->end_mark; parser->state = YAML_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE; SEQUENCE_START_EVENT_INIT(*event, anchor, tag, implicit, YAML_BLOCK_SEQUENCE_STYLE, start_mark, end_mark); return 1; } else if (block && token->type == YAML_BLOCK_MAPPING_START_TOKEN) { end_mark = token->end_mark; parser->state = YAML_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE; MAPPING_START_EVENT_INIT(*event, anchor, tag, implicit, YAML_BLOCK_MAPPING_STYLE, start_mark, end_mark); return 1; } else if (anchor || tag) { yaml_char_t *value = YAML_MALLOC(1); if (!value) { parser->error = YAML_MEMORY_ERROR; goto error; } value[0] = '\0'; parser->state = POP(parser, parser->states); SCALAR_EVENT_INIT(*event, anchor, tag, value, 0, implicit, 0, YAML_PLAIN_SCALAR_STYLE, start_mark, end_mark); return 1; } else { yaml_parser_set_parser_error_context(parser, (block ? "while parsing a block node" : "while parsing a flow node"), start_mark, "did not find expected node content", token->start_mark); goto error; } } } error: yaml_free(anchor); yaml_free(tag_handle); yaml_free(tag_suffix); yaml_free(tag); return 0; } /* * Parse the productions: * block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END * ******************** *********** * ********* */ static int yaml_parser_parse_block_sequence_entry(yaml_parser_t *parser, yaml_event_t *event, int first) { yaml_token_t *token; if (first) { token = PEEK_TOKEN(parser); if (!PUSH(parser, parser->marks, token->start_mark)) return 0; SKIP_TOKEN(parser); } token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type == YAML_BLOCK_ENTRY_TOKEN) { yaml_mark_t mark = token->end_mark; SKIP_TOKEN(parser); token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type != YAML_BLOCK_ENTRY_TOKEN && token->type != YAML_BLOCK_END_TOKEN) { if (!PUSH(parser, parser->states, YAML_PARSE_BLOCK_SEQUENCE_ENTRY_STATE)) return 0; return yaml_parser_parse_node(parser, event, 1, 0); } else { parser->state = YAML_PARSE_BLOCK_SEQUENCE_ENTRY_STATE; return yaml_parser_process_empty_scalar(parser, event, mark); } } else if (token->type == YAML_BLOCK_END_TOKEN) { parser->state = POP(parser, parser->states); (void)POP(parser, parser->marks); SEQUENCE_END_EVENT_INIT(*event, token->start_mark, token->end_mark); SKIP_TOKEN(parser); return 1; } else { return yaml_parser_set_parser_error_context(parser, "while parsing a block collection", POP(parser, parser->marks), "did not find expected '-' indicator", token->start_mark); } } /* * Parse the productions: * indentless_sequence ::= (BLOCK-ENTRY block_node?)+ * *********** * */ static int yaml_parser_parse_indentless_sequence_entry(yaml_parser_t *parser, yaml_event_t *event) { yaml_token_t *token; token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type == YAML_BLOCK_ENTRY_TOKEN) { yaml_mark_t mark = token->end_mark; SKIP_TOKEN(parser); token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type != YAML_BLOCK_ENTRY_TOKEN && token->type != YAML_KEY_TOKEN && token->type != YAML_VALUE_TOKEN && token->type != YAML_BLOCK_END_TOKEN) { if (!PUSH(parser, parser->states, YAML_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE)) return 0; return yaml_parser_parse_node(parser, event, 1, 0); } else { parser->state = YAML_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE; return yaml_parser_process_empty_scalar(parser, event, mark); } } else { parser->state = POP(parser, parser->states); SEQUENCE_END_EVENT_INIT(*event, token->start_mark, token->start_mark); return 1; } } /* * Parse the productions: * block_mapping ::= BLOCK-MAPPING_START * ******************* * ((KEY block_node_or_indentless_sequence?)? * *** * * (VALUE block_node_or_indentless_sequence?)?)* * * BLOCK-END * ********* */ static int yaml_parser_parse_block_mapping_key(yaml_parser_t *parser, yaml_event_t *event, int first) { yaml_token_t *token; if (first) { token = PEEK_TOKEN(parser); if (!PUSH(parser, parser->marks, token->start_mark)) return 0; SKIP_TOKEN(parser); } token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type == YAML_KEY_TOKEN) { yaml_mark_t mark = token->end_mark; SKIP_TOKEN(parser); token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type != YAML_KEY_TOKEN && token->type != YAML_VALUE_TOKEN && token->type != YAML_BLOCK_END_TOKEN) { if (!PUSH(parser, parser->states, YAML_PARSE_BLOCK_MAPPING_VALUE_STATE)) return 0; return yaml_parser_parse_node(parser, event, 1, 1); } else { parser->state = YAML_PARSE_BLOCK_MAPPING_VALUE_STATE; return yaml_parser_process_empty_scalar(parser, event, mark); } } else if (token->type == YAML_BLOCK_END_TOKEN) { parser->state = POP(parser, parser->states); (void)POP(parser, parser->marks); MAPPING_END_EVENT_INIT(*event, token->start_mark, token->end_mark); SKIP_TOKEN(parser); return 1; } else { return yaml_parser_set_parser_error_context(parser, "while parsing a block mapping", POP(parser, parser->marks), "did not find expected key", token->start_mark); } } /* * Parse the productions: * block_mapping ::= BLOCK-MAPPING_START * * ((KEY block_node_or_indentless_sequence?)? * * (VALUE block_node_or_indentless_sequence?)?)* * ***** * * BLOCK-END * */ static int yaml_parser_parse_block_mapping_value(yaml_parser_t *parser, yaml_event_t *event) { yaml_token_t *token; token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type == YAML_VALUE_TOKEN) { yaml_mark_t mark = token->end_mark; SKIP_TOKEN(parser); token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type != YAML_KEY_TOKEN && token->type != YAML_VALUE_TOKEN && token->type != YAML_BLOCK_END_TOKEN) { if (!PUSH(parser, parser->states, YAML_PARSE_BLOCK_MAPPING_KEY_STATE)) return 0; return yaml_parser_parse_node(parser, event, 1, 1); } else { parser->state = YAML_PARSE_BLOCK_MAPPING_KEY_STATE; return yaml_parser_process_empty_scalar(parser, event, mark); } } else { parser->state = YAML_PARSE_BLOCK_MAPPING_KEY_STATE; return yaml_parser_process_empty_scalar(parser, event, token->start_mark); } } /* * Parse the productions: * flow_sequence ::= FLOW-SEQUENCE-START * ******************* * (flow_sequence_entry FLOW-ENTRY)* * * ********** * flow_sequence_entry? * * * FLOW-SEQUENCE-END * ***************** * flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? * * */ static int yaml_parser_parse_flow_sequence_entry(yaml_parser_t *parser, yaml_event_t *event, int first) { yaml_token_t *token; if (first) { token = PEEK_TOKEN(parser); if (!PUSH(parser, parser->marks, token->start_mark)) return 0; SKIP_TOKEN(parser); } token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type != YAML_FLOW_SEQUENCE_END_TOKEN) { if (!first) { if (token->type == YAML_FLOW_ENTRY_TOKEN) { SKIP_TOKEN(parser); token = PEEK_TOKEN(parser); if (!token) return 0; } else { return yaml_parser_set_parser_error_context(parser, "while parsing a flow sequence", POP(parser, parser->marks), "did not find expected ',' or ']'", token->start_mark); } } if (token->type == YAML_KEY_TOKEN) { parser->state = YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE; MAPPING_START_EVENT_INIT(*event, NULL, NULL, 1, YAML_FLOW_MAPPING_STYLE, token->start_mark, token->end_mark); SKIP_TOKEN(parser); return 1; } else if (token->type != YAML_FLOW_SEQUENCE_END_TOKEN) { if (!PUSH(parser, parser->states, YAML_PARSE_FLOW_SEQUENCE_ENTRY_STATE)) return 0; return yaml_parser_parse_node(parser, event, 0, 0); } } parser->state = POP(parser, parser->states); (void)POP(parser, parser->marks); SEQUENCE_END_EVENT_INIT(*event, token->start_mark, token->end_mark); SKIP_TOKEN(parser); return 1; } /* * Parse the productions: * flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? * *** * */ static int yaml_parser_parse_flow_sequence_entry_mapping_key(yaml_parser_t *parser, yaml_event_t *event) { yaml_token_t *token; token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type != YAML_VALUE_TOKEN && token->type != YAML_FLOW_ENTRY_TOKEN && token->type != YAML_FLOW_SEQUENCE_END_TOKEN) { if (!PUSH(parser, parser->states, YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE)) return 0; return yaml_parser_parse_node(parser, event, 0, 0); } else { yaml_mark_t mark = token->end_mark; SKIP_TOKEN(parser); parser->state = YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE; return yaml_parser_process_empty_scalar(parser, event, mark); } } /* * Parse the productions: * flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? * ***** * */ static int yaml_parser_parse_flow_sequence_entry_mapping_value(yaml_parser_t *parser, yaml_event_t *event) { yaml_token_t *token; token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type == YAML_VALUE_TOKEN) { SKIP_TOKEN(parser); token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type != YAML_FLOW_ENTRY_TOKEN && token->type != YAML_FLOW_SEQUENCE_END_TOKEN) { if (!PUSH(parser, parser->states, YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE)) return 0; return yaml_parser_parse_node(parser, event, 0, 0); } } parser->state = YAML_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE; return yaml_parser_process_empty_scalar(parser, event, token->start_mark); } /* * Parse the productions: * flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? * * */ static int yaml_parser_parse_flow_sequence_entry_mapping_end(yaml_parser_t *parser, yaml_event_t *event) { yaml_token_t *token; token = PEEK_TOKEN(parser); if (!token) return 0; parser->state = YAML_PARSE_FLOW_SEQUENCE_ENTRY_STATE; MAPPING_END_EVENT_INIT(*event, token->start_mark, token->start_mark); return 1; } /* * Parse the productions: * flow_mapping ::= FLOW-MAPPING-START * ****************** * (flow_mapping_entry FLOW-ENTRY)* * * ********** * flow_mapping_entry? * ****************** * FLOW-MAPPING-END * **************** * flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? * * *** * */ static int yaml_parser_parse_flow_mapping_key(yaml_parser_t *parser, yaml_event_t *event, int first) { yaml_token_t *token; if (first) { token = PEEK_TOKEN(parser); if (!PUSH(parser, parser->marks, token->start_mark)) return 0; SKIP_TOKEN(parser); } token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type != YAML_FLOW_MAPPING_END_TOKEN) { if (!first) { if (token->type == YAML_FLOW_ENTRY_TOKEN) { SKIP_TOKEN(parser); token = PEEK_TOKEN(parser); if (!token) return 0; } else { return yaml_parser_set_parser_error_context(parser, "while parsing a flow mapping", POP(parser, parser->marks), "did not find expected ',' or '}'", token->start_mark); } } if (token->type == YAML_KEY_TOKEN) { SKIP_TOKEN(parser); token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type != YAML_VALUE_TOKEN && token->type != YAML_FLOW_ENTRY_TOKEN && token->type != YAML_FLOW_MAPPING_END_TOKEN) { if (!PUSH(parser, parser->states, YAML_PARSE_FLOW_MAPPING_VALUE_STATE)) return 0; return yaml_parser_parse_node(parser, event, 0, 0); } else { parser->state = YAML_PARSE_FLOW_MAPPING_VALUE_STATE; return yaml_parser_process_empty_scalar(parser, event, token->start_mark); } } else if (token->type != YAML_FLOW_MAPPING_END_TOKEN) { if (!PUSH(parser, parser->states, YAML_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE)) return 0; return yaml_parser_parse_node(parser, event, 0, 0); } } parser->state = POP(parser, parser->states); (void)POP(parser, parser->marks); MAPPING_END_EVENT_INIT(*event, token->start_mark, token->end_mark); SKIP_TOKEN(parser); return 1; } /* * Parse the productions: * flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? * * ***** * */ static int yaml_parser_parse_flow_mapping_value(yaml_parser_t *parser, yaml_event_t *event, int empty) { yaml_token_t *token; token = PEEK_TOKEN(parser); if (!token) return 0; if (empty) { parser->state = YAML_PARSE_FLOW_MAPPING_KEY_STATE; return yaml_parser_process_empty_scalar(parser, event, token->start_mark); } if (token->type == YAML_VALUE_TOKEN) { SKIP_TOKEN(parser); token = PEEK_TOKEN(parser); if (!token) return 0; if (token->type != YAML_FLOW_ENTRY_TOKEN && token->type != YAML_FLOW_MAPPING_END_TOKEN) { if (!PUSH(parser, parser->states, YAML_PARSE_FLOW_MAPPING_KEY_STATE)) return 0; return yaml_parser_parse_node(parser, event, 0, 0); } } parser->state = YAML_PARSE_FLOW_MAPPING_KEY_STATE; return yaml_parser_process_empty_scalar(parser, event, token->start_mark); } /* * Generate an empty scalar event. */ static int yaml_parser_process_empty_scalar(yaml_parser_t *parser, yaml_event_t *event, yaml_mark_t mark) { yaml_char_t *value; value = YAML_MALLOC(1); if (!value) { parser->error = YAML_MEMORY_ERROR; return 0; } value[0] = '\0'; SCALAR_EVENT_INIT(*event, NULL, NULL, value, 0, 1, 0, YAML_PLAIN_SCALAR_STYLE, mark, mark); return 1; } /* * Parse directives. */ static int yaml_parser_process_directives(yaml_parser_t *parser, yaml_version_directive_t **version_directive_ref, yaml_tag_directive_t **tag_directives_start_ref, yaml_tag_directive_t **tag_directives_end_ref) { yaml_tag_directive_t default_tag_directives[] = { {(yaml_char_t *)"!", (yaml_char_t *)"!"}, {(yaml_char_t *)"!!", (yaml_char_t *)"tag:yaml.org,2002:"}, {NULL, NULL} }; yaml_tag_directive_t *default_tag_directive; yaml_version_directive_t *version_directive = NULL; struct { yaml_tag_directive_t *start; yaml_tag_directive_t *end; yaml_tag_directive_t *top; } tag_directives = { NULL, NULL, NULL }; yaml_token_t *token; if (!STACK_INIT(parser, tag_directives, yaml_tag_directive_t*)) goto error; token = PEEK_TOKEN(parser); if (!token) goto error; while (token->type == YAML_VERSION_DIRECTIVE_TOKEN || token->type == YAML_TAG_DIRECTIVE_TOKEN) { if (token->type == YAML_VERSION_DIRECTIVE_TOKEN) { if (version_directive) { yaml_parser_set_parser_error(parser, "found duplicate %YAML directive", token->start_mark); goto error; } if (token->data.version_directive.major != 1 || token->data.version_directive.minor != 1) { yaml_parser_set_parser_error(parser, "found incompatible YAML document", token->start_mark); goto error; } version_directive = YAML_MALLOC_STATIC(yaml_version_directive_t); if (!version_directive) { parser->error = YAML_MEMORY_ERROR; goto error; } version_directive->major = token->data.version_directive.major; version_directive->minor = token->data.version_directive.minor; } else if (token->type == YAML_TAG_DIRECTIVE_TOKEN) { yaml_tag_directive_t value; value.handle = token->data.tag_directive.handle; value.prefix = token->data.tag_directive.prefix; if (!yaml_parser_append_tag_directive(parser, value, 0, token->start_mark)) goto error; if (!PUSH(parser, tag_directives, value)) goto error; } SKIP_TOKEN(parser); token = PEEK_TOKEN(parser); if (!token) goto error; } for (default_tag_directive = default_tag_directives; default_tag_directive->handle; default_tag_directive++) { if (!yaml_parser_append_tag_directive(parser, *default_tag_directive, 1, token->start_mark)) goto error; } if (version_directive_ref) { *version_directive_ref = version_directive; } if (tag_directives_start_ref) { if (STACK_EMPTY(parser, tag_directives)) { *tag_directives_start_ref = *tag_directives_end_ref = NULL; STACK_DEL(parser, tag_directives); } else { *tag_directives_start_ref = tag_directives.start; *tag_directives_end_ref = tag_directives.top; } } else { STACK_DEL(parser, tag_directives); } if (!version_directive_ref) yaml_free(version_directive); return 1; error: yaml_free(version_directive); while (!STACK_EMPTY(parser, tag_directives)) { yaml_tag_directive_t tag_directive = POP(parser, tag_directives); yaml_free(tag_directive.handle); yaml_free(tag_directive.prefix); } STACK_DEL(parser, tag_directives); return 0; } /* * Append a tag directive to the directives stack. */ static int yaml_parser_append_tag_directive(yaml_parser_t *parser, yaml_tag_directive_t value, int allow_duplicates, yaml_mark_t mark) { yaml_tag_directive_t *tag_directive; yaml_tag_directive_t copy = { NULL, NULL }; for (tag_directive = parser->tag_directives.start; tag_directive != parser->tag_directives.top; tag_directive ++) { if (strcmp((char *)value.handle, (char *)tag_directive->handle) == 0) { if (allow_duplicates) return 1; return yaml_parser_set_parser_error(parser, "found duplicate %TAG directive", mark); } } copy.handle = yaml_strdup(value.handle); copy.prefix = yaml_strdup(value.prefix); if (!copy.handle || !copy.prefix) { parser->error = YAML_MEMORY_ERROR; goto error; } if (!PUSH(parser, parser->tag_directives, copy)) goto error; return 1; error: yaml_free(copy.handle); yaml_free(copy.prefix); return 0; } yaml/src/Makevars0000644000176200001440000000003413614635636013511 0ustar liggesusersPKG_CPPFLAGS = -I. -DNDEBUG yaml/src/writer.c0000644000176200001440000000771413614635636013511 0ustar liggesusers #include "yaml_private.h" /* * Declarations. */ static int yaml_emitter_set_writer_error(yaml_emitter_t *emitter, const char *problem); YAML_DECLARE(int) yaml_emitter_flush(yaml_emitter_t *emitter); /* * Set the writer error and return 0. */ static int yaml_emitter_set_writer_error(yaml_emitter_t *emitter, const char *problem) { emitter->error = YAML_WRITER_ERROR; emitter->problem = problem; return 0; } /* * Flush the output buffer. */ YAML_DECLARE(int) yaml_emitter_flush(yaml_emitter_t *emitter) { int low, high; assert(emitter); /* Non-NULL emitter object is expected. */ assert(emitter->write_handler); /* Write handler must be set. */ assert(emitter->encoding); /* Output encoding must be set. */ emitter->buffer.last = emitter->buffer.pointer; emitter->buffer.pointer = emitter->buffer.start; /* Check if the buffer is empty. */ if (emitter->buffer.start == emitter->buffer.last) { return 1; } /* If the output encoding is UTF-8, we don't need to recode the buffer. */ if (emitter->encoding == YAML_UTF8_ENCODING) { if (emitter->write_handler(emitter->write_handler_data, emitter->buffer.start, emitter->buffer.last - emitter->buffer.start)) { emitter->buffer.last = emitter->buffer.start; emitter->buffer.pointer = emitter->buffer.start; return 1; } else { return yaml_emitter_set_writer_error(emitter, "write error"); } } /* Recode the buffer into the raw buffer. */ low = (emitter->encoding == YAML_UTF16LE_ENCODING ? 0 : 1); high = (emitter->encoding == YAML_UTF16LE_ENCODING ? 1 : 0); while (emitter->buffer.pointer != emitter->buffer.last) { unsigned char octet; unsigned int width; unsigned int value; size_t k; /* * See the "reader.c" code for more details on UTF-8 encoding. Note * that we assume that the buffer contains a valid UTF-8 sequence. */ /* Read the next UTF-8 character. */ octet = emitter->buffer.pointer[0]; width = (octet & 0x80) == 0x00 ? 1 : (octet & 0xE0) == 0xC0 ? 2 : (octet & 0xF0) == 0xE0 ? 3 : (octet & 0xF8) == 0xF0 ? 4 : 0; value = (octet & 0x80) == 0x00 ? octet & 0x7F : (octet & 0xE0) == 0xC0 ? octet & 0x1F : (octet & 0xF0) == 0xE0 ? octet & 0x0F : (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; for (k = 1; k < width; k ++) { octet = emitter->buffer.pointer[k]; value = (value << 6) + (octet & 0x3F); } emitter->buffer.pointer += width; /* Write the character. */ if (value < 0x10000) { emitter->raw_buffer.last[high] = value >> 8; emitter->raw_buffer.last[low] = value & 0xFF; emitter->raw_buffer.last += 2; } else { /* Write the character using a surrogate pair (check "reader.c"). */ value -= 0x10000; emitter->raw_buffer.last[high] = 0xD8 + (value >> 18); emitter->raw_buffer.last[low] = (value >> 10) & 0xFF; emitter->raw_buffer.last[high+2] = 0xDC + ((value >> 8) & 0xFF); emitter->raw_buffer.last[low+2] = value & 0xFF; emitter->raw_buffer.last += 4; } } /* Write the raw buffer. */ if (emitter->write_handler(emitter->write_handler_data, emitter->raw_buffer.start, emitter->raw_buffer.last - emitter->raw_buffer.start)) { emitter->buffer.last = emitter->buffer.start; emitter->buffer.pointer = emitter->buffer.start; emitter->raw_buffer.last = emitter->raw_buffer.start; emitter->raw_buffer.pointer = emitter->raw_buffer.start; return 1; } else { return yaml_emitter_set_writer_error(emitter, "write error"); } } yaml/src/r_ext.c0000644000176200001440000001505313614635636013311 0ustar liggesusers#include "r_ext.h" SEXP Ryaml_KeysSymbol = NULL; SEXP Ryaml_TagSymbol = NULL; SEXP Ryaml_IdenticalFunc = NULL; SEXP Ryaml_FormatFunc = NULL; SEXP Ryaml_PasteFunc = NULL; SEXP Ryaml_DeparseFunc = NULL; SEXP Ryaml_ClassFunc = NULL; SEXP Ryaml_CollapseSymbol = NULL; SEXP Ryaml_Sentinel = NULL; SEXP Ryaml_SequenceStart = NULL; SEXP Ryaml_MappingStart = NULL; SEXP Ryaml_MappingEnd = NULL; char Ryaml_error_msg[ERROR_MSG_SIZE]; void Ryaml_set_error_msg(const char *format, ...) { va_list args; int result; va_start(args, format); result = vsnprintf(Ryaml_error_msg, ERROR_MSG_SIZE, format, args); if (result >= ERROR_MSG_SIZE) { warning("an error occurred, but the message was too long to format properly"); /* ensure the string is null terminated */ Ryaml_error_msg[ERROR_MSG_SIZE-1] = 0; } } /* Returns true if obj is a named list */ int Ryaml_is_named_list(s_obj) SEXP s_obj; { SEXP s_names = NULL; if (TYPEOF(s_obj) != VECSXP) return 0; s_names = GET_NAMES(s_obj); return (TYPEOF(s_names) == STRSXP && LENGTH(s_names) == LENGTH(s_obj)); } /* Call R's paste() function with collapse */ SEXP Ryaml_collapse(s_obj, collapse) SEXP s_obj; char *collapse; { SEXP s_call = NULL, s_retval = NULL; PROTECT(s_call = lang3(Ryaml_PasteFunc, s_obj, ScalarString(mkCharCE(collapse, CE_UTF8)))); SET_TAG(CDDR(s_call), Ryaml_CollapseSymbol); s_retval = eval(s_call, R_GlobalEnv); UNPROTECT(1); return s_retval; } /* Return a string representation of the object for error messages */ SEXP Ryaml_inspect(s_obj) SEXP s_obj; { SEXP s_call = NULL, s_str = NULL, s_result = NULL; /* Using format/paste here is not really what I want, but without * jumping through all kinds of hoops so that I can get the output * of print(), this is the most effort I want to put into this. */ PROTECT(s_call = lang2(Ryaml_FormatFunc, s_obj)); s_str = eval(s_call, R_GlobalEnv); UNPROTECT(1); PROTECT(s_str); s_result = Ryaml_collapse(s_str, " "); UNPROTECT(1); return s_result; } SEXP Ryaml_get_classes(s_obj) SEXP s_obj; { SEXP s_call = NULL, s_result = NULL; PROTECT(s_call = lang2(Ryaml_ClassFunc, s_obj)); s_result = eval(s_call, R_GlobalEnv); UNPROTECT(1); return s_result; } /* Return 1 if obj is of the specified class */ int Ryaml_has_class(s_obj, name) SEXP s_obj; char *name; { SEXP s_classes = NULL; int i = 0, len = 0, result = 0; PROTECT(s_obj); PROTECT(s_classes = Ryaml_get_classes(s_obj)); if (TYPEOF(s_classes) == STRSXP) { len = length(s_classes); for (i = 0; i < len; i++) { if (strcmp(CHAR(STRING_ELT(s_classes, i)), name) == 0) { result = 1; break; } } } UNPROTECT(2); return result; } SEXP Ryaml_sanitize_handlers(s_handlers) SEXP s_handlers; { SEXP s_handlers_2 = NULL, s_handler = NULL, s_names = NULL, s_names_2 = NULL, s_name = NULL; const char *name = NULL, *name_2 = NULL; cetype_t encoding = CE_NATIVE; int i = 0; if (s_handlers == R_NilValue) { return R_NilValue; } else if (!Ryaml_is_named_list(s_handlers)) { error("handlers must be either NULL or a named list of functions"); return R_NilValue; } else { PROTECT(s_names = GET_NAMES(s_handlers)); PROTECT(s_handlers_2 = allocVector(VECSXP, length(s_handlers))); PROTECT(s_names_2 = allocVector(STRSXP, length(s_names))); for (i = 0; i < length(s_handlers); i++) { /* Possibly convert name to UTF-8 */ PROTECT(s_name = STRING_ELT(s_names, i)); encoding = getCharCE(s_name); if (encoding == CE_UTF8) { SET_STRING_ELT(s_names_2, i, s_name); } else { name = CHAR(s_name); name_2 = reEnc(name, encoding, CE_UTF8, 1); UNPROTECT(1); /* s_name */ PROTECT(s_name = mkCharCE(name_2, CE_UTF8)); SET_STRING_ELT(s_names_2, i, s_name); } name = CHAR(s_name); /* Validate handler */ s_handler = VECTOR_ELT(s_handlers, i); if (TYPEOF(s_handler) != CLOSXP) { warning("Your handler for type '%s' is not a function; using default", name); s_handler = R_NilValue; } else if (strcmp(name, "merge") == 0 || strcmp(name, "default") == 0) { /* custom handlers for merge and default are illegal */ warning("Custom handling for type '%s' is not allowed; handler ignored", name); s_handler = R_NilValue; } SET_VECTOR_ELT(s_handlers_2, i, s_handler); UNPROTECT(1); /* s_name */ } SET_NAMES(s_handlers_2, s_names_2); s_handlers = s_handlers_2; UNPROTECT(3); /* s_names, s_names_2, s_handlers_2 */ } return s_handlers_2; } SEXP Ryaml_find_handler(s_handlers, name) SEXP s_handlers; const char *name; { SEXP s_names = NULL, s_name = NULL, s_retval = R_NilValue; const char *handler_name = NULL; int i = 0, found = 0; /* Look for a custom R handler */ if (s_handlers != R_NilValue) { PROTECT(s_names = GET_NAMES(s_handlers)); for (i = 0; i < length(s_names); i++) { PROTECT(s_name = STRING_ELT(s_names, i)); if (s_name != NA_STRING) { handler_name = CHAR(s_name); if (strcmp(handler_name, name) == 0) { /* Found custom handler */ s_retval = VECTOR_ELT(s_handlers, i); found = 1; } } UNPROTECT(1); /* s_name */ if (found) break; } UNPROTECT(1); /* s_names */ } return s_retval; } int Ryaml_run_handler(s_handler, s_arg, s_result) SEXP s_handler; SEXP s_arg; SEXP *s_result; { SEXP s_cmd = NULL; int err = 0; PROTECT(s_cmd = lang2(s_handler, s_arg)); *s_result = R_tryEval(s_cmd, R_GlobalEnv, &err); UNPROTECT(1); return err; } R_CallMethodDef callMethods[] = { {"unserialize_from_yaml", (DL_FUNC)&Ryaml_unserialize_from_yaml, 8}, {"serialize_to_yaml", (DL_FUNC)&Ryaml_serialize_to_yaml, 9}, {NULL, NULL, 0} }; void R_init_yaml(DllInfo *dll) { Ryaml_KeysSymbol = install("keys"); Ryaml_TagSymbol = install("tag"); Ryaml_CollapseSymbol = install("collapse"); Ryaml_IdenticalFunc = findFun(install("identical"), R_GlobalEnv); Ryaml_FormatFunc = findFun(install("format"), R_GlobalEnv); Ryaml_PasteFunc = findFun(install("paste"), R_GlobalEnv); Ryaml_DeparseFunc = findFun(install("deparse"), R_GlobalEnv); Ryaml_ClassFunc = findFun(install("class"), R_GlobalEnv); Ryaml_Sentinel = install("sentinel"); Ryaml_SequenceStart = install("sequence.start"); Ryaml_MappingStart = install("mapping.start"); Ryaml_MappingEnd = install("mapping.end"); R_registerRoutines(dll, NULL, callMethods, NULL, NULL); R_useDynamicSymbols(dll, FALSE); R_forceSymbols(dll, TRUE); } yaml/src/dumper.c0000644000176200001440000002352313614635636013465 0ustar liggesusers #include "yaml_private.h" /* * API functions. */ YAML_DECLARE(int) yaml_emitter_open(yaml_emitter_t *emitter); YAML_DECLARE(int) yaml_emitter_close(yaml_emitter_t *emitter); YAML_DECLARE(int) yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document); /* * Clean up functions. */ static void yaml_emitter_delete_document_and_anchors(yaml_emitter_t *emitter); /* * Anchor functions. */ static void yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index); static yaml_char_t * yaml_emitter_generate_anchor(yaml_emitter_t *emitter, int anchor_id); /* * Serialize functions. */ static int yaml_emitter_dump_node(yaml_emitter_t *emitter, int index); static int yaml_emitter_dump_alias(yaml_emitter_t *emitter, yaml_char_t *anchor); static int yaml_emitter_dump_scalar(yaml_emitter_t *emitter, yaml_node_t *node, yaml_char_t *anchor); static int yaml_emitter_dump_sequence(yaml_emitter_t *emitter, yaml_node_t *node, yaml_char_t *anchor); static int yaml_emitter_dump_mapping(yaml_emitter_t *emitter, yaml_node_t *node, yaml_char_t *anchor); /* * Issue a STREAM-START event. */ YAML_DECLARE(int) yaml_emitter_open(yaml_emitter_t *emitter) { yaml_event_t event; yaml_mark_t mark = { 0, 0, 0 }; assert(emitter); /* Non-NULL emitter object is required. */ assert(!emitter->opened); /* Emitter should not be opened yet. */ STREAM_START_EVENT_INIT(event, YAML_ANY_ENCODING, mark, mark); if (!yaml_emitter_emit(emitter, &event)) { return 0; } emitter->opened = 1; return 1; } /* * Issue a STREAM-END event. */ YAML_DECLARE(int) yaml_emitter_close(yaml_emitter_t *emitter) { yaml_event_t event; yaml_mark_t mark = { 0, 0, 0 }; assert(emitter); /* Non-NULL emitter object is required. */ assert(emitter->opened); /* Emitter should be opened. */ if (emitter->closed) return 1; STREAM_END_EVENT_INIT(event, mark, mark); if (!yaml_emitter_emit(emitter, &event)) { return 0; } emitter->closed = 1; return 1; } /* * Dump a YAML document. */ YAML_DECLARE(int) yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document) { yaml_event_t event; yaml_mark_t mark = { 0, 0, 0 }; assert(emitter); /* Non-NULL emitter object is required. */ assert(document); /* Non-NULL emitter object is expected. */ emitter->document = document; if (!emitter->opened) { if (!yaml_emitter_open(emitter)) goto error; } if (STACK_EMPTY(emitter, document->nodes)) { if (!yaml_emitter_close(emitter)) goto error; yaml_emitter_delete_document_and_anchors(emitter); return 1; } assert(emitter->opened); /* Emitter should be opened. */ emitter->anchors = (yaml_anchors_t*)yaml_malloc(sizeof(*(emitter->anchors)) * (document->nodes.top - document->nodes.start)); if (!emitter->anchors) goto error; memset(emitter->anchors, 0, sizeof(*(emitter->anchors)) * (document->nodes.top - document->nodes.start)); DOCUMENT_START_EVENT_INIT(event, document->version_directive, document->tag_directives.start, document->tag_directives.end, document->start_implicit, mark, mark); if (!yaml_emitter_emit(emitter, &event)) goto error; yaml_emitter_anchor_node(emitter, 1); if (!yaml_emitter_dump_node(emitter, 1)) goto error; DOCUMENT_END_EVENT_INIT(event, document->end_implicit, mark, mark); if (!yaml_emitter_emit(emitter, &event)) goto error; yaml_emitter_delete_document_and_anchors(emitter); return 1; error: yaml_emitter_delete_document_and_anchors(emitter); return 0; } /* * Clean up the emitter object after a document is dumped. */ static void yaml_emitter_delete_document_and_anchors(yaml_emitter_t *emitter) { int index; if (!emitter->anchors) { yaml_document_delete(emitter->document); emitter->document = NULL; return; } for (index = 0; emitter->document->nodes.start + index < emitter->document->nodes.top; index ++) { yaml_node_t node = emitter->document->nodes.start[index]; if (!emitter->anchors[index].serialized) { yaml_free(node.tag); if (node.type == YAML_SCALAR_NODE) { yaml_free(node.data.scalar.value); } } if (node.type == YAML_SEQUENCE_NODE) { STACK_DEL(emitter, node.data.sequence.items); } if (node.type == YAML_MAPPING_NODE) { STACK_DEL(emitter, node.data.mapping.pairs); } } STACK_DEL(emitter, emitter->document->nodes); yaml_free(emitter->anchors); emitter->anchors = NULL; emitter->last_anchor_id = 0; emitter->document = NULL; } /* * Check the references of a node and assign the anchor id if needed. */ static void yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index) { yaml_node_t *node = emitter->document->nodes.start + index - 1; yaml_node_item_t *item; yaml_node_pair_t *pair; emitter->anchors[index-1].references ++; if (emitter->anchors[index-1].references == 1) { switch (node->type) { case YAML_SEQUENCE_NODE: for (item = node->data.sequence.items.start; item < node->data.sequence.items.top; item ++) { yaml_emitter_anchor_node(emitter, *item); } break; case YAML_MAPPING_NODE: for (pair = node->data.mapping.pairs.start; pair < node->data.mapping.pairs.top; pair ++) { yaml_emitter_anchor_node(emitter, pair->key); yaml_emitter_anchor_node(emitter, pair->value); } break; default: break; } } else if (emitter->anchors[index-1].references == 2) { emitter->anchors[index-1].anchor = (++ emitter->last_anchor_id); } } /* * Generate a textual representation for an anchor. */ #define ANCHOR_TEMPLATE "id%03d" #define ANCHOR_TEMPLATE_LENGTH 16 static yaml_char_t * yaml_emitter_generate_anchor(SHIM(yaml_emitter_t *emitter), int anchor_id) { yaml_char_t *anchor = YAML_MALLOC(ANCHOR_TEMPLATE_LENGTH); if (!anchor) return NULL; sprintf((char *)anchor, ANCHOR_TEMPLATE, anchor_id); return anchor; } /* * Serialize a node. */ static int yaml_emitter_dump_node(yaml_emitter_t *emitter, int index) { yaml_node_t *node = emitter->document->nodes.start + index - 1; int anchor_id = emitter->anchors[index-1].anchor; yaml_char_t *anchor = NULL; if (anchor_id) { anchor = yaml_emitter_generate_anchor(emitter, anchor_id); if (!anchor) return 0; } if (emitter->anchors[index-1].serialized) { return yaml_emitter_dump_alias(emitter, anchor); } emitter->anchors[index-1].serialized = 1; switch (node->type) { case YAML_SCALAR_NODE: return yaml_emitter_dump_scalar(emitter, node, anchor); case YAML_SEQUENCE_NODE: return yaml_emitter_dump_sequence(emitter, node, anchor); case YAML_MAPPING_NODE: return yaml_emitter_dump_mapping(emitter, node, anchor); default: assert(0); /* Could not happen. */ break; } return 0; /* Could not happen. */ } /* * Serialize an alias. */ static int yaml_emitter_dump_alias(yaml_emitter_t *emitter, yaml_char_t *anchor) { yaml_event_t event; yaml_mark_t mark = { 0, 0, 0 }; ALIAS_EVENT_INIT(event, anchor, mark, mark); return yaml_emitter_emit(emitter, &event); } /* * Serialize a scalar. */ static int yaml_emitter_dump_scalar(yaml_emitter_t *emitter, yaml_node_t *node, yaml_char_t *anchor) { yaml_event_t event; yaml_mark_t mark = { 0, 0, 0 }; int plain_implicit = (strcmp((char *)node->tag, YAML_DEFAULT_SCALAR_TAG) == 0); int quoted_implicit = (strcmp((char *)node->tag, YAML_DEFAULT_SCALAR_TAG) == 0); SCALAR_EVENT_INIT(event, anchor, node->tag, node->data.scalar.value, node->data.scalar.length, plain_implicit, quoted_implicit, node->data.scalar.style, mark, mark); return yaml_emitter_emit(emitter, &event); } /* * Serialize a sequence. */ static int yaml_emitter_dump_sequence(yaml_emitter_t *emitter, yaml_node_t *node, yaml_char_t *anchor) { yaml_event_t event; yaml_mark_t mark = { 0, 0, 0 }; int implicit = (strcmp((char *)node->tag, YAML_DEFAULT_SEQUENCE_TAG) == 0); yaml_node_item_t *item; SEQUENCE_START_EVENT_INIT(event, anchor, node->tag, implicit, node->data.sequence.style, mark, mark); if (!yaml_emitter_emit(emitter, &event)) return 0; for (item = node->data.sequence.items.start; item < node->data.sequence.items.top; item ++) { if (!yaml_emitter_dump_node(emitter, *item)) return 0; } SEQUENCE_END_EVENT_INIT(event, mark, mark); if (!yaml_emitter_emit(emitter, &event)) return 0; return 1; } /* * Serialize a mapping. */ static int yaml_emitter_dump_mapping(yaml_emitter_t *emitter, yaml_node_t *node, yaml_char_t *anchor) { yaml_event_t event; yaml_mark_t mark = { 0, 0, 0 }; int implicit = (strcmp((char *)node->tag, YAML_DEFAULT_MAPPING_TAG) == 0); yaml_node_pair_t *pair; MAPPING_START_EVENT_INIT(event, anchor, node->tag, implicit, node->data.mapping.style, mark, mark); if (!yaml_emitter_emit(emitter, &event)) return 0; for (pair = node->data.mapping.pairs.start; pair < node->data.mapping.pairs.top; pair ++) { if (!yaml_emitter_dump_node(emitter, pair->key)) return 0; if (!yaml_emitter_dump_node(emitter, pair->value)) return 0; } MAPPING_END_EVENT_INIT(event, mark, mark); if (!yaml_emitter_emit(emitter, &event)) return 0; return 1; } yaml/COPYING0000644000176200001440000000725613614635636012276 0ustar liggesusersR YAML package ============== Copyright (c) 2008-2016 Vanderbilt University Copyright (c) 2016-2018 Vanderbilt University Medical Center Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Vanderbilt University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. libyaml ======= Copyright (c) 2006-2016 Kirill Simonov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Syck ==== (for most of implicit.re) Copyright (c) 2003 why the lucky stiff Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. yaml/R/0000755000176200001440000000000013614635636011432 5ustar liggesusersyaml/R/zzz.R0000644000176200001440000000011313614635636012405 0ustar liggesusers.onUnload <- function(libpath) { library.dynam.unload("yaml", libpath) } yaml/R/as.yaml.R0000644000176200001440000000073113614635636013122 0ustar liggesusers`as.yaml` <- function(x, line.sep = c('\n', '\r\n', '\r'), indent = 2, omap = FALSE, column.major = TRUE, unicode = TRUE, precision = getOption('digits'), indent.mapping.sequence = FALSE, handlers = NULL) { line.sep <- match.arg(line.sep) res <- .Call(C_serialize_to_yaml, x, line.sep, indent, omap, column.major, unicode, precision, indent.mapping.sequence, handlers, PACKAGE="yaml") Encoding(res) <- "UTF-8" res } yaml/R/read_yaml.R0000644000176200001440000000204213614635636013510 0ustar liggesusers`read_yaml` <- function(file, fileEncoding = "UTF-8", text, error.label, ...) { if (missing(file) && !missing(text)) { if (missing(error.label)) { error.label <- NULL } file <- textConnection(text, encoding = "UTF-8") on.exit(close(file)) } else if (is.character(file)) { if (missing(error.label)) { error.label <- file } file <- if (nzchar(fileEncoding)) file(file, "rt", encoding = fileEncoding) else file(file, "rt") on.exit(close(file)) } else if (inherits(file, "connection")) { if (missing(error.label)) { # try to guess filename s <- try(summary(file), silent = TRUE) if (!inherits(s, "try-error") && is.list(s) && "description" %in% names(s)) { error.label <- s$description } } if (!isOpen(file, "rt")) { open(file, "rt") on.exit(close(file)) } } else { stop("'file' must be a character string or connection") } string <- paste(readLines(file), collapse="\n") yaml.load(string, error.label = error.label, ...) } yaml/R/yaml.load.R0000644000176200001440000000110113614635636013426 0ustar liggesusers`yaml.load` <- function(string, as.named.list = TRUE, handlers = NULL, error.label = NULL, eval.expr = getOption("yaml.eval.expr", TRUE), merge.precedence = c("order", "override"), merge.warning = FALSE) { string <- enc2utf8(paste(string, collapse = "\n")) eval.warning <- missing(eval.expr) && is.null(getOption("yaml.eval.expr")) merge.precedence <- match.arg(merge.precedence) .Call(C_unserialize_from_yaml, string, as.named.list, handlers, error.label, eval.expr, eval.warning, merge.precedence, merge.warning, PACKAGE="yaml") } yaml/R/write_yaml.R0000644000176200001440000000100213614635636013722 0ustar liggesusers`write_yaml` <- function(x, file, fileEncoding = "UTF-8", ...) { result <- as.yaml(x, ...) if (is.character(file)) { file <- if (nzchar(fileEncoding)) { file(file, "w", encoding = fileEncoding) } else { file(file, "w") } on.exit(close(file)) } else if (!isOpen(file, "w")) { open(file, "w") on.exit(close(file)) } if (!inherits(file, "connection")) { stop("'file' must be a character string or connection") } cat(result, file=file, sep="") } yaml/R/yaml.load_file.R0000644000176200001440000000126413614635636014437 0ustar liggesusers`yaml.load_file` <- function(input, error.label, ...) { if (missing(error.label)) { if (inherits(input, "connection")) { # try to guess filename s <- try(summary(input), silent = TRUE) if (!inherits(s, "try-error") && is.list(s) && "description" %in% names(s)) { error.label <- s$description } } else if (is.character(input) && nzchar(input[1])) { error.label <- input[1] } else { error.label <- NULL } } if (is.character(input)) { con <- file(input, encoding = 'UTF-8') on.exit(close(con), add = TRUE) } else { con <- input } yaml.load(readLines(con), error.label = error.label, ...) } yaml/MD50000644000176200001440000000400013615210612011513 0ustar liggesusersbbba10d958545e3425cc20f316d9da75 *COPYING a221dbad3bb496c69f7bc2ccb9658a79 *DESCRIPTION 98b6f1d7e7a36de0353698db9027b614 *LICENSE 17a16244d43d0cfe5439535f4695dd7f *NAMESPACE b901a42735294b31cf1d91b526714858 *R/as.yaml.R 2e2a7e7cf24956e202fc4c73bad09b90 *R/read_yaml.R 41bba07a114c49bbdfe2b025403c89c0 *R/write_yaml.R 8b6372c5e7440430f4cfadd9fe168a24 *R/yaml.load.R 363c5793776f7b432b777ecf3966d950 *R/yaml.load_file.R 59d3ef441a03e63a67784ef7d111f8fe *R/zzz.R 9769fdbe8107879a75976b3f8e66c479 *inst/CHANGELOG f0a1646d0486e242e59e280cd842ae0d *inst/THANKS 209752829e0543e43d1aa9dcf4c8ddf9 *inst/implicit.re 0625a433dcef8def801ea9ed2a7396c9 *inst/tests/files/merge.yml b70b0ade3126a031e5bf77d8aa9af4f2 *inst/tests/files/test.yml a3d76202661af5763f653752a987b7de *inst/tests/test_as_yaml.R ea58113433e31b75ea47a91797e97106 *inst/tests/test_read_yaml.R f0258adf12eff3e4ef781d06ed5ca659 *inst/tests/test_write_yaml.R b34ce0cb01f20793d5a29f9e5a7d790b *inst/tests/test_yaml_load.R 950db19b6035d7ba6d9af37347a5a501 *inst/tests/test_yaml_load_file.R 52adf737c1e77a60b9026b4b1f04de55 *man/as.yaml.Rd 66bde0aa5a9040fba09ba45f3e119f52 *man/read_yaml.Rd 466b1a01bd0eead68a25633559e01ad3 *man/write_yaml.Rd ca2808ac99ec3e31a8e7bd8b370dcd32 *man/yaml.load.Rd 6b0576349be43cf3d872205b41aff77d *src/Makevars e65bbd59ff4430b3b9bade49423ea066 *src/api.c 948f2ac1f42b36f9b6ed10ce0291bdb9 *src/dumper.c e6ebb61bb61785949c0ba20ea827a3e7 *src/emitter.c ac7bfac5c1629dd498b3344af3aef5a7 *src/implicit.c 48ac40f4d75fe6b851db1baad7b3c82d *src/loader.c e5b8c176168b678cdcfb14fdaf2e98d3 *src/parser.c 8d0c428a404cd1121f4aa7f763eae8aa *src/r_emit.c 4c4c2e8ee0a13ab91af0a0db7d869b8c *src/r_ext.c acc43ebc0942c7ec36f4f13ef2fc4747 *src/r_ext.h 856b7bea0f5378e69b0651661e6a8ba9 *src/r_parse.c efcea8b4a8bcfbc96ae32abd0f18e472 *src/reader.c a344bb1d48ab1800e4b872bb6f026156 *src/scanner.c 7b2e815cfd48dbfd462f3bd317059e3f *src/writer.c c2131d7abf1b9c86588c73aef741c14e *src/yaml.h 4b597a363c432743e5c0172eb5fc053d *src/yaml_private.h ca7a25cb389c2c02888c3ce9095e65bc *tests/RUnit.R yaml/inst/0000755000176200001440000000000013614635636012206 5ustar liggesusersyaml/inst/implicit.re0000644000176200001440000000553413614635636014357 0ustar liggesusers#include "yaml.h" char * Ryaml_find_implicit_tag(str, len) const char *str; size_t len; { /* This bit was taken from implicit.re, which is in the Syck library. * * Copyright (C) 2003 why the lucky stiff */ const char *cursor, *marker; cursor = str; /*!re2c re2c:define:YYCTYPE = "char"; re2c:define:YYCURSOR = cursor; re2c:define:YYMARKER = marker; re2c:yyfill:enable = 0; NULL = [\000] ; ANY = [\001-\377] ; DIGIT = [0-9] ; DIGITSC = [0-9,] ; DIGITSP = [0-9.] ; YEAR = DIGIT DIGIT DIGIT DIGIT ; MON = DIGIT DIGIT ; SIGN = [-+] ; HEX = [0-9a-fA-F,] ; OCT = [0-7,] ; INTHEX = SIGN? "0x" HEX+ ; INTOCT = SIGN? "0" OCT+ ; INTSIXTY = SIGN? DIGIT DIGITSC* ( ":" [0-5]? DIGIT )+ ; INTCANON = SIGN? ( "0" | [1-9] DIGITSC* ) ; FLOATFIX = SIGN? ( DIGIT DIGITSC* )? "." DIGITSC* ; FLOATEXP = SIGN? ( DIGIT DIGITSC* )? "." DIGITSP* [eE] SIGN DIGIT+ ; FLOATSIXTY = SIGN? DIGIT DIGITSC* ( ":" [0-5]? DIGIT )+ "." DIGITSC* ; INF = ( "inf" | "Inf" | "INF" ) ; FLOATINF = [+]? "." INF ; FLOATNEGINF = [-] "." INF ; FLOATNAN = "." ( "nan" | "NaN" | "NAN" ) ; NULLTYPE = ( "~" | "null" | "Null" | "NULL" )? ; BOOLYES = ( "y" | "Y" | "yes" | "Yes" | "YES" | "true" | "True" | "TRUE" | "on" | "On" | "ON" ) ; BOOLNO = ( "n" | "N" | "no" | "No" | "NO" | "false" | "False" | "FALSE" | "off" | "Off" | "OFF" ) ; INTNA = ".na.integer" ; FLOATNA = ".na.real" ; STRNA = ".na.character" ; BOOLNA = ".na" ; TIMEZ = ( "Z" | [-+] DIGIT DIGIT ( ":" DIGIT DIGIT )? ) ; TIMEYMD = YEAR "-" MON "-" MON ; TIMEISO = YEAR "-" MON "-" MON [Tt] MON ":" MON ":" MON ( "." DIGIT* )? TIMEZ ; TIMESPACED = YEAR "-" MON "-" MON [ \t]+ MON ":" MON ":" MON ( "." DIGIT* )? [ \t]+ TIMEZ ; TIMECANON = YEAR "-" MON "-" MON "T" MON ":" MON ":" MON ( "." DIGIT* [1-9]+ )? "Z" ; MERGE = "<<" ; DEFAULTKEY = "=" ; NULLTYPE NULL { return "null"; } BOOLYES NULL { return "bool#yes"; } BOOLNO NULL { return "bool#no"; } BOOLNA NULL { return "bool#na"; } INTHEX NULL { return "int#hex"; } INTOCT NULL { return "int#oct"; } INTSIXTY NULL { return "int#base60"; } INTNA NULL { return "int#na"; } INTCANON NULL { return "int"; } FLOATFIX NULL { return "float#fix"; } FLOATEXP NULL { return "float#exp"; } FLOATSIXTY NULL { return "float#base60"; } FLOATINF NULL { return "float#inf"; } FLOATNEGINF NULL { return "float#neginf"; } FLOATNAN NULL { return "float#nan"; } FLOATNA NULL { return "float#na"; } TIMEYMD NULL { return "timestamp#ymd"; } TIMEISO NULL { return "timestamp#iso8601"; } TIMESPACED NULL { return "timestamp#spaced"; } TIMECANON NULL { return "timestamp"; } STRNA NULL { return "str#na"; } DEFAULTKEY NULL { return "default"; } MERGE NULL { return "merge"; } ANY { return "str"; } */ } yaml/inst/THANKS0000644000176200001440000000057313614635636013126 0ustar liggesusersThanks to the following people for helping out: - Jeff Horner, for general advice and incorporating the syck parser into this package Patches submitted by: - Brendan O'Connor (dfe10f6) - Yihui Xie (d8e65c2, 069bb89, 0e34af1..67496b6) - reikoch (ce28a1e) - Hadley Wickham (867d1b0) - Will Beasley (edefae9) - Zhuoer Dong (8febaba..28c89f4, 0e34af1) - Gregory R. Warnes (e3bf914) yaml/inst/tests/0000755000176200001440000000000013614635636013350 5ustar liggesusersyaml/inst/tests/test_write_yaml.R0000644000176200001440000000034613614635636016711 0ustar liggesuserstest_output_is_written_to_a_file_when_a_filename_is_specified <- function() { filename <- tempfile() write_yaml(1:3, filename) output <- readLines(filename) unlink(filename) checkEquals(c("- 1", "- 2", "- 3"), output) } yaml/inst/tests/test_read_yaml.R0000644000176200001440000000213013614635636016463 0ustar liggesuserstest_reading_from_a_connection_works <- function() { filename <- tempfile() cat("foo: 123", file=filename, sep="\n") foo <- file(filename, 'r') x <- read_yaml(foo) close(foo) unlink(filename) checkEquals(123L, x$foo) } test_reading_from_specified_filename_works <- function() { filename <- tempfile() cat("foo: 123", file=filename, sep="\n") x <- read_yaml(filename) unlink(filename) checkEquals(123L, x$foo) } test_reading_from_text_works <- function() { x <- read_yaml(text="foo: 123") checkEquals(123L, x$foo) } test_reading_a_complicated_document_works <- function() { filename <- system.file(file.path("tests", "files", "test.yml"), package = "yaml") x <- read_yaml(filename) expected <- list( foo = list(one=1, two=2), bar = list(three=3, four=4), baz = list(list(one=1, two=2), list(three=3, four=4)), quux = list(one=1, two=2, three=3, four=4, five=5, six=6), corge = list( list(one=1, two=2, three=3, four=4, five=5, six=6), list(xyzzy=list(one=1, two=2, three=3, four=4, five=5, six=6)) ) ) checkEquals(expected, x) } yaml/inst/tests/files/0000755000176200001440000000000013614635636014452 5ustar liggesusersyaml/inst/tests/files/merge.yml0000644000176200001440000000061313614635636016274 0ustar liggesusers--- - &CENTER { x: 1, "y": 2 } - &LEFT { x: 0, "y": 2 } - &BIG { r: 10 } - &SMALL { r: 1 } # All the following maps are equal: - # Explicit keys x: 1 "y": 2 r: 10 label: center/big - # Merge one map << : *CENTER r: 10 label: center/big - # Merge multiple maps << : [ *CENTER, *BIG ] label: center/big - # Override << : [ *BIG, *LEFT, *SMALL ] x: 1 label: center/big yaml/inst/tests/files/test.yml0000644000176200001440000000026313614635636016155 0ustar liggesusersfoo: &foo one: 1 two: 2 bar: &bar three: 3 four: 4 baz: - *foo - *bar quux: &quux <<: *foo <<: *bar five: 5 six: 6 corge: - *quux - xyzzy: <<: *quux yaml/inst/tests/test_as_yaml.R0000644000176200001440000003003313614635636016156 0ustar liggesuserstest_named_list_is_converted <- function() { checkEquals("foo: bar\n", as.yaml(list(foo="bar"))) x <- list(foo=1:10, bar=c("junk", "test")) y <- yaml.load(as.yaml(x)) checkEquals(y$foo, x$foo) checkEquals(y$bar, x$bar) x <- list(foo=1:10, bar=list(foo=11:20, bar=letters[1:10])) y <- yaml.load(as.yaml(x)) checkEquals(x$foo, y$foo) checkEquals(x$bar$foo, y$bar$foo) checkEquals(x$bar$bar, y$bar$bar) # nested lists x <- list(foo = list(a = 1, b = 2), bar = list(b = 4)) y <- yaml.load(as.yaml(x)) checkEquals(x$foo$a, y$foo$a) checkEquals(x$foo$b, y$foo$b) checkEquals(x$bar$b, y$bar$b) } test_data_frame_is_converted <- function() { x <- data.frame(a=1:10, b=letters[1:10], c=11:20) y <- as.data.frame(yaml.load(as.yaml(x))) checkEquals(x$a, y$a) checkEquals(x$b, y$b) checkEquals(x$c, y$c) x <- as.yaml(x, column.major = FALSE) y <- yaml.load(x) checkEquals(5L, y[[5]]$a) checkEquals("e", y[[5]]$b) checkEquals(15L, y[[5]]$c) } test_empty_nested_list_is_converted <- function() { x <- list(foo=list()) checkEquals("foo: []\n", as.yaml(x)) } test_empty_nested_data_frame_is_converted <- function() { x <- list(foo=data.frame()) checkEquals("foo: {}\n", as.yaml(x)) } test_empty_nested_vector_is_converted <- function() { x <- list(foo=character()) checkEquals("foo: []\n", as.yaml(x)) } test_list_is_converted_as_omap <- function() { x <- list(a=1:2, b=3:4) expected <- "!omap\n- a:\n - 1\n - 2\n- b:\n - 3\n - 4\n" checkEquals(expected, as.yaml(x, omap=TRUE)) } test_nested_list_is_converted_as_omap <- function() { x <- list(a=list(c=list(e=1L, f=2L)), b=list(d=list(g=3L, h=4L))) expected <- "!omap\n- a: !omap\n - c: !omap\n - e: 1\n - f: 2\n- b: !omap\n - d: !omap\n - g: 3\n - h: 4\n" checkEquals(expected, as.yaml(x, omap=TRUE)) } test_omap_is_loaded <- function() { x <- yaml.load(as.yaml(list(a=1:2, b=3:4, c=5:6, d=7:8), omap=TRUE)) checkEquals(c("a", "b", "c", "d"), names(x)) } test_numeric_is_converted_correctly <- function() { result <- as.yaml(c(1, 5, 10, 15)) checkEquals(result, "- 1.0\n- 5.0\n- 10.0\n- 15.0\n", label = result) } test_multiline_string_is_converted <- function() { checkEquals("|-\n foo\n bar\n", as.yaml("foo\nbar")) checkEquals("- foo\n- |-\n bar\n baz\n", as.yaml(c("foo", "bar\nbaz"))) checkEquals("foo: |-\n foo\n bar\n", as.yaml(list(foo = "foo\nbar"))) checkEquals("a:\n- foo\n- bar\n- |-\n baz\n quux\n", as.yaml(data.frame(a = c('foo', 'bar', 'baz\nquux')))) } test_function_is_converted <- function() { x <- function() { runif(100) } expected <- "!expr |\n function ()\n {\n runif(100)\n }\n" result <- as.yaml(x) checkEquals(expected, result) } test_list_with_unnamed_items_is_converted <- function() { x <- list(foo=list(list(a = 1L, b = 2L), list(a = 3L, b = 4L))) expected <- "foo:\n- a: 1\n b: 2\n- a: 3\n b: 4\n" result <- as.yaml(x) checkEquals(result, expected) } test_pound_signs_are_escaped_in_strings <- function() { result <- as.yaml("foo # bar") checkEquals("'foo # bar'\n", result) } test_null_is_converted <- function() { checkEquals("~\n", as.yaml(NULL)) } test_different_line_seps_are_used <- function() { result <- as.yaml(c('foo', 'bar'), line.sep = "\n") checkEquals("- foo\n- bar\n", result) result <- as.yaml(c('foo', 'bar'), line.sep = "\r\n") checkEquals("- foo\r\n- bar\r\n", result) result <- as.yaml(c('foo', 'bar'), line.sep = "\r") checkEquals("- foo\r- bar\r", result) } test_custom_indent_is_used <- function() { result <- as.yaml(list(foo=list(bar=list('foo', 'bar'))), indent = 3) checkEquals("foo:\n bar:\n - foo\n - bar\n", result) } test_block_sequences_in_mapping_context_are_indented_when_option_is_true <- function() { result <- as.yaml(list(foo=list(bar=list('foo', 'bar'))), indent.mapping.sequence = TRUE) checkEquals("foo:\n bar:\n - foo\n - bar\n", result) } test_indent_value_is_validated <- function() { checkException(as.yaml(list(foo=list(bar=list('foo', 'bar'))), indent = 0)) } test_strings_are_escaped_properly <- function() { result <- as.yaml("12345") checkEquals("'12345'\n", result) } test_unicode_strings_are_not_escaped <- function() { # list('име' = 'Александар', 'презиме' = 'Благотић') a <- "\u0438\u043C\u0435" # name 1 b <- "\u0410\u043B\u0435\u043A\u0441\u0430\u043D\u0434\u0430\u0440" # value 1 c <- "\u043F\u0440\u0435\u0437\u0438\u043C\u0435" # name 2 d <- "\u0411\u043B\u0430\u0433\u043E\u0442\u0438\u045B" # value 2 x <- list(b, d) names(x) <- c(a, c) expected <- paste(a, ": ", b, "\n", c, ": ", d, "\n", sep="") result <- as.yaml(x, unicode = TRUE) checkEquals(expected, result, label = result) } test_unicode_strings_are_escaped <- function() { # 'é' x <- "\u00e9" result <- as.yaml(x, unicode = FALSE) checkEquals("\"\\xE9\"\n", result) } test_unicode_strings_are_not_escaped_by_default <- function() { # list('é') x <- list("\u00e9") result <- as.yaml(x) checkEquals("- \u00e9\n", result) } test_named_list_with_unicode_character_is_correct_converted <- function() { x <- list(special.char = "\u00e9") result <- as.yaml(x) checkEquals("special.char: \u00e9\n", result) } test_unknown_objects_cause_error <- function() { checkException(as.yaml(expression(foo <- bar))) } test_inf_is_emitted_properly <- function() { result <- as.yaml(Inf) checkEquals(".inf\n", result) } test_negative_inf_is_emitted_properly <- function() { result <- as.yaml(-Inf) checkEquals("-.inf\n", result) } test_nan_is_emitted_properly <- function() { result <- as.yaml(NaN) checkEquals(".nan\n", result) } test_logical_na_is_emitted_properly <- function() { result <- as.yaml(NA) checkEquals(".na\n", result) } test_numeric_na_is_emitted_properly <- function() { result <- as.yaml(NA_real_) checkEquals(".na.real\n", result) } test_integer_na_is_emitted_properly <- function() { result <- as.yaml(NA_integer_) checkEquals(".na.integer\n", result) } test_character_na_is_emitted_properly <- function() { result <- as.yaml(NA_character_) checkEquals(".na.character\n", result) } test_true_is_emitted_properly <- function() { result <- as.yaml(TRUE) checkEquals("yes\n", result) } test_false_is_emitted_properly <- function() { result <- as.yaml(FALSE) checkEquals("no\n", result) } test_named_list_keys_are_escaped_properly <- function() { result <- as.yaml(list(n = 123)) checkEquals("'n': 123.0\n", result) } test_data_frame_keys_are_escaped_properly_when_row_major <- function() { result <- as.yaml(data.frame(n=1:3), column.major = FALSE) checkEquals("- 'n': 1\n- 'n': 2\n- 'n': 3\n", result) } test_scientific_notation_is_valid_yaml <- function() { result <- as.yaml(10000000) checkEquals("1.0e+07\n", result) } test_precision_must_be_in_the_range_1..22 <- function() { checkException(as.yaml(12345, precision = -1)) checkException(as.yaml(12345, precision = 0)) checkException(as.yaml(12345, precision = 23)) } test_factor_with_missing_values_is_emitted_properly <- function() { x <- factor('foo', levels=c('bar', 'baz')) result <- as.yaml(x) checkEquals(".na.character\n", result) } test_very_small_negative_float_is_emitted_properly <- function() { result <- as.yaml(-7.62e-24) checkEquals("-7.62e-24\n", result) } test_very_small_positive_float_is_emitted_properly <- function() { result <- as.yaml(7.62e-24) checkEquals("7.62e-24\n", result) } test_numeric_zero_is_emitted_properly <- function() { result <- as.yaml(0.0) checkEquals("0.0\n", result) } test_numeric_negative_zero_is_emitted_properly <- function() { result <- as.yaml(-0.0) checkEquals("-0.0\n", result) } test_custom_handler_is_run_for_first_class <- function() { x <- "foo" class(x) <- "bar" result <- as.yaml(x, handlers = list(bar = function(x) paste0("x", x, "x"))) checkEquals("xfoox\n", result) } test_custom_handler_is_run_for_second_class <- function() { x <- "foo" class(x) <- c("bar", "baz") result <- as.yaml(x, handlers = list(baz = function(x) paste0("x", x, "x"))) checkEquals("xfoox\n", result) } test_custom_handler_with_verbatim_result <- function() { result <- as.yaml(TRUE, handlers = list( logical = function(x) { result <- ifelse(x, "true", "false") class(result) <- "verbatim" return(result) } )) checkEquals("true\n", result) } test_custom_handler_with_sequence_result <- function() { result <- as.yaml(c(1, 2, 3), handlers = list( numeric = function(x) { x + 1 } )) checkEquals("- 2.0\n- 3.0\n- 4.0\n", result) } test_custom_handler_with_mapping_result <- function() { result <- as.yaml(1, handlers = list( numeric = function(x) { list(foo = 1:2, bar = 3:4) } )) checkEquals("foo:\n- 1\n- 2\nbar:\n- 3\n- 4\n", result) } test_custom_handler_with_function_result <- function() { result <- as.yaml(1, handlers = list( numeric = function(x) { function(y) y + 1 } )) expected <- "!expr |\n function (y)\n y + 1\n" checkEquals(expected, result) } test_custom_tag_for_function <- function() { f <- function(x) x + 1 attr(f, "tag") <- "!foo" expected <- "!foo |\n function (x)\n x + 1\n" result <- as.yaml(f) checkEquals(expected, result) } test_custom_tag_for_numeric_sequence <- function() { x <- c(1, 2, 3) attr(x, "tag") <- "!foo" expected <- "!foo\n- 1.0\n- 2.0\n- 3.0\n" result <- as.yaml(x) checkEquals(expected, result) } test_custom_tag_for_numeric_scalar <- function() { x <- 1 attr(x, "tag") <- "!foo" expected <- "!foo 1.0\n" result <- as.yaml(x) checkEquals(expected, result) } test_custom_tag_for_integer_sequence <- function() { x <- 1L:3L attr(x, "tag") <- "!foo" expected <- "!foo\n- 1\n- 2\n- 3\n" result <- as.yaml(x) checkEquals(expected, result) } test_custom_tag_for_integer_scalar <- function() { x <- 1L attr(x, "tag") <- "!foo" expected <- "!foo 1\n" result <- as.yaml(x) checkEquals(expected, result) } test_custom_tag_for_logical_sequence <- function() { x <- c(TRUE, FALSE) attr(x, "tag") <- "!foo" expected <- "!foo\n- yes\n- no\n" result <- as.yaml(x) checkEquals(expected, result) } test_custom_tag_for_logical_scalar <- function() { x <- TRUE attr(x, "tag") <- "!foo" expected <- "!foo yes\n" result <- as.yaml(x) checkEquals(expected, result) } test_custom_tag_for_factor_sequence <- function() { x <- factor(c("foo", "bar")) attr(x, "tag") <- "!foo" expected <- "!foo\n- foo\n- bar\n" result <- as.yaml(x) checkEquals(expected, result) } test_custom_tag_for_factor_scalar <- function() { x <- factor("foo") attr(x, "tag") <- "!foo" expected <- "!foo foo\n" result <- as.yaml(x) checkEquals(expected, result) } test_custom_tag_for_character_sequence <- function() { x <- c("foo", "bar") attr(x, "tag") <- "!foo" expected <- "!foo\n- foo\n- bar\n" result <- as.yaml(x) checkEquals(expected, result) } test_custom_tag_for_character_scalar <- function() { x <- "foo" attr(x, "tag") <- "!foo" expected <- "!foo foo\n" result <- as.yaml(x) checkEquals(expected, result) } test_custom_tag_for_data_frame <- function() { x <- data.frame(a = 1:3, b = 4:6) attr(x, "tag") <- "!foo" expected <- "!foo\na:\n- 1\n- 2\n- 3\nb:\n- 4\n- 5\n- 6\n" result <- as.yaml(x) checkEquals(expected, result) } test_custom_tag_for_data_frame_column <- function() { x <- data.frame(a = 1:3, b = 4:6) attr(x$a, "tag") <- "!foo" expected <- "a: !foo\n- 1\n- 2\n- 3\nb:\n- 4\n- 5\n- 6\n" result <- as.yaml(x) checkEquals(expected, result) } test_custom_tag_for_omap <- function() { x <- list(a=1:2, b=3:4) attr(x, "tag") <- "!foo" expected <- "!foo\n- a:\n - 1\n - 2\n- b:\n - 3\n - 4\n" result <- as.yaml(x, omap = TRUE) checkEquals(expected, result) } test_custom_tag_for_named_list <- function() { x <- list(a=1:2, b=3:4) attr(x, "tag") <- "!foo" expected <- "!foo\na:\n- 1\n- 2\nb:\n- 3\n- 4\n" result <- as.yaml(x) checkEquals(expected, result) } test_custom_tag_for_unnamed_list <- function() { x <- list(1, 2, 3) attr(x, "tag") <- "!foo" expected <- "!foo\n- 1.0\n- 2.0\n- 3.0\n" result <- as.yaml(x) checkEquals(expected, result) } yaml/inst/tests/test_yaml_load.R0000644000176200001440000004756213614635636016511 0ustar liggesuserstest_named_list_is_not_returned <- function() { x <- yaml.load("hey: man\n123: 456\n", FALSE) checkEquals(2L, length(x)) checkEquals(2L, length(attr(x, "keys"))) x <- yaml.load("- dude\n- sup\n- 1.2345", FALSE) checkEquals(3L, length(x)) checkEquals(0L, length(attr(x, "keys"))) checkEquals("sup", x[[2]]) x <- yaml.load("dude:\n - 123\n - sup", FALSE) checkEquals(1L, length(x)) checkEquals(1L, length(attr(x, "keys"))) checkEquals(2L, length(x[[1]])) } test_key_conflicts_throw_errors <- function() { checkException(yaml.load("hey: buddy\nhey: guy")); } test_named_list_is_returned <- function() { x <- yaml.load("hey: man\n123: 456\n", TRUE) checkEquals(2L, length(x)) checkEquals(2L, length(names(x))) checkEquals(c("123", "hey"), sort(names(x))) checkEquals("man", x$hey) } test_uniform_sequences_are_coerced <- function() { x <- yaml.load("- 1\n- 2\n- 3") checkEquals(1:3, x) x <- yaml.load("- yes\n- no\n- yes") checkEquals(c(TRUE, FALSE, TRUE), x) x <- yaml.load("- 3.1\n- 3.14\n- 3.141") checkEquals(c(3.1, 3.14, 3.141), x) x <- yaml.load("- hey\n- hi\n- hello") checkEquals(c("hey", "hi", "hello"), x) } test_tag_type_conflicts_throws_error <- function() { checkException(yaml.load("!!str [1, 2, 3]")) checkException(yaml.load("!!str {foo: bar}")) } test_sequences_are_not_collapsed <- function() { x <- yaml.load("- [1, 2]\n- 3\n- [4, 5]") checkEquals(list(1:2, 3L, 4:5), x) } test_named_maps_are_merged_without_warnings <- function() { x <- yaml.load("foo: bar\n<<: {baz: boo}", TRUE) checkEquals(2L, length(x)) checkEquals("bar", x$foo) checkEquals("boo", x$baz) expected <- list(foo = 'bar', quux = 'quux', baz = 'blah') warnings <- captureWarnings({ x <- yaml.load("foo: bar\n<<: [{quux: quux}, {foo: doo}, {foo: junk}, {baz: blah}, {baz: boo}]", TRUE) }) checkEquals(expected, x) checkEquals(0L, length(warnings)) warnings <- captureWarnings({ x <- yaml.load("foo: bar\n<<: {foo: baz}\n<<: {foo: quux}") }) checkEquals(1L, length(x)) checkEquals("bar", x$foo) checkEquals(0L, length(warnings)) warnings <- captureWarnings({ x <- yaml.load("<<: {foo: bar}\nfoo: baz") }) checkEquals(list(foo = 'bar'), x) checkEquals(0L, length(warnings)) } test_named_maps_are_merged_with_warnings <- function() { x <- yaml.load("foo: bar\n<<: {baz: boo}", as.named.list = TRUE, merge.warning = TRUE) checkEquals(2L, length(x)) checkEquals("bar", x$foo) checkEquals("boo", x$baz) expected <- list(foo = 'bar', quux = 'quux', baz = 'blah') warnings <- captureWarnings({ x <- yaml.load("foo: bar\n<<: [{quux: quux}, {foo: doo}, {foo: junk}, {baz: blah}, {baz: boo}]", as.named.list = TRUE, merge.warning = TRUE) }) checkEquals(expected, x) checkEquals(c("Duplicate map key ignored during merge: 'foo'", "Duplicate map key ignored during merge: 'foo'", "Duplicate map key ignored during merge: 'baz'"), warnings) warnings <- captureWarnings({ x <- yaml.load("foo: bar\n<<: {foo: baz}\n<<: {foo: quux}", as.named.list = TRUE, merge.warning = TRUE) }) checkEquals(1L, length(x)) checkEquals("bar", x$foo) checkEquals(c("Duplicate map key ignored during merge: 'foo'", "Duplicate map key ignored during merge: 'foo'"), warnings) warnings <- captureWarnings({ x <- yaml.load("<<: {foo: bar}\nfoo: baz", as.named.list = TRUE, merge.warning = TRUE) }) checkEquals(list(foo = 'bar'), x) checkEquals(c("Duplicate map key ignored after merge: 'foo'"), warnings) } test_unnamed_maps_are_merged_without_warnings <- function() { x <- yaml.load("foo: bar\n<<: {baz: boo}", as.named.list = FALSE) checkEquals(2L, length(x)) checkEquals(list("foo", "baz"), attr(x, 'keys')) checkEquals("bar", x[[1]]) checkEquals("boo", x[[2]]) warnings <- captureWarnings({ x <- yaml.load("foo: bar\n<<: [{quux: quux}, {foo: doo}, {baz: boo}]", as.named.list = FALSE) }) checkEquals(3L, length(x)) checkEquals(list("foo", "quux", "baz"), attr(x, 'keys')) checkEquals("bar", x[[1]]) checkEquals("quux", x[[2]]) checkEquals("boo", x[[3]]) checkEquals(0L, length(warnings)) warnings <- captureWarnings({ x <- yaml.load("<<: {foo: bar}\nfoo: baz", as.named.list = FALSE) }) checkEquals(1L, length(x)) checkEquals(list("foo"), attr(x, 'keys')) checkEquals("bar", x[[1]]) checkEquals(0L, length(warnings)) } test_unnamed_maps_are_merged_with_warnings <- function() { warnings <- captureWarnings({ x <- yaml.load("foo: bar\n<<: {baz: boo}", as.named.list = FALSE, merge.warning = TRUE) }) checkEquals(2L, length(x)) checkEquals(list("foo", "baz"), attr(x, 'keys')) checkEquals("bar", x[[1]]) checkEquals("boo", x[[2]]) checkEquals(0L, length(warnings)) warnings <- captureWarnings({ x <- yaml.load("foo: bar\n<<: [{quux: quux}, {foo: doo}, {baz: boo}]", as.named.list = FALSE, merge.warning = TRUE) }) checkEquals(3L, length(x)) checkEquals(list("foo", "quux", "baz"), attr(x, 'keys')) checkEquals("bar", x[[1]]) checkEquals("quux", x[[2]]) checkEquals("boo", x[[3]]) checkEquals("Duplicate map key ignored during merge: 'foo'", warnings) warnings <- captureWarnings({ x <- yaml.load("<<: {foo: bar}\nfoo: baz", as.named.list = FALSE, merge.warning = TRUE) }) checkEquals(1L, length(x)) checkEquals(list("foo"), attr(x, 'keys')) checkEquals("bar", x[[1]]) checkEquals(c("Duplicate map key ignored after merge: 'foo'"), warnings) } test_duplicate_keys_throws_an_error <- function() { checkException(yaml.load("foo: bar\nfoo: baz\n", TRUE)) } test_duplicate_keys_with_merge_first_does_not_throw_an_error <- function() { result <- try(yaml.load("<<: {foo: bar}\nfoo: baz\n", TRUE)) checkTrue(!inherits(result, "try-error")) } test_invalid_merges_throw_errors <- function() { checkException(yaml.load("foo: bar\n<<: [{leet: hax}, blargh, 123]", TRUE)) checkException(yaml.load("foo: bar\n<<: [123, blargh, {leet: hax}]", TRUE)) checkException(yaml.load("foo: bar\n<<: junk", TRUE)) } test_syntax_errors_throw_errors <- function() { checkException(yaml.load("[whoa, this is some messed up]: yaml?!: dang")) } test_null_types_are_converted <- function() { x <- yaml.load("~") checkEquals(NULL, x) } #test_should_handle_binary_type <- function() { # x <- yaml.load("!!binary 0b101011") # checkEquals("0b101011", x) #} test_bool_yes_type_is_converted <- function() { x <- yaml.load("yes") checkEquals(TRUE, x) } test_bool_no_type_is_converted <- function() { x <- yaml.load("no") checkEquals(FALSE, x) } test_int_hex_type_is_converted <- function() { x <- yaml.load("0xF") checkEquals(15L, x) } test_int_oct_type_is_converted <- function() { x <- yaml.load("015") checkEquals(13L, x) } #test_should_handle_int_base60_type <- function() { # x <- yaml.load("1:20") # checkEquals("1:20", x) #} test_int_type_is_converted <- function() { x <- yaml.load("31337") checkEquals(31337L, x) } test_explicit_int_type_is_converted <- function() { x <- yaml.load("!!int 31337") checkEquals(31337L, x) } #test_should_handle_float_base60_type <- function() { # x <- yaml.load("1:20.5") # checkEquals("1:20.5", x) #} test_float_nan_type_is_converted <- function() { x <- yaml.load(".NaN") checkTrue(is.nan(x)) } test_float_inf_type_is_converted <- function() { x <- yaml.load(".inf") checkEquals(Inf, x) } test_float_neginf_type_is_converted <- function() { x <- yaml.load("-.inf") checkEquals(-Inf, x) } test_float_type_is_converted <- function() { x <- yaml.load("123.456") checkEquals(123.456, x) } #test_should_handle_timestamp_iso8601_type <- function() { # x <- yaml.load("!timestamp#iso8601 2001-12-14t21:59:43.10-05:00") # checkEquals("2001-12-14t21:59:43.10-05:00", x) #} #test_should_handle_timestamp_spaced_type <- function() { # x <- yaml.load("!timestamp#spaced 2001-12-14 21:59:43.10 -5") # checkEquals("2001-12-14 21:59:43.10 -5", x) #} #test_should_handle_timestamp_ymd_type <- function() { # x <- yaml.load("!timestamp#ymd 2008-03-03") # checkEquals("2008-03-03", x) #} #test_should_handle_timestamp_type <- function() { # x <- yaml.load("!timestamp 2001-12-14t21:59:43.10-05:00") # checkEquals("2001-12-14t21:59:43.10-05:00", x) #} test_aliases_are_handled <- function() { x <- yaml.load("- &foo bar\n- *foo") checkEquals(c("bar", "bar"), x) } test_str_type_is_converted <- function() { x <- yaml.load("lickety split") checkEquals("lickety split", x) } test_bad_anchors_are_handled <- function() { warnings <- captureWarnings({ x <- yaml.load("*blargh") }) expected <- "_yaml.bad-anchor_" class(expected) <- "_yaml.bad-anchor_" checkEquals(expected, x) checkEquals("Unknown anchor: blargh", warnings) } test_custom_null_handler_is_applied <- function() { x <- yaml.load("~", handlers=list("null"=function(x) { "argh!" })) checkEquals("argh!", x) } test_custom_binary_handler_is_applied <- function() { x <- yaml.load("!binary 0b101011", handlers=list("binary"=function(x) { "argh!" })) checkEquals("argh!", x) } test_custom_bool_yes_handler_is_applied <- function() { x <- yaml.load("yes", handlers=list("bool#yes"=function(x) { "argh!" })) checkEquals("argh!", x) } test_custom_bool_no_handler_is_applied <- function() { x <- yaml.load("no", handlers=list("bool#no"=function(x) { "argh!" })) checkEquals("argh!", x) } test_custom_int_hex_handler_is_applied <- function() { x <- yaml.load("0xF", handlers=list("int#hex"=function(x) { "argh!" })) checkEquals("argh!", x) } test_custom_int_oct_handler_is_applied <- function() { x <- yaml.load("015", handlers=list("int#oct"=function(x) { "argh!" })) checkEquals("argh!", x) } test_int_base60_is_not_coerced_by_default <- function() { x <- yaml.load("1:20") checkEquals("1:20", x) } test_custom_int_base60_handler_is_applied <- function() { x <- yaml.load("1:20", handlers=list("int#base60"=function(x) { "argh!" })) checkEquals("argh!", x) } test_custom_int_handler_is_applied <- function() { x <- yaml.load("31337", handlers=list("int"=function(x) { "argh!" })) checkEquals("argh!", x) } test_custom_float_base60_handler_is_applied <- function() { x <- yaml.load("1:20.5", handlers=list("float#base60"=function(x) { "argh!" })) checkEquals("argh!", x) } test_custom_float_nan_handler_is_applied <- function() { x <- yaml.load(".NaN", handlers=list("float#nan"=function(x) { "argh!" })) checkEquals("argh!", x) } test_custom_float_inf_handler_is_applied <- function() { x <- yaml.load(".inf", handlers=list("float#inf"=function(x) { "argh!" })) checkEquals("argh!", x) } test_custom_float_neginf_handler_is_applied <- function() { x <- yaml.load("-.inf", handlers=list("float#neginf"=function(x) { "argh!" })) checkEquals("argh!", x) } test_custom_float_handler_is_applied <- function() { x <- yaml.load("123.456", handlers=list("float#fix"=function(x) { "argh!" })) checkEquals("argh!", x) } test_custom_timestamp_iso8601_handler_is_applied <- function() { x <- yaml.load("2001-12-14t21:59:43.10-05:00", handlers=list("timestamp#iso8601"=function(x) { "argh!" })) checkEquals("argh!", x) } #test_should_use_custom_timestamp_spaced_handler <- function() { # x <- yaml.load('!"timestamp#spaced" 2001-12-14 21:59:43.10 -5', handlers=list("timestamp#spaced"=function(x) { "argh!" })) # checkEquals("argh!", x) #} test_custom_timestamp_ymd_handler_is_applied <- function() { x <- yaml.load("2008-03-03", handlers=list("timestamp#ymd"=function(x) { "argh!" })) checkEquals("argh!", x) } test_custom_merge_handler_is_not_applied <- function() { warnings <- captureWarnings({ x <- yaml.load("foo: &foo\n bar: 123\n baz: 456\n\njunk:\n <<: *foo\n bah: 789", handlers=list("merge"=function(x) { "argh!" })) }) checkEquals(list(foo=list(bar=123, baz=456), junk=list(bar=123, baz=456, bah=789)), x) checkEquals("Custom handling for type 'merge' is not allowed; handler ignored", warnings) } test_custom_str_handler_is_applied <- function() { x <- yaml.load("lickety split", handlers=list("str"=function(x) { "argh!" })) checkEquals("argh!", x) } test_handler_for_unknown_type_is_applied <- function() { x <- yaml.load("!viking pillage", handlers=list(viking=function(x) { paste(x, "the village") })) checkEquals("pillage the village", x) } test_custom_seq_handler_is_applied <- function() { x <- yaml.load("- 1\n- 2\n- 3", handlers=list(seq=function(x) { as.integer(x) + 3L })) checkEquals(4:6, x) } test_custom_map_handler_is_applied <- function() { x <- yaml.load("foo: bar", handlers=list(map=function(x) { x$foo <- paste(x$foo, "yarr"); x })) checkEquals("bar yarr", x$foo) } test_custom_typed_seq_handler_is_applied <- function() { x <- yaml.load("!foo\n- 1\n- 2", handlers=list(foo=function(x) { as.integer(x) + 1L })) checkEquals(2:3, x) } test_custom_typed_map_handler_is_applied <- function() { x <- yaml.load("!foo\nuno: 1\ndos: 2", handlers=list(foo=function(x) { x$uno <- "uno"; x$dos <- "dos"; x })) checkEquals(list(uno="uno", dos="dos"), x) } # NOTE: this works, but R_tryEval doesn't return when called non-interactively #test_should_handle_a_bad_handler <- function() { # x <- yaml.load("foo", handlers=list(str=function(x) { blargh })) # str(x) #} test_empty_documents_are_loaded <- function() { x <- yaml.load("") checkEquals(NULL, x) x <- yaml.load("# this document only has\n # wickedly awesome comments") checkEquals(NULL, x) } test_omaps_are_loaded <- function() { x <- yaml.load("--- !omap\n- foo:\n - 1\n - 2\n- bar:\n - 3\n - 4") checkEquals(2L, length(x)) checkEquals(c("foo", "bar"), names(x)) checkEquals(1:2, x$foo) checkEquals(3:4, x$bar) } test_omaps_are_loaded_when_named_is_false <- function() { x <- yaml.load("--- !omap\n- 123:\n - 1\n - 2\n- bar:\n - 3\n - 4", FALSE) checkEquals(2L, length(x)) checkEquals(list(123L, "bar"), attr(x, "keys")) checkEquals(1:2, x[[1]]) checkEquals(3:4, x[[2]]) } test_named_opam_with_duplicate_key_causes_error <- function() { checkException(yaml.load("--- !omap\n- foo:\n - 1\n - 2\n- foo:\n - 3\n - 4")) } test_unnamed_omap_with_duplicate_key_causes_error <- function() { checkException(yaml.load("--- !omap\n- foo:\n - 1\n - 2\n- foo:\n - 3\n - 4", FALSE)) } test_invalid_omap_causes_error <- function() { checkException(yaml.load("--- !omap\nhey!")) checkException(yaml.load("--- !omap\n- sup?")) } test_expressions_are_implicitly_converted_with_warning <- function() { warnings <- captureWarnings({ x <- yaml.load("!expr |\n function() \n {\n 'hey!'\n }") }) checkEquals("function", class(x)) checkEquals("hey!", x()) checkEquals("Evaluating R expressions (!expr) will soon require explicit `eval.expr` option (see yaml.load help)", warnings) } test_expressions_are_explicitly_converted_without_warning <- function() { warnings <- captureWarnings({ x <- yaml.load("!expr |\n function() \n {\n 'hey!'\n }", eval.expr = TRUE) }) checkEquals("function", class(x)) checkEquals("hey!", x()) checkEquals(0, length(warnings)) } test_expressions_are_explicitly_not_converted <- function() { x <- yaml.load("!expr 123 + 456", eval.expr = FALSE) checkEquals("123 + 456", x) } test_invalid_expressions_cause_error <- function() { checkException(yaml.load("!expr |\n 1+")) } # NOTE: this works, but R_tryEval doesn't return when called non-interactively #test_should_error_for_expressions_with_eval_errors <- function() { # x <- try(yaml.load("!expr |\n 1 + non.existent.variable")) # assert(inherits(x, "try-error")) #} test_maps_are_in_ordered <- function() { x <- yaml.load("{a: 1, b: 2, c: 3}") checkEquals(c('a', 'b', 'c'), names(x)) } test_illegal_recursive_anchor_is_handled <- function() { warnings <- captureWarnings({ x <- yaml.load('&foo {foo: *foo}') }) expected <- "_yaml.bad-anchor_" class(expected) <- "_yaml.bad-anchor_" checkEquals(expected, x$foo) checkEquals("Unknown anchor: foo", warnings) } test_dereferenced_aliases_have_unshared_names <- function() { x <- yaml.load('{foo: &foo {one: 1, two: 2}, bar: *foo}') x$foo$one <- 'uno' checkEquals(1L, x$bar$one) } test_multiple_anchors_are_handled <- function() { x <- yaml.load('{foo: &foo {one: 1}, bar: &bar {two: 2}, baz: *foo, quux: *bar}') expected <- list( foo = list(one = 1), bar = list(two = 2), baz = list(one = 1), quux = list(two = 2) ) checkEquals(expected, x) } test_quoted_strings_are_preserved <- function() { x <- yaml.load("'12345'") checkEquals("12345", x) } test_inf_is_loaded_properly <- function() { result <- yaml.load(".inf\n...\n") checkEquals(Inf, result) } test_negative_inf_is_loaded_properly <- function() { result <- yaml.load("-.inf\n...\n") checkEquals(-Inf, result) } test_nan_is_loaded_properly <- function() { result <- yaml.load(".nan\n...\n") checkEquals(NaN, result) } test_logical_na_is_loaded_properly <- function() { result <- yaml.load(".na\n...\n") checkEquals(NA, result) } test_numeric_na_is_loaded_properly <- function() { result <- yaml.load(".na.real\n...\n") checkEquals(NA_real_, result) } test_integer_na_is_loaded_properly <- function() { result <- yaml.load(".na.integer\n...\n") checkEquals(NA_integer_, result) } test_character_na_is_loaded_properly <- function() { result <- yaml.load(".na.character\n...\n") checkEquals(NA_character_, result) } test_true_is_loaded_properly_from_y <- function() { result <- yaml.load("y\n...\n") checkEquals(TRUE, result) } test_false_is_loaded_properly_from_n <- function() { result <- yaml.load("n\n...\n") checkEquals(FALSE, result) } test_numeric_sequence_with_missing_values_loads_properly <- function() { result <- yaml.load("[1.2, 3.4, .na.real]") checkEquals(c(1.2, 3.4, NA), result) } test_integer_sequence_with_missing_values_loads_properly <- function() { result <- yaml.load("[1, 2, .na.integer]") checkEquals(c(1, 2, NA), result) } test_string_sequence_with_missing_values_loads_properly <- function() { result <- yaml.load("[foo, bar, .na.character]") checkEquals(c("foo", "bar", NA), result) } test_logical_sequence_with_missing_values_loads_properly <- function() { result <- yaml.load("[y, n, .na]") checkEquals(c(TRUE, FALSE, NA), result) } test_numeric_sequence_with_nans_loads_properly <- function() { result <- yaml.load("[1.2, 3.4, .nan]") checkEquals(c(1.2, 3.4, NaN), result) } test_numeric_represented_in_exponential_form_is_loaded_properly <- function() { checkEquals(1000000, yaml.load("1.0e+06")) }; test_numeric_without_leading_digits_is_loaded_properly <- function() { checkEquals(0.9, yaml.load(".9")) }; test_integer_overflow_creates_a_warning <- function() { checkWarning(result <- yaml.load("2147483648")) checkEquals(NA_integer_, result) checkWarning(result <- yaml.load("2147483649")) checkEquals(NA_integer_, result) } test_numeric_overflow_creates_a_warning <- function() { checkWarning(result <- yaml.load("1.797693e+309")) checkEquals(NA_real_, result) } test_list_of_one_list_is_loaded_properly <- function() { result <- yaml.load('a:\n -\n - b\n - c\n') checkEquals(list(a = list(c("b", "c"))), result) } test_override_merge_precedence <- function() { doc <- "[ &one { foo: bar }, { <<: *one, foo: baz } ]" expected <- list(list(foo = 'bar'), list(foo = 'baz')) result <- yaml.load(doc, merge.precedence = "override") checkEquals(expected, result) } test_explicit_bool_tag_for_true_value <- function() { doc <- "!!bool 'true'" expected <- TRUE result <- yaml.load(doc) checkEquals(expected, result) } test_explicit_bool_tag_for_false_value <- function() { doc <- "!!bool 'false'" expected <- FALSE result <- yaml.load(doc) checkEquals(expected, result) } test_explicit_bool_tag_for_na_value <- function() { doc <- "!!bool '.na'" expected <- NA result <- yaml.load(doc) checkEquals(expected, result) } test_explicit_bool_tag_for_invalid_value <- function() { doc <- "!!bool foo" expected <- NA warnings <- captureWarnings({ result <- yaml.load(doc) }) checkEquals(expected, result) checkEquals(c("NAs introduced by coercion: foo is not a recognized boolean value"), warnings) } yaml/inst/tests/test_yaml_load_file.R0000644000176200001440000000517413614635636017501 0ustar liggesuserstest_reading_from_a_connection_works <- function() { filename <- tempfile() cat("foo: 123", file=filename, sep="\n") foo <- file(filename, 'r') x <- yaml.load_file(foo) close(foo) unlink(filename) checkEquals(123L, x$foo) } test_reading_from_specified_filename_works <- function() { filename <- tempfile() cat("foo: 123", file=filename, sep="\n") x <- yaml.load_file(filename) unlink(filename) checkEquals(123L, x$foo) } test_reading_a_complicated_document_works <- function() { filename <- system.file(file.path("tests", "files", "test.yml"), package = "yaml") x <- yaml.load_file(filename) expected <- list( foo = list(one = 1, two = 2), bar = list(three = 3, four = 4), baz = list(list(one = 1, two = 2), list(three = 3, four = 4)), quux = list(one = 1, two = 2, three = 3, four = 4, five = 5, six = 6), corge = list( list(one = 1, two = 2, three = 3, four = 4, five = 5, six = 6), list(xyzzy = list(one = 1, two = 2, three = 3, four = 4, five = 5, six = 6)) ) ) checkEquals(expected, x) } test_expressions_are_implicitly_converted_with_warning <- function() { warnings <- captureWarnings({ filename <- tempfile() cat("!expr 123 + 456", file=filename, sep="\n") foo <- file(filename, 'r') x <- yaml.load_file(foo) close(foo) unlink(filename) }) checkEquals("numeric", class(x)) checkEquals(579, x) checkEquals("Evaluating R expressions (!expr) will soon require explicit `eval.expr` option (see yaml.load help)", warnings) } test_expressions_are_explicitly_converted_without_warning <- function() { warnings <- captureWarnings({ filename <- tempfile() cat("!expr 123 + 456", file=filename, sep="\n") foo <- file(filename, 'r') x <- yaml.load_file(foo, eval.expr = TRUE) close(foo) unlink(filename) }) checkEquals("numeric", class(x)) checkEquals(579, x) checkEquals(0, length(warnings)) } test_expressions_are_unconverted <- function() { filename <- tempfile() cat("!expr 123 + 456", file=filename, sep="\n") foo <- file(filename, 'r') x <- yaml.load_file(foo, eval.expr = FALSE) close(foo) unlink(filename) checkEquals("character", class(x)) checkEquals("123 + 456", x) } test_merge_specification_example_with_merge_override <- function() { filename <- system.file(file.path("tests", "files", "merge.yml"), package = "yaml") x <- yaml.load_file(filename, merge.precedence = "override") expected <- list(x = 1, y = 2, r = 10, label = "center/big") checkNamedListEquals(expected, x[[5]]) checkNamedListEquals(expected, x[[6]]) checkNamedListEquals(expected, x[[7]]) checkNamedListEquals(expected, x[[8]]) } yaml/inst/CHANGELOG0000644000176200001440000001172313614636276013425 0ustar liggesusersv2.2.1 - add merge.precedence option to yaml.load - fix improper handling of explicit '!bool' tag (reported by Luke Goodsell) - fix memory issue flagged by valgrind - update LibYAML to 0.2.2 - fix some -Wall warnings to appease CRAN v2.2.0 - add custom handlers to as.yaml - add processing of 'verbatim' class in as.yaml - add processing of 'tag' class in as.yaml - change examples/tests to write to tempfiles to appease CRAN - fix as.yaml representation of very small negative floating point numbers (reported by Ryan Welch) - properly ignore a map key that would override a key from a merged map (reported by Bradley Saul) - gracefully fail compilation if GCC fast-math flag is enabled (reported by Dmitriy Selivanov) - switch from testthat to RUnit for unit testing since RUnit has fewer dependencies and does not require compilation v2.1.19 - fix unserialization issues with int#base60 tag (reported by Nancy Irisarri) - add eval.expr option to yaml.load function - fix issue with error.label argument (patch by Gregory R. Warnes) - fix a few garbage collection protection issues v2.1.18 - fix protection stack bugs (reported by Rich FitzJohn) v2.1.17 - rewrite parsing functionality using pairlists instead of a self-managed protection stack in order to appease rchk - use MARK_NOT_MUTABLE instead of SET_NAMED, which is deprecated - show warning when duplicate map key is ignored during a merge v2.1.16 - fix error checking bug regarding number conversions v2.1.15 - improve handling of UTF-8 encoded files - add Github URL to description file - add `read_yaml` and `write_yaml` convenience functions - add `error.label` parameter to `yaml.load` and `yaml.load_file` - recognize floating point numbers without leading 0 - fix nested list issue - show warning for integer/real overflow v2.1.14 - mark character input/output as UTF-8 (patch submitted by Yihui Xie) - update LibYAML to 0.1.7 v2.1.13 - fix integer overflow issue - explicitly cast pointers from char to yaml_char_t, and vice versa v2.1.12 - properly emit factors with NAs (bug submitted by Jenny Bryan) - update LibYAML to 0.1.6 v2.1.11 - update LibYAML to 0.1.5 v2.1.10 - properly escape names in data frames and lists (bug submitted by Paul Hodor) - remove extra digit in Windows when formatting exponential numbers v2.1.9 - CRAN maintainers changed memcpy to memmove v2.1.8 - properly emit and consume numeric values in scientific notiation (bug submitted by Gergely Daróczi) - add 'precision' argument to as.yaml to control how many digits are printed when emitting v2.1.7 - properly emit and consume special values: Inf, -Inf, NaN, NA, TRUE, and FALSE (bug submitted by Richard Cotton) - Inf is emitted as '.inf' - -Inf as '-.inf' - NaN as '.nan' - TRUE is now emitted as 'yes', and FALSE as 'no' - because the YAML specification does not specify how to handle NA values, the various NA types are emitted as follows: - NA: .na - NA_real_: .na.real - NA_integer_: .na.integer - NA_character_: .na.character v2.1.6 - add unicode option to as.yaml() (bug submissions by Gergely Daróczi and Aleksandar Blagotić) v2.1.5 - fix yaml.load() ignoring explicit quotes around strings (bug submitted by Jonas Zimmermann) - fix as.yaml() not quoting strings that need to be quoted v2.1.4 - replace lang5() function for backwards compatibility with R < 2.12.0 (bug submitted by Philipp Hofmann) v2.1.3 - fix as.yaml() converting numeric vectors incorrectly (bug submitted by Markus Göker) v2.1.2 - fix multiple anchor bug (bug submitted by apshaar) v2.1.1 - remove redundant yaml-package help page - fix solaris compilation error - remove printf/assert symbols from the compiled library v2.1.0 - re-wrote as.yaml in C (using libyaml's emitter) - removed the `pre.indent` option to as.yaml, mainly because libyaml doesn't support pre-indention and I'm not sure the option is useful anymore; will revisit if requested v2.0.0 - switch from the Syck parser to the libyaml (0.1.4) parser - changed behavior: - sequences of sequences no longer collapse when they contain the same type; ex: yaml.load("[1, [2, 3], 4]") returns list(1L, c(2L, 3L), 4L) v1.2.0 - added support for loading R expressions (using the !expr tag) - added multiline string support - added support for nameless lists in as.yaml (converts to a sequence) v1.1.0 - added support for omaps - added yaml.load_file function to read from files/connections - using format instead of as.character now in as.yaml.default v1.0.2 - fixed as.yaml bug where a nested empty list wasn't converted correctly v1.0.1 - yaml.load will now load empty documents (bug submitted by Jeff Klingner) - as.yaml will return '[]' for empty objects (patch submitted by Brendan O'Connor) - as.yaml will now create valid YAML for a list that contains a list of length one (bug submitted by Gabor Grothendieck)