mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 05:48:12 +00:00
LibGUI: Make the GML parser a bit more fault-tolerant
It will now fail and whine in the debug log instead of asserting.
This commit is contained in:
parent
64ba41ea13
commit
6e0976d858
2 changed files with 46 additions and 18 deletions
|
@ -47,22 +47,26 @@ static void swallow_whitespace(GenericLexer& scanner)
|
||||||
scanner.consume_while([](auto ch) { return isspace(ch); });
|
scanner.consume_while([](auto ch) { return isspace(ch); });
|
||||||
}
|
}
|
||||||
|
|
||||||
static JsonValue parse_core_object(GenericLexer& scanner)
|
static Optional<JsonValue> parse_core_object(GenericLexer& scanner)
|
||||||
{
|
{
|
||||||
JsonObject object;
|
JsonObject object;
|
||||||
JsonArray children;
|
JsonArray children;
|
||||||
|
|
||||||
// '@Foo' means new Core::Object of class Foo
|
// '@Foo' means new Core::Object of class Foo
|
||||||
if (!scanner.consume_specific('@'))
|
if (!scanner.consume_specific('@')) {
|
||||||
ASSERT_NOT_REACHED();
|
dbgln("Expected '@'");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
auto class_name = scanner.consume_while([](auto ch) { return is_valid_class_name_character(ch); });
|
auto class_name = scanner.consume_while([](auto ch) { return is_valid_class_name_character(ch); });
|
||||||
object.set("class", JsonValue(class_name));
|
object.set("class", JsonValue(class_name));
|
||||||
|
|
||||||
swallow_whitespace(scanner);
|
swallow_whitespace(scanner);
|
||||||
|
|
||||||
if (!scanner.consume_specific('{'))
|
if (!scanner.consume_specific('{')) {
|
||||||
ASSERT_NOT_REACHED();
|
dbgln("Expected '{{'");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
swallow_whitespace(scanner);
|
swallow_whitespace(scanner);
|
||||||
|
|
||||||
|
@ -77,34 +81,57 @@ static JsonValue parse_core_object(GenericLexer& scanner)
|
||||||
if (scanner.peek() == '@') {
|
if (scanner.peek() == '@') {
|
||||||
// It's a child object.
|
// It's a child object.
|
||||||
auto value = parse_core_object(scanner);
|
auto value = parse_core_object(scanner);
|
||||||
ASSERT(value.is_object());
|
if (!value.has_value())
|
||||||
children.append(move(value));
|
return {};
|
||||||
|
if (!value.value().is_object()) {
|
||||||
|
dbgln("Expected child to be Core::Object");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
children.append(value.release_value());
|
||||||
} else {
|
} else {
|
||||||
// It's a property.
|
// It's a property.
|
||||||
auto property_name = scanner.consume_while([](auto ch) { return is_valid_property_name_character(ch); });
|
auto property_name = scanner.consume_while([](auto ch) { return is_valid_property_name_character(ch); });
|
||||||
swallow_whitespace(scanner);
|
swallow_whitespace(scanner);
|
||||||
|
|
||||||
ASSERT(!property_name.is_empty());
|
if (property_name.is_empty()) {
|
||||||
|
dbgln("Expected non-empty property name");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
if (!scanner.consume_specific(':'))
|
if (!scanner.consume_specific(':')) {
|
||||||
ASSERT_NOT_REACHED();
|
dbgln("Expected ':'");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
swallow_whitespace(scanner);
|
swallow_whitespace(scanner);
|
||||||
|
|
||||||
JsonValue value;
|
JsonValue value;
|
||||||
if (scanner.peek() == '@') {
|
if (scanner.peek() == '@') {
|
||||||
value = parse_core_object(scanner);
|
auto parsed_value = parse_core_object(scanner);
|
||||||
ASSERT(value.is_object());
|
if (!parsed_value.has_value())
|
||||||
|
return {};
|
||||||
|
if (!parsed_value.value().is_object()) {
|
||||||
|
dbgln("Expected property to be Core::Object");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
value = parsed_value.release_value();
|
||||||
} else {
|
} else {
|
||||||
auto value_string = scanner.consume_line();
|
auto value_string = scanner.consume_line();
|
||||||
value = JsonValue::from_string(value_string).release_value();
|
auto parsed_value = JsonValue::from_string(value_string);
|
||||||
|
if (!parsed_value.has_value()) {
|
||||||
|
dbgln("Expected property to be JSON value");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
value = parsed_value.release_value();
|
||||||
}
|
}
|
||||||
object.set(property_name, move(value));
|
object.set(property_name, move(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!scanner.consume_specific('}'))
|
if (!scanner.consume_specific('}')) {
|
||||||
ASSERT_NOT_REACHED();
|
dbgln("Expected '}'");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
if (!children.is_empty())
|
if (!children.is_empty())
|
||||||
object.set("children", move(children));
|
object.set("children", move(children));
|
||||||
|
@ -117,10 +144,10 @@ JsonValue parse_gml(const StringView& string)
|
||||||
GenericLexer scanner(string);
|
GenericLexer scanner(string);
|
||||||
auto root = parse_core_object(scanner);
|
auto root = parse_core_object(scanner);
|
||||||
|
|
||||||
if (root.is_null())
|
if (!root.has_value())
|
||||||
return JsonValue();
|
return JsonValue();
|
||||||
|
|
||||||
return root;
|
return root.release_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -936,7 +936,8 @@ void Widget::set_override_cursor(Gfx::StandardCursor cursor)
|
||||||
bool Widget::load_from_gml(const StringView& gml_string)
|
bool Widget::load_from_gml(const StringView& gml_string)
|
||||||
{
|
{
|
||||||
auto value = parse_gml(gml_string);
|
auto value = parse_gml(gml_string);
|
||||||
ASSERT(value.is_object());
|
if (!value.is_object())
|
||||||
|
return false;
|
||||||
return load_from_json(value.as_object());
|
return load_from_json(value.as_object());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue