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

@helios-lang/ir

v0.3.3

Published

Helios-lang intermediate representation

Downloads

279

Readme

IR

Helios-lang Intermediate Representation (IR) library. This library takes care of:

  • Mutual recursion
  • Static analysis
  • Optimization
  • Detection of final dependencies
  • UPLC generation

The IR doesn't support operators.

Syntax

The Helios-lang IR is low-level human readable programming language with a very simple syntax:

__helios__value__contains_policy = (self) -> {
    (mph) -> {
        mph = __helios__mintingpolicyhash____to_data(mph);
        recurse = (map) -> {
            __core__chooseList(
                map,
                () -> {false},
                () -> {
                    __core__ifThenElse(
                        __core__equalsData(__core__fstPair(__core__headList__safe(map)), mph),
                        () -> {true},
                        () -> {recurse(__core__tailList__safe(map))}
                    )()
                }
            )()
        };
        recurse(self)
    }
};
...

The Helios-lang IR is stripped down version of the Helios language itself. Type annotations have been removed, assignments are only possible for single values. There are no const or func statements and everything is an expression.

Assignment

Assignment is syntactic sugar for a literal function passed as an arg to another literal function.

For example:

x = <expr-x>;
<expr-next>

Becomes:

(x) -> {
    <expr-next>
}(<expr-x>)

Many subsequent assignments look like:

(x) -> {
    (y) -> {
        (z) -> {
            <expr-nexts>
        }(<expr-z>)
    }(<expr-y>)
}(<expr-x>)

Mutual recursion

UPLC doesn't allow mutual recursion directly, so function definitions that appear after the expressions where they are being used must be injected.

For example:

a = b();

b = () -> {
    a
};

b()

Becomes:

a = (b) -> {
    b(b)()
};

b = (b) -> {
    () -> {
        a(b)
    }
};

b(b)()

Reshuffling to avoid mutual recursion should be done in a way that preserves the original order as much as possible. Once a set of mutually recursive expressions are detected they should be left in the same order. This is done because the compiler has no idea how frequently each function will be called.

How to detect a set of mutually dependent expressions

A graph is first created of all assigned expressions in a scope. The indirect dependencies are pushed until all nodes are aware of all dependencies. Then loop through the list of nodes in original order:

  • and node that depends on itself is (mutually) recursive
    • if any of the other dependencies don't depend on the same node, continue
    • if none of the other dependencies depend on the same node, collect all these nodes (assuring the same for each), and copy into the final list
  • if some of dependencies haven't been copied to the new list yet, continue
  • if all dependencies have been copied, copy the current node as well

Infinite loop handling

Will be detected during static analysis phase

Scopes

Although subsequent assignments form nested AST elements, they are treated as being part of the same scope. Scope boundaries are formed by function literals that aren't called immediately.

Optimizaton

Common sub-expression factorization

Common sub-expressions should be hoisted. For this to work each expression must have a unique key. Variable names must be unique in case of shadowing.

Expressions stringified in such a way form the keys mapping to those original expressions.

For example:

a = <common-expr>;

b = <common-expr>;

a + b

Becomes:

common = <common-expr>;

a = common;

b = common;

a + b

One of the difficulties is lifting of common sub-expressions:

fn_a = () -> {
    x = <common-expr>;
    ...
};

fn_b = () -> {
    y = <common-expr>;
    ...
};

fn_a() + fn_b()

This optimization algorithm must thus be aware of branches. Hence it makes sense to use information from the static analysis.