Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Usage question: Changing type checking of operators #15

Open
cshaa opened this issue Jun 12, 2020 · 4 comments
Open

Usage question: Changing type checking of operators #15

cshaa opened this issue Jun 12, 2020 · 4 comments

Comments

@cshaa
Copy link

cshaa commented Jun 12, 2020

I'm frequently working with symbolic mathematical expressions. Sadly, JavaScript doesn't support operator overloading, so I have to write a lot of ugly code like add(u, mul(2, v)). I would love to make a transformer that changes operators into function calls:

u + 2*v   →  op_add( u, op_mul(2, v) )

This transformation alone is quite easy, I just have to find BinaryExpression(A, op, B) and turn it into CallExpression(fn, [A, B]). However I would love to use the type checking of the resulting function. In the handbook you say that:

“Type checking should not happen after transforming. If it does it's more than likely a bug - file an issue!”

Does this mean that my transformed code will still be type-checked as if it were a + b, not op_add(a, b)? Or is it just a misunderstanding on my side?

Thanks!

@itsdouges
Copy link
Owner

hey mate! no you're exactly right - it will only type check as if it were a + b - by the time we run our transformers typechecking has already happened and there isn't any opportunity to change it

we can however write type def transformers if you want to change what typedefs are generated

@cshaa
Copy link
Author

cshaa commented Jul 25, 2020

Hey! Thank you so much for your reply – and for the handbook! I'd be lost without it.

These are some unfortunate news. In order for my “modified TypeScript” to be useful at all, I need custom type-checking… This means that transformers can't do the job for me and I'll have to use the Compiler API directly and make TypeScript generate types when I want to, right?

I'll try messing with the compiler and if I manage to do something interesting, I'll let you know 😁️

@itsdouges
Copy link
Owner

yeah you basically need to use it directly.. no idea how it will go! if you find anything interesting report back :D

@cshaa
Copy link
Author

cshaa commented Aug 5, 2020

With the help of the StackOverflow community (see my question) I was able to get the compilation working. I had to write a few utility functions that are available in a gist, with them the code looks like this:

import * as ts from "typescript"
import { rewriteNode, rewriteChildren } from "./rewrite";


function compile(fileNames: string[], options: ts.CompilerOptions): void
{
  let program = ts.createProgram(fileNames, options)

  for (let input of program.getSourceFiles())
  if (input.fileName.endsWith('.m.ts'))
    rewriteNode(input, visitor)

  logDiagnostics(ts.getPreEmitDiagnostics(program))

  let emitResult = program.emit()

  logDiagnostics(emitResult.diagnostics)

  let exitCode = emitResult.emitSkipped ? 1 : 0;
  console.log(`Process exiting with code '${exitCode}'.`)
  process.exit(exitCode)
}

const operatorNameMap: { [op in ts.SyntaxKind]?: string } = {
  [ts.SyntaxKind.PlusToken]: 'op_plus',
  [ts.SyntaxKind.MinusToken]: 'op_minus',
  [ts.SyntaxKind.AsteriskToken]: 'op_asterisk',
  [ts.SyntaxKind.SlashToken]: 'op_slash'
}

function visitor(node: ts.Node): ts.Node {
  if (node.kind === ts.SyntaxKind.BinaryExpression) {
    let expr = node as ts.BinaryExpression;
    const name = operatorNameMap[expr.operatorToken.kind]

    if (name !== undefined) {
      const identifier = ts.createIdentifier(name);
      return ts.createCall(identifier, undefined, [expr.left, expr.right]);
    }
  }

  return rewriteChildren(node, visitor);
}

function logDiagnostics(arr: readonly ts.Diagnostic[]) { /* long uninteresting code */ }

compile(['sample.m.ts'], {
  target: ts.ScriptTarget.ES5,
  module: ts.ModuleKind.CommonJS
})

Now I need to hook it up to the language services…

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants