sibilant

repository·main·Indexed 19 days ago

https://github.com/jbr/sibilant

A Lisp-inspired language that compiles to readable and idiomatic JavaScript. Sibilant is highly extensible, allowing for in-source modification of keywords and macros, and is self-hosted (written 100% in Sibilant). It includes a CLI for compilation and execution, a built-in REPL, and a comprehensive set of macros for flow control, hash management, and list operations. It also supports .son files for JSON-querying tasks and integrates with Node.js require.extensions.

Tokens
6K
Snippets
17
Records
38
Agent score
64%

What's inside sibilant

  1. Overview of Sibilant language features

    main

    Sibilant is a Lisp-inspired language that is parsed and compiled by JavaScript. Key characteristics include:

    • JavaScript Target: It compiles to JavaScript and aims to produce readable, idiomatic JS output. The switching cost between Sibilant and the resulting JS should be low.
    • Macro System: Macros can be defined in Sibilant and are included at compile time.
    • Expression-Oriented: The language prefers expressions over statements, often utilizing self-executing functions to achieve this.
    • Extensibility: The language is designed to be highly modifiable in-source, allowing users to rename, remove, or redefine keywords and macros.
    • Self-Hosted: The Sibilant compiler itself is written 100% in Sibilant.
  2. How Pipe and Thunk macros work in Sibilant

    main

    Sibilant supports functional programming patterns through piping macros:

    Pipe (|>)

    The pipe macro (aliased as |>) takes a value and passes it through a sequence of function calls. It supports a placeholder # which can be used to inject the piped value into a specific position within a function call.

    Pipe Thunk (#->)

    The pipeThunk macro (aliased as #->) allows for delayed execution. It creates a structure where the piped value is passed into a function that is treated as a thunk.

    Tap

    tap allows you to perform an action on a value (the body) without changing the value itself, effectively 'tapping' into the pipeline.

    /* Example of pipe usage (conceptual) */
    value |> functionCall
    
    /* Example of pipe thunk usage (conceptual) */
    value #-> thunkFunction
  3. Use Sibilant Regex Macros

    main

    Sibilant provides macros for string manipulation and pattern matching that map to standard JavaScript regex operations.

    Match and Replace

    • .match(string, regexp): Uses the string's match method with a provided regex.
    • match? (regex, pattern, flags, string): A predicate that returns whether a string matches a pattern with specific flags.
    • .replace(string, regex, replacement): Uses the string's replace method.
    • replace-all (string, pattern, replacement): A macro for global replacement.
  4. Use Sibilant Core Predicates

    main

    Sibilant's core namespace contains macros for common type and value checks. These are typically used within Sibilant source files to implement conditional logic.

    Examples of macro behavior based on the core implementation:

    • undefined? checks if arguments are typeof === 'undefined'.
    • defined? checks if arguments are typeof !== 'undefined'.
    • exists? is a combination of defined? and a null check.
    • array? (and its alias list?) verifies the object is an Array via its constructor name.
    • hash? (and its alias object?) verifies the object is an object, not null, and not an Array.
  5. Compile Sibilant files to specific directories

    main

    By default, Sibilant prints the compiled JavaScript to stdout. To save the output to a file, use the -o (or --output) flag.

    • If the input is a standard Sibilant file, the output will be [basename].js.
    • If the input is a .son file, the output will be [basename].json.
    • If the -m (or --sourcemap) flag is used, a .map file will also be generated in the specified output or sourcemap directory.
    # Compile input.sibilant to ./dist/input.js
    sibilant -f input.sibilant -o ./dist
    
    # Compile input.sibilant to ./dist/input.js with a sourcemap in ./maps/
    sibilant -f input.sibilant -o ./dist -m ./maps
  6. Use Sibilant with Node.js `require`

    main

    Sibilant hooks into Node.js's require.extensions to allow direct importing of .sibilant and .son files.

    • .sibilant files: Transpiled to JavaScript and executed as modules.
    • .son files: Transpiled and parsed as JSON. The resulting JSON object is exported as the module.

    Example:

    // If my-data.son contains sibilant-style JSON
    const data = require('./my-data.son');
    console.log(data.someKey);
  7. Use the Sibilant CLI

    main

    The Sibilant CLI allows you to compile Sibilant code into JavaScript, execute Sibilant code directly, or enter an interactive REPL.

    If no arguments are provided, the CLI starts the REPL. If a file is provided, it compiles the file. If code is provided via eval, it executes the resulting JavaScript in a sandbox.

    # Example: Compile a file to stdout
    sibilant input.sibilant
    
    # Example: Execute a file directly
    sibilant -x input.sibilant
    
    # Example: Evaluate a string of code
    sibilant -e "my sibilant code"
    
    # Example: Compile a .son file to JSON
    sibilant -f input.son
  8. Use the Sibilant REPL

    main

    You can interact with Sibilant using its built-in REPL (Read-Eval-Print Loop). This allows you to execute Sibilant expressions and see the resulting JavaScript and output immediately. Sibilant follows Lisp-inspired conventions, such as using prefix notation for operations.

    $ sibilant
    sibilant> (+ 1 2)
    (1 + 2)
    result: 3
    sibilant> (console.log "hello world")
    console.log("hello world")
    hello world
  9. Pretty print Sibilant ASTs

    main

    The sibilant.prettyPrint(node, color, entry) function provides a human-readable, colorized representation of a Sibilant AST node or an array of nodes.

    Parameters:

    • node: The AST node or array to print.
    • color (boolean): Whether to use ANSI color codes. Defaults to true.
    • entry (boolean): Whether to treat the node as an entry point. Defaults to true.

    Sub-methods:

    • sibilant.prettyPrint.root(node, color, entry): Prints the contents of a root node.
    • sibilant.prettyPrint.output(node, color): Prints a node specifically formatted for output.
  10. Use flow control macros: when, unless, and if

    main

    Sibilant includes macros to handle conditional logic with syntax that differs from standard JavaScript:

    • when(condition, body): Executes the body if the condition is truthy. It wraps the body in an if block.
    • unless(condition, body): Executes the body if the condition is falsy. It transpiles to an IIFE containing an if (!condition) block.
    • if(condition, branch, [elseBranch, ...]): A flexible macro for alternating conditions and branches. It can handle complex conditional chains by interleaving conditions and their corresponding execution blocks.
  11. Manage Sibilant dependencies

    main

    Sibilant tracks dependencies discovered during the transpilation process.

    • sibilant.dependencies: An object where keys are file paths and values are arrays of files that depend on them.
    • sibilant.recordDependency(from, to): Manually records that from depends on to.
    • sibilant.flatDependencies(): Returns a flattened array of all recorded dependencies.