Files
ladybird/Meta/Utils/CSSGrammar/Parser/parser.py
Callum Law 09418e8c77 Meta: Implement CSS value type parsing code generation
To do this we parse the grammar supplied in `ValueTypes.json` and
generate the appropriate parsing function in `CSS::Parser::Parser`

Only a small subset of the CSS grammar (i.e. types `<foo>` and
alternatives `<foo> | <bar>`) is implemented so far, it will be
expanded in later commits.
2026-04-29 11:42:57 +01:00

62 lines
2.1 KiB
Python

from Utils.CSSGrammar.Parser.grammar_node import CombinatorGrammarNode
from Utils.CSSGrammar.Parser.grammar_node import CombinatorType
from Utils.CSSGrammar.Parser.grammar_node import ComponentValueGrammarNode
from Utils.CSSGrammar.Parser.grammar_node import GrammarNode
from Utils.CSSGrammar.Parser.token import Token
from Utils.CSSGrammar.Parser.token import TokenType
from Utils.CSSGrammar.Parser.tokenizer import Tokenizer
class Parser:
def __init__(self, tokens: list[Token]) -> None:
self.tokens = tokens
self.index = 0
@classmethod
def parse_value_definition_grammar(cls, input: str) -> GrammarNode:
parser = cls(Tokenizer.tokenize(input))
value = parser.parse_alternatives()
if not parser.peek().is_token_type(TokenType.END_OF_FILE):
raise SyntaxError("CSSGrammar::Parser: Unexpected trailing input")
return value
def parse_alternatives(self) -> GrammarNode:
children = [self.parse_component_value()]
while self.peek().is_token_type(TokenType.SINGLE_BAR):
self.consume()
children.append(self.parse_component_value())
if len(children) == 1:
return children[0]
return CombinatorGrammarNode(CombinatorType.ALTERNATIVES, children)
def parse_component_value(self) -> GrammarNode:
# https://drafts.csswg.org/css-values-4/#component-multipliers
# FIXME: Support component multipliers
if not self.peek().is_token_type(TokenType.COMPONENT_VALUE):
raise SyntaxError("CSSGrammar::Parser: Expected a component value")
return ComponentValueGrammarNode(self.consume().component_value())
def peek(self, offset: int = 0) -> Token:
index = min(self.index + offset, len(self.tokens) - 1)
return self.tokens[index]
def consume(self) -> Token:
token = self.peek()
if not token.is_token_type(TokenType.END_OF_FILE):
self.index += 1
return token
def parse_value_definition_grammar(input: str) -> GrammarNode:
return Parser.parse_value_definition_grammar(input)