///|
let decimal : @string.Regex = @string.Regex::unsafe_from_string(
"^[+\\-]?(0|[1-9](_?[0-9])*)$",
)
///|
let hexadecimal : @string.Regex = @string.Regex::unsafe_from_string(
"^0x[0-9a-fA-F](_?[0-9a-fA-F])*$",
)
///|
let octal : @string.Regex = @string.Regex::unsafe_from_string(
"^0o[0-7](_?[0-7])*$",
)
///|
let binary : @string.Regex = @string.Regex::unsafe_from_string(
"^0b[01](_?[01])*$",
)
///|
let floating : @string.Regex = @string.Regex::unsafe_from_string(
"^[+\\-]?(0|[1-9](_?[0-9])*)([.][0-9](_?[0-9])*)?([eE][+\\-]?[0-9](_?[0-9])*)?$",
)
///|
let date_time : @string.Regex = @string.Regex::unsafe_from_string(
"^([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]([Tt ][0-9][0-9]:[0-9][0-9]:[0-9][0-9]([.][0-9]+)?([Zz]|[+\\-][0-9][0-9]:[0-9][0-9])?)?|[0-9][0-9]:[0-9][0-9]:[0-9][0-9]([.][0-9]+)?)$",
)
///|
fn number(s : String, start : Int32, end : Int32) -> Int32 {
let mut n = 0
for i = start; i < end; i = i + 1 {
n = n * 10 + s.code_unit_at(i).to_int() - 48
}
n
}
///|
fn valid_date_time(s : String) -> Bool {
if date_time.execute(s) is None {
return false
}
let has_date = s.length() >= 10 && s.code_unit_at(4).to_int() == 45
let time_start = if has_date { 11 } else { 0 }
if has_date {
let y = number(s, 0, 4)
let m = number(s, 5, 7)
let d = number(s, 8, 10)
if m < 1 || m > 12 {
return false
}
let days = month_days(y, m)
if d < 1 || d > days {
return false
}
}
if s.length() == 10 && has_date {
return true
}
if number(s, time_start, time_start + 2) > 23 ||
number(s, time_start + 3, time_start + 5) > 59 ||
number(s, time_start + 6, time_start + 8) > 60 {
return false
}
let n = s.length()
if has_date &&
n >= 6 &&
(
s.code_unit_at(n - 6).to_int() == 43 ||
s.code_unit_at(n - 6).to_int() == 45
) {
if number(s, n - 5, n - 3) > 23 || number(s, n - 2, n) > 59 {
return false
}
}
if has_date && number(s, time_start + 6, time_start + 8) == 60 {
let last = s.code_unit_at(n - 1).to_int()
let numeric_offset = n >= 6 &&
(
s.code_unit_at(n - 6).to_int() == 43 ||
s.code_unit_at(n - 6).to_int() == 45
)
if last == 90 || last == 122 || numeric_offset {
let offset = if numeric_offset {
let minutes = number(s, n - 5, n - 3) * 60 + number(s, n - 2, n)
if s.code_unit_at(n - 6).to_int() == 45 {
-minutes
} else {
minutes
}
} else {
0
}
return valid_leap_second(s, offset)
}
}
true
}
///|
fn month_days(year : Int32, month : Int32) -> Int32 {
if month == 2 {
if year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) {
29
} else {
28
}
} else if month == 4 || month == 6 || month == 9 || month == 11 {
30
} else {
31
}
}
///|
/// Positive UTC leap-second dates from IERS Leap_Second.dat, Bulletin 72,
/// July 2026. The file lists effective dates; these are the preceding days.
/// https://hpiers.obspm.fr/iers/bul/bulc/Leap_Second.dat
let leap_second_dates : Array[Int32] = [
19720630, 19721231, 19731231, 19741231, 19751231, 19761231, 19771231, 19781231,
19791231, 19810630, 19820630, 19830630, 19850630, 19871231, 19891231, 19901231,
19920630, 19930630, 19940630, 19951231, 19970630, 19981231, 20051231, 20081231,
20120630, 20150630, 20161231,
]
///|
/// RFC 3339 section 5.7 requires the same UTC instant in every zone. Local
/// date-times and local times have no known offset and cannot use this check.
fn valid_leap_second(s : String, offset : Int32) -> Bool {
let mut year = number(s, 0, 4)
let mut month = number(s, 5, 7)
let mut day = number(s, 8, 10)
let mut minutes = number(s, 11, 13) * 60 + number(s, 14, 16) - offset
if minutes < 0 {
minutes += 1440
day -= 1
if day == 0 {
month -= 1
if month == 0 {
year -= 1
month = 12
}
day = month_days(year, month)
}
} else if minutes >= 1440 {
minutes -= 1440
day += 1
if day > month_days(year, month) {
day = 1
month += 1
if month == 13 {
year += 1
month = 1
}
}
}
minutes == 1439 &&
leap_second_dates.contains(year * 10000 + month * 100 + day)
}
///|
fn valid_atom(s : String) -> Bool {
if ["true", "false", "inf", "+inf", "-inf", "nan", "+nan", "-nan"].contains(s) {
return true
}
if valid_date_time(s) {
return true
}
let base = if hexadecimal.execute(s) is Some(_) {
16
} else if octal.execute(s) is Some(_) {
8
} else if binary.execute(s) is Some(_) {
2
} else if decimal.execute(s) is Some(_) {
10
} else {
0
}
if base != 0 {
let digits = s.replace_all(old="_", new="")
let digits = if base == 10 { digits } else { digits[2:].to_owned() }
return try {
ignore(@string.parse_int64(digits, base~))
true
} catch {
_ => false
}
}
(s.contains(".") || s.contains("e") || s.contains("E")) &&
floating.execute(s) is Some(_)
}
///|
/// A declaration arena models TOML's implicit, explicit and dotted tables,
/// and resolves headers through the most recent array-of-tables element.
priv enum Declaration {
Implicit
Explicit
Dotted
}
///|
priv struct Table {
fields : Map[String, Binding]
mut declaration : Declaration
}
///|
priv enum Binding {
Child(Table)
TableArray(Array[Table])
Leaf
}
///|
fn new_table(declaration : Declaration) -> Table {
{ fields: Map([]), declaration, }
}
///|
fn child(
source : String,
table : Table,
key : String,
at : Int32,
dotted : Bool,
) -> Table raise Diagnostic {
match table.fields.get(key) {
Some(Child(t)) => {
if dotted {
match t.declaration {
Explicit =>
raise problem(
source,
at,
InvalidToml,
"Cannot extend an explicit table with dotted keys",
)
Implicit | Dotted => ()
}
}
t
}
Some(TableArray(ts)) if !dotted => ts[ts.length() - 1]
Some(_) =>
raise problem(source, at, InvalidToml, "Key is already a value: " + key)
None => {
let t = new_table(if dotted { Dotted } else { Implicit })
table.fields[key] = Child(t)
t
}
}
}
///|
fn declare_entry(
source : String,
table : Table,
entry : Entry,
) -> Unit raise Diagnostic {
let mut t = table
for i = 0; i < entry.keys.length() - 1; i = i + 1 {
t = child(source, t, entry.keys[i], entry.start, true)
}
let key = entry.keys[entry.keys.length() - 1]
if t.fields.contains(key) {
raise problem(source, entry.start, InvalidToml, "Duplicate key: " + key)
}
t.fields[key] = Leaf
}
///|
fn validate_inline(
source : String,
entries : Array[Entry],
) -> Unit raise Diagnostic {
let root = new_table(Explicit)
for entry in entries {
declare_entry(source, root, entry)
}
}
///|
fn validate_document(
source : String,
statements : Array[Statement],
) -> Unit raise Diagnostic {
let root = new_table(Explicit)
let mut current = root
for statement in statements {
match statement {
Assignment(_, entry) => declare_entry(source, current, entry)
Header(path, kind, span) => {
let aot = kind is ArrayOfTables
let mut t = root
for i = 0; i < path.length() - 1; i = i + 1 {
t = child(source, t, path[i], span.start, false)
}
let key = path[path.length() - 1]
match t.fields.get(key) {
None => {
current = new_table(Explicit)
t.fields[key] = if aot {
TableArray([current])
} else {
Child(current)
}
}
Some(TableArray(ts)) if aot => {
current = new_table(Explicit)
ts.push(current)
}
Some(Child(existing)) if !aot =>
match existing.declaration {
Implicit => {
existing.declaration = Explicit
current = existing
}
Explicit | Dotted =>
raise problem(
source,
span.start,
InvalidToml,
"Table is already defined",
)
}
_ =>
raise problem(
source,
span.start,
InvalidToml,
"Conflicting table definition",
)
}
}
}
}
}