mirror of
https://gitlab.gnome.org/jwestman/blueprint-compiler.git
synced 2025-05-04 15:59:08 -04:00
parser: Shorter code for groups
Add a "grammar" property on AstNode types so they can be used in grammar expressions as groups
This commit is contained in:
parent
8d587b62a0
commit
76f7befd68
10 changed files with 119 additions and 159 deletions
|
@ -23,6 +23,7 @@ from .ast_utils import *
|
||||||
from .errors import CompileError, CompilerBugError, MultipleErrors
|
from .errors import CompileError, CompilerBugError, MultipleErrors
|
||||||
from . import gir
|
from . import gir
|
||||||
from .lsp_utils import Completion, CompletionItemKind, SemanticToken, SemanticTokenType
|
from .lsp_utils import Completion, CompletionItemKind, SemanticToken, SemanticTokenType
|
||||||
|
from .parse_tree import *
|
||||||
from .tokenizer import Token
|
from .tokenizer import Token
|
||||||
from .utils import lazy_prop
|
from .utils import lazy_prop
|
||||||
from .xml_emitter import XmlEmitter
|
from .xml_emitter import XmlEmitter
|
||||||
|
@ -99,6 +100,12 @@ class UI(AstNode):
|
||||||
|
|
||||||
|
|
||||||
class GtkDirective(AstNode):
|
class GtkDirective(AstNode):
|
||||||
|
grammar = Statement(
|
||||||
|
Match("using").err("File must start with a \"using Gtk\" directive (e.g. `using Gtk 4.0;`)"),
|
||||||
|
Match("Gtk").err("File must start with a \"using Gtk\" directive (e.g. `using Gtk 4.0;`)"),
|
||||||
|
UseNumberText("version").expected("a version number for GTK"),
|
||||||
|
)
|
||||||
|
|
||||||
@validate("version")
|
@validate("version")
|
||||||
def gtk_version(self):
|
def gtk_version(self):
|
||||||
if self.tokens["version"] not in ["4.0"]:
|
if self.tokens["version"] not in ["4.0"]:
|
||||||
|
@ -120,6 +127,12 @@ class GtkDirective(AstNode):
|
||||||
|
|
||||||
|
|
||||||
class Import(AstNode):
|
class Import(AstNode):
|
||||||
|
grammar = Statement(
|
||||||
|
"using",
|
||||||
|
UseIdent("namespace").expected("a GIR namespace"),
|
||||||
|
UseNumberText("version").expected("a version number"),
|
||||||
|
)
|
||||||
|
|
||||||
@validate("namespace", "version")
|
@validate("namespace", "version")
|
||||||
def namespace_exists(self):
|
def namespace_exists(self):
|
||||||
gir.get_namespace(self.tokens["namespace"], self.tokens["version"])
|
gir.get_namespace(self.tokens["namespace"], self.tokens["version"])
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
""" Contains all the syntax beyond basic objects, properties, signal, and
|
""" Contains all the syntax beyond basic objects, properties, signal, and
|
||||||
templates. """
|
templates. """
|
||||||
|
|
||||||
from .gtk_a11y import a11y
|
from .gtk_a11y import A11y
|
||||||
from .gtk_combo_box_text import items
|
from .gtk_combo_box_text import Items
|
||||||
from .gtk_file_filter import mime_types, patterns, suffixes
|
from .gtk_file_filter import mime_types, patterns, suffixes
|
||||||
from .gtk_layout import layout
|
from .gtk_layout import Layout
|
||||||
from .gtk_menu import menu
|
from .gtk_menu import menu
|
||||||
from .gtk_size_group import widgets
|
from .gtk_size_group import Widgets
|
||||||
from .gtk_string_list import strings
|
from .gtk_string_list import Strings
|
||||||
from .gtk_styles import styles
|
from .gtk_styles import Styles
|
||||||
|
|
||||||
OBJECT_HOOKS = [menu]
|
OBJECT_HOOKS = [menu]
|
||||||
|
|
||||||
OBJECT_CONTENT_HOOKS = [
|
OBJECT_CONTENT_HOOKS = [
|
||||||
a11y, styles, layout, mime_types, patterns, suffixes, widgets, items,
|
A11y, Styles, Layout, mime_types, patterns, suffixes, Widgets, Items,
|
||||||
strings,
|
Strings,
|
||||||
]
|
]
|
||||||
|
|
|
@ -105,20 +105,13 @@ def _get_docs(gir, name):
|
||||||
).doc
|
).doc
|
||||||
|
|
||||||
|
|
||||||
class A11y(AstNode):
|
|
||||||
@validate("accessibility")
|
|
||||||
def container_is_widget(self):
|
|
||||||
self.validate_parent_type("Gtk", "Widget", "accessibility properties")
|
|
||||||
|
|
||||||
|
|
||||||
def emit_xml(self, xml: XmlEmitter):
|
|
||||||
xml.start_tag("accessibility")
|
|
||||||
for child in self.children:
|
|
||||||
child.emit_xml(xml)
|
|
||||||
xml.end_tag()
|
|
||||||
|
|
||||||
|
|
||||||
class A11yProperty(BaseTypedAttribute):
|
class A11yProperty(BaseTypedAttribute):
|
||||||
|
grammar = Statement(
|
||||||
|
UseIdent("name"),
|
||||||
|
":",
|
||||||
|
value.expected("a value"),
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tag_name(self):
|
def tag_name(self):
|
||||||
name = self.tokens["name"]
|
name = self.tokens["name"]
|
||||||
|
@ -151,23 +144,23 @@ class A11yProperty(BaseTypedAttribute):
|
||||||
return _get_docs(self.root.gir, self.tokens["name"])
|
return _get_docs(self.root.gir, self.tokens["name"])
|
||||||
|
|
||||||
|
|
||||||
a11y_prop = Group(
|
class A11y(AstNode):
|
||||||
A11yProperty,
|
grammar = [
|
||||||
Statement(
|
|
||||||
UseIdent("name"),
|
|
||||||
":",
|
|
||||||
value.expected("a value"),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
a11y = Group(
|
|
||||||
A11y,
|
|
||||||
[
|
|
||||||
Keyword("accessibility"),
|
Keyword("accessibility"),
|
||||||
"{",
|
"{",
|
||||||
Until(a11y_prop, "}"),
|
Until(A11yProperty, "}"),
|
||||||
]
|
]
|
||||||
)
|
|
||||||
|
@validate("accessibility")
|
||||||
|
def container_is_widget(self):
|
||||||
|
self.validate_parent_type("Gtk", "Widget", "accessibility properties")
|
||||||
|
|
||||||
|
|
||||||
|
def emit_xml(self, xml: XmlEmitter):
|
||||||
|
xml.start_tag("accessibility")
|
||||||
|
for child in self.children:
|
||||||
|
child.emit_xml(xml)
|
||||||
|
xml.end_tag()
|
||||||
|
|
||||||
|
|
||||||
@completer(
|
@completer(
|
||||||
|
|
|
@ -28,19 +28,6 @@ from ..parser_utils import *
|
||||||
from ..xml_emitter import XmlEmitter
|
from ..xml_emitter import XmlEmitter
|
||||||
|
|
||||||
|
|
||||||
class Items(AstNode):
|
|
||||||
@validate("items")
|
|
||||||
def container_is_combo_box_text(self):
|
|
||||||
self.validate_parent_type("Gtk", "ComboBoxText", "combo box items")
|
|
||||||
|
|
||||||
|
|
||||||
def emit_xml(self, xml: XmlEmitter):
|
|
||||||
xml.start_tag("items")
|
|
||||||
for child in self.children:
|
|
||||||
child.emit_xml(xml)
|
|
||||||
xml.end_tag()
|
|
||||||
|
|
||||||
|
|
||||||
class Item(BaseTypedAttribute):
|
class Item(BaseTypedAttribute):
|
||||||
tag_name = "item"
|
tag_name = "item"
|
||||||
attr_name = "id"
|
attr_name = "id"
|
||||||
|
@ -61,15 +48,25 @@ item = Group(
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
items = Group(
|
|
||||||
Items,
|
class Items(AstNode):
|
||||||
[
|
grammar = [
|
||||||
Keyword("items"),
|
Keyword("items"),
|
||||||
"[",
|
"[",
|
||||||
Delimited(item, ","),
|
Delimited(item, ","),
|
||||||
"]",
|
"]",
|
||||||
]
|
]
|
||||||
)
|
|
||||||
|
@validate("items")
|
||||||
|
def container_is_combo_box_text(self):
|
||||||
|
self.validate_parent_type("Gtk", "ComboBoxText", "combo box items")
|
||||||
|
|
||||||
|
|
||||||
|
def emit_xml(self, xml: XmlEmitter):
|
||||||
|
xml.start_tag("items")
|
||||||
|
for child in self.children:
|
||||||
|
child.emit_xml(xml)
|
||||||
|
xml.end_tag()
|
||||||
|
|
||||||
|
|
||||||
@completer(
|
@completer(
|
||||||
|
|
|
@ -27,19 +27,6 @@ from ..parser_utils import *
|
||||||
from ..xml_emitter import XmlEmitter
|
from ..xml_emitter import XmlEmitter
|
||||||
|
|
||||||
|
|
||||||
class Layout(AstNode):
|
|
||||||
@validate("layout")
|
|
||||||
def container_is_widget(self):
|
|
||||||
self.validate_parent_type("Gtk", "Widget", "layout properties")
|
|
||||||
|
|
||||||
|
|
||||||
def emit_xml(self, xml: XmlEmitter):
|
|
||||||
xml.start_tag("layout")
|
|
||||||
for child in self.children:
|
|
||||||
child.emit_xml(xml)
|
|
||||||
xml.end_tag()
|
|
||||||
|
|
||||||
|
|
||||||
class LayoutProperty(BaseAttribute):
|
class LayoutProperty(BaseAttribute):
|
||||||
tag_name = "property"
|
tag_name = "property"
|
||||||
|
|
||||||
|
@ -58,14 +45,24 @@ layout_prop = Group(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
layout = Group(
|
|
||||||
Layout,
|
class Layout(AstNode):
|
||||||
Sequence(
|
grammar = Sequence(
|
||||||
Keyword("layout"),
|
Keyword("layout"),
|
||||||
"{",
|
"{",
|
||||||
Until(layout_prop, "}"),
|
Until(layout_prop, "}"),
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
@validate("layout")
|
||||||
|
def container_is_widget(self):
|
||||||
|
self.validate_parent_type("Gtk", "Widget", "layout properties")
|
||||||
|
|
||||||
|
|
||||||
|
def emit_xml(self, xml: XmlEmitter):
|
||||||
|
xml.start_tag("layout")
|
||||||
|
for child in self.children:
|
||||||
|
child.emit_xml(xml)
|
||||||
|
xml.end_tag()
|
||||||
|
|
||||||
|
|
||||||
@completer(
|
@completer(
|
||||||
|
|
|
@ -27,19 +27,9 @@ from ..parser_utils import *
|
||||||
from ..xml_emitter import XmlEmitter
|
from ..xml_emitter import XmlEmitter
|
||||||
|
|
||||||
|
|
||||||
class Widgets(AstNode):
|
|
||||||
@validate("widgets")
|
|
||||||
def container_is_size_group(self):
|
|
||||||
self.validate_parent_type("Gtk", "SizeGroup", "size group properties")
|
|
||||||
|
|
||||||
def emit_xml(self, xml: XmlEmitter):
|
|
||||||
xml.start_tag("widgets")
|
|
||||||
for child in self.children:
|
|
||||||
child.emit_xml(xml)
|
|
||||||
xml.end_tag()
|
|
||||||
|
|
||||||
|
|
||||||
class Widget(AstNode):
|
class Widget(AstNode):
|
||||||
|
grammar = UseIdent("name")
|
||||||
|
|
||||||
@validate("name")
|
@validate("name")
|
||||||
def obj_widget(self):
|
def obj_widget(self):
|
||||||
object = self.root.objects_by_id.get(self.tokens["name"])
|
object = self.root.objects_by_id.get(self.tokens["name"])
|
||||||
|
@ -58,21 +48,23 @@ class Widget(AstNode):
|
||||||
xml.put_self_closing("widget", name=self.tokens["name"])
|
xml.put_self_closing("widget", name=self.tokens["name"])
|
||||||
|
|
||||||
|
|
||||||
widgets = Group(
|
class Widgets(AstNode):
|
||||||
Widgets,
|
grammar = [
|
||||||
[
|
|
||||||
Keyword("widgets"),
|
Keyword("widgets"),
|
||||||
"[",
|
"[",
|
||||||
Delimited(
|
Delimited(Widget, ","),
|
||||||
Group(
|
|
||||||
Widget,
|
|
||||||
UseIdent("name"),
|
|
||||||
),
|
|
||||||
",",
|
|
||||||
),
|
|
||||||
"]",
|
"]",
|
||||||
]
|
]
|
||||||
)
|
|
||||||
|
@validate("widgets")
|
||||||
|
def container_is_size_group(self):
|
||||||
|
self.validate_parent_type("Gtk", "SizeGroup", "size group properties")
|
||||||
|
|
||||||
|
def emit_xml(self, xml: XmlEmitter):
|
||||||
|
xml.start_tag("widgets")
|
||||||
|
for child in self.children:
|
||||||
|
child.emit_xml(xml)
|
||||||
|
xml.end_tag()
|
||||||
|
|
||||||
|
|
||||||
@completer(
|
@completer(
|
||||||
|
|
|
@ -28,20 +28,9 @@ from ..parser_utils import *
|
||||||
from ..xml_emitter import XmlEmitter
|
from ..xml_emitter import XmlEmitter
|
||||||
|
|
||||||
|
|
||||||
class Items(AstNode):
|
|
||||||
@validate("items")
|
|
||||||
def container_is_string_list(self):
|
|
||||||
self.validate_parent_type("Gtk", "StringList", "StringList items")
|
|
||||||
|
|
||||||
|
|
||||||
def emit_xml(self, xml: XmlEmitter):
|
|
||||||
xml.start_tag("items")
|
|
||||||
for child in self.children:
|
|
||||||
child.emit_xml(xml)
|
|
||||||
xml.end_tag()
|
|
||||||
|
|
||||||
|
|
||||||
class Item(AstNode):
|
class Item(AstNode):
|
||||||
|
grammar = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value_type(self):
|
def value_type(self):
|
||||||
return StringType()
|
return StringType()
|
||||||
|
@ -54,20 +43,24 @@ class Item(AstNode):
|
||||||
xml.end_tag()
|
xml.end_tag()
|
||||||
|
|
||||||
|
|
||||||
item = Group(
|
class Strings(AstNode):
|
||||||
Item,
|
grammar = [
|
||||||
value,
|
|
||||||
)
|
|
||||||
|
|
||||||
strings = Group(
|
|
||||||
Items,
|
|
||||||
[
|
|
||||||
Keyword("strings"),
|
Keyword("strings"),
|
||||||
"[",
|
"[",
|
||||||
Delimited(item, ","),
|
Delimited(Item, ","),
|
||||||
"]",
|
"]",
|
||||||
]
|
]
|
||||||
)
|
|
||||||
|
@validate("items")
|
||||||
|
def container_is_string_list(self):
|
||||||
|
self.validate_parent_type("Gtk", "StringList", "StringList items")
|
||||||
|
|
||||||
|
|
||||||
|
def emit_xml(self, xml: XmlEmitter):
|
||||||
|
xml.start_tag("items")
|
||||||
|
for child in self.children:
|
||||||
|
child.emit_xml(xml)
|
||||||
|
xml.end_tag()
|
||||||
|
|
||||||
|
|
||||||
@completer(
|
@completer(
|
||||||
|
|
|
@ -27,7 +27,21 @@ from ..parser_utils import *
|
||||||
from ..xml_emitter import XmlEmitter
|
from ..xml_emitter import XmlEmitter
|
||||||
|
|
||||||
|
|
||||||
|
class StyleClass(AstNode):
|
||||||
|
grammar = UseQuoted("name")
|
||||||
|
|
||||||
|
def emit_xml(self, xml):
|
||||||
|
xml.put_self_closing("class", name=self.tokens["name"])
|
||||||
|
|
||||||
|
|
||||||
class Styles(AstNode):
|
class Styles(AstNode):
|
||||||
|
grammar = [
|
||||||
|
Keyword("styles"),
|
||||||
|
"[",
|
||||||
|
Delimited(StyleClass, ","),
|
||||||
|
"]",
|
||||||
|
]
|
||||||
|
|
||||||
@validate("styles")
|
@validate("styles")
|
||||||
def container_is_widget(self):
|
def container_is_widget(self):
|
||||||
self.validate_parent_type("Gtk", "Widget", "style classes")
|
self.validate_parent_type("Gtk", "Widget", "style classes")
|
||||||
|
@ -39,28 +53,6 @@ class Styles(AstNode):
|
||||||
xml.end_tag()
|
xml.end_tag()
|
||||||
|
|
||||||
|
|
||||||
class StyleClass(AstNode):
|
|
||||||
def emit_xml(self, xml):
|
|
||||||
xml.put_self_closing("class", name=self.tokens["name"])
|
|
||||||
|
|
||||||
|
|
||||||
styles = Group(
|
|
||||||
Styles,
|
|
||||||
[
|
|
||||||
Keyword("styles"),
|
|
||||||
"[",
|
|
||||||
Delimited(
|
|
||||||
Group(
|
|
||||||
StyleClass,
|
|
||||||
UseQuoted("name")
|
|
||||||
),
|
|
||||||
",",
|
|
||||||
),
|
|
||||||
"]",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@completer(
|
@completer(
|
||||||
applies_in=[ast.ObjectContent],
|
applies_in=[ast.ObjectContent],
|
||||||
applies_in_subclass=("Gtk", "Widget"),
|
applies_in_subclass=("Gtk", "Widget"),
|
||||||
|
|
|
@ -24,7 +24,6 @@ import typing as T
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from .ast import AstNode
|
|
||||||
from .errors import assert_true, CompilerBugError, CompileError, UnexpectedTokenError
|
from .errors import assert_true, CompilerBugError, CompileError, UnexpectedTokenError
|
||||||
from .tokenizer import Token, TokenType
|
from .tokenizer import Token, TokenType
|
||||||
|
|
||||||
|
@ -77,7 +76,7 @@ class ParseGroup:
|
||||||
self.keys[key] = val
|
self.keys[key] = val
|
||||||
self.tokens[key] = token
|
self.tokens[key] = token
|
||||||
|
|
||||||
def to_ast(self) -> AstNode:
|
def to_ast(self):
|
||||||
""" Creates an AST node from the match group. """
|
""" Creates an AST node from the match group. """
|
||||||
children = [child.to_ast() for child in self.children]
|
children = [child.to_ast() for child in self.children]
|
||||||
|
|
||||||
|
@ -522,5 +521,7 @@ def to_parse_node(value) -> ParseNode:
|
||||||
return Match(value)
|
return Match(value)
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
return Sequence(*value)
|
return Sequence(*value)
|
||||||
|
elif isinstance(value, type) and hasattr(value, "grammar"):
|
||||||
|
return Group(value, getattr(value, "grammar"))
|
||||||
else:
|
else:
|
||||||
return value
|
return value
|
||||||
|
|
|
@ -29,24 +29,6 @@ from .extensions import OBJECT_HOOKS, OBJECT_CONTENT_HOOKS
|
||||||
def parse(tokens) -> T.Tuple[ast.UI, T.Optional[MultipleErrors]]:
|
def parse(tokens) -> T.Tuple[ast.UI, T.Optional[MultipleErrors]]:
|
||||||
""" Parses a list of tokens into an abstract syntax tree. """
|
""" Parses a list of tokens into an abstract syntax tree. """
|
||||||
|
|
||||||
gtk_directive = Group(
|
|
||||||
ast.GtkDirective,
|
|
||||||
Statement(
|
|
||||||
Match("using").err("File must start with a \"using Gtk\" directive (e.g. `using Gtk 4.0;`)"),
|
|
||||||
Match("Gtk").err("File must start with a \"using Gtk\" directive (e.g. `using Gtk 4.0;`)"),
|
|
||||||
UseNumberText("version").expected("a version number for GTK"),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
import_statement = Group(
|
|
||||||
ast.Import,
|
|
||||||
Statement(
|
|
||||||
"using",
|
|
||||||
UseIdent("namespace").expected("a GIR namespace"),
|
|
||||||
UseNumberText("version").expected("a version number"),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
object = Group(
|
object = Group(
|
||||||
ast.Object,
|
ast.Object,
|
||||||
None
|
None
|
||||||
|
@ -152,8 +134,8 @@ def parse(tokens) -> T.Tuple[ast.UI, T.Optional[MultipleErrors]]:
|
||||||
ui = Group(
|
ui = Group(
|
||||||
ast.UI,
|
ast.UI,
|
||||||
[
|
[
|
||||||
gtk_directive,
|
ast.GtkDirective,
|
||||||
ZeroOrMore(import_statement),
|
ZeroOrMore(ast.Import),
|
||||||
Until(AnyOf(
|
Until(AnyOf(
|
||||||
*OBJECT_HOOKS,
|
*OBJECT_HOOKS,
|
||||||
template,
|
template,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue