Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Oh no.
// Check it out here:
// https://www.typescriptlang.org/play?ts=4.1.0-dev.20201001#code/C4TwDgpgBAMg9gdwgJwMYEMDOECCAbMAC3SgF4oBydCqAH0oCMb6LVnKATdiibgM24BzboW4BLbgCtuAa255uAW24A7bnG5huAR27JumbsG4BXbgDduCbgA9uIbgC8KAbgBQoSFACqYSGixcAmIyKABhdDAxYHQ8MUcIAB54JADsfCJ0AD53T2gM4gYIYFCUlAx04JJ6X38KoMzc8GgAORNFIuRMUIoABm4ARm4AJm4AZm4AFm4AVm4ANm4Adm4ADm4ATlcPZqgAZRAOuDxu8goAWm4AfQodrwBJDggVYDE+MRRTqAL0IpL6NodFB0faHBjHTBNLwAdUI0QgmDA6FQ0DOlBBFAAOmoMZjjLiBLi1FDoABFExwYAI0IAIgoNIxdJJUAAKsh0OZPrEegAabhZbiJbgAP24AGptnkoI8egBiSW7MJ4LBfCgAOgVXm8KjEnK63LOACpblKAKIAeQAYj1NdBLWIdZhCBAODbmThgMBkGIGCYqebICoegBtW3fT3e31UpVwbA9AC6YY9Xp9fogEWwqtFGIAetwACTcY0YgCE3FoYdN2hMBsopCTEdTVK+ycjaYDzxBrab6bwseg9G7UfTgW69CrNbwzLCxC6oUez1e70+INh8MRyIHUHJlOp9DZHK5eBBMvoSpVIO1uqPIIt1sHjeHkLcd2gMAgNlNyGQcGQiQAsgimDoII0AflSKgcN0mApioghZKEADeUCKEBIEQAAXFAgGYMBoFQAAvu4r6sugMhBHgiRuFANFQAAalA4HPFBUAwd6cE8tRtEzugXRuAh5BcTRDFMZB3QAAb5ohDp8MCYQEVJMnAgASgR4lQAA-FAQm0TRYSMTYEEsTxc5aTpum6SyZEUYkyk8uEs6YAhWHmbRDEYTpdHEVKVnkdqryUZ5BlGdBsGCJxukmd0oksWxDqCPxoRBTFEmKSosnIPsMTIMACmIVFeVKZlykIrl6lmRZtF7NlJQebpXkvlKYRwComDtBALTgYkezBcxoXsfBZA6T1KVQHemmuTRwZ3vZ9qOs6HDxlAdVVb1YlQJJ0npXJs6FdtxWlWpE2VVNJn2SVMFLStU17LNDpiE6Lrxt5iotW1qGdYZdGxGIHBUbpI2GX1rFhRF3Fve1rqjcGcUcSDA1LeQzWte1n3AN1WRg3ps6hMj70usGvTxljUAXSUSMQ6hHDBgMxOJYJkU46NUXHSdwZnaTpVXTpKgQHqL0PCoYB+j09xC36YZhCY36Lj0Usyy8YYABJYD8fwmciVKZWcKuYGrxQa6gWvK6rj7toGPS60O5vPCbetm1SE61hQVsOxATsKMyuv2l0wB7BAeAQEbv6W1gPswf7gfB-oXtYDuVKh5g8e8LHmBrs2SIoon6cIpnKckaaNiQEbC4vG8Hza5QhfF8ApdLhXYbvjY1XoAn5CITpYvC8AWGw+FOlW1U6uzprKBYeCxwQOgKgk67KbDh749wJP0+z6b882yoS8rzPA9h2IvuR0HwC-tvger3vScUlSWE4N+6AgIkydOWvadwhnm5n1Pu+6dXx91+XMeUAJ7nxUG4AijVdhi2iGIWILcqSJC7iLUafcBJQA7rpJBPdpTi2AK-fWwBDZaywnwWI2B8Fu0XlAUhJwIAUI3v6QMJCyF0MvuHP2Adj6n2oSw1+ycsLBjprpXWOcNwomYbQkmf8S5PDLsuZAEjsDgMgVqMAHBW4QHgUkHSAAFZAfMxBwBMNFIG60m5aJJmjNaLEdG8VeLEZIH4tFZHptpXS5pFDRESHogxRjMD2XIiAOAfAoBowQgAMlCeBAW0Awh7HiZw6O-0qqJJPplFBoMdJ40htY7oMNQbw3iojcIlMOpdSPtHTGWTpb6JeEQ4EGSBq41KdTImrismlLRt9OIf0KlpIQqNXmepNJuJOk3L8P4-ziTiT1Pp3DtQfhri6KAqAR5G2BDSKS8tamELWVrAiNJxLOVGTRJuLIurQPsXgLR3VUm-iyDkFRb4PznMMskmiWjcmwCcTEKkJNXklEafFUIWjgxYKEeDFGVMvn5IGvZPuxTskfXAt036iQAVVMijUxc9T0mmNimFZpUKCZExJmTL5fciX42prTTiaDqkK12bxUeeKQpjStKzSqoK55tkYc8Jao0vQmGgBpSaFlxnfl-EKO8WFtSoD7NgV01sE7YCjmkigzkxWgvBctF8WLGW4q+aIvOnLxUfhznsPO3Vfl0PCNiupeyUDnVKpqhlOzDWjQPHqYCx5RUnW+TYL1R5rUaPstsnFjrkDOpgq6-V7rI1fKvN67kFUxkfiTcGixdqDWRujcAY5tE9XcXtUy9k6zWXA3PLhE8ro-VppsFWzAjwQ1-OzfG5l5a82xuLTmjtWsvkAPkd0OtlUm6Dori221GLdVuojX2hp+LujKupCOs1Nhl2TrDSW3FXaZ26QlZMxI4kAWyt5kXY+yzNn5W3ZGg5RyYkBotVa4aNqvnmJtSTcNDr50VvWsazcZLSoUsyfS-dLyuq+HUVSG5WbkIiPfrnT+UAhXQAIpjUi5F8CUTJvZf9KIHkPrOeyZNgUAavtGu+0Ns7v1lv7Z64jR5AMwWA3C9pZGNHBm9gfCOdzkACsXTw2hIyxUHqlcehj+o8CnsWRe10FAtk3p-QRCgRzlonPU2c8pH7OYxsI+mnUJH3lZQ0W+n5VG41ztowutlGbJNMcBQJvubGqo2s4-vQ+vH+NspoXGVdYHA0QbURomD2m4PuZ42q7hKH7LwfXHnRRqH0Nk01exqkbm35xc3F54GKHTX+YBYkSDwWbWbvQVAWLH9xGCbjGh3d10A0TLE7Zn10nz3rLkwp3tVnkDKZU48kiTdG3NpfSZijZm-nUdLSyr5jaa32ZY-FZzpyPzjpQIVoL0GSuwfK6rIeBtI0JcIkll1embCrb-CNhOY3m4fp0gChbHEOnEqhgJ2F8V4VhURZ0lFP0-oYs-Yp7rD3BBUshoTCFNFyVArgqDqmNN4xLbbZZ6bo0CF5doppt5RXNsaNK2FvWe2pvlqwtFnbmB2FzIUch5Awqjt1aLR81zg9MjDx-dl9aPmRUiY-I1qZ52sLSKpK6YM6BziOBwOcAAWktQQlJKCdfbd15Tqn6uTY9QJ87w7ucBbeVm5Le6Mc88lXz2R9cgGC+WSLsXEvJe9HOBsK45wZdy-k9errLLlf9alE3Ddl2wICcoxNizNGUcCeXf4nSUPHNhUR1+ondGw9uwzCurVTP168vdtWWI7OWK5dTf65bn5jdHuXa1pZHW3eK49313V-rMfoz1y6g3elAeh7ZR7dHjOOM8p7B7HP3Q8-qbr0bw94lS8+DPeX+Xlfkfls97X+tBXseaK26Fsny6qG5dqzp-Nzekch-LV85dMZfPa6XxtlfuPts9+HB2Le1XEv079+l5dd-+-U9p-ntdvOS9uzL7J6fOPXFefVXfzc7dbKDS-BBa-XbFnfbH9BLGLdPHsO-EnGnR-HfL3XYBZNrIXQ1JGeJSnIUQsR5KUZrOBXjXGQg3jIUY0Ug3YPDCAcg65SggghJSLP8GgQ0SgegrwTqGCF0ZgynKg9grhTg7gug5kAAESMQYEDiEN415xEKIIoENEkJIhbhymhGiEICkIRBREgkXEpyULYJUIFF4OgE0OAG0OAEIH-BMDwFeGMON2UJoIoD5AsOMy0J0NsR2WcMmVcI4KFCFE8KsJsMID2B9DiDgn8JDlMLcNFFCJqnCJwA4EkE3BeFiMrhmRUIlCSLsXCI9iyMCLEKFHrHyO8NsKsOKPiKCIoDzAqOsJ0NNEghqPCGoLqJIOZDCJ0IACFp5BA2ici3CyxGjwidExBIAhiOjSiKAKxPD7gpDhDajZjZQ+Bl4NVmRFjmpFAwBA5m5WD2jRCkkKA1jl5zh+jkArh+jnAFipDcVzQVA8AQBpjji0khR5Q7iWgWofhXiVDZQcAxgrg1RqBPDG0HiniXjFCXCViTiNQwTlRcJlijiVC1R1iNAESVQdi9inFDjhi6i0Tl4rhrjeJzgLj0Bbj3Q3ZkT8TZiRdExPDj9Agii8SZiTiRdSB0SGSqSGERxsAWQxA8AngaS2T3iKARdhROTl5uSSImTsBqjWS3ipVxT0AcwpS4AZSpQ5T3ZWjFSVCRd8x1TNTdhtT+iYi9S3CJSjTNjZSk9AgJipiLS6iRdaBrTGS3Y5UFUXQ-jLTQSeSM8ZCTA5DcBqToSAjYSxTgwRcbStS7T5Swy4iUTfTSBaB0BjSvBtTC4vR0AWSOCTCkznT0BSBSB0z8g4yIBAzgzk8fTCzSAizuSgA
type LowercaseAlpha = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';
type UppercaseAlpha = Capitalize<LowercaseAlpha>;
type Alphabet = LowercaseAlpha | UppercaseAlpha;
type Numbers = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
type Symbols = '-' | '_'
type Identifiers = Alphabet | Number | Symbols;
type Whitespace = ' ' | '\n' | '\t' | '\f' | '\n';
type Quotes = "'" | '"';
type Traversal = ',' | '>' | '<' | '~' | '+';
type Id = '#';
type Class = '.';
type Universal = '*'
type EOF = '';
type Finished = '';
type AttributeOpen = '[';
type AttributeClose = ']';
type AttributeCases = '~' | '^' | '$' | '*' | '!' | '|';
type Equal = '=';
type Attributes = AttributeOpen | AttributeClose | AttributeCases | Equal;
type Chars = Identifiers | Whitespace | Quotes | Traversal | Id | Class | Universal | EOF | Attributes;
type LexError<Message extends string> = { message: Message };
type TakeAll<
V extends string,
Chars
> =
V extends `${infer C}${infer R}` ?
C extends Chars ?
TakeAll<R, Chars> :
V :
V;
type TakeUntil<
V extends string,
Chars extends string
> =
V extends `${infer Start}${Chars}${infer Rest}` ?
Start :
V;
type ConsumeNext<S extends string> =
S extends EOF ?
[EOF, Finished] :
S extends `${infer Char}${infer Rest}` ?
[Char, Rest] :
[S, Finished];
type ConsumeNextValid<
S extends string,
Consumed extends [string, string] = ConsumeNext<S>,
Char = Consumed[0],
Rest = Consumed[1],
> =
Char extends Chars ?
[Char, Rest] :
never;
type Input = 'Input';
type Current = 'Current';
type HasAlphabetCharacter = 'HasAlphabetCharacter';
type HasAttributeOpen = 'HasAttributeOpen';
type HasAttributeEqual = 'HasAttributeEqual';
type HasFirstSelector = 'HasFirstSelector';
type HasQuote = 'HasQuote';
type HasWhitespace = 'HasWhitespace';
type ExpectIdentifier = 'ExpectIdentifier';
type LexState = {
Input: string,
HasAlphabetCharacter: boolean,
HasAttributeEqual: boolean,
HasAttributeOpen: boolean,
HasFirstSelector: boolean,
HasQuote: Array<Quotes>,
HasWhitespace: boolean,
ExpectIdentifier: boolean
}
type InitialState<Input extends string> = {
Input: Input,
HasAlphabetCharacter: false,
HasAttributeEqual: false,
HasAttributeOpen: false,
HasFirstSelector: false,
HasQuote: [],
HasWhitespace: false,
ExpectIdentifier: false
}
type UpdateState<
Previous extends LexState,
Next extends Partial<LexState>
> =
Omit<Previous, keyof Next> & Next;
type CSSSelector<
Selector extends string,
Consumed extends [string, string] = ConsumeNext<Selector>,
CurrentCharacter extends string = Consumed[0]
> =
ConsumeNextValid<Selector> extends never ?
LexError<`CSS Selector: Unexpected character "${CurrentCharacter}"`> :
LexText<InitialState<Selector>>;
type LexText<
State extends LexState,
Text extends string = State[Input],
Consumed extends [string, string] = ConsumeNextValid<Text>,
CurrentCharacter extends string = Consumed[0],
Rest extends string = Consumed[1],
> =
CurrentCharacter extends EOF ?
State[HasAttributeOpen] extends true ?
LexError<'EOF: Unclosed Attribute selector'> :
State[Input] :
CurrentCharacter extends Whitespace ?
LexWhiteSpace<State, CurrentCharacter, Rest> :
CurrentCharacter extends Traversal ?
LexTraversal<State, CurrentCharacter, Rest> :
CurrentCharacter extends Universal ?
LexUniversal<State, CurrentCharacter, Rest> :
CurrentCharacter extends Class | Id ?
LexClassId<State, CurrentCharacter, Rest> :
CurrentCharacter extends Identifiers ?
LexIdentifier<State, Text> :
CurrentCharacter extends Attributes ?
LexAttribute<State, CurrentCharacter, Rest> :
LexError<`Text: Unexpected "${CurrentCharacter}"`>;
type LexWhiteSpace<
State extends LexState,
CurrentCharacter extends Whitespace,
Rest extends string,
> =
LexText<UpdateState<State, { HasWhitespace: true }>, TakeAll<Rest, Whitespace>>;
type LexTraversal<
State extends LexState,
CurrentCharacter extends Traversal,
Rest extends string,
> =
State[HasFirstSelector] extends false ?
LexError<`Traversal: Unexpected '${CurrentCharacter}'`> :
LexText<State, Rest>;
type LexUniversal<
State extends LexState,
CurrentCharacter extends Universal,
Rest extends string
> =
State[HasFirstSelector] extends false ?
LexText<UpdateState<State, { HasFirstSelector: true, HasWhitespace: false }>, Rest> :
State[HasWhitespace] extends true ?
LexText<UpdateState<State, { HasWhitespace: false }>, Rest> :
LexError<`Universal: Unexpected '${CurrentCharacter}''`>;
type LexClassId<
State extends LexState,
CurrentCharacter extends Class | Id,
Rest extends string
> =
LexIdentifier<UpdateState<State, { HasAlphabetCharacter: false }>, Rest>;
type LexIdentifier<
State extends LexState,
Text extends string,
Consumed extends [string, string] = ConsumeNextValid<Text>,
CurrentCharacter extends string = Consumed[0],
Rest extends string = Consumed[1]
> =
CurrentCharacter extends Alphabet ?
LexText<UpdateState<State, { HasAlphabetCharacter: true, HasFirstSelector: true }>, Rest> :
State[HasAlphabetCharacter] extends false ?
LexError<`Identifier: Expected [a-zA-Z] got '${CurrentCharacter}'`> :
CurrentCharacter extends Identifiers ?
LexText<State, Rest> :
LexError<`Identifier: Expected [a-zA-Z0-9_-] got '${CurrentCharacter}'`>;
type LexAttribute<
State extends LexState,
CurrentCharacter extends Attributes,
Rest extends string
> =
CurrentCharacter extends AttributeCases ?
State[HasAttributeEqual] extends true ?
LexError<`Attribute: Unexpected '${CurrentCharacter}'`> :
LexText<State, Rest> :
CurrentCharacter extends Equal ?
State[HasAttributeEqual] extends true ?
LexError<`Attribute: Unexpected '${CurrentCharacter}'`> :
LexText<UpdateState<State, { HasAttributeEqual: true }>, Rest> :
CurrentCharacter extends AttributeClose ?
LexText<UpdateState<State, { HasAttributeOpen: false }>, Rest> :
State[HasAttributeOpen] extends true ?
LexError<`Attribute: Unexpected '${CurrentCharacter}'`> :
LexIdentifier<UpdateState<State, { HasAlphabetCharacter: false, HasAttributeOpen: true }>, Rest>;
type UnexpectedCharacter = CSSSelector<'$'>;
type UniversalSelector = CSSSelector<'*'>;
type WhitespaceUniversalSelector = CSSSelector<' * '>;
type NestedUniversalSelector = CSSSelector<'* *'>;
type DoubleUniversalSelectorError = CSSSelector<'**'>;
type StartWithDescendentSelectorError = CSSSelector<'>'>;
type StartWithMultiSelectorError = CSSSelector<','>;
type StartWithParentSelectorError = CSSSelector<'<'>;
type StartWithSiblingSelectorError = CSSSelector<'~'>;
type StartWithAdjacentSelectorError = CSSSelector<'+'>;
type StartWithEqualSelectorError = CSSSelector<'='>;
type StartWithStartSelectorError = CSSSelector<'^'>;
type StartWithEndSelectorError = CSSSelector<'$'>;
type StartWithBangSelectorError = CSSSelector<'!'>;
type StartWithPipeSelectorError = CSSSelector<'|'>;
type IDSelector = CSSSelector<'#foo'>;
type IDComplexSelector = CSSSelector<'#foo-Bar_Baz'>;
type IDCharacterOnlySelectorError = CSSSelector<'#'>;
type IDNonAlphaSelectorError = CSSSelector<'#A3_.a'>;
type ClassCharacterOnlySelectorError = CSSSelector<'.'>;
type ClassSelector = CSSSelector<'.foo'>;
type ClassComplexSelector = CSSSelector<'.foo__Bar--Baz'>;
type AttributeSelector = CSSSelector<'[a]'>;
type AttributeCaseEqualSelector = CSSSelector<'[a=foo]'>;
type AttributeCaseTildeSelector = CSSSelector<'[a~=foo]'>;
type AttributeCaseStartSelector = CSSSelector<'[a^=foo]'>;
type AttributeCaseEndSelector = CSSSelector<'[a$=foo]'>;
type AttributeCaseBangSelector = CSSSelector<'[a~=foo]'>;
type AttributeCasePipeSelector = CSSSelector<'[a|=foo]'>;
type AttributeUnclosedSelectorError = CSSSelector<'[a'>;
type AttributeDoubleAttributeSelectorError = CSSSelector<'[[a'>;
type AttributeCaseSelectorError = CSSSelector<'[a=|a]'>;
type AttributeCaseExtraEqualSelectorError = CSSSelector<'[a==]'>;
type AttributeCaseDoubleCaseSelectorError = CSSSelector<'[a=a=]'>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment