parseDataFile function

DynamicMap parseDataFile(
  1. String file
)

Parse a Remote Flutter Widgets text data file.

This data is usually used in conjunction with DynamicContent.

Parsing this format is about ten times slower than parsing the binary variant; see decodeDataBlob. As such it is strongly discouraged, especially in resource-constrained contexts like mobile applications.

Format

This format is inspired by JSON, but with the following changes:

  • end-of-line comments are supported, using the "//" syntax from C (and Dart).

  • block comments are supported, using the "/.../" syntax from C (and Dart).

  • map keys may be unquoted when they are alphanumeric.

  • "null" is not a valid value except as a value in maps, where it is equivalent to omitting the corresponding key entirely. This is allowed primarily as a development aid to explicitly indicate missing keys. (The data model itself, and the binary format (see decodeDataBlob), do not contain this feature.)

  • integers and doubles are distinguished explicitly, via the presence of the decimal point and/or exponent characters. Integers may also be expressed as hex literals. Numbers are explicitly 64 bit precision; specifically, signed 64 bit integers, or binary64 floating point numbers.

  • files are always rooted at a map. (Different versions of JSON are ambiguous or contradictory about this.)

Here is the BNF:

root ::= WS* map WS*

Every Remote Flutter Widget text data file must match the root production.

map ::= "{" ( WS* entry WS* "," )* WS* entry? WS* "}"
entry ::= ( identifier | string ) WS* ":" WS* ( value | "null" )

Maps are comma-separated lists of name-value pairs (a single trailing comma is permitted), surrounded by braces. The key must be either a quoted string or an unquoted identifier. The value must be either the keyword "null", which is equivalent to the entry being omitted, or a value as defined below. Duplicates (notwithstanding entries that use the keyword "null") are not permitted. In general the use of the "null" keyword is discouraged and is supported only to allow mechanically-generated data to consistently include every key even when one has no value.

value ::= map | list | integer | double | string | "true" | "false"

The "true" and "false" keywords represented the boolean true and false values respectively.

list ::= "[" ( WS* value WS* "," )* WS* value? WS* "]"

Following the pattern set by maps, lists are comma-separated lists of values (a single trailing comma is permitted), surrounded by brackets.

identifier ::= letter ( letter | digit )*
letter ::= <a character in the ranges A-Z, a-z, or the underscore>
digit ::= <a character in the range 0-9>

Identifiers are alphanumeric sequences (with the underscore considered a letter) that do not start with a digit.

string ::= DQ stringbodyDQ* DQ | SQ stringbodySQ* SQ
DQ ::= <U+0022>
stringbodyDQ ::= escape | characterDQ
characterDQ ::= <U+0000..U+10FFFF except U+000A, U+0022, U+005C>
SQ ::= <U+0027>
stringbodySQ ::= escape | characterSQ
characterSQ ::= <U+0000..U+10FFFF except U+000A, U+0027, U+005C>
escape ::= <U+005C> ("b" | "f" | "n" | "r" | "t" | symbol | unicode)
symbol ::= <U+0022, U+0027, U+005C, or U+002F>
unicode ::= "u" hex hex hex hex
hex ::= <a character in the ranges A-F, a-f, or 0-9>

Strings are double-quoted (U+0022, ") or single-quoted (U+0027, ') sequences of zero or more Unicode characters that do not include a newline, the quote character used, or the backslash character, mixed with zero or more escapes.

Escapes are a backslash character followed by another character, as follows:

  • \b: represents U+0008
  • \f: represents U+000C
  • \n: represents U+000A
  • \r: represents U+000D
  • \t: represents U+0009
  • ": represents U+0022 (")
  • ': represents U+0027 (')
  • /: represents U+002F (/)
  • \: represents U+005C ()
  • \uXXXX: represents the UTF-16 codepoint with code XXXX.

Characters outside the basic multilingual plane must be represented either as literal characters, or as two escapes identifying UTF-16 surrogate pairs. (This is for compatibility with JSON.)

integer ::= "-"? decimal | hexadecimal
decimal ::= digit+
hexadecimal ::= ("0x" | "0X") hex+

The "digit" (0-9) and "hex" (0-9, A-F, a-f) terminals are described earlier.

In the "decimal" form, the digits represent their value in base ten. A leading hyphen indicates the number is negative. In the "hexadecimal" form, the number is always positive, and is represented by the hex digits interpreted in base sixteen.

The numbers represented must be in the range -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.

double ::= "-"? digit+ ("." digit+)? (("e" | "E") "-"? digit+)?

Floating point numbers are represented by an optional negative sign indicating the number is negative, a significand with optional fractional component in the form of digits in base ten giving the integer component followed optionally by a decimal point and further base ten digits giving the fractional component, and an exponent which itself is represented by an optional negative sign indicating a negative exponent and a sequence of digits giving the base ten exponent itself.

The numbers represented must be values that can be expressed in the IEEE754 binary64 format.

WS ::= ( <U+0020> | <U+000A> | "//" comment* <U+000A or EOF> | "/*" blockcomment "*/" )
comment ::= <U+0000..U+10FFFF except U+000A>
blockcomment ::= <any number of U+0000..U+10FFFF characters not containing the sequence U+002A U+002F>

The WS token is used to represent where whitespace and comments are allowed.

See also:

  • parseLibraryFile, which uses a superset of this format to decode Remote Flutter Widgets text library files.
  • decodeDataBlob, which decodes the binary variant of this format.

Implementation

DynamicMap parseDataFile(String file) {
  final _Parser parser = _Parser(_tokenize(file), null);
  return parser.readDataFile();
}