5959 // The root map (map is called table in TOML world)
6060 root_map map [string ]ast.Value
6161 root_map_key DottedKey
62+ value_is_immutable bool
63+ immutable []DottedKey
6264 explicit_declared []DottedKey
6365 explicit_declared_array_of_tables []DottedKey
6466 implicit_declared []DottedKey
@@ -276,6 +278,14 @@ fn todo_msvc_astring2dkey(s []string) DottedKey {
276278 return s
277279}
278280
281+ // check_immutable returns an error if `key` has been declared as immutable.
282+ fn (p &Parser) check_immutable (key DottedKey) ! {
283+ if p.immutable.len > 0 && p.immutable.has (key) {
284+ return error (@MOD + '.' + @STRUCT + '.' + @FN +
285+ ' key `${key .str ()}` is immutable. Unexpected mutation at "${p .tok .kind }" "${p .tok .lit }" in this (excerpt): "...${p .excerpt ()}..."' )
286+ }
287+ }
288+
279289// check_explicitly_declared returns an error if `key` has been explicitly declared.
280290fn (p &Parser) check_explicitly_declared (key DottedKey) ! {
281291 if p.explicit_declared.len > 0 && p.explicit_declared.has (key) {
@@ -612,6 +622,15 @@ pub fn (mut p Parser) root_table() ! {
612622 continue
613623 }
614624
625+ // Disallow mutation of immutable values (inline tables)
626+ if dotted_key.len > 1 {
627+ for part in dotted_key {
628+ dotted_part := DottedKey ([part])
629+ if p.explicit_declared.has (dotted_part) {
630+ p.check_immutable (dotted_part)!
631+ }
632+ }
633+ }
615634 // Disallow re-declaring the key
616635 p.check_explicitly_declared (dotted_key)!
617636 p.explicit_declared << dotted_key
@@ -740,7 +759,7 @@ pub fn (mut p Parser) table_contents(mut tbl map[string]ast.Value) ! {
740759// The V map type is corresponding to a "table" in TOML.
741760pub fn (mut p Parser) inline_table (mut tbl map [string ]ast.Value) ! {
742761 util.printdbg (@MOD + '.' + @STRUCT + '.' + @FN, 'parsing inline table into ${ptr_str (tbl )}...' )
743-
762+ defer { p. value_is_immutable = true }
744763 mut previous_token_was_value := false
745764 for p.tok.kind != .eof {
746765 p.next ()!
@@ -786,8 +805,15 @@ pub fn (mut p Parser) inline_table(mut tbl map[string]ast.Value) ! {
786805 dotted_key , val := p.dotted_key_value ()!
787806
788807 sub_table , key := p.sub_table_key (dotted_key)
789-
790808 mut t := p.find_in_table (mut tbl, sub_table)!
809+
810+ // Disallow mutation of immutable values (inline tables)
811+ if p.explicit_declared.has (dotted_key) {
812+ left_most := DottedKey ([dotted_key[0 ]])
813+ if t.len > 0 {
814+ p.check_immutable (left_most)!
815+ }
816+ }
791817 unsafe {
792818 util.printdbg (@MOD + '.' + @STRUCT + '.' + @FN, 'inserting @6 "${key }" = ${val } into ${ptr_str (t )}' )
793819 t[key.str ()] = val
@@ -907,13 +933,16 @@ pub fn (mut p Parser) double_array_of_tables(mut table map[string]ast.Value) ! {
907933
908934 p.check_explicitly_declared (dotted_key)!
909935
936+ first := DottedKey ([dotted_key[0 ]]) // The array that holds the entries
937+ last := DottedKey ([dotted_key[1 ]]) // The key the parsed array data should be added to
938+
939+ // Disallow re-declaring last part
940+ p.check_explicitly_declared (last)!
941+
910942 if ! p.explicit_declared_array_of_tables.has (dotted_key) {
911943 p.explicit_declared_array_of_tables << dotted_key
912944 }
913945
914- first := DottedKey ([dotted_key[0 ]]) // The array that holds the entries
915- last := DottedKey ([dotted_key[1 ]]) // The key the parsed array data should be added to
916-
917946 mut t_arr := & []ast.Value (unsafe { nil })
918947 mut t_map := ast.Value (ast.Null{})
919948
@@ -1245,17 +1274,20 @@ pub fn (mut p Parser) key() !ast.Key {
12451274pub fn (mut p Parser) key_value () ! (ast.Key, ast.Value) {
12461275 util.printdbg (@MOD + '.' + @STRUCT + '.' + @FN, 'parsing key value pair...' )
12471276 key := p.key ()!
1277+ dotted_key := DottedKey ([key.str ()])
1278+ p.explicit_declared << p.build_abs_dotted_key (dotted_key)
12481279 p.next ()!
12491280 p.ignore_while (space_formatting)
12501281 p.check (.assign)! // Assignment operator
12511282 p.ignore_while (space_formatting)
12521283 value := p.value ()!
1284+ if p.value_is_immutable {
1285+ if ! p.immutable.has (dotted_key) {
1286+ p.immutable << p.build_abs_dotted_key (dotted_key) // Mark the key we are assigning to as immutable
1287+ }
1288+ p.value_is_immutable = false
1289+ }
12531290 util.printdbg (@MOD + '.' + @STRUCT + '.' + @FN, 'parsed key value pair. `${key } = ${value }`' )
1254-
1255- p.explicit_declared << p.build_abs_dotted_key (DottedKey ([
1256- key.str (),
1257- ]))
1258-
12591291 return key, value
12601292}
12611293
@@ -1265,14 +1297,19 @@ pub fn (mut p Parser) dotted_key_value() !(DottedKey, ast.Value) {
12651297 util.printdbg (@MOD + '.' + @STRUCT + '.' + @FN, 'parsing dotted key value pair...' )
12661298 p.ignore_while (space_formatting)
12671299 dotted_key := p.dotted_key ()!
1300+ p.explicit_declared << p.build_abs_dotted_key (dotted_key)
12681301 p.ignore_while (space_formatting)
12691302 p.check (.assign)!
12701303 p.ignore_while (space_formatting)
12711304 value := p.value ()!
1305+ if p.value_is_immutable {
1306+ if ! p.immutable.has (dotted_key) {
1307+ p.immutable << p.build_abs_dotted_key (dotted_key) // Mark the key we are assigning to as immutable
1308+ }
1309+ p.value_is_immutable = false
1310+ }
12721311 util.printdbg (@MOD + '.' + @STRUCT + '.' + @FN, 'parsed dotted key value pair `${dotted_key } = ${value }`...' )
12731312
1274- p.explicit_declared << p.build_abs_dotted_key (dotted_key)
1275-
12761313 return dotted_key, value
12771314}
12781315
@@ -1281,7 +1318,6 @@ pub fn (mut p Parser) dotted_key_value() !(DottedKey, ast.Value) {
12811318pub fn (mut p Parser) value () ! ast.Value {
12821319 util.printdbg (@MOD + '.' + @STRUCT + '.' + @FN, 'parsing value from token "${p .tok .kind }" "${p .tok .lit }"...' )
12831320 mut value := ast.Value (ast.Null{})
1284-
12851321 if p.tok.kind == .number {
12861322 number_or_date := p.number_or_date ()!
12871323 value = number_or_date
0 commit comments