Skip to content

RubenHunter/pitscript-lsp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PitScript Language - VS Code Extension

Author: Ruben Moons

A custom scripting language inspired by Formula 1 terminology with full VS Code Language Server integration. This project demonstrates building a complete compiler with lexer, parser, semantic analysis, and IDE features (diagnostics, code completion) using ANTLR4.


📋 Table of Contents

  1. Project Overview
  2. PitScript Language Syntax
  3. Grammar (EBNF)
  4. Diagnostics
  5. Code Completion
  6. Build & Run Instructions
  7. Testing
  8. SCREENSHOTS

Project Overview

Project Structure

ruben.moons.1/
├─ package.json                  # Root workspace config (npm workspaces, shared scripts)
├─ tsconfig.json                 # Root TypeScript config (shared settings)
├─ README.md                     # This file (full documentation)
│
├─ ts-template-package/          # COMPILER: Parser and semantic analyzer
│  ├─ src/ts/
│  │  ├─ gen/                    # ANTLR4-generated files (lexer, parser)
│  │  │  ├─ PitScriptLexer.ts
│  │  │  ├─ PitScriptParser.ts
│  │  │  └─ ...
│  │  ├─ CustomVisitor.ts        # Semantic analysis (type checking, unused variables)
│  │  └─ index.ts                # Main export: parseSourceCode()
│  ├─ test/
│  │  └─ index.test.ts           # Unit & E2E tests
│  ├─ tools/
│  │  └─ antlr-4.13.1-complete.jar  # ANTLR parser generator
│  ├─ PitScript.g4               # ANTLR4 grammar definition
│  ├─ jest.config.js             # Jest test framework configuration
│  ├─ tsconfig.json              # TypeScript compiler settings
│  └─ package.json               # Package dependencies and scripts
│
└─ lsp-sample/                   # EXTENSION: VS Code Language Server
   ├─ client/                    # VS Code extension entry point
   ├─ server/                    # Language server (diagnostics, completion)
   ├─ syntaxes/                  # TextMate syntax highlighting
   ├─ themes/                    # Color themes
   ├─ test/                      # E2E test files
   ├─ language-configuration.json  # Bracket pairs, comments, auto-close
   ├─ tsconfig.json              # TypeScript compiler settings
   ├─ README.md                  # Quick run guide for this extension
   └─ package.json               # Extension manifest and dependencies

PitScript Language Syntax

Complete Example (All Features)

This example demonstrates every syntax feature supported by PitScript:

// ========== VARIABLE DECLARATIONS (3 types) ==========
pace lapTime;           // int type
radio driverName;       // string type
flag isRaining;         // boolean type

// ========== LITERALS & ASSIGNMENTS ==========
lapTime = 95;           // number literal
driverName = "Verstappen";  // string literal
isRaining = red;        // boolean literal (green or red)

// ========== ARITHMETIC EXPRESSIONS ==========
pace fastestLap;
fastestLap = lapTime + 5;           // addition
fastestLap = lapTime - 2;           // subtraction
fastestLap = lapTime * 2;           // multiplication
fastestLap = lapTime / 10;          // division

// ========== COMPARISON EXPRESSIONS ==========
flag comparison1;
comparison1 = lapTime == 95;        // equality
comparison1 = lapTime != 100;       // inequality
comparison1 = lapTime < 100;        // less than
comparison1 = lapTime > 90;         // greater than
comparison1 = lapTime <= 95;        // less or equal
comparison1 = lapTime >= 90;        // greater or equal

// ========== CONTROL STRUCTURE: IF/ELSE (sector) ==========
sector (lapTime < 90) {
    driverName = "Leclerc";
} alternate {
    driverName = "Alonso";
}

// ========== CONTROL STRUCTURE: WHILE LOOP (stint) ==========
stint (lapTime > 85) {
    lapTime = lapTime - 1;
}

// ========== CONTROL STRUCTURE: FOR LOOP (lap) ==========
lap (pace i; i < 10; i = i + 1) {
    lapTime = lapTime + 1;
}

// ========== FUNCTION DECLARATION (pit) ==========
pit calculateStrategy(pace baseTime, radio driver): radio {
    radio strategy;
    
    sector (baseTime < 90) {
        strategy = "Soft tyres";
    } alternate {
        strategy = "Medium tyres";
    }
    
    boxbox strategy;
}

// ========== FUNCTION CALL ==========
radio finalStrategy;
finalStrategy = calculateStrategy(85, "Verstappen");

Grammar (EBNF)

Complete EBNF specification of PitScript grammar:

(* PitScript Grammar - Formula 1 Themed Language *)

program = statement* ;

statement = variableDeclaration 
          | functionDeclaration 
          | assignment 
          | ifStatement 
          | whileStatement 
          | forStatement 
          | functionCall SEMI 
          | returnStatement ;

(* DECLARATIONS *)
variableDeclaration = type ID SEMI ;

type = "pace" | "radio" | "flag" ;

functionDeclaration = "pit" ID "(" parameterList? ")" ":" type "{" statement_list "}" ;

parameterList = parameter ("," parameter)* ;

parameter = ID ":" type ;

returnStatement = "boxbox" expression SEMI ;

(* ASSIGNMENTS *)
assignment = ID "=" expression SEMI ;

expression = conditionalExpression ;

conditionalExpression = additive_expression 
                       (("==" | "!=" | "<" | ">" | "<=" | ">=") additive_expression)* ;

additive_expression = multiplicative_expression 
                     (("+" | "-") multiplicative_expression)* ;

multiplicative_expression = primary_expression 
                           (("*" | "/") primary_expression)* ;

primary_expression = INT 
                    | STRING 
                    | BOOLEAN 
                    | ID 
                    | functionCall 
                    | "(" expression ")" ;

(* CONTROL STRUCTURES *)
ifStatement = "sector" "(" expression ")" "{" statement_list "}" 
              ("alternate" "{" statement_list "}")? ;

whileStatement = "stint" "(" expression ")" "{" statement_list "}" ;

forStatement = "lap" "(" assignment expression SEMI assignment ")" "{" statement_list "}" ;

functionCall = ID "(" argumentList? ")" ;

argumentList = expression ("," expression)* ;

(* TERMINALS *)
INT     = [0-9]+ ;
STRING  = '"' [^"]* '"' ;
BOOLEAN = "green" | "red" ;
ID      = [a-zA-Z_][a-zA-Z0-9_]* ;

Diagnostics

PitScript implements 3 diagnostic checks: 1 syntax error type + 2 semantic error/warning types.

Diagnostic Types Supported

1. ❌ Syntax Errors (ANTLR Parser)

Syntax errors detected during parsing. Examples:

  • Missing semicolons
  • Mismatched brackets
  • Unexpected tokens

2. ❌ Error: Type Mismatch (Semantic Analysis)

Severity: ERROR
Trigger: Assigning a value of wrong type to a variable

Example Code:

pace lapTime;
lapTime = "ninety";  // ❌ ERROR: Type mismatch - cannot assign radio to pace variable 'lapTime'

How it Works:

  • Parser tracks variable declarations: lapTime is type pace (int)
  • On assignment, CustomVisitor checks if assigned value type matches
  • String literal "ninety" has type radio, not pace → ERROR

3. ⚠️ Warning: Unused Variable (Semantic Analysis)

Severity: WARNING
Trigger: Variable declared but never read/used in code

Example Code:

pace unusedValue;     // ⚠️ WARNING: Variable "unusedValue" is declared but never used
pace count;
count = 10;           // ✅ OK - "count" is used in assignment

How it Works:

  • CustomVisitor tracks all variable declarations in _variableUsage map
  • When a variable is read (assignment RHS, condition, function arg), marks it as used
  • After parsing completes, checks _variableUsage for unused variables
  • Reports position of declaration for easy navigation

Testing Diagnostics

See all diagnostics in action: lsp-sample/test/comprehensive-test.pit (see Build & Run Instructions)


Code Completion

PitScript provides 4 forms of code completion (exceeds minimum requirement of 2):

1. 📝 Variable Completion

Suggests all declared variables in the current scope

pace lapTime;
radio driverName;

lapTime = 100;
// Type: l → Suggests "lapTime"
// Shows: lapTime (pace variable)

2. 🔑 Keyword Completion

Suggests language keywords and types

  • Control flow: sector, alternate, stint, lap, pit, boxbox
  • Types: pace, radio, flag
  • Boolean values: green, red

3. 🔧 Function Completion

Suggests declared functions with signature info

pit calculateSpeed(pace time): pace { ... }

// Type: calc → Suggests "calculateSpeed"
// Shows: calculateSpeed(pace time): pace

4. 📋 Snippet Completion

Live templates for common constructs (IntelliJ-style):

Snippet Expands To
sector sector (condition) { ... }
sectoralternate sector (condition) { ... } alternate { ... }
stint stint (condition) { ... }
lap lap (pace i; i < 10; i = i + 1) { ... }
pit pit name(params): type { ... }

Build & Run Instructions

Prerequisites

  • Node.js 14+
  • ANTLR 4.13.1 (bundled in ts-template-package/tools/)

Build Order (no production build)

Step 1: Install all dependencies

npm run install-all

Step 2: Build the compiler package

npm run build --workspace=ts-template-package

This generates ANTLR files and compiles TypeScript:

  • Runs ANTLR4 to generate PitScriptLexer.ts and PitScriptParser.ts
  • Compiles TypeScript to dist/

Step 3: Compile the VS Code extension

npm run compile --workspace=lsp-sample

Compiles client and server TypeScript to out/.

Note: We have not set up or tested a single "production build" script that builds both packages; use the separate commands above.

Running the Extension

Open the lsp-sample folder in VS Code:

code lsp-sample

Press F5 to launch Extension Development Host. Open a .pit file to see:

  • ✨ Syntax highlighting
  • 🔴 Diagnostic errors and warnings
  • 💡 Code completion (Ctrl+Space)

Development

During normal extension debugging (F5), changes are detected and diagnostics update live. A dedicated watch mode is not required for our workflow.


Testing

Run All Tests

npm run test

This runs tests in both packages:

  • ts-template-package: Parser unit tests
  • lsp-sample: E2E tests (if configured)

Parser Unit Tests Only

cd ts-template-package
npm test

Test Coverage

Location: ts-template-package/test/index.test.ts

Test Categories:

  1. ✅ AST Generation Tests

    • Correct parsing of variable declarations (pace, radio, flag)
    • Correct parsing of assignments
    • Correct parsing of function declarations with parameters
    • Correct parsing of control structures (sector, stint, lap)
  2. ❌ Syntax Error Tests

    • Missing semicolons reported
    • Mismatched brackets reported
    • Unexpected tokens reported
  3. 🔍 Semantic Error Tests

    • Type mismatch errors detected and positioned correctly
    • Undefined variable errors detected
    • Error messages are clear and actionable
  4. ⚠️ Semantic Warning Tests

    • Unused variable warnings detected at declaration position
    • Used variables are NOT flagged
    • Variables used in conditions/assignments are tracked
  5. 💡 Code Completion Tests

    • Parser returns complete variable map
    • Parser returns complete function declarations
    • Parser returns complete function calls
    • Information sufficient for LSP to provide completions

Screenshots

VS Code Syntax Highlighting

Syntax Highlighting

PitScript F1 theme applied to comprehensive-test.pit showing colored keywords (red-ferrari), types (orange-mclarenPapaya), operators (cyan-mercedes), strings (green-astonMartin), numbers (blue-redBull/racingBulls) see THEME-COLORS.md for more colour information

Diagnostics in Action

Diagnostics

Three diagnostics shown in comprehensive-test.pit:

  • Line 70: Unused variable warning (yellow squiggle)
  • Line 74: Type mismatch error (red squiggle)
  • Line 94: Undefined variable error (red squiggle)

Code Completion

Code Completion

Completion menu showing variables, keywords, and function suggestions


Technical Details

Compiler Architecture

Parser: ts-template-package/src/ts/index.ts

  • Exports parseSourceCode(input: string): ParsedResult
  • Returns: variables map, functions map, AST, errors array

Lexer/Parser: ANTLR4-generated from PitScript.g4

  • PitScriptLexer.ts - Tokenization
  • PitScriptParser.ts - Syntax analysis → AST

Semantic Analyzer: ts-template-package/src/ts/CustomVisitor.ts

  • Extends PitScriptVisitor (ANTLR-generated)
  • Implements visitor pattern for AST traversal
  • Tracks variable types and usage
  • Detects type mismatches and unused variables

Language Server Architecture

Client: lsp-sample/client/src/extension.ts

  • VS Code extension entry point
  • Launches language server process

Server: lsp-sample/server/src/server.ts

  • Runs parseSourceCode() on document change
  • Converts parser errors → LSP diagnostics
  • Provides variable/function completions

Naming & References

Project Structure Reference

  • ts-template-package: Compiler package (parser + semantic analysis). Kept separate to mirror typical multi-package setups and allow reuse/testing in isolation.

  • lsp-sample: VS Code language server extension. Name aligns with Microsoft’s sample structure for client/server LSP projects.

  • ANTLR4 documentation: https://www.antlr.org/

MY map structure (root with two packages; extension split into client/, server/, syntaxes/, themes/) follows the patterns recommended in the guide above and common community examples.


References

About

A custom scripting language inspired by Formula 1 terminology with full VS Code Language Server integration.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors