Zephyr

CodeMirror 6 meets ANTLR

👋 Hello there!

Zephyr is a tiny language written in an ANTLR grammar with a very thin layer on top of what you get out of the box from ANTLR (basically, the bare minimum). The goal of this little language is to be used as an example for connecting CodeMirror 6 to an ANTLR style grammar.

CodeMirror 6 was built around Lezer, its own parser system. Lezer has support for common languages, and has a grammar syntax if you want to use Lezer. But, what if you've already written a grammar and parser, and don't want to rewrite it for CodeMirror 6 compatibility?

This demo shows how to do just that–view the source on GitHub to see how it works.

Why “Zephyr”? I asked ChatGPT to make up a name for this little language and it said:

How about “Zephyr”? It suggests a fresh, light, and airy language that can help developers build software quickly and easily. It also sounds distinct and memorable, which can help it stand out in a crowded programming language landscape.

Editor

Token Preview

This table includes a live preview of all of the tokens in the editor. Change the editor to see the tokens change!

/* Welcome to Zephyr! This is a little toy language to show how to connect an ANTLR language server to CodeMirror 6. • It supports variable assignment & comments. • Semicolons are required at the end of statements. • Only numbers and strings can be assigned to variables. Here's a block comment! */
  • typeIndex: 5
  • typeName: blockComment
  • startIndex: 0
  • stopIndex: 314
// It also supports line comments, if you prefer those.
  • typeIndex: 6
  • typeName: lineComment
  • startIndex: 316
  • stopIndex: 370
// You can do variable assignments with `const`:
  • typeIndex: 6
  • typeName: lineComment
  • startIndex: 373
  • stopIndex: 420
const
  • typeIndex: 1
  • typeName: const
  • startIndex: 422
  • stopIndex: 426
myFirstVariable
  • typeIndex: 9
  • typeName: identifier
  • startIndex: 428
  • stopIndex: 442
=
  • typeIndex: 3
  • typeName: assign
  • startIndex: 444
  • stopIndex: 444
1000
  • typeIndex: 7
  • typeName: number
  • startIndex: 446
  • stopIndex: 449
;
  • typeIndex: 4
  • typeName: semicolon
  • startIndex: 450
  • stopIndex: 450
// And `let` works for variable assignments, too:
  • typeIndex: 6
  • typeName: lineComment
  • startIndex: 453
  • stopIndex: 501
let
  • typeIndex: 2
  • typeName: let
  • startIndex: 503
  • stopIndex: 505
anotherVariable
  • typeIndex: 9
  • typeName: identifier
  • startIndex: 507
  • stopIndex: 521
=
  • typeIndex: 3
  • typeName: assign
  • startIndex: 523
  • stopIndex: 523
'This is another variable!'
  • typeIndex: 8
  • typeName: string
  • startIndex: 525
  • stopIndex: 551
;
  • typeIndex: 4
  • typeName: semicolon
  • startIndex: 552
  • stopIndex: 552
// That's it. Edit this code to try it out!
  • typeIndex: 6
  • typeName: lineComment
  • startIndex: 555
  • stopIndex: 597
<EOF>
  • typeIndex: -1
  • typeName: unknown
  • startIndex: 598
  • stopIndex: 597

Grammar

This is the ANTLR grammar for Zephyr–it's used to generate the langauge server.

Lexer

lexer grammar ZephyrLexer;

CONST : 'const' ;
LET : 'let' ;

ASSIGN: '=' ;
SEMICOLON: ';' ;

BLOCK_COMMENT: '/*' .*? '*/' -> channel(HIDDEN);
LINE_COMMENT: '//' ~[\r\n\u2028\u2029]* -> channel(HIDDEN);

NUMBER : [0-9]+ ;
STRING: '\'' .*? '\'';
IDENTIFIER: [a-zA-Z]+ ;
WHITESPACE: [ \t\n\r\f]+ -> skip ;

Parser

parser grammar ZephyrParser;

program : statement* ;

statement : keyword identifier assign expression terminator ;

expression : NUMBER | IDENTIFIER | STRING;

keyword: CONST | LET ;

identifier: IDENTIFIER;
assign: ASSIGN;
terminator: SEMICOLON;
Made with ❤️ by Trevor Harmon.