The TOML is parsed by the 1.0.0 grammar, character by character, and the part most parsers are lenient about is how tables come into existence. A table can be defined by a [header], by dotted keys such as apple.color = "red", or implicitly as the parent of a deeper header, and each table may be defined only once. So [package] twice is an error; so is a [fruit.apple] header after apple.color was set under [fruit]; so are dotted keys that reach into a table another header defined; and an inline table { } or an array written as a value is complete as written, so no later header can add to it. Each of these errors names the line of the first definition. The parser is checked against the TOML 1.0 cases of toml-test, the language-independent TOML test suite: it reads all 208 valid documents to the expected values and rejects every invalid one that can be pasted as text.
Most values map directly to JSON, which lacks three things TOML has. It has no date type, so the four TOML date and time types become RFC 3339 strings, 1979-05-27 07:32:00Z becoming "1979-05-27T07:32:00Z", and converting back gives a string, not a date. It has no infinity or NaN, so inf and nan become null. And JavaScript reads JSON numbers as 64-bit floats, so an integer beyond 2^53, which a TOML integer can be, is written with every digit but flagged, because JSON.parse would round it. Each of the three comes with a warning naming the keys. Hexadecimal, octal and binary integers are written in decimal, so 0o755 becomes 493; floats keep their decimal point, so 1.0 does not turn into the integer 1; and keys stay in the order they were written.
Going the other way, an object becomes a [table] and an array of objects becomes an array of tables, one [[name]] section per element. Inside each table the plain values come first and the sub-tables after them, because in TOML every key below a [header] belongs to that header, so keys can come out in a different order from the JSON. Keys are quoted only when they hold something other than letters, digits, - and _. A string with line breaks becomes a multi-line string, a string with backslashes, such as a Windows path or a regular expression, is put in single quotes where that avoids escaping, and an array too long for 80 columns gets one element per line. TOML has no null: a null member is left out, with a warning, and a null inside an array is an error, because leaving it out would move every element after it. TOML 1.1 syntax, such as line breaks inside inline tables, \e and \x escapes, and times without seconds, is refused with a message that says so.