Skip to content

Instantly share code, notes, and snippets.

@erukiti
Last active October 11, 2017 10:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erukiti/2a1b0e881540b9db9793d32f4e8186c7 to your computer and use it in GitHub Desktop.
Save erukiti/2a1b0e881540b9db9793d32f4e8186c7 to your computer and use it in GitHub Desktop.
演算子オーバライドっぽいbabelプラグインの実験

HOW TO USE

$ npm i babel-core@next babel-types@next babylon@next babel-plugin-syntax-flow babel-plugin-syntax-typescript -S
$ node override.js
const {transform} = require('babel-core')
const WasCreated = Symbol('WasCreated')
const src = `
class Integer {
value: number
constructor(value: number) {
this.value = Math.floor(value)
}
['+'](other: Integer) {
return new Integer(this.value + other.value)
}
['*'](other: Integer) {
return new Integer(this.value * other.value)
}
['-'](other: Integer) {
return new Integer(this.value - other.value)
}
['/'](other: Integer) {
return new Integer(this.value / other.value)
}
}
const hoge: Integer = new Integer(1)
const fuga: Integer = new Integer(2)
const piyo: Integer = new Integer(3)
console.log(hoge + fuga * (piyo + hoge) / fuga)
`
const overridePlugin = ({types: t}) => {
const getTypeAnnotation = nodePath => {
if (!t.isIdentifier(nodePath.node)) {
return null
}
const {identifier} = nodePath.scope.bindings[nodePath.node.name]
if(!('typeAnnotation') in identifier) {
return null
}
if (identifier.typeAnnotation.type === 'TypeAnnotation' &&
identifier.typeAnnotation.typeAnnotation.type === 'GenericTypeAnnotation' &&
t.isIdentifier(identifier.typeAnnotation.typeAnnotation.id)
) {
return identifier.typeAnnotation.typeAnnotation.id.name
}
if (identifier.typeAnnotation.type === 'TSTypeAnnotation' &&
identifier.typeAnnotation.typeAnnotation.type === 'TSTypeReference' &&
t.isIdentifier(identifier.typeAnnotation.typeAnnotation.typeName)
) {
return identifier.typeAnnotation.typeAnnotation.typeName.name
}
return null
}
const _isMayBeTransformed = (node) => {
return t.isCallExpression(node) &&
t.isMemberExpression(node.callee) &&
t.isStringLiteral(node.callee.property) &&
node.arguments.length === 1
}
const _getType = (nodePath, overrideClasses) => {
const typeAnnotation = getTypeAnnotation(nodePath)
if (typeAnnotation && overrideClasses.indexOf(typeAnnotation) !== -1) {
return typeAnnotation
}
const {node} = nodePath
if (!_isMayBeTransformed(node)) {
return null
}
const argType = _getType(nodePath.get('arguments')[0], overrideClasses)
const objType = _getType(nodePath.get('callee.object'), overrideClasses)
if (argType && argType === objType && overrideClasses.indexOf(argType) !== -1) {
return argType
}
return null
}
const visitor = {
BinaryExpression: {
exit: (nodePath, state) => {
if (nodePath[WasCreated]) {
return
}
const leftType = _getType(nodePath.get('left'), state.opts.overrideClasses)
const rightType = _getType(nodePath.get('right'), state.opts.overrideClasses)
if (!leftType || !rightType || leftType !== rightType) {
return
}
const newAst = t.callExpression(t.memberExpression(nodePath.node.left, t.stringLiteral(nodePath.node.operator), true), [nodePath.node.right])
nodePath.replaceWith(newAst)
nodePath[WasCreated] = true
}
}
}
return {
visitor,
}
}
const {code} = transform(src, {plugins: [[plugin, {overrideClasses: ['Integer']}], 'syntax-typescript']})
// const {code} = transform(src, {plugins: [[overridePlugin, {overrideClasses: ['Integer']}], 'syntax-flow']})
console.log('before:')
console.log(src.trim())
console.log('\nafter:')
console.log(code)
class Integer {
value: number;
constructor(value: number) {
this.value = Math.floor(value);
}
['+'](other: Integer) {
return new Integer(this.value + other.value);
}
['*'](other: Integer) {
return new Integer(this.value * other.value);
}
['-'](other: Integer) {
return new Integer(this.value - other.value);
}
['/'](other: Integer) {
return new Integer(this.value / other.value);
}
}
const hoge: Integer = new Integer(1);
const fuga: Integer = new Integer(2);
const piyo: Integer = new Integer(3);
console.log(hoge["+"](fuga["*"](piyo["+"](hoge))["/"](fuga)));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment