polyscript

Write JavaScript in your JavaScript using polyscript!

Usage no npm install needed!

<script type="module">
  import polyscript from 'https://cdn.skypack.dev/polyscript';
</script>

README

polyscript experimental

Write JavaScript in your JavaScript using polyscript!

Disclaimer: this is, as far as I know, almost always a very bad idea, with the exception of some special use cases - namely, performance optimisation when dealing with data of arbitrary dimensions. Use as a last resort.

Example

Here's a trivial example for adding arrays together:

var sum2d = sum(2)
var sum3d = sum(3)

sum2d([1, 1], [2, 3]) // [3, 4]

@function sum(d)(a, b) {
  var c = []
  <% for (var i = 0; i < d; i += 1) { %>
    c[@i] = a[@i] + b[@i]
  <% } %>
  return c
}

Essentially, polyscript gives you a special type of function which is prefixed with a @ takes two sets of arguments. This function, when called, returns a new function that's transformed according to a syntax very similar to EJS. In fact, part of the transpiler's code is a modified version of TJ Holowaychuk's implementation.

The first set of arguments will get passed to the generated code, and the second set are used by the generated function. @i is just shorthand for <%= i %>.

Installation

$ npm install polyscript

Usage

Using polyscript as a module, you get one function: it takes a string in polyscript and converts it to javascript.

var ps = require('polyscript')
var fs = require('fs')

console.log(
  ps(fs.readFileSync(__filename))
)

You can use it as a CLI tool by installing it globally - it will either convert the file you specify as the first argument or read from stdin:

$ polyscript index.pjs > index.js
$ cat index.pjs | polyscript > index.js

Why?

Take a vanilla JavaScript function for adding arrays of arbitrary lengths together:

function sum(a, b) {
  var c = []
  for (var i = 0; i < a.length; i += 1) {
    c[i] = a[i] + b[i]
  }
  return c
}

If you knew you were only handling arrays either 2 or 3 elements, you can just swap it out for this and skip on the for loop:

function sum2d(a, b) {
  return [a[0] + b[0], a[1] + b[1]]
}

function sum3d(a, b) {
  return [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
}

Both of these approaches would ordinarily suffice, unless you want to squeeze more performance from these functions while keeping some flexibility and avoiding duplication. A compromise you could take would be provide a function which takes a specific number of dimensions and returns a generated function tailored to your needs. This is a lot harder to read though:

function sumnd(d) {
  return Function(
    'return function sum'+d+'(a, b) {'
  + '  return ['
  + '  ' + ngroup(d, npair).join(', ')
  + '  ]'
  + '}'
  )()
}

function npair(d) {
  return 'a['+d+'] + b['+d+']'
}

function ngroup(n, fn) {
  var a = []
  for (var i = 0; i < n; i += 1) {
    a.push(fn(n))
  }
  return a
}

Here's the original polyscript example again:

@function sum(d)(a, b) {
  var c = []
  <% for (var i = 0; i < d; i += 1) { %>
    c[@i] = a[@i] + b[@i]
  <% } %>
  return c
}

It's essentially taking the function-generation-via-strings approach under the hood, but hiding it away into a more compact syntax. It's still a little confusing, but much easier to read, and structured almost exactly like the original generic example.

You can also treat this more or less the same as you would a normal function: it gets hoisted, can be passed around as a variable, bound, gets a lexical scope, etc.

These examples are fairly trivial - for an actual use case see the code of some of the ndarray modules on npm.

Known Issues

I haven't spent much time with this yet - feel free to fix these and submit a pull request :)

  • @functions can be created in comments/strings.
  • @i works from strings - only <%= i %> should work.

License

(The MIT License)

Copyright (c) 2013 Hugh Kennedy hughskennedy@gmail.com Original EJS implementation copyright (c) 2009-2010 TJ Holowaychuk tj@vision-media.ca