Visitar URL original
Transform usage example · Issue #14 · AssemblyScript/examples · GitHub
Skip to content

Transform usage example #14

Description

@jnordberg

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.

Activity

  1. yjhmelody commented on Aug 2, 2021

    @yjhmelody
  2. dcodeIO commented on Aug 2, 2021

    @dcodeIO
    Member

    More transform that may serve as examples:

  3. jnordberg commented on Aug 3, 2021

    @jnordberg
    Author

    Thanks for those references! I'm experimenting with implementing something akin to C++'s constexpr as 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
  4. MaxGraey commented on Aug 3, 2021

    @MaxGraey
    Member

    Perhaps this is an interesting idea for implementing Partial Evaluation
    (which can be "online", "offline" and most flexibly "hybrid")

  5. yjhmelody commented on Dec 9, 2022

    @yjhmelody

    @dcodeIO Hi, Maybe Need a new example to show transform in typescript.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions