Repository navigation
Transform usage example #14
Description
Activity
@jnordberg See https://github.com/willemneal/visitor-as, it can help you.
Reacted by Johan NordbergMore transform that may serve as examples:
Reacted by Johan Nordberg- addedquestionFurther information is requestedFurther information is requested
on Aug 2, 2021 Thanks for those references! I'm experimenting with implementing something akin to C++'s
constexpras a decorator and was able to do it pretty easily, very cool to be able to extend the language like this :)Attaching my transform that does this for posterity, it's super unsafe easy to break but works as a proof of concept, I think it could be made safe by actually compiling the code into another wasm module that's executed by the transform and only allowing a safe set of instructions to be used in the body of functions marked with
@staticEval.Example usage:
// @ts-ignore: decorator @staticEval function hash(value: string): i32 { let l = value.length, rv = 0 for (let i = 0; i < l; i++) { rv = (rv << 5) - rv + value.charCodeAt(i) } return rv } function getString() { return 'hello runtime!' } const hash1 = hash('hello') // i32.const 99162322 const hash2 = hash(getString()) // call $assembly/index/hash
Transform:
import { Parser, NodeKind, FunctionDeclaration, IdentifierExpression, CallExpression, Expression, IntegerLiteralExpression, LiteralExpression, LiteralKind, FloatLiteralExpression, StringLiteralExpression, } from 'assemblyscript' import { SimpleParser, TransformVisitor } from 'visitor-as' class StaticEvalTransform extends TransformVisitor { staticFunctions: { [name: string]: Function } = {} visitCallExpression(node: CallExpression): Expression { if (node.expression instanceof IdentifierExpression) { const fn = this.staticFunctions[node.expression.text] // someone is calling a staticEval function if (fn) { // only allow constant as arguments let args = node.args .filter((arg) => arg.kind == NodeKind.LITERAL) .map((value) => { const arg = value as LiteralExpression switch (arg.literalKind) { case LiteralKind.INTEGER: // yes this will explode :) return parseInt( (arg as IntegerLiteralExpression).value.toString() ) case LiteralKind.FLOAT: return parseFloat( (arg as FloatLiteralExpression).value.toString() ) case LiteralKind.STRING: return (arg as StringLiteralExpression).value } }) .filter((arg) => arg !== undefined) if (args.length === node.args.length) { // call fn with args and inline the result let result = fn(...args) let res = SimpleParser.parseExpression( JSON.stringify(result) ) res.range = node.range return res } } } return super.visitCallExpression(node) } afterParse(parser: Parser): void { this.staticFunctions = {} const sources = this.program.sources.filter((node) => !node.isLibrary) // find all top-level staticEval functions for (const source of sources) { for (const statement of source.statements) { if (statement instanceof FunctionDeclaration) { let staticEval = statement.decorators?.some( (decorator) => decorator.name.kind == NodeKind.IDENTIFIER && (<IdentifierExpression>decorator.name).text === 'staticEval' ) if (!staticEval) continue let params = statement.signature.parameters.map( (param) => param.name.text ) this.staticFunctions[statement.name.text] = Function( ...params, statement.body.range.toString() ) } } } // visit all statements this.visit(sources) } } export = StaticEvalTransform
Reacted by Max GraeyPerhaps this is an interesting idea for implementing Partial Evaluation
(which can be "online", "offline" and most flexibly "hybrid")@dcodeIO Hi, Maybe Need a new example to show transform in typescript.
It would be awesome to have a usage example for transforms that showed how to transform some code. I want to try implementing a custom decorator as a preprocessor macro (as hinted by the docs you can do) but I'm not sure where to start.