Skip to content

Instantly share code, notes, and snippets.

@dk00
Last active January 27, 2017 12:07
Show Gist options
  • Select an option

  • Save dk00/d1291a66bccdc79bc3771501781e39eb to your computer and use it in GitHub Desktop.

Select an option

Save dk00/d1291a66bccdc79bc3771501781e39eb to your computer and use it in GitHub Desktop.
Livescript AST to babel AST converter
``import * as types from 'babel-types'``
function L
start: line: it.first_line, column: it.first_column
end: line: it.last_line, column: it.last_column
[none = [] empty = {} REF = 1 ASSIGN = 2 DECL = 4 PARAM = 8]
function pass => it
type-name = (.constructor.display-name)
function t node, scope
convert = t[type-name node] || t.unk
node.children .= map -> node[it]
convert node, scope
..loc = L node
t <<< types
function merge scope, nested
Object.keys nested .forEach (key) -> scope[key] .|.= nested[key]
scope
function collect scope, convert, node
convert t node, scope
merge scope, ..scope
function reduce nodes, upper, types=none
scope = Object.create upper
args = nodes.map (node, index) ->
convert = collect.bind void scope, types[index] || pass
node.map? convert or convert node
[args, scope]
function define {types=none, pre=pass, post=pass, args, build}
=> (node, upper) ->
scope = pre upper, node
[nodes, scope] = post ...(reduce node.children, scope, types), upper, node
params = args? node or []
t[build] ...params ++ nodes
..scope = scope
#Node types
function map-values object, value
Object.keys object .reduce (result, key) ->
result[key]? = value object[key]
result
, {}
function set-assign scope, type
map-values scope, -> if it.&.PARAM then (it.&.~PARAM).|.type else it
function make-assign args, scope,, node
type = if node.op == \= then DECL else ASSIGN
[args, set-assign scope, type]
function declare names
t.variableDeclaration \let names.map -> t.variableDeclarator t.id it
function close-scope upper, scope
to-declare = -> (upper[it].|.0) < DECL && scope[it] >= DECL
Object.keys scope
declared = ..filter to-declare
referenced = ..filter -> !to-declare it
declarations = declare declared if declared.length > 0
[declarations, referenced]
function make-block [body] scope, upper
[declarations, referenced] = close-scope upper, scope
body = body.reduce (body, node) ->
body ++= (node.lines || []) ++ node
, []
body.unshift that if declarations
scope = {[k, scope[k]] for k in referenced}
[[body] scope]
function omit-declared => it if it < DECL
function make-function [params, block] scope
if params.length == 0 && block.scope.it
params.push t.id \it
block.scope.it = DECL
block.body[*-1] = t.return block.body[*-1]
[[params, block] map-values block.scope, omit-declared]
#Child types
function derive convert, node
that <<< node{scope, lines} if convert node
statement = derive.bind void ->
t.toStatement switch
| t.isExpression it => t.expressionStatement it
| _ => it
expr = derive.bind void ->
| it?expression => that
| t.isExpression it => it
function lval node
node.scope[that] .|.= PARAM if node.name
node
t <<<
id: -> t.identifier it
unk: -> t.id (type-name it) + \$
return: -> if expr it then t.returnStatement that else it
Var: -> (t.id it.value) <<< scope: (it.value): REF
Assign: define do
build: \assignmentExpression types: [lval, expr]
post: make-assign, args: (node) -> [node.op]
Block: define do
build: \blockStatement types: [statement] pre: -> {}
post: make-block
Fun: define do
build: \functionExpression types: [lval, statement]
pre: (, node) -> if node.params.length == 0 then it: DECL else {}
post: make-function, args: (node) -> [t.id node.name || '']
function transform root
program = t root, top: DECL
..type = \Program
p program.scope
t.file program, [] []
..loc = program.loc
``export default transform``
readFileSync = require \fs .readFileSync
ast = require \livescript .ast
transform = require \./transform
gen = require \babel-generator .\default
function compile code => gen transform ast code
module.exports = compile
{
"name": "livescript-babel",
"version": "0.0.1",
"description": "Livescript AST to babel AST transformer",
"main": "compile.ls",
"dependencies": {
"babel-types": "",
"livescript": "",
"babel-generator": ""
}
}
@dk00
Copy link
Author

dk00 commented Dec 20, 2016

Moved to github


const

  • Must look through whole block before deciding to use const or not = 2nd pass.
  • Performance impact is not significant (yet).
  • Not only apply to ls, should be handled by a babel plugin at js side.
  • Moving declarations to first assignments can also done at js side.

=

Almost every side effect executed in the same order of they occurrence, expect:
[val=console.log('default')] = console.log('expr'), []

expr
default

LVal

  • memberExpression
  • arrayPattern
    • restElement
  • objectPattern
    • restProperty
  • assignmentPattern

Assignment

  • right part executed first
  • derive to compatible type

a ?= b => a ? a = b

Expression Test Consequent Alternate
a ? b a != null a b
a? = b b != null a=b void
a? b typeof a == \function a b void
a?b a != null a.b void

Scopes

  • it is marked as declared for inner nodes of functions, and will be declared actually when referenced.
  • local declared variables are removed when returning from a block

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment