# ANTLR4 Runtime for Dart

From version 4.9 onwards antlr's dart generated code is null sound safety compatible and sets the minimum dart sdk version to 2.12.0.

### First steps

#### 1. Install ANTLR4

[The getting started guide](https://github.com/antlr/antlr4/blob/master/doc/getting-started.md) 
should get you started.

#### 2. Install the Dart ANTLR runtime

Each target language for ANTLR has a runtime package for running parser 
generated by ANTLR4. The runtime provides a common set of tools for using your parser.

Install the runtime with the same version as the main ANTLR tool:

Add this to your package's pubspec.yaml file:
```yaml
...
dependencies:
  antlr4: <ANTLR version>
...
```

#### 3. Generate your parser

You use the ANTLR4 "tool" to generate a parser. These will reference the ANTLR 
runtime, installed above.

Suppose you're using a UNIX system and have set up an alias for the ANTLR4 tool 
as described in [the getting started guide](https://github.com/antlr/antlr4/blob/master/doc/getting-started.md). 
To generate your Dart parser, run the following command:

```shell script
antlr4 -Dlanguage=Dart MyGrammar.g4
```

For a full list of antlr4 tool options, please visit the 
[tool documentation page](https://github.com/antlr/antlr4/blob/master/doc/tool-options.md).

### Complete example

Suppose you're using the JSON grammar from https://github.com/antlr/grammars-v4/tree/master/json.

Then, invoke `antlr4 -Dlanguage=Dart JSON.g4`. The result of this is a 
collection of `.dart` including:

* JsonLexer.dart
* JsonParser.dart
* JsonBaseListener.dart
* JsonListener.dart (if you have not activated the -no-listener option)
* JsonVisitor.dart (if you have activated the -visitor option)

We'll write a small main func to call the generated parser/lexer 
(assuming they are separate). This one writes out the encountered 
`ParseTreeContext`'s:

```dart
import 'package:antlr4/antlr4.dart';
import 'package:my_project/JSONParser.dart';
import 'package:my_project/JSONLexer.dart';

class TreeShapeListener implements ParseTreeListener {
  @override
  void enterEveryRule(ParserRuleContext ctx) {
    print(ctx.text);
  }

  @override
  void exitEveryRule(ParserRuleContext node) {
  }

  @override
  void visitErrorNode(ErrorNode node) {
  }

  @override
  void visitTerminal(TerminalNode node) {
  }
}

void main(List<String> args) async {
  JSONLexer.checkVersion();
  JSONParser.checkVersion();
  final input = await InputStream.fromPath(args[0]);
  final lexer = JSONLexer(input);
  final tokens = CommonTokenStream(lexer);
  final parser = JSONParser(tokens);
  parser.addErrorListener(DiagnosticErrorListener());
  final tree = parser.json();
  ParseTreeWalker.DEFAULT.walk(TreeShapeListener(), tree);
}
```

Create a `example.json` file:
```json
{"a":1}
```

Parse the input file:

```shell script
dart bin/main.dart example.json
```

The expected output is:

```
{"a":1}
{"a":1}
{"a":1}
"a":1
1
```

### Debug

We have some logs in place that can ease the debugging process, in order to turn these logs on you can enable the following environment declarations:

- ANTLR_LEXER_DEBUG
- ANTLR_LEXER_DFA_DEBUG
- ANTLR_PARSER_DEBUG
- ANTLR_PARSER_LIST_ATN_DECISIONS_DEBUG
- ANTLR_PARSER_DFA_DEBUG
- ANTLR_PARSER_RETRY_DEBUG

If you're using flutter, you can define these variables by adding an `--dart-define` arguments, eg. `flutter run --dart-define LEXER_DEBUG=false`
