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

@isdk/bash-parser

v0.9.4

Published

Parses bash source code to produce an AST.

Downloads

148

Readme

bash-parser

Parses bash source code to produce an AST.

Table of Contents

About This Fork

This project is a fork of the original bash-parser project, which appears to be unmaintained.

Objectives

  • Minimize dependencies where feasible
  • Migrate the codebase to TypeScript
  • Ensure compatibility with Deno instead of Node.js
  • Update the API to support asynchronous resolvers
  • Perform code cleanup and refactoring
  • Distribute releases via JSR

Non-Objectives

  • No guarantee of compatibility with the original project
  • No guarantee of full bash compatibility, though contributions are welcome
  • Maintain only a bash parser to simplify the project, without separate posix and bash parsers

Features

  • Parses bash source code to produce an AST
  • Supports asynchronous resolvers
  • Compatible with Deno

Installation

deno add @ein/bash-parser
# or
jsr add @ein/bash-parser

Usage

import parse from '@ein/bash-parser';

const ast = await parse('echo ciao');

ast result is:

{
  type: "Script",
  commands: [
    {
      type: "Command",
      name: {
        text: "echo",
        type: "Word"
      },
      suffix: [
        {
          text: "ciao",
          type: "Word"
        }
      ]
    }
  ]
}

Options

You can pass an options object to the parse function to customize its behavior.

export type Options = {
  /**
   * Which mode to use for the parsing. The mode specifies the tokenizer, lexer phases, grammar, and AST builder to use. Default is `bash`.
   */
  mode?: string;

  /**
   * If `true`, includes lines and columns information in the source file.
   */
  insertLOC?: boolean;

  /**
   * A callback to resolve shell alias. If specified, the parser calls it whenever it needs to resolve an alias. It should return the resolved code if the alias exists, otherwise `null`. If the option is not specified, the parser won't try to resolve any alias.
   *
   * @param name - The name of the alias to resolve.
   * @returns The resolved code if the alias exists, otherwise `null`.
   */
  resolveAlias?: (name: string) => Promise<string | undefined>;

  /**
   * A callback to resolve environment variables. If specified, the parser calls it whenever it needs to resolve an environment variable. It should return the value if the variable is defined, otherwise `null`. If the option is not specified, the parser won't try to resolve any environment variable.
   *
   * @param name - The name of the environment variable to resolve.
   * @returns The value if the variable is defined, otherwise `null`.
   */
  resolveEnv?: (name: string) => Promise<string | null>;

  /**
   * A callback to resolve path globbing. If specified, the parser calls it whenever it needs to resolve path globbing. It should return the expanded path. If the option is not specified, the parser won't try to resolve any path globbing.
   *
   * @param text - The text to resolve.
   * @returns The expanded path.
   */
  resolvePath?: (text: string) => Promise<string>;

  /**
   * A callback to resolve users' home directories. If specified, the parser calls it whenever it needs to resolve a tilde expansion. If the option is not specified, the parser won't try to resolve any tilde expansion. When the callback is called with a null value for `username`, the callback should return the current user's home directory.
   *
   * @param username - The username whose home directory to resolve, or `null` for the current user.
   * @returns The home directory of the specified user, or the current user's home directory if `username` is `null`.
   */
  resolveHomeUser?: (username: string | null) => Promise<string>;

  /**
   * A callback to resolve parameter expansion. If specified, the parser calls it whenever it needs to resolve a parameter expansion. It should return the result of the expansion. If the option is not specified, the parser won't try to resolve any parameter expansion.
   *
   * @param parameter - The name of the parameter to resolve.
   * @returns The result of the parameter expansion.
   */
  resolveParameter?: (parameter: string) => Promise<string>;

  /**
   * A callback to execute a `simple_command`. If specified, the parser calls it whenever it needs to resolve a command substitution. It receives as argument the AST of a `simple_command` node, and shall return the output of the command. If the option is not specified, the parser won't try to resolve any command substitution.
   *
   * @param command - The name of the command.
   * @param cmdAST - The AST of the simple command to execute.
   * @returns The output of the command.
   */
  execCommand?: (command: string, scriptAST: AstNodeScript) => Promise<string>;

  /**
   * A callback to execute an `arithmetic_expansion`. If specified, the parser calls it whenever it needs to resolve an arithmetic substitution. It receives as argument the AST of an `arithmetic_expansion` node, and shall return the result of the calculation. If the option is not specified, the parser won't try to resolve any arithmetic expansion substitution. Please note that the arithmetic expression AST is built using [babel/parser](https://babeljs.io/docs/babel-parser), the AST specification can be found there.
   *
   * @param expression - The arithmetic expression to evaluate.
   * @param arithmeticAST - The AST of the arithmetic expression to evaluate.
   * @returns The result of the calculation.
   */
  runArithmeticExpression?: (expression: string, arithmeticAST: AstNode) => Promise<string>;
};

Example usage with options:

import parse from '@ein/bash-parser';

const options = {
  mode: 'bash',
  insertLOC: true,
  resolveEnv: (name) => process.env[name] || null,
};

const ast = await parse('echo $HOME', options);

Documentation

AST

The Abstract Syntax Tree (AST) types represent the hierarchical structure of the source code. Each node in the AST corresponds to a specific language construct.

All the types are described in types.ts.`

How the parser works

Lexer (Tokenizer)

The lexer, also known as the tokenizer, is responsible for converting the raw source code into a sequence of tokens. Tokens are the smallest units of meaning, such as keywords, operators, identifiers, and literals.

Process:
  • The tokenize function takes a set of reducers and returns a generator function that processes the source code.
  • It iterates through the source code character by character, applying reducers to generate tokens.
  • The state is updated and tokens are emitted as they are identified.

Parser

The parser takes the sequence of tokens produced by the lexer and organizes them into a structured format called an Abstract Syntax Tree (AST). The AST represents the hierarchical structure of the source code.

Process:
  • The astBuilder function creates an AST builder object with methods to construct various AST nodes.
  • These methods are used to build different parts of the AST, such as commands, loops, conditionals, etc.
  • The AST nodes include location information to map back to the original source code.

AST Builder

The AST builder is a utility that helps in constructing the AST nodes. It provides methods to create different types of nodes and set their properties, including source location information.

Process:
  • The AST builder methods are used by the parser to create nodes for different language constructs.
  • Each method takes relevant information (e.g., patterns, body, location) and returns an AST node.

Flow

  1. Tokenization: The lexer (tokenize function) processes the source code and generates tokens.
  2. Parsing: The parser consumes these tokens and uses the AST builder to construct the AST.
  3. AST Building: The AST builder methods are called by the parser to create nodes for different constructs, organizing the tokens into a hierarchical structure.

Example Flow

Source Code: let x = 5;

  1. Lexer: Converts to tokens: LET, IDENTIFIER(x), EQUALS, NUMBER(5), SEMICOLON
  2. Parser: Consumes tokens and uses AST builder to create nodes:
    • VariableDeclaration node with child nodes for Identifier and Literal
  3. AST Builder: Methods like variableDeclaration, identifier, and literal are called to create and link nodes.

Contributing

Contributions are welcome! Please see the CONTRIBUTING.md file for guidelines on how to contribute to this project.

License

This project is licensed under the MIT License. See the LICENSE file for more details. Other included code are described in CREDITS.md.

Contact

For questions or support, please open an issue on the GitHub repository.