Skip to content

Commit

Permalink
Placeholder args
Browse files Browse the repository at this point in the history
commit 5fa8631
Author: William C. Johnson <wcjohnson@oigroup.net>
Date:   Sun Jul 16 20:42:22 2017 -0400

    Allow placeholder to be changed

commit ee2f7a7
Author: William C. Johnson <wcjohnson@oigroup.net>
Date:   Sun Jul 16 20:20:23 2017 -0400

    Support rest/spread placeholder args

commit cd625f1
Author: William C. Johnson <wcjohnson@oigroup.net>
Date:   Sun Jul 16 19:41:02 2017 -0400

    Initial placeholderArgs implementation + testing
  • Loading branch information
wcjohnson committed Jul 17, 2017
1 parent 0a5f7a0 commit fbc0be5
Show file tree
Hide file tree
Showing 33 changed files with 311 additions and 8 deletions.
11 changes: 11 additions & 0 deletions src/config.lsc
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ export getMetadata() ->
description: "Resolve object-block ambiguity by parsing object literals whenever possible."
valueType: "boolean"
}
placeholderArgs: {
description: "Specify arguments for a function using placeholders in the body."
valueType: "boolean"
}
placeholder: {
description: "Specify an identifier to be transformed into PlaceholderExpression."
valueType: "string"
}
}
}

Expand All @@ -83,6 +91,9 @@ export getParserOpts(pluginOpts, initialParserOpts) ->
if pluginOpts?.flippedImports: plugins.push("flippedImports")
if pluginOpts?.enhancedComprehension: plugins.push("enhancedComprehension")
if pluginOpts?.preferObjectLiteral: plugins.push("objectBlockAmbiguity_preferObject")
if pluginOpts?.placeholderArgs: plugins.push("syntacticPlaceholder")
if pluginOpts?.placeholder:
parserOpts.placeholder = pluginOpts.placeholder

// TODO: watch upstream on pattern matching; default to their syntax when complete
// patternMatchingVersion = pluginOpts?.patternMatching or "v4"
Expand Down
13 changes: 13 additions & 0 deletions src/fixAst.lsc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Perform non-semantic AST transformations like ensuring function bodies
// are blocks, etc.
import { isNamedArrowFunction } from './functions'
import { toBlockStatement } from './blocks'

export fixAst(programPath) ->
programPath.traverse({
Method(path): void ->
{ node } = path
if isNamedArrowFunction(node):
node.body = toBlockStatement(node.body)
path.replaceWith(node)
})
25 changes: 17 additions & 8 deletions src/index.lsc
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { placeAtNode as atNode, placeTreeAtLocWhenUnplaced as allAtLoc, getLoc,

import {
addImplicitReturns, replaceWithPlainFunction, replaceWithArrowFunction,
replaceWithBoundFunction, isNamedArrowFunction
replaceWithBoundFunction
} from "./functions";
import { blockToExpression, toBlockStatement, iife } from "./blocks";
import { blockToExpression, iife } from "./blocks";
import { getShadowingIdentifiers, checkFalsePositiveReference } from "./variables";
import { ensureConstructor, bindMethodsInConstructor, bindMethods } from "./classes";
import * as matching from "./match";
Expand All @@ -18,6 +18,8 @@ import { resetHelpers } from "./helpers";
import { markIdentifier } from "./stdlib";
import { locatePluginOpts, getParserOpts, parseConfigurationDirectives } from './config'
import { getFileTypeInfo, createCompilerState, initializeCompilerState, postprocess } from './compilerState'
import { fixAst } from './fixAst'
import { transformPlaceholders } from './placeholders'

Lightscript(babel) ->
const { types: t } = babel;
Expand All @@ -40,15 +42,27 @@ Lightscript(babel) ->
// TODO: helpers -> compilerState
resetHelpers(path)

path.traverse({
//// AST transforms
// Perform basic ast fixups (block bodies, etc)
fixAst(path)

// Transform placeholder expressions first.
if compilerState.opts.placeholderArgs:
transformPlaceholders(path)

// Main LSC transforms
path.traverse({
ForInArrayStatement(path): void ->
// TODO: push the linter conditional down into the transform
// and get linter status from compilerState
if opts.__linter:
lintForInArrayStatement(path)
else:
transformForInArrayStatement(path)

ForInObjectStatement(path): void ->
// TODO: push the linter conditional down into the transform
// and get linter status from compilerState
if opts.__linter:
lintForInObjectStatement(path)
else:
Expand Down Expand Up @@ -124,11 +138,6 @@ Lightscript(babel) ->
elif (path.node.generator):
replaceWithBoundFunction(path)

Method(path): void ->
if isNamedArrowFunction(path.node):
// XXX: non-idiomatic ast mutation, could cause problems...
path.node.body = toBlockStatement(path.node.body)

ClassBody(path): void ->
let fatArrows = [], fatStaticArrows = [], constructorPath;
path.node.body.forEach((method, i) => {
Expand Down
4 changes: 4 additions & 0 deletions src/lscNodeTypes.lsc
Original file line number Diff line number Diff line change
Expand Up @@ -326,3 +326,7 @@ export registerLightscriptNodeTypes(t): void ->
definePluginType("MatchPlaceholderExpression", {
aliases: ["Expression"]
});

definePluginType("PlaceholderExpression", {
aliases: ["Expression"]
});
73 changes: 73 additions & 0 deletions src/placeholders.lsc
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import t from './types'

registerPlaceholder(functions, path, programPath) ->
fnPath = path.getFunctionParent()
if fnPath == programPath || !fnPath.node || !fnPath.node.params:
throw path.buildCodeFrameError("Placeholders cannot be used outside functions.")

if fnPath.node.params.length:
throw path.buildCodeFrameError("Placeholders cannot be used in functions with arguments.")

if path.parentPath?.node?.type == "SpreadElement":
if path.node?.index?:
throw path.buildCodeFrameError("Cannot use indices with spread placeholders.")

let fnInfo = functions.get(fnPath)
if fnInfo:
fnInfo.placeholders.push(path)
else:
functions.set(fnPath, { functionPath: fnPath, placeholders: [ path ] })

liftPlaceholders(fnPath, phPaths) ->
// Collate placeholders
ixPhPaths = phPaths.filter(x -> x.node.index?)
nixPhPaths = phPaths.filter(x-> not x.node.index?)
ixPhPaths.sort((a, b) -> a.node.index - b.node.index)
nixPhPaths.sort((a, b) ->
// SpreadElements are last
if a.parentPath?.node.type == "SpreadElement":
1
elif b.parentPath?.node.type == "SpreadElement":
-1
else:
0
)
sortedPhPaths = ixPhPaths.concat(nixPhPaths)

// Formulate args
args = []
let prevIndex = undefined, restArg = undefined
for elem phPath in sortedPhPaths:
{ node } = phPath
if phPath.parentPath?.node.type == "SpreadElement":
if not restArg:
now restArg = fnPath.scope.generateUidIdentifier("arg")
args.push(t.restElement(restArg))
phPath.replaceWith(restArg)
elif node.index? and node.index == prevIndex:
// Repeated use of an index = same arg.
phPath.replaceWith(args[args.length - 1])
else:
args.push(fnPath.scope.generateUidIdentifier("arg"))
now prevIndex = node.index
phPath.replaceWith(args[args.length - 1])

// Lift args to function level
fnNode = fnPath.node
fnNode.params = args
fnPath.replaceWith(fnNode)

// Collect all functions with placeholders into a Map, one entry per function
findFunctionsWithPlaceholders(programPath) ->
fns = new Map()
programPath.traverse({
PlaceholderExpression(path): void ->
registerPlaceholder(fns, path, programPath)
})
fns

export transformPlaceholders(programPath) ->
fns = programPath~findFunctionsWithPlaceholders()
fns.forEach( fnInfo ->
fnInfo.functionPath~liftPlaceholders(fnInfo.placeholders)
)
30 changes: 30 additions & 0 deletions test/fixtures/placeholder-args/arrow-types/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-> _

=> _

f() -> _

g() => _

fa() -/> _

ga() =/> _

fg() -*> _

gg() =*> _

fga() -*/> _

gga() -*/> _

{
h() -> _
i() => _
j: -> _
}

class X {
k() -> _
l() => _
}
60 changes: 60 additions & 0 deletions test/fixtures/placeholder-args/arrow-types/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
var _obj;

(function (_arg) {
return _arg;
});

(_arg2) => _arg2;

function f(_arg3) {
return _arg3;
}

const g = (_arg4) => _arg4;

async function fa(_arg5) {
return _arg5;
}

const ga = async (_arg6) => _arg6;

function* fg(_arg7) {
return _arg7;
}

function* gg(_arg8) {
return _arg8;
}

gg = gg.bind(this)async function* fga(_arg9) {
return _arg9;
}

async function* gga(_arg10) {
return _arg10;
}

_obj = {
h(_arg11) {
return _arg11;
},
i(_arg12) {
return _arg12;
},
j: function (_arg13) {
return _arg13;
}
}, _obj.i = _obj.i.bind(_obj), _obj;

class X {
constructor() {
this.l = this.l.bind(this);
}

k(_arg14) {
return _arg14;
}
l(_arg15) {
return _arg15;
}
}
3 changes: 3 additions & 0 deletions test/fixtures/placeholder-args/basic/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-> _

-> _(_)
7 changes: 7 additions & 0 deletions test/fixtures/placeholder-args/basic/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
(function (_arg) {
return _arg;
});

(function (_arg2, _arg3) {
return _arg2(_arg3);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'use @oigroup/lightscript with placeholder: "P"'

-> P
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use @oigroup/lightscript with placeholder: "P"';

(function (_arg) {
return _arg;
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-> P
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(function (_arg) {
return _arg;
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": [["lightscript", { "placeholderArgs": true, "placeholder": "P" }]]
}
3 changes: 3 additions & 0 deletions test/fixtures/placeholder-args/expr/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
x.map(-> _ + _1)

y.map(=> _ + _1)
5 changes: 5 additions & 0 deletions test/fixtures/placeholder-args/expr/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
x.map(function (_arg, _arg2) {
return _arg2 + _arg;
});

y.map((_arg3, _arg4) => _arg4 + _arg3);
3 changes: 3 additions & 0 deletions test/fixtures/placeholder-args/mixed/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-> _._1

-> _(_._)._1[_2]
5 changes: 5 additions & 0 deletions test/fixtures/placeholder-args/mixed/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
(function (_arg, _arg2) {
return _arg2._arg;
});(function (_arg3, _arg4, _arg5, _arg6, _arg7) {
return _arg5(_arg6._arg7)._arg3[_arg4];
});
1 change: 1 addition & 0 deletions test/fixtures/placeholder-args/nested/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-> _.map(-> _ + 1)
5 changes: 5 additions & 0 deletions test/fixtures/placeholder-args/nested/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
(function (_arg) {
return _arg.map(function (_arg2) {
return _arg2 + 1;
});
});
3 changes: 3 additions & 0 deletions test/fixtures/placeholder-args/options.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": [["lightscript", { "placeholderArgs": true }]]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
_ + 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"throws": "Placeholders cannot be used outside functions."
}
7 changes: 7 additions & 0 deletions test/fixtures/placeholder-args/positional/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-> _2._1

-> _1._1

-> _2._2._1._1

-> _8(_7)
9 changes: 9 additions & 0 deletions test/fixtures/placeholder-args/positional/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(function (_arg, _arg2) {
return _arg2._arg;
});(function (_arg3) {
return _arg3._arg3;
});(function (_arg4, _arg5) {
return _arg5._arg5._arg4._arg4;
});(function (_arg6, _arg7) {
return _arg7(_arg6);
});
2 changes: 2 additions & 0 deletions test/fixtures/placeholder-args/setter-fixme/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class X:
set y() -> _
3 changes: 3 additions & 0 deletions test/fixtures/placeholder-args/setter-fixme/options.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"throws": "setter should have exactly one param (2:2)"
}
2 changes: 2 additions & 0 deletions test/fixtures/placeholder-args/spread-index-illegal/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
->
a = [..._1]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"throws": "Cannot use indices with spread placeholders."
}
9 changes: 9 additions & 0 deletions test/fixtures/placeholder-args/spread/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-> f(_2, _1, _, ..._)

->
a = [..._]
f(_2, _1, _, a)

->
a = [..._]
b = [..._]
Loading

0 comments on commit fbc0be5

Please sign in to comment.