diff --git a/src/algebra_ast.js b/src/algebra_ast.js new file mode 100644 index 0000000..779e646 --- /dev/null +++ b/src/algebra_ast.js @@ -0,0 +1,36 @@ +const seq = (...args) => ({ + toAlgebra: () => args.map(optimizeStep).map(toAlgebra).filter(nonEmptyExpressions).join('.') +}) + +const parallel = (...args) => ({ + type: 'parallel', + args: args, + optimize: () => { + const parallelArgs = args.filter(arg => arg.type === 'parallel') + if (parallelArgs.length === args.length) { + return parallel(...parallelArgs.map(arg => arg.args).reduce((arr, a) => arr.concat(a), [])) + } + return parallel(...args) + }, + toAlgebra: () => { + let parallelPrograms = args.map(optimizeStep).map(toAlgebra).filter(nonEmptyExpressions) + if (parallelPrograms.length > 1) { + return `(${parallelPrograms.join('|')})` + } + + return parallelPrograms.join('|') + } + +}) + +const ambient = (name, ...args) => ({ + toAlgebra: () => { + return `${name}[${args.map(optimizeStep).map(toAlgebra).filter(nonEmptyExpressions).join('|')}]` + } +}) + +const optimizeStep = node => node.optimize === undefined ? node : node.optimize() +const toAlgebra = node => node.toAlgebra === undefined ? node.toString() : node.toAlgebra() +const nonEmptyExpressions = string => string.length > 0 + +module.exports = {ambient, seq, parallel} \ No newline at end of file diff --git a/src/ambients_ast.js b/src/ambients_ast.js new file mode 100644 index 0000000..41afa22 --- /dev/null +++ b/src/ambients_ast.js @@ -0,0 +1,130 @@ +const { literal, verifyPrimitive } = require('./primitives.js') +const { ambient, seq, parallel } = require('./algebra_ast.js') + +const parameterDeclaration = (name) => ({ + toAmbient: (scope) => { + return ambient(name, 'in_ arg.open arg.open_') + } +}) +const variableExpression = (name) => ({ + toAmbient: (scope) => { + return `open ${name}` + } +}) +const binaryExpression = (left, right, operator) => { + let primitive = verifyPrimitive(left, right) + switch (operator) { + case '+': + return primitive.plus(left, right) + case '*': + return primitive.multiply(left, right) + default: + throw new Error(`Operator '${operator}' is not supported`) + } + +} + +const callExpression = (functionName) => ({ + toAmbient: (scope) => { + return scope.functionCall(functionName) + } +}) + +const funcEnvelope = (expression) => ({ + toAmbient: (scope) => { + return ambient('func', + expression.toAmbient(scope), + 'open_') + } +}) + +const functionExpression = (args, expression) => ({ + toAmbient: (scope) => { + return parallel( + scope.functionArgs(args), + expression.toAmbient(scope)) + } +}) + +const functionDefinition = (name, body) => ({ + toAmbient: (scope) => { + let newScope = scope.newScope(name) + return ambient(name, + newScope.capabilities(), + seq('in_ call.open call', parallel( + body.toAmbient(newScope), + 'open return.open_' + )) + ) + } +}) + +const programFile = (declarations, resultStatement) => ({ + toAmbient: (scope) => { + const algebra = declarations + .map(declaration => declaration.toAmbient(scope).toAlgebra()) + .map(code => code.replace(/\r?\n\s*|\r\s*/g, '').replace(/\s+/g, ' ')) + .join('|') + return algebra + } +}) + +class Scope { + constructor (name, parentScope) { + this._name = name + this._parentScope = parentScope + this._auths = [] + } + + functionArgs (args) { + return parallel(...args.map(arg => arg.toAmbient(this))) + } + + functionCall (functionName) { + let scopesToPass = this.allow('call', functionName) + let outCalls = scopesToPass.map(x => `out ${x}.`) + let inReturns = scopesToPass.reverse().map(x => `in ${x}.`) + + return parallel( + ambient('call', + seq(`${outCalls}in ${functionName}.open_`, + ambient('return', `open_.${inReturns}in func`))), + ambient('func', + seq(`in_ ${functionName}.open ${functionName}.open_`)), + 'open func') + } + + newScope (name) { + return new Scope(name, this) + } + + capabilities () { + return { + toAlgebra: () => { + return this._auths.map((auth) => `out_ ${auth.exit}.in_ ${auth.enter}`).join('|') + } + } + } + + allow (exit, enter) { + if (this._parentScope === undefined) { + return [] + } + this._auths.push({ exit: exit, enter: enter }) + return [this._name].concat(this._parentScope.allow(exit, enter)) + } +} + +module.exports = { + Scope, + literal, + binaryExpression, + functionExpression, + functionDefinition, + programFile, + callExpression, + parameterDeclaration, + variableExpression, + funcEnvelope + +} \ No newline at end of file diff --git a/src/esprima_ast_mapper.js b/src/esprima_ast_mapper.js new file mode 100644 index 0000000..6789cdc --- /dev/null +++ b/src/esprima_ast_mapper.js @@ -0,0 +1,31 @@ +const esprima = require('esprima') + +const astMapper = () => ({ + mappers: {}, + match: function (nodetype, func) { + this.mappers[nodetype] = func + }, + parse: function (node, context) { + if (Array.isArray(node)) { + return node + .map((n) => this.parse(n, context)) + .filter(x => x) + .reduce((acc, x) => acc.concat(x), []) + } + let mappingFunc = this.mappers[node.type] + if (mappingFunc === undefined) + return undefined + + return mappingFunc(node, context) + }, + parseAndMap: function (js) { + let counter = 0 + let esprimaMapper = (node, meta) => { + console.log(`${++counter}: ${node.type} (${js.substring(meta.start.offset, meta.end.offset)}) - (${Object.keys(node)})`) + } + return this.parse(esprima.parseScript(js, {}, esprimaMapper)) + } +}) + + +module.exports = astMapper \ No newline at end of file diff --git a/src/index.js b/src/index.js index 54d6255..ad80fee 100644 --- a/src/index.js +++ b/src/index.js @@ -1,181 +1,42 @@ -const esprima = require('esprima') -let rules = {} +const ast = require('./ambients_ast.js') +const astMapper = require('./esprima_ast_mapper.js') -const findAlias = (name, aliases) => { - if (aliases[name] && aliases[aliases[name]]) - return findAlias(aliases[name], aliases) - return aliases[name] -} - -rules.VariableDeclaration = function (block, parent, depth, currentPath, options) { - // Currently a pass thru to VariableDeclarator - // TODO: multiple declarations i.e. x = y = 3? - return parseBlock(block.declarations[0], block, depth, currentPath, options) -} - -rules.VariableDeclarator = function (block, parent, depth, currentPath, options) { - const name = parseBlock(block.id, block, depth, currentPath, options) - const init = parseBlock(block.init, block, depth, currentPath, options) - - if (block.init.type === "Literal" && parent.type === "VariableDeclaration") { - return "" - } else if (block.init.type === "Identifier" && parent.type === "VariableDeclaration") { - options.aliases[name] = init - return "" - } - - return name + '[' + init + ']' -} - -rules.ArrowFunctionExpression = function (block, parent, depth, currentPath, options) { - const parentName = parent.id ? parent.id.name : null - const params = block.params - const paramNames = params.map(e => e.name) - const hasParams = (params && params.length > 0) - - options.names[parentName] = {args: paramNames} // save the param names to a global cache for using as a lookup index elsewhere in the compiler - - let functionBody = "" - let functionName = "" - let functionType = "" +module.exports = function (js) { - if (block.body.type === "BinaryExpression") { - const operator = block.body.operator - if (operator === "+") { - functionName = `concat` - functionType = `string` - } - } + const rootScope = new ast.Scope() + let jsAst = astMapper() - if (hasParams) { - let syntax = 'func[' - for (const param of block.params) { - syntax += parseBlock(param, block, depth, currentPath, options) - syntax += `[in_ arg.open arg.in ${functionType}.in ${functionName}]|` + jsAst.match('Identifier', (id, context) => { + switch (context) { + case 'AFE.Params': return ast.parameterDeclaration(id.name) + case 'AFE.Body': return ast.variableExpression(id.name) } - syntax += parseBlock(block.body, block, depth, currentPath, options) - syntax += '|open_]|' - - return syntax - } else { - let syntax = 'in_ call.open call' - - if (block.body.type === "Identifier" && options.names[block.body.name]) { - // Replace variable directly with its (previously compiled) value - syntax += '.(' - const value = options.names[block.body.name].value - syntax += value + '|' - } else if (block.body.type === "Literal") { - syntax += '.(' - syntax += parseBlock(block.body, parent, depth, currentPath, options) - } else { - syntax += '.(' - if (block.type === 'ArrowFunctionExpression' && block.body.name) { - const source = parent.id.name - const target = block.body.name - syntax += `out_ call.in_ ${target}|` - syntax += `call[out ${source}.in ${target}.open_.return[open_.in ${source}.in func]]|` - syntax += `func[in_ ${target}.open ${target}.open_]|open func.` - } else { - syntax += parseBlock(block.body, parent, depth, currentPath, options) - } + }) + jsAst.match('Literal', (node) => ast.literal(node.value)) + jsAst.match('BinaryExpression', (node) => ast.binaryExpression(jsAst.parse(node.left), jsAst.parse(node.right), node.operator)) + jsAst.match('ArrowFunctionExpression', (node, context) => { + switch (context) { + case 'AFE.Body': + return ast.funcEnvelope( + ast.functionExpression( + jsAst.parse(node.params, 'AFE.Params'), + jsAst.parse(node.body, 'AFE.Body'))) + default: + return ast.functionExpression( + jsAst.parse(node.params, 'AFE.Params'), + jsAst.parse(node.body, 'AFE.Body')) } - - syntax += 'open return.open_)' - return syntax - } -} - -rules.ExpressionStatement = function (block, parent, depth, currentPath, options, ambients) { - return parseBlock(block.expression, parent, depth, currentPath, options, ambients) -} - -rules.CallExpression = function (block, parent, depth, currentPath, options, ambients) { - const callee = block.callee.callee ? block.callee.callee.name : block.callee.name - const alias = findAlias(callee, options.aliases) - const funcName = alias || callee - const parentName = parent.id ? parent.id.name : '' - - const args = block.arguments - const paramNames = options.names[funcName] ? options.names[funcName].args : [] - const params = args.map((e, i) => { - const opts = {target: paramNames[i], isLast: i === paramNames.length - 1} - return parseBlock(e, parent, depth, currentPath, {...opts, ...options}, ambients) }) - const canBeCalled = (parent.type === 'VariableDeclarator' && parent.init.type === 'CallExpression') && - (parent !== 'root') - - let syntax = '' - - if (parent === 'root') { - syntax += `call[in ${funcName}.open_.return[open_.in func]]|` - syntax += `func[in_ ${funcName}.open ${funcName}.open_]|open func` - } else { - syntax += canBeCalled ? `in_ call.open call|` : '' - syntax += `out_ call.in_ ${funcName}|` - syntax += `call[out ${parentName}.in ${funcName}.open_.return[open_.in ${parentName}.in func]]` - syntax += '|' - syntax += `func[in_ ${funcName}.open ${funcName}.(` - syntax += params.length > 0 ? (params.join('') + '|open func.') : '' - syntax += 'open_)]|open func.' - syntax += canBeCalled ? `open return.open_` : '' - } - return syntax -} - -rules.Identifier = (block, parent, depth, currentPath, options, ambients) => block.name + jsAst.match('VariableDeclarator', (node) => ast.functionDefinition(node.id.name, jsAst.parse(node.init))) + jsAst.match('VariableDeclaration', (node) => jsAst.parse(node.declarations)) -rules.Literal = (block, parent, depth, currentPath, options, ambients) => { - let ambient = '' - const value = block.value - const type = typeof value - const parentBody = parent.init.body + jsAst.match('CallExpression', (node) => ast.callExpression(node.callee.name)) + jsAst.match('ExpressionStatement', (node) => jsAst.parse(node.expression)) + jsAst.match('Program', (node) => ast.programFile(jsAst.parse(node.body))) - if (parentBody && parentBody.type === "CallExpression") { - const { target, isLast } = options - const p = isLast ? "" : "|" - ambient = `arg[${type}[${value}[]]|in ${target}.open_]${p}` - } else if (parent.type === "VariableDeclarator") { - ambient = `${type}[${value}[]]` - options.names[parent.id.name] = {value: ambient} - ambient += '|' - } - return ambient -} - -rules.BinaryExpression = function (block, parent, depth, currentPath, options, ambients) { - // TODO: Switch on operator like above ^^ - const left = parseBlock(block.left, block, depth, currentPath, options) - const right = parseBlock(block.right, block, depth, currentPath, options) - return `string[concat[in_ ${left}|in_ ${right}]|in_ ${left}|in_ ${right}]` -} - -rules.Program = function (block, parent, depth, currentPath, options) { - let ambients = '' - block.body.forEach((e, i) => { - // console.log("parse:", e) - const isLast = (i === block.body.length - 1) - const parsed = parseBlock(e, parent, depth, currentPath, options) - ambients += parsed + (isLast || parsed === "" ? "" : "|") - }) - return ambients -} - -const parseBlock = (block, parent, depth, currentPath, options) => { - parent = parent || 'root' - let ambients = '' - if (rules[block.type]) { - ambients += rules[block.type](block, parent, depth, currentPath, options) - } else { - console.log(`Unknown block type "${block.type}" in block:`) - console.log(block) - } - return ambients -} -module.exports = function (js) { - const parsed = esprima.parseScript(js) - return parseBlock(parsed, null, 0, '', {names: {}, aliases: {}}) + let program = jsAst.parseAndMap(js) + return program.toAmbient(rootScope) } diff --git a/src/primitives.js b/src/primitives.js new file mode 100644 index 0000000..e361b72 --- /dev/null +++ b/src/primitives.js @@ -0,0 +1,46 @@ +let primitives = {} +primitives.string = { + literal: (value) => ({ + type: 'string', + toAmbient: (scope) => `string[${value}[]]` + }), + plus: (left, right) => ({ + type: 'string', + toAmbient: (scope) => `string[concat[left[${left.toAmbient(scope)}]|right[${right.toAmbient(scope)}]]]` + }) +} +primitives.number = { + literal: (value) => ({ + type: 'number', + toAmbient: (scope) => `int[i${value}[]]` + }), + plus: (left, right) => ({ + type: 'number', + toAmbient: (scope) => `int[plus[left[${left.toAmbient(scope)}]|right[${right.toAmbient(scope)}]]]` + }), + multiply: (left, right) => ({ + type: 'number', + toAmbient: (scope) => `int[multiply[left[${left.toAmbient(scope)}]|right[${right.toAmbient(scope)}]]]` + }) +} + +const literal = (value) => { + let primitive = primitives[typeof value] + if (primitive === undefined || primitive.literal === undefined) { + throw new Error(`primitive '${typeof value}' is not supported as literal`) + } + return primitive.literal(value) +} + +const verifyPrimitive = (left, right) => { + let primitive = primitives[left.type] + if (primitive === undefined || primitive.literal === undefined) { + throw new Error(`primitive '${left.type}' is not supported for plus-operator`) + } + if (left.type !== right.type) { + throw new Error(`Compiler does not support implicit type conversions for binary ops`) + } + return primitive +} + +module.exports = {literal, verifyPrimitive} \ No newline at end of file diff --git a/test/fixtures/js/lang_features/001-constant_function.ambient b/test/fixtures/js/lang_features/001-constant_function.ambient deleted file mode 100644 index 6d32ab0..0000000 --- a/test/fixtures/js/lang_features/001-constant_function.ambient +++ /dev/null @@ -1,9 +0,0 @@ -a[ - in_ call.open call.( - string[hello[]]| - open return.open_ - ) -]| -call[in a.open_.return[open_.in func]]| -func[in_ a.open a.open_]| -open func diff --git a/test/fixtures/js/lang_features/001-constant_function.js b/test/fixtures/js/lang_features/001-constant_function.js deleted file mode 100644 index c824a0b..0000000 --- a/test/fixtures/js/lang_features/001-constant_function.js +++ /dev/null @@ -1,3 +0,0 @@ -/* Constant function */ -const a = () => "hello" -a() diff --git a/test/fixtures/js/lang_features/001-single-function-declaration.ambient b/test/fixtures/js/lang_features/001-single-function-declaration.ambient new file mode 100644 index 0000000..273f124 --- /dev/null +++ b/test/fixtures/js/lang_features/001-single-function-declaration.ambient @@ -0,0 +1,6 @@ +a[ + in_ call.open call.( + string[a[]]| + open return.open_ + ) +] \ No newline at end of file diff --git a/test/fixtures/js/lang_features/001-single-function-declaration.js b/test/fixtures/js/lang_features/001-single-function-declaration.js new file mode 100644 index 0000000..f2451d8 --- /dev/null +++ b/test/fixtures/js/lang_features/001-single-function-declaration.js @@ -0,0 +1 @@ +const a = () => "a" diff --git a/test/fixtures/js/lang_features/002-fcall_const_func.ambient b/test/fixtures/js/lang_features/002-fcall_const_func.ambient deleted file mode 100644 index 1aebb0c..0000000 --- a/test/fixtures/js/lang_features/002-fcall_const_func.ambient +++ /dev/null @@ -1,21 +0,0 @@ -a[ - in_ call.open call.( - string[hello[]]| - open return.open_ - ) -]| -b[ - in_ call.open call.( - out_ call.in_ a| - call[ - out b.in a.open_.return[open_.in b.in func] - ]| - func[ - in_ a.open a.(open_) - ]| - open func.open return.open_ - ) -]| -call[in b.open_.return[open_.in func]]| -func[in_ b.open b.open_]| -open func diff --git a/test/fixtures/js/lang_features/002-fcall_const_func.js b/test/fixtures/js/lang_features/002-fcall_const_func.js deleted file mode 100644 index 5cd5e82..0000000 --- a/test/fixtures/js/lang_features/002-fcall_const_func.js +++ /dev/null @@ -1,4 +0,0 @@ -/* Function calling a constant function */ -const a = () => "hello" -const b = () => a() -b() diff --git a/test/fixtures/js/lang_features/002-multiple-function-declarations.ambient b/test/fixtures/js/lang_features/002-multiple-function-declarations.ambient new file mode 100644 index 0000000..49dfb53 --- /dev/null +++ b/test/fixtures/js/lang_features/002-multiple-function-declarations.ambient @@ -0,0 +1,12 @@ +a[ + in_ call.open call.( + string[a[]]| + open return.open_ + ) +]| +b[ + in_ call.open call.( + string[b[]]| + open return.open_ + ) +] diff --git a/test/fixtures/js/lang_features/002-multiple-function-declarations.js b/test/fixtures/js/lang_features/002-multiple-function-declarations.js new file mode 100644 index 0000000..25b85e6 --- /dev/null +++ b/test/fixtures/js/lang_features/002-multiple-function-declarations.js @@ -0,0 +1,2 @@ +const a = () => "a" +const b = () => "b" \ No newline at end of file diff --git a/test/fixtures/js/lang_features/003-assign_fvar.ambient b/test/fixtures/js/lang_features/003-assign_fvar.ambient deleted file mode 100644 index 6d32ab0..0000000 --- a/test/fixtures/js/lang_features/003-assign_fvar.ambient +++ /dev/null @@ -1,9 +0,0 @@ -a[ - in_ call.open call.( - string[hello[]]| - open return.open_ - ) -]| -call[in a.open_.return[open_.in func]]| -func[in_ a.open a.open_]| -open func diff --git a/test/fixtures/js/lang_features/003-assign_fvar.js b/test/fixtures/js/lang_features/003-assign_fvar.js deleted file mode 100644 index 4319f03..0000000 --- a/test/fixtures/js/lang_features/003-assign_fvar.js +++ /dev/null @@ -1,4 +0,0 @@ -/* Assign function to a variable */ -const a = () => "hello" -const b = a -b() diff --git a/test/fixtures/js/lang_features/003-string-function-declarations.ambient b/test/fixtures/js/lang_features/003-string-function-declarations.ambient new file mode 100644 index 0000000..a46ecc5 --- /dev/null +++ b/test/fixtures/js/lang_features/003-string-function-declarations.ambient @@ -0,0 +1,38 @@ +a[ + in_ call.open call.( + string[a[]]| + open return.open_ + ) +]| +b[ + in_ call.open call.( + string[b[]]| + open return.open_ + ) +]| +c[ + in_ call.open call.( + string[ + concat[ + left[string[a[]]]| + right[string[b[]]] + ] + ]| + open return.open_ + ) +]| +d[ + in_ call.open call.( + string[ + concat[ + left[ + string[concat[ + left[string[a[]]]| + right[string[b[]]] + ]]]| + right[string[c[]]] + ] + ]| + open return.open_ + ) +] diff --git a/test/fixtures/js/lang_features/003-string-function-declarations.js b/test/fixtures/js/lang_features/003-string-function-declarations.js new file mode 100644 index 0000000..3e7776d --- /dev/null +++ b/test/fixtures/js/lang_features/003-string-function-declarations.js @@ -0,0 +1,4 @@ +const a = () => "a" +const b = () => 'b' +const c = () => "a" + "b" +const d = () => "a" + "b" + "c" diff --git a/test/fixtures/js/lang_features/004-assign_fvar_alias.ambient b/test/fixtures/js/lang_features/004-assign_fvar_alias.ambient deleted file mode 100644 index 6d32ab0..0000000 --- a/test/fixtures/js/lang_features/004-assign_fvar_alias.ambient +++ /dev/null @@ -1,9 +0,0 @@ -a[ - in_ call.open call.( - string[hello[]]| - open return.open_ - ) -]| -call[in a.open_.return[open_.in func]]| -func[in_ a.open a.open_]| -open func diff --git a/test/fixtures/js/lang_features/004-assign_fvar_alias.js b/test/fixtures/js/lang_features/004-assign_fvar_alias.js deleted file mode 100644 index 20027ee..0000000 --- a/test/fixtures/js/lang_features/004-assign_fvar_alias.js +++ /dev/null @@ -1,5 +0,0 @@ -/* Assign a variable to a variable that has a function */ -const a = () => "hello" -const b = a -const c = b -c() diff --git a/test/fixtures/js/lang_features/004-int-function-declarations.ambient b/test/fixtures/js/lang_features/004-int-function-declarations.ambient new file mode 100644 index 0000000..87e6b6a --- /dev/null +++ b/test/fixtures/js/lang_features/004-int-function-declarations.ambient @@ -0,0 +1,87 @@ +a[ + in_ call.open call.( + int[i42[]]| + open return.open_ + ) +]| +b[ + in_ call.open call.( + int[ + plus[ + left[int[i2[]]]| + right[int[i2[]]] + ] + ]| + open return.open_ + ) +]| +c[ + in_ call.open call.( + int[ + multiply[ + left[int[i2[]]]| + right[int[i2[]]] + ] + ]| + open return.open_ + ) +]| +d[ + in_ call.open call.( + int[ + plus[ + left[ + int[multiply[left[int[i1[]]]|right[int[i2[]]]]] + ]| + right[int[i3[]]]] + ]| + open return.open_)]| +e[ + in_ call.open call.( + int[ + plus[ + left[int[i1[]]]| + right[ + int[ + multiply[ + left[int[i2[]]]| + right[int[i3[]]] + ] + ] + ] + ] + ]| + open return.open_)]| +f[ + in_ call.open call.( + ( + x[in_ arg.open arg.open_]| + open x + )| + open return.open_) +]| +g[ + in_ call.open call.( + func[ + ( + x[in_ arg.open arg.open_]| + open x + )| + open_ + ]| + open return.open_) +]| +h[ + in_ call.open call.( + func[ + func[ + ( + x[in_ arg.open arg.open_]| + open x + )| + open_ + ]| + open_ + ]| + open return.open_) +] \ No newline at end of file diff --git a/test/fixtures/js/lang_features/004-int-function-declarations.js b/test/fixtures/js/lang_features/004-int-function-declarations.js new file mode 100644 index 0000000..76df9e2 --- /dev/null +++ b/test/fixtures/js/lang_features/004-int-function-declarations.js @@ -0,0 +1,8 @@ +const a = () => 42 +const b = () => 2 + 2 +const c = () => 2 * 2 +const d = () => 1 * 2 + 3 +const e = () => 1 + 2 * 3 +const f = (x) => x +const g = () => (x) => x +const h = () => () => (x) => x \ No newline at end of file diff --git a/test/fixtures/js/lang_features/005-function-call.ambient b/test/fixtures/js/lang_features/005-function-call.ambient new file mode 100644 index 0000000..5421879 --- /dev/null +++ b/test/fixtures/js/lang_features/005-function-call.ambient @@ -0,0 +1,18 @@ +( + call[in a.open_.return[open_.in func]]| + func[in_ a.open a.open_]| + open func +)| +b[ + out_ call.in_ a| + ( + call[ + out b.in a.open_.return[open_.in b.in func] + ]| + func[in_ a.open a.open_]| + open func + ) +] + + + diff --git a/test/fixtures/js/lang_features/005-function-call.js b/test/fixtures/js/lang_features/005-function-call.js new file mode 100644 index 0000000..c66a4cc --- /dev/null +++ b/test/fixtures/js/lang_features/005-function-call.js @@ -0,0 +1,2 @@ +a() +let b = a() \ No newline at end of file diff --git a/test/fixtures/js/lang_features/005-return_ref_constant.ambient b/test/fixtures/js/lang_features/005-return_ref_constant.ambient deleted file mode 100644 index 8d3c4a1..0000000 --- a/test/fixtures/js/lang_features/005-return_ref_constant.ambient +++ /dev/null @@ -1,9 +0,0 @@ -b[ - in_ call.open call.( - string[hello[]]| - open return.open_ - ) -]| -call[in b.open_.return[open_.in func]]| -func[in_ b.open b.open_]| -open func diff --git a/test/fixtures/js/lang_features/005-return_ref_constant.js b/test/fixtures/js/lang_features/005-return_ref_constant.js deleted file mode 100644 index d017028..0000000 --- a/test/fixtures/js/lang_features/005-return_ref_constant.js +++ /dev/null @@ -1,4 +0,0 @@ -/* Return a (constant) value from a from a function */ -const a = "hello" -const b = () => a -b() diff --git a/test/fixtures/js/lang_features/006-fcall_fcall.ambient b/test/fixtures/js/lang_features/006-fcall_fcall.ambient deleted file mode 100644 index d2f7409..0000000 --- a/test/fixtures/js/lang_features/006-fcall_fcall.ambient +++ /dev/null @@ -1,32 +0,0 @@ -a[ - in_ call.open call.( - string[hello[]]| - open return.open_ - ) -]| -b[ - in_ call.open call| - out_ call.in_ a| - call[ - out b.in a.open_.return[open_.in b.in func] - ]| - func[ - in_ a.open a.(open_) - ]| - open func.open return.open_ -]| -c[ - in_ call.open call.( - out_ call.in_ b| - call[ - out c.in b.open_.return[open_.in c.in func] - ]| - func[ - in_ b.open b.open_ - ]| - open func.open return.open_ - ) -]| -call[in c.open_.return[open_.in func]]| -func[in_ c.open c.open_]| -open func diff --git a/test/fixtures/js/lang_features/006-fcall_fcall.js b/test/fixtures/js/lang_features/006-fcall_fcall.js deleted file mode 100644 index deebe44..0000000 --- a/test/fixtures/js/lang_features/006-fcall_fcall.js +++ /dev/null @@ -1,5 +0,0 @@ -/* Constant function, variable expression, return variable */ -const a = () => "hello" -const b = a() -const c = () => b -c() diff --git a/test/js2amb.spec.js b/test/js2amb.spec.js index 81024d9..3f8957c 100644 --- a/test/js2amb.spec.js +++ b/test/js2amb.spec.js @@ -4,35 +4,34 @@ const js2amb = require('../src') const fs = require('fs') const FIXTURES_PATH = 'test/fixtures/js/' const normalizeAmbientsCode = code => code.replace(/\r?\n\s*|\r\s*/g, '').replace(/\s+/g, ' ') -const testFixtures = (path) => { +const testFixtures = (path, tests) => { const fixtures = fs.readdirSync(path) const fixturePairs = fixtures - .filter(item => item.endsWith('.ambient')) - .map((filename, index) => { - return { - ambient: filename, - js: fixtures[index * 2 + 1] - } - }) + .filter(item => item.endsWith('.ambient')) + .map((filename, index) => { + return { + ambient: filename, + js: fixtures[index * 2 + 1] + } + }) fixturePairs.forEach((filePair) => { console.log(`Compiling: ${path + filePair.js}`) const javascript = fs.readFileSync(path + filePair.js).toString().trim() const expectedAmbient = normalizeAmbientsCode(fs.readFileSync(path + filePair.ambient).toString()) - assert.strictEqual( + it(`${tests}: ${filePair.js}`, () => { + + assert.strictEqual( js2amb(javascript), expectedAmbient, `Compiling ${filePair.js} didn't result in ${filePair.ambient}` - ) + ) + }) }) } describe('JS Compiler', function () { - it('language concepts to ambient syntax', () => { - testFixtures(FIXTURES_PATH + 'lang_features/') - }) - it('computation abstractions to ambient syntax', () => { - testFixtures(FIXTURES_PATH + 'abstractions/') - }) + testFixtures(FIXTURES_PATH + 'lang_features/','language concepts') + testFixtures(FIXTURES_PATH + 'abstractions/', 'computation abstractions') })