config: remove some duplication in the parser

There's still a fair amount lurking in here, but I believe this logic
is sound. Rather than duplicating the map/list logic under the
opposing key, we set the logic up to use the second loop around
(this is was how dedents worked, and now it also works for indents).

I'm not convinced this is as easy to follow, and it did lead me to add
some additional unreachables to the code, which should maybe be turned
into error returns instead. It does reduce the odds of a code change
missing a copied instance, which I think is a good thing.
This commit is contained in:
torque 2023-09-23 01:07:04 -07:00
parent 4fe340ea9b
commit 465d21eaae
Signed by: torque
SSH Key Fingerprint: SHA256:nCrXefBNo6EbjNSQhv0nXmEg/VuNq3sMF5b8zETw3Tk

View File

@ -757,7 +757,7 @@ pub const Parser = struct {
.value => switch (stack.getLast().*) { .value => switch (stack.getLast().*) {
// these three states are never reachable here. flow_list and // these three states are never reachable here. flow_list and
// flow_map are parsed with a separate state machine. These // flow_map are parsed with a separate state machine. These
// value tyeps can only be present by themselves as the first // value types can only be present by themselves as the first
// line of the document, in which case the document consists // line of the document, in which case the document consists
// only of that single line: this parser jumps immediately into // only of that single line: this parser jumps immediately into
// the .done state, bypassing the .value state in which this // the .done state, bypassing the .value state in which this
@ -799,7 +799,7 @@ pub const Parser = struct {
// //
// the first line here creates the expect_shift, but the second line // the first line here creates the expect_shift, but the second line
// is a valid continuation of the list despite not being indented // is a valid continuation of the list despite not being indented
if (expect_shift == .indent and line.indent != .indent) if (!flop and (expect_shift == .indent and line.indent != .indent))
try list.append(Value.newScalar(arena_alloc)); try list.append(Value.newScalar(arena_alloc));
// Consider: // Consider:
@ -838,47 +838,33 @@ pub const Parser = struct {
.line_string, .space_string => |str| { .line_string, .space_string => |str| {
// string pushes the stack // string pushes the stack
const new_string = try appendListGetValue(list, try Value.fromString(arena_alloc, str)); const new_string = try appendListGetValue(list, try Value.fromString(arena_alloc, str));
try stack.append(new_string);
try new_string.string.append(in_line.lineEnding()); try new_string.string.append(in_line.lineEnding());
try stack.append(new_string);
expect_shift = .none; expect_shift = .none;
}, },
} }
}, },
.list_item => |value| { .list_item => |value| {
switch (line.indent) { if (flop or (line.indent == .none or line.indent == .dedent)) {
// for dedent, the stack has already been popped, so this should be fine expect_shift = .none;
.none, .dedent => { switch (value) {
expect_shift = .none; .empty => expect_shift = .indent,
switch (value) { .scalar => |str| try list.append(try Value.fromScalar(arena_alloc, str)),
.empty => expect_shift = .indent, .line_string, .space_string => |str| try list.append(try Value.fromString(arena_alloc, str)),
.scalar => |str| try list.append(try Value.fromScalar(arena_alloc, str)), .flow_list => |str| try list.append(try parseFlowList(arena_alloc, str, self.dupe_behavior)),
.line_string, .space_string => |str| try list.append(try Value.fromString(arena_alloc, str)), .flow_map => |str| try list.append(try parseFlowMap(arena_alloc, str, self.dupe_behavior)),
.flow_list => |str| try list.append(try parseFlowList(arena_alloc, str, self.dupe_behavior)), }
.flow_map => |str| try list.append(try parseFlowMap(arena_alloc, str, self.dupe_behavior)), } else if (line.indent == .indent) {
} if (expect_shift != .indent) return error.UnexpectedIndent;
},
// a new list is being created
.indent => {
if (expect_shift != .indent)
return error.UnexpectedIndent;
const new_list = try appendListGetValue(list, Value.newList(arena_alloc)); const new_list = try appendListGetValue(list, Value.newList(arena_alloc));
try stack.append(new_list); try stack.append(new_list);
expect_shift = .none;
expect_shift = .none; continue :flipflop;
switch (value) { } else unreachable;
.empty => expect_shift = .indent,
.scalar => |str| try new_list.list.append(try Value.fromScalar(arena_alloc, str)),
.line_string, .space_string => |str| try new_list.list.append(try Value.fromString(arena_alloc, str)),
.flow_list => |str| try new_list.list.append(try parseFlowList(arena_alloc, str, self.dupe_behavior)),
.flow_map => |str| try new_list.list.append(try parseFlowMap(arena_alloc, str, self.dupe_behavior)),
}
},
}
}, },
.map_item => |pair| { .map_item => {
// this prong cannot be hit on dedent in a valid way. // this prong cannot be hit on dedent in a valid way.
// //
// - // -
@ -894,17 +880,7 @@ pub const Parser = struct {
const new_map = try appendListGetValue(list, Value.newMap(arena_alloc)); const new_map = try appendListGetValue(list, Value.newMap(arena_alloc));
try stack.append(new_map); try stack.append(new_map);
expect_shift = .none; expect_shift = .none;
continue :flipflop;
switch (pair.val) {
.empty => {
dangling_key = try arena_alloc.dupe(u8, pair.key);
expect_shift = .indent;
},
.scalar => |str| try new_map.map.put(pair.key, try Value.fromScalar(arena_alloc, str)),
.line_string, .space_string => |str| try new_map.map.put(pair.key, try Value.fromString(arena_alloc, str)),
.flow_list => |str| try new_map.map.put(pair.key, try parseFlowList(arena_alloc, str, self.dupe_behavior)),
.flow_map => |str| try new_map.map.put(pair.key, try parseFlowMap(arena_alloc, str, self.dupe_behavior)),
}
}, },
} }
}, },
@ -916,7 +892,7 @@ pub const Parser = struct {
// //
// the first line here creates the expect_shift, but the second line // the first line here creates the expect_shift, but the second line
// is a valid continuation of the map despite not being indented // is a valid continuation of the map despite not being indented
if (expect_shift == .indent and line.indent != .indent) { if (!flop and (expect_shift == .indent and line.indent != .indent)) {
try putMap( try putMap(
map, map,
dangling_key orelse return error.Fail, dangling_key orelse return error.Fail,
@ -963,7 +939,7 @@ pub const Parser = struct {
dangling_key = null; dangling_key = null;
}, },
.list_item => |value| { .list_item => {
// this prong cannot be hit on dedent in a valid way. // this prong cannot be hit on dedent in a valid way.
// //
// map: // map:
@ -978,21 +954,13 @@ pub const Parser = struct {
const new_list = try putMapGetValue(map, dangling_key.?, Value.newList(arena_alloc), self.dupe_behavior); const new_list = try putMapGetValue(map, dangling_key.?, Value.newList(arena_alloc), self.dupe_behavior);
try stack.append(new_list); try stack.append(new_list);
dangling_key = null; dangling_key = null;
expect_shift = .none; expect_shift = .none;
switch (value) { continue :flipflop;
.empty => expect_shift = .indent,
.scalar => |str| try new_list.list.append(try Value.fromScalar(arena_alloc, str)),
.line_string, .space_string => |str| try new_list.list.append(try Value.fromString(arena_alloc, str)),
.flow_list => |str| try new_list.list.append(try parseFlowList(arena_alloc, str, self.dupe_behavior)),
.flow_map => |str| try new_list.list.append(try parseFlowMap(arena_alloc, str, self.dupe_behavior)),
}
}, },
.map_item => |pair| { .map_item => |pair| {
expect_shift = .none; if (flop or (line.indent == .none or line.indent == .dedent)) {
switch (line.indent) { expect_shift = .none;
// for dedent, the stack has already been popped, so this should be fine switch (pair.val) {
.none, .dedent => switch (pair.val) {
.empty => { .empty => {
expect_shift = .indent; expect_shift = .indent;
dangling_key = try arena_alloc.dupe(u8, pair.key); dangling_key = try arena_alloc.dupe(u8, pair.key);
@ -1001,27 +969,15 @@ pub const Parser = struct {
.line_string, .space_string => |str| try putMap(map, pair.key, try Value.fromString(arena_alloc, str), self.dupe_behavior), .line_string, .space_string => |str| try putMap(map, pair.key, try Value.fromString(arena_alloc, str), self.dupe_behavior),
.flow_list => |str| try putMap(map, pair.key, try parseFlowList(arena_alloc, str, self.dupe_behavior), self.dupe_behavior), .flow_list => |str| try putMap(map, pair.key, try parseFlowList(arena_alloc, str, self.dupe_behavior), self.dupe_behavior),
.flow_map => |str| try putMap(map, pair.key, try parseFlowMap(arena_alloc, str, self.dupe_behavior), self.dupe_behavior), .flow_map => |str| try putMap(map, pair.key, try parseFlowMap(arena_alloc, str, self.dupe_behavior), self.dupe_behavior),
}, }
// a new map is being created } else if (line.indent == .indent) {
.indent => { if (expect_shift != .indent or dangling_key == null) return error.UnexpectedValue;
if (expect_shift != .indent or dangling_key == null) return error.UnexpectedValue;
const new_map = try putMapGetValue(map, dangling_key.?, Value.newMap(arena_alloc), self.dupe_behavior); const new_map = try putMapGetValue(map, dangling_key.?, Value.newMap(arena_alloc), self.dupe_behavior);
try stack.append(new_map); try stack.append(new_map);
dangling_key = null; dangling_key = null;
continue :flipflop;
switch (pair.val) { } else unreachable;
.empty => {
expect_shift = .indent;
dangling_key = try arena_alloc.dupe(u8, pair.key);
},
.scalar => |str| try new_map.map.put(pair.key, try Value.fromScalar(arena_alloc, str)),
.line_string, .space_string => |str| try new_map.map.put(pair.key, try Value.fromString(arena_alloc, str)),
.flow_list => |str| try new_map.map.put(pair.key, try parseFlowList(arena_alloc, str, self.dupe_behavior)),
.flow_map => |str| try new_map.map.put(pair.key, try parseFlowMap(arena_alloc, str, self.dupe_behavior)),
}
},
}
}, },
} }
}, },
@ -1319,7 +1275,7 @@ pub const FlowParser = struct {
// forbid these characters so that flow dictionary keys cannot start // forbid these characters so that flow dictionary keys cannot start
// with characters that regular dictionary keys cannot start with // with characters that regular dictionary keys cannot start with
// (even though they're unambiguous in this specific context). // (even though they're unambiguous in this specific context).
'{', '[', '#', '>', '|', ',' => return error.BadToken, '{', '[', '#', '-', '>', '|', ',' => return error.BadToken,
':' => { ':' => {
// we have an empty map key // we have an empty map key
dangling_key = ""; dangling_key = "";