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 🙏

© 2024 – Pkg Stats / Ryan Hefner

elf.js

v0.3.0

Published

elf (ECMAScript Language Framework) is a DSL for creating reusable and extensible programing language implementations.

Downloads

1

Readme

ELF - ECMAScript Language Framework

Introduction

ELF let's you create reusable and extensible programming language implementations in JavaScript. It implements abstractions for creating parsers and lexers with automatic error-recovery, easy handling of operator precedence and (basic) AST walkers with support for pattern-matching.

ELF takes advantage of JavaScript's prototypical nature so the way you create languages is by 'cloning' existing ones can in turn be cloned by more specific ones. This, combined with the ability to mixin and borrow rules, makes it very easy to extend and compose existing languages.

Installation

> mkdir my-lang
> cd my-lang
> npm install elf.js

Calculator Example

var elf = require('elf.js'), _;

var Calculator = elf.Language.clone(function () {
  this.number(/\d+/)
  
  this.prefix("-")
  
  this.infix("+", 10)
  this.infix("-", 10)
  this.infix("*", 20)
  this.infix("/", 20)
  
  this.stmt ("print", function (node) {
    node.first = this.expression();
    node.arity = "statement";
    return node;
  });
  
  this.skip(/\s+/)
  this.eol(";")
});

var Interpreter = elf.Walker.clone(function () {
  this.match('-', [ _ ], function (node, right) {
  	return -this.walk(right);
  })
  
  this.match('-', [ _ , _ ], function (node, left, right) {
  	return this.walk(left) - this.walk(right);
  })
  
  this.match('+', function (node, left, right) {
  	return this.walk(left) + this.walk(right);
  })
  
  this.match('*', function (node, left, right) {
  	return this.walk(left) * this.walk(right);
  })
  
  this.match('*', [ _ , 2 ], function (node, left, right) {
  	return this.walk(left) >> 1;
  })
  
  this.match('/', function (node, left, right) {
  	return this.walk(left) / this.walk(right);
  })
  
  this.match('print', function (node, first) {
  	console.log(this.walk(first));
  });
  
  this.match(_, function (node) {
  	return node.value;
  })
});

var ast = Calculator.parse('print 1 + 2 * 3;print 4-2;');

Interpreter.walk(ast);

API

elf.Object

Provides an abstraction over JavaScript's implementation of protoypal inheritence.

  • .clone(initializer<function|object>)

    Returns a new object whose prototype link points to the object being cloned. It accepts either a function or an object with initialization logic which will be applied to the new object.

  • .extend(…mixins)

    Copies all properties from each of the mixin objects over to itself.

  • .slots()

    Returns an array of all it's properties.

elf.Token

Represents a lexical token.

  • .create(type, value)

    Creates and returns a new token object from the current one with type and value set

  • .error(message<string>)

    Turns the token into an error token

  • .pos(pos<object>)

    Updates the token position

elf.Lexer

Provides a lexer abstraction on top of which you can base your own more specific lexers

  • .name(match<regexp|string>, [action<function>])

    Creates a rule for matching identifiers based on the provided match object.

  • .number(match<regexp|string>, [action<function>])

    Creates a rule for matching number literals based on the provided match object.

  • .string(match<regexp|string>, [action<function>])

    Creates a rule for matching strings literals based on the provided match object.

  • .regex(match<regexp|string>, [action<function>])

    Creates a rule for matching regex literals based on the provided match object.

  • .operator(match<regexp|string>, [action<function>])

    Creates a rule for matching operators based on the provided match object.

  • .eol(match<regexp|string>, [action<function>])

    Creates a rule for matching end-of-line operators based on the provided match object.

  • .skip(match<regexp|string>)

    Tells the lexer to skip anything matching the provided match object.

  • .rule(name, regex, action, arity)

    A lower-level matcher that all of the previous rules are based upon.

    All of the above methods accepts an optional action function that will be invoked when the rule matches. The context inside the action is the matched string. It can return one of three things: a value (which will be set as the value on the current token), an array of tokens or null (in which case the lexer will ignore the match).

elf.Parser

Provides a parser abstraction on top of which you can base your own more specific parsers

  • .advance([id])

    Advances to the next token, if an id is provided an error node will be created if it doesn't match the id of that token.

  • .expression([rbp])

    Parses a single expression, if an rbp (right binding power) is provided then it will only parse as long as the next token has the same binding power or higher.

  • .statement()

    Parses a single statement.

  • .block(openTag, closeTag)

    Parses a block of code delimited by openTag and closeTag.

  • .stmt(id, std<function(node)>)

    Creates a rule for matching a statement. A statement can only appear at the beginning of an expression.

  • .prefix(id, [nud<function(node)>])

    Creates a rule for matching prefix tokens, similar to stmt exept that it can appear multiple times in an expression.

  • .infix(id, bp, [led<function(node, left)>])

    Creates a rule for matching tokens in infix position. Can appear multiple times in an expression.

  • .infixr(id, bp, [led<function(node, left)>])

    Like infix except that it associates to the right.

  • .borrow(parser, ...rules)

    Let's you borrow a set of rules from another parser.

  • .token

    Always points to the current token.

  • .tokens

    A collection of all the remaining tokens. It has a .peek([n]) method that let's you look at the next token without consuming it.

  • .parse(tokens<Array>)

    Accepts a collection of tokens and returns an AST.

elf.Language

Provides a unified abstraction for lexers and parsers.

  • .name(regex<regexp|string>, [action<function(token)>])

    Creates a rule for matching identifiers based on the provided match object.

  • .number(regex<regexp|string>, [action<function(token)>])

    Creates a rule for matching numbers based on the provided match object.

  • .string(regex<regexp|string>, [action<function(token)>])

    Creates a rule for matching strings based on the provided match object.

  • .regex(regex<regexp|string>, [action<function(token)>])

    Creates a rule for matching regular expressions based on the provided match object.

  • .eol(regex<regexp|string>, [action<function(token)>])

    Creates a rule for matching end-of-line operators based on the provided match object.

  • .skip(regex<regexp|string>, [action<function(token)>])

    Tells the language to skip anything matching the provided match object.

  • .stmt(id<string>, std<function(node)>);

    Creates a rule for matching a statement. A statement can only appear at the beginning of an expression.

  • .prefix(id<string>, [nud<function(node)>])

    Creates a rule for matching prefix tokens, similar to stmt exept that it can appear multiple times in an expression.

  • .infix(id<string>, bp<integer>, [led<function(node, left)>])

    Creates a rule for matching tokens in infix position. Can appear multiple times in an expression.

  • .infixr(id<string>, bp<integer>, [led<function(node, left)>])

    Like infix except that it associates to the right.

  • .borrow(language, ...rules)

    Let's you borrow a set of rules from another parser.

  • .parse(input<string>)

    Accepts a program string and returns an AST.

elf.Walker

Recursively walks the AST and applies your matchers.

  • .match(match<string|number>, [pattern<array>], action<function(node, child1, child2, …, childN)>)

    Executes the action if the type or value of the current node matches the match argument and if the provided (optional) pattern matches.

  • .walk(ast<AST|Node|Array>)

    Walks the ast and returns whatever your actions return.

elf.Walker

Recursively walks the AST collecting error nodes.

  • .walk(ast<AST|Node|Array>)

    Walks the ast and generates an error report.