Skip to content

Instantly share code, notes, and snippets.

@Gerrit0
Last active February 14, 2020 18:59
Show Gist options
  • Save Gerrit0/bd8607759adcc2fdd9a5feba5c14e4e8 to your computer and use it in GitHub Desktop.
Save Gerrit0/bd8607759adcc2fdd9a5feba5c14e4e8 to your computer and use it in GitHub Desktop.
TypeScript type grammar - paste this into https://omrelli.ug/nearley-playground/
@{%
const nth = n => d => d[n]
const intrinsic = d => ({ type: 'intrinsic', value: d[0] })
%}
# I skipped function types and object types, running low on time.
Main -> _ Type0 _ {% d => JSON.stringify(d[1]) %}
# Handle precedence, lowest first
Type0 -> ConditionalType {% id %}
| UnionType {% id %}
| Type1 {% id %}
Type1 -> IntersectionType {% id %}
| Type2 {% id %}
Type2 ->
ArrayType {% id %}
| IndexedAccessType {% id %}
| InferredType {% id %}
| IntrinsicType {% id %}
| MappedType {% id %}
| ReferenceType {% id %}
| TupleType {% id %}
| TypeOperatorType {% id %}
| TypeQueryType {% id %}
| "(" _ Type0 _ ")" {% nth(2) %}
TypeList -> Type0
| Type0 _ "," _ TypeList {% d => [d[0]].concat(d[4]) %}
ArrayType -> Type1 _ "[" _ "]" {% d => ({ type: 'array', valueType: d[0] }) %}
ConditionalType -> Type1 SP "extends" SP Type0 _ "?" _ Type0 _ ":" _ Type0 {% d => ({
type: 'conditional', checkType: d[0], extendsType: d[4], trueType: d[8], falseType: d[12]
}) %}
IndexedAccessType -> Type1 _ "[" Type0 _ "]" {% d => ({ type: 'indexed-access', objectType: d[0], accessType: d[3] }) %}
InferredType -> "infer" SP TypeName {% d => ({ type: 'infer', name: d[2] }) %}
IntersectionType -> Type1 _ "&" _ Type2 {% d => ({ type: 'intersection', left: d[0], right: d[4] }) %}
IntrinsicType -> "any" {% intrinsic %}
| "unknown" {% intrinsic %}
| "never" {% intrinsic %}
| "number" {% intrinsic %}
| "boolean" {% intrinsic %}
| "true" {% intrinsic %}
| "false" {% intrinsic %}
| "bigint" {% intrinsic %}
| "symbol" {% intrinsic %}
MappedType -> "{" (_ ReadonlyModifier):? _ "[" _ TypeName _ "in" _ Type0 _ "]" (_ QuestionModifier):? _ ":" _ Type0 _ "}"
{% d => ({
type: 'mapped',
readonlyModifier: d[1] ? d[1][1] : null,
questionModifier: d[12] ? d[12][1] : null,
keyName: d[5],
keyType: d[9],
valueType: d[16]
})%}
ReferenceType ->
TypeName {% d => ({ type: 'reference', target: d[0] }) %}
| TypeName _ "<" TypeList _ ">" {% d => ({ type: 'reference', target: d[0], args: d[3] }) %}
TupleType -> "[" _ TypeList _ "]" {% d => ({ type: 'tuple', items: d[2] }) %}
TypeOperatorType -> ("readonly" | "keyof") SP Type1 {% d => ({ type: 'operator', operator: d[0][0], type: d[2] }) %}
TypeQueryType -> "typeof" SP Identifier {% d => ({ type: 'query', name: d[2] }) %}
UnionType -> Type0 _ "|" _ Type1 {% d => ({ type: 'union', left: d[0], right: d[4] }) %}
ReadonlyModifier -> [+-]:? _ "readonly" {% d => d[0] != "-" %}
QuestionModifier -> [+-]:? _ "?" {% d => d[0] != "-" %}
TypeName -> Identifier {% id %}
# This is wrong, identifiers can start with lower case... but because
# I don't have a scanner here, avoid the ambiguity with IntrinsicType by pretending
# they all start with an uppercase letter.
Identifier -> [A-Z] [A-Za-z0-9]:* {% d => d[0] + d[1].join('') %}
SP -> [\s]:+ {% () => null %}
_ -> [\s]:* {% () => null %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment