语法文件支持 & 解析器/PSI 生成,适用于 IntelliJ IDEA
/statusIcon.svg?guest=1) [][jb:x] [][jb:slack]
An IntelliJ IDEA plugin for language plugin developers.
Adds BNF Grammars and JFlex file editing support, and a parser/PSI code generator.
Quick links: Latest dev build, Changelog, Tutorial, How-to
[!IMPORTANT]
Since 2022.3, Grammar-Kit plugin requires Java 17.
Open-source plugins built with Grammar-Kit:
See also Custom Language Support Tutorial.
For Kotlin Multiplatform parsers using the syntax-api, set generate=[parser-api="syntax"] in the grammar header.
The parser will be generated as a Kotlin object. PSI classes are still generated as Java.
See How-to for details.
Invoking the parser generator from the IDE as described above is the preferred way. For build-time generation, use the gradle-grammar-kit-plugin, keeping the following limitations in mind:
mixin and psiImplUtilClass attributes are not supported. These attributes reference user-written classes that typically extend or implement the generated PSI interfaces, so compiling them requires the generator's output — while the generator, in turn, needs them to produce correctly-typed code. Resolving this cycle would require a two-pass build, which the Gradle plugin does not implement. The in-IDE generator avoids the problem because it relies on IntelliJ Java support which supports incomplete code.If your grammar relies on mixin or psiImplUtilClass, generate the parser and PSI from the IDE and commit the generated sources instead of running the generator from Gradle.
See Parsing Expression Grammar (PEG) for basic syntax. Use ::= for ← symbol. You can also use [ .. ] for optional sequences and { | | } for choices as these variants are popular in real-world grammars. Grammar-Kit source code is the main example of Grammar-Kit application. The grammar for BNF parser and PSI generation can be found here.
Here's how it may look like:
…`
The basic syntax is extended with global attributes and rule attributes. Attributes are specified by the list of name=value pairs enclosed in braces. Rule attributes are placed right after the rule definition. Global attributes are placed on top or separated from a rule definition with a semicolon.
Generator generates a static method for each BNF expression as follows:
static boolean rule_name(..) // rule top level expression
static boolean rule_name_0(..) // rule sub-expression
... // ...
static boolean rule_name_N1_N2_..._NX // rule sub-sub-...-sub-expressionNaming a rule like rule_name_N1_N2_..._NX shall be avoided.
With generate=[parser-api="syntax"], the generator produces a Kotlin object instead of a Java class.
Each rule becomes a function taking SyntaxGeneratedParserRuntime instead of PsiBuilder:
fun rule_name(runtime_: SyntaxGeneratedParserRuntime, level_: Int): BooleanOne can specify an attribute for several rules at once in a global attributes block:
{
extends(".*_expr")=expr // applies to all .*_expr rules
pin(".*_list(?:_\d+)*")=1 // applies to all .*_list rules and their sub-expressions
}private (PSI tree): skip node creation and let its child nodes be included in its parent.
left (PSI tree): take an AST node on the left (previous sibling) and enclose it by becoming its parent.
inner (PSI tree): take an AST node on the left (previous sibling) and inject itself into it by becoming its child.
upper (PSI tree): take the parent node and replace it by adopting all its children.
meta (parser): a parametrized rule; its parse function can take other parse functions as parameters.
external (parser): a rule with a hand-written parse function; no parsing code is generated.
fake (PSI classes): a rule for shaping the generated PSI classes; only PSI classes are generated.
Modifiers can be combined, inner should only be used together with left, private left is equivalent to private left inner, fake should not be combined with private.
By default, rules are public, i.e. non-private, non-fake, etc.
The external expression > is simply an inline variant of an external rule. It can also be used to specify a meta rule along with arguments.
For example:
meta comma_separated_list ::= <> ( ',' <> ) *
option_list ::= >External rule expression syntax is the same as a body of external expression:
external manually_parsed_rule ::= methodName param1 param2 ...External expressions and external rules interpret double- and single-quoted strings differently. Generally, anything that appears in an external expression after rule or method name is treated as a parameter and passed "as is" except single-quoted strings which are unquoted first. This helps to pass qualified enum constants, java expressions, etc.
Rule references in parameter list are implemented as GeneratedParserUtilBase.Parser instances.
Explicit tokens are declared via tokens global attribute, e.g. in token_name=token_value form. A token name is for the IElementType token constant, a token value is usually its string representation in single or double-quotes.
Tokens in grammar can be referenced by name or by value in single or double-quotes. It is recommended to use values where possible for better readability. Names can be used to resolve conflicts when there is an unquoted token value that also matches some rule.
Implicit tokens are tokens not specified via tokens attribute. Unquoted implicit tokens (aka keyword tokens) have names equals to their values. Quoted implicit tokens (aka text-matched tokens) are slow because they are matched by text and not by an IElementType constant returned by a lexer. Text-matched tokens can span more than one real token returned by the lexer.
Rules, tokens, and text-matched tokens have different colors.
pin (value: a number or pattern) tunes the parser to handle incomplete matches. A sequence matches if its prefix up to a pinned item matches. On successfully reaching the pinned item the parser tries to match the rest items whether they match or not. Pin value indicates the desired item by either a number {pin=2} or pattern {pin="rule_B"}. By default, the pin is applied to the top sequence expression. Sub-expressions can be included using a target pattern: {pin(".*")=1} applies to all sub-sequences.
recoverWhile (value: predicate rule) matches any number of tokens after the rule matching completes with any result. This attribute helps the parser recover when an unmatched token sequence is encountered. See HOWTO section for more.
name (value: string) specifies a name for a rule to be used in error reports. For example, *name("_.expr")=expression changes expression error messages to "<expression> required" instead of a long list of tokens.
The generator can split parser code into several classes for better support of large grammars.
In simple cases, a parser will consist just of several generated classes.
The actual error recovery and reporting code as well as the parser-based completion provider support code and the basic token matching code reside in a parserUtilClass class. It may be altered by specifying some other class that extends or mimics the original GeneratedParserUtilBase. There's no need to keep a copy of GeneratedParserUtilBase in a project, it is included in IntelliJ Platform since version 12.1.
When using parser-api="syntax", the runtime is SyntaxGeneratedParserRuntime (from com.intellij.platform.syntax.util.runtime),
specified via syntaxParserUtilObject attribute. It replaces GeneratedParserUtilBase and uses SyntaxTreeBuilder instead of PsiBuilder.
The manual parsing code, i.e. external rules must be implemented the same way as generated, by a static method in the parserUtilClass class or any other class that will be imported via parserImports attribute like this:
{
parserImports=["static org.sample.ManualParsing.*"]
}IElementType constants generated by the parser generator have to be recognized and returned by the lexer. The JFlex-based lexer can be generated fr
暂无开放 Issues,或尚未同步最近议题。