npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@codincod/codemirror-lang-pascal

v0.3.0

Published

Pascal language support for the CodeMirror code editor

Readme

@codincod/codemirror-lang-pascal NPM version

[ CHANGELOG ]

This package implements Pascal language support for the CodeMirror code editor, using a Lezer grammar written for this package.

No Lezer grammar for Pascal existed before this one. A Pascal file in a CodeMirror 6 editor was read by the stream mode carried over from CodeMirror 5, which recognises keywords, strings, numbers and comments, and has no tree to fold, indent or navigate by.

Written in part for CodinCod, a competitive coding platform, where it colours the editor people solve puzzles in.

This code is released under an MIT license.

Usage

import {EditorView, basicSetup} from "codemirror"
import {pascal} from "@codincod/codemirror-lang-pascal"

const view = new EditorView({
  parent: document.body,
  doc: `unit Greeting;

interface

type
  TGreeter = class
  private
    FName: string;
  public
    constructor Create(const AName: string);
    procedure Say;
  end;

implementation

constructor TGreeter.Create(const AName: string);
begin
  FName := AName;
end;

procedure TGreeter.Say;
begin
  WriteLn('hello, ', FName);
end;

end.
`,
  extensions: [basicSetup, pascal()]
})

Coverage

Object Pascal as Free Pascal and Delphi compile it. Units, programs, libraries and packages; classes, records, interfaces, helpers and metaclasses; properties with their specifiers; generics on both the Delphi spelling and the Free Pascal one; variant records; procedural types; RTTI attributes; the whole directive vocabulary; inline variables; anonymous methods; assembler blocks; and every number and string spelling the two compilers accept.

Three other dialects are read as far as they go without costing Object Pascal anything. PascalABC.NET has match, yield, ranges as values, type classes and a variable that takes its type from its value. Pascaline has joins, fixed and an array written with its length. Standard Pascal passes a subprogram as a parameter.

The top level is deliberately looser than a compiler's. An editor is shown fragments as often as whole files, so a bare statement, a lone declaration and a unit cut off halfway all parse.

Measured against 36092 files of published Pascal, 433 MiB, 95.02% parse with no error node. By project:

| Project | Files | Clean | | --- | ---: | ---: | | Free Pascal | 22332 | 96.57% | | Lazarus | 4976 | 96.32% | | PascalABC.NET | 4405 | 90.62% | | Castle Game Engine | 2470 | 95.43% | | mORMot | 576 | 97.92% | | Indy | 803 | 95.89% | | Pascal-P6 | 530 | 47.55% |

The five Object Pascal projects together are 96.44% clean. Much of what is left in the other two is not meant to parse: 246 of Pascal-P6's 278 failures are its ISO conformance suite, files whose whole purpose is to be rejected, and PascalABC.NET keeps a directory of compiler error tests beside its samples. Set those aside, along with Free Pascal's must-not-compile tests, and 95.70% of the remaining 35653 files parse.

The extensions counted are .pas, .pp, .inc, .dpr, .lpr and .dpk, which is what lezer-survey knows Pascal by and needs no flag to use. An include file is a piece of a unit rather than a unit, and Free Pascal writes 6752 of them; a Delphi package is a file Delphi compiles. Both are the language and both are counted.

The corpus does not ship with this package. Every project in it has its own licence, so npm run corpus takes the path to your own copy.

What a tokenizer has to settle

Some things about Pascal cannot be written as grammar rules, and they are the whole of tokens.ts.

Case does not matter. BEGIN, Begin and begin are one word, and real code uses all three. The reserved words are therefore recognised by a function that lowercases a word before looking it up, rather than by a list of literals in the grammar file.

Most of the words are not reserved. read, write, name, index, message, default, private and the calling conventions are directives. They mean something in one position and are ordinary names everywhere else, and a single unit will often use one both ways. They come in through @external extend, which offers the parser the keyword and leaves the identifier standing wherever the keyword cannot go.

An angle bracket is two different things. TList<Integer> and a < b open on the same character. Where a type belongs there is no comparison to be had and the parser says so. Where an expression belongs the answer is further along the line, so the tokenizer reads ahead to find it.

An assembler block is not Pascal. asm ... end holds another language, with its own comment characters and its own uses for [ and %. It is taken whole by a tokenizer that looks only for the end that closes it.

A modifier says nothing about what it binds. static behind a header is a directive and ends with a semicolon; in front of one it belongs to the method or the field that follows. Free Pascal's [public, alias: 'x'] and an RTTI attribute are the same bracket, told apart by the word inside it. Both answers lie past what a parser is allowed to look at, so a tokenizer reads ahead and hands back a different token.

The branch of a conditional the compiler would not read is skipped. A compiler reads one arm of a {$ifdef} and an editor is shown both, so a grammar that reads what is in front of it sees one declaration written twice. Everything from an {$else} to the {$endif} that closes it is passed over, which is what the compiler does with the same file, and the only reading that yields a tree.

What the tree does not say

A subprogram body is a sibling of its header, never a child of another subprogram. Nothing before the body says whether there will be one: an interface entry, a forward, an external and a full definition all open the same way, and the answer can be thousands of lines off. A parser that goes looking nests every header in a unit's interface section inside the one above it, and buries implementation at the bottom. So a header ends the moment the next declaration starts, and a procedure nested in another stands beside it. Folding, indentation and highlighting want the body, and none of them wants the nesting.

A parenthesised list is not read for meaning. The same brackets spell a call, an index, an enumeration, a record constant and an array constant, and which one is meant depends on what the name in front of them refers to. This grammar knows what no name refers to, so it separates the five by what is inside the brackets and no further.

^A is a name, not a control character. Turbo Pascal wrote control characters as a caret and a letter, and Delphi still accepts #13^J. The same caret spells a pointer dereference, and PLooper^ and ^ALooper differ only in which side of the name it falls on. Real code dereferences constantly and writes control characters rarely, so the caret is always the operator.

A few PascalABC.NET spellings are left out. Tuples, slices, if used as an expression, a nullable T? and parameterless lambdas each collide with something Object Pascal writes, and Object Pascal is what the grammar is for. A tuple type and an enumeration are the same brackets around the same names; a nullable type and the conditional expression are the same question mark.