Quick start on ANTLR4 in Rust - part1

This is my note in adopting and learning ANTLR4Rust

Series


Install nightly version of Rust (and make it default if you want for convenience).

1
2
$ rustup toolchain install nightly
$ rustup default nightly

Get ANTLR4 runtime for Rust from here:

Prepare a grammar. I will use an example grammar: https://raw.githubusercontent.com/antlr/grammars-v4/master/csv/CSV.g4

Generate a parser:

1
$ java -jar <ANTLR4 runtime path> -Dlanguage=Rust CSV.g4

You will get csvlexer.rs, csvlistener.rs , csvparser.rs . Place them into your project src directory.

Add dependencies in Cargo.toml:

1
2
3
[dependencies]
lazy_static = "1.4"
antlr-rust = "0.1"

Add a feature and import lazy_static macros to the root module:

1
2
3
#![feature(try_blocks)]
#[macro_use]
extern crates lazy_static;

Import common and essential things:

1
2
3
4
use antlr_rust::common_token_stream::CommonTokenStream;
use antlr_rust::input_stream::InputStream;
use antlr_rust::parser_rule_context::ParserRuleContext;
use antlr_rust::tree::{ErrorNode, ParseTreeListener, TerminalNode};

Import grammar-specific things:

1
2
3
4
5
6
mod csvlexer;
mod csvlistener;
mod csvparser;
use csvlexer::CSVLexer;
use csvlistener::CSVListener;
use csvparser::*;

Implement ParseTreeListener, a supertraint of CSVListener:

1
2
3
4
5
6
struct Listener;impl ParseTreeListener for Listener {
fn visit_terminal(&mut self, node: &TerminalNode) {}
fn visit_error_node(&mut self, node: &ErrorNode) {}
fn enter_every_rule(&mut self, ctx: &dyn ParserRuleContext) {}
fn exit_every_rule(&mut self, ctx: &dyn ParserRuleContext) {}
}

Implement CSVListener:

1
2
3
4
5
6
7
8
9
10
impl CSVListener for Listener {
fn enter_csvFile(&mut self, _ctx: &CsvFileContext) {}
fn exit_csvFile(&mut self, _ctx: &CsvFileContext) {}
fn enter_hdr(&mut self, _ctx: &HdrContext) {}
fn exit_hdr(&mut self, _ctx: &HdrContext) {}
fn enter_row(&mut self, _ctx: &RowContext) {}
fn exit_row(&mut self, _ctx: &RowContext) {}
fn enter_field(&mut self, _ctx: &FieldContext) {}
fn exit_field(&mut self, _ctx: &FieldContext) {}
}

Read and parse an input. Note that csvFile in the last line is a rule name in CSV.g4:

1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
let input = String::from(
"This, is, a, header
This, is, a, row
");
let lexer = CSVLexer::new(Box::new(InputStream::new(input)));
let token_source = CommonTokenStream::new(lexer);
let mut parser = CSVParser::new(Box::new(token_source));
parser.add_parse_listener(Box::new(Listener {}));
let result = parser.csvFile();
assert!(result.is_ok());
}

The minimal working example can be found here.