From 99bafd0d45be3d784d76110356c0162a16417b89 Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Thu, 10 Sep 2020 23:59:10 -0700 Subject: [PATCH] add an initial css lexer+parser+printer (#20) --- internal/css_ast/css_ast.go | 111 +++ internal/css_lexer/css_lexer.go | 786 +++++++++++++++++++++ internal/css_lexer/css_lexer_test.go | 104 +++ internal/css_parser/css_parser.go | 410 +++++++++++ internal/css_parser/css_parser_selector.go | 322 +++++++++ internal/css_printer/css_printer.go | 231 ++++++ 6 files changed, 1964 insertions(+) create mode 100644 internal/css_ast/css_ast.go create mode 100644 internal/css_lexer/css_lexer.go create mode 100644 internal/css_lexer/css_lexer_test.go create mode 100644 internal/css_parser/css_parser.go create mode 100644 internal/css_parser/css_parser_selector.go create mode 100644 internal/css_printer/css_printer.go diff --git a/internal/css_ast/css_ast.go b/internal/css_ast/css_ast.go new file mode 100644 index 00000000000..0977337fc81 --- /dev/null +++ b/internal/css_ast/css_ast.go @@ -0,0 +1,111 @@ +package css_ast + +import ( + "github.com/evanw/esbuild/internal/css_lexer" + "github.com/evanw/esbuild/internal/logger" +) + +type AST struct { + Rules []R +} + +// This interface is never called. Its purpose is to encode a variant type in +// Go's type system. +type R interface { + isRule() +} + +type RAtImport struct { + PathText string + PathRange logger.Range +} + +type RKnownAt struct { + Name css_lexer.Token + Prelude []css_lexer.Token + Rules []R +} + +type RUnknownAt struct { + Name css_lexer.Token + Prelude []css_lexer.Token + Block []css_lexer.Token +} + +type RSelector struct { + Selectors []ComplexSelector + Rules []R +} + +type RQualified struct { + Prelude []css_lexer.Token + Rules []R +} + +type RDeclaration struct { + Key css_lexer.Token + Value []css_lexer.Token + Important bool +} + +type RBadDeclaration struct { + Tokens []css_lexer.Token +} + +func (*RAtImport) isRule() {} +func (*RKnownAt) isRule() {} +func (*RUnknownAt) isRule() {} +func (*RSelector) isRule() {} +func (*RQualified) isRule() {} +func (*RDeclaration) isRule() {} +func (*RBadDeclaration) isRule() {} + +type ComplexSelector struct { + Selectors []CompoundSelector +} + +type CompoundSelector struct { + Combinator string // Optional, may be "" + TypeSelector *NamespacedName + SubclassSelectors []SS + PseudoClassSelectors []SSPseudoClass // If present, these follow a ":" character +} + +type NamespacedName struct { + // If present, this is an identifier or "*" or "" and is followed by a "|" character + NamespacePrefix *string + + // This is an identifier or "*" or "&" + Name string +} + +// This interface is never called. Its purpose is to encode a variant type in +// Go's type system. +type SS interface { + isSubclassSelector() +} + +type SSHash struct { + Name string +} + +type SSClass struct { + Name string +} + +type SSAttribute struct { + NamespacedName NamespacedName + MatcherOp string + MatcherValue string + MatcherModifier byte +} + +type SSPseudoClass struct { + Name string + Args []css_lexer.Token +} + +func (*SSHash) isSubclassSelector() {} +func (*SSClass) isSubclassSelector() {} +func (*SSAttribute) isSubclassSelector() {} +func (*SSPseudoClass) isSubclassSelector() {} diff --git a/internal/css_lexer/css_lexer.go b/internal/css_lexer/css_lexer.go new file mode 100644 index 00000000000..1e398e2dce8 --- /dev/null +++ b/internal/css_lexer/css_lexer.go @@ -0,0 +1,786 @@ +package css_lexer + +import ( + "strings" + "unicode/utf8" + + "github.com/evanw/esbuild/internal/logger" +) + +type T uint8 + +const eof = -1 +const replacementCharacter = 0xFFFD + +const ( + TEndOfFile T = iota + + TAtKeyword + TBadString + TBadURL + TCDC // "-->" + TCDO // "\"", + "\"", TCDC, "\"-->\""}, + {"