目录

我的学习分享

记录精彩的程序人生

X

NetBeans Parsing API

Parsing API defines contract between parsers registerred for diferent languages and the rest of IDE. It is language neutral and it supports language embeddings.

The basic Parsing API constructs are Source, Snapshot and Embedding. Source identificates some concrete file or document. There is always at most one Source for one FileObject. Snapshot represents some concrete content of Source, and it is immutable. And Embedding represents some part of Snapshot written in different language. Embedding can contain some virtual code that is not contained in outside language too. Content of Embedding is represented by Snapshot and it can contain another Embeddings, so embeddings are recursive.

Parser is represented by abstract class called Parser. The only way how to register a new parser for some concrete mime type is to implement ParserFactory, and register it in your manifest file in folder called "Editors/" + mimeType. A new instance of Parser is always created for some concrete Snapshot, or collection of Snapshots. One instance of parser can be reused for more Snapshots created from the same Source. Result of parsing (AST, syntax errors, semantic information) is represented by Parser.Result class. Parser creates a new instance of Parser.Result for each Task. Following example shows how to integrate parser to the NetBeans using Parsing API:

      class FooParserFactory extends ParserFactory {
      
          public Parser createParser (
              Collection<Snapshot> snapshots
          ) {
              return new FooParser ();
          }
          
          private static class FooParser extends Parser {
          
              private boolean         cancelled = false;
              private AST             ast;
              
              public void parse (
                  Snapshot            snapshot, 
                  Task                task, 
                  SchedulerEvent      event
              ) throws ParseException {
                  cancelled = false;
                  for (...) {
                      // parsing snapshot.getText ();
                      if (cancelled) return;
                  }
                  ast = ...;
              }
              
              public abstract Result getResult (
                  Task                task,
                  SchedulerEvent      event
              ) throws ParseException {
                  return new FooResult (ast);
              }

              public void cancel () {
                  cancelled = true;
              }

              public void addChangeListener (
                  ChangeListener      changeListener
              ) {
              }

              public void removeChangeListener (
                  ChangeListener      changeListener
              ) {
              }
          }
          
          public static class FooResult extends Result {
            
              private AST           ast;
              private boolean       valid = true;
              
              FooResult (
                  AST               ast
              ) {
                  this.ast = ast;
              }
              
              public AST getAST () {
                  if (!valid) throw new InvalidResultException ();
                  return ast;
              }
              
              public void invalidate () {
                  valid = false;
                  ast = null;
              }
          }
      }

Parsing API defines two basic kinds of Tasks. High priority UserTasks and SchedulerTasks.

Execution of UserTask is synchnonous and it stops execution of all other tasks. There are two types of UserTask. Simple UserTask that supports some computations based on one block code written in one language. And MultiLanguageUserTask (???) that supports scanning of all blocks of code written in different languages embedded in one Source.

User of Parsing API can register various implementations of SchedulerTask for any language. Each SchedulerTask is registered for some specific Scheduler. Scheduler defines when task shoud be started (for example when current editor is changed, when some nodes are selected in Project View, or when cursor position is changed).

There are two specific tasks that can define language embedding. So task implementator can recognize blocks of embedded languages. Parsing API contains support for high priority ParsingAPI

Use Cases

  1. Parsing API Client can always ask for parser result.
    This direct call has top level priority (stops all other requests).
    Its designed to support direct user to IDE interaction
    (like request for code completion) that have to be as
    fast as possible. Such type of task is called UserTask.
    This approach is used not only for parsing of file currently
    edited in Editor, but it supports Refactoring too.
  2. Parsing API Implementation listens on various changes in IDE
    (typing in the current document, cursor movements, switching
    of editor tabs, changes of classpath). These changes affects parse
    tree of the current document, and various visualisation features
    based on parser results (text coloring, hints, error stripe).
    Parsing API Client can register some tasks that should run when
    such change is done (Task). Implemementation of Parsing API than
    calls all such registerred tasks. These calls has lower priority than
    UserTasks, and they should not block user interaction with IDE
    (typing in editor).
  3. Parsing API defines how to recognize embedded languages. It should
    be possible to create snapshot of some file, call some recognizers that
    finds blocks of embedded languages and run different parsers for embedded blocks.
    It should be able to use the same parser for top level language and any
    embedded block of code written in the same language. Embedding recognizer
    can add some virtual code (code that does not exists in top level source)
    to the embedded block. There should be some support for translation
    of offsets between top level source and virtual source generated
    for embedded piece of code.