#! /usr/bin/env python

# Copyright (C) 2016-2017 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1.  Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
# 2.  Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# This tool has a couple of helpful macros to process Wasm files from the wasm.json.

import json
import math
import re

class Wasm:
    def __init__(self, scriptName, jsonPath):
        wasmFile = open(jsonPath, "r")
        wasm = json.load(open(jsonPath, "r"))
        wasmFile.close()
        for pre in wasm["preamble"]:
            if pre["name"] == "version":
                self.expectedVersionNumber = str(pre["value"])
        self.preamble = wasm["preamble"]
        self.types = wasm["type"]
        self.opcodes = wasm["opcode"]
        self.header = """/*
 * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

// DO NO EDIT! - This file was generated by """ + scriptName

    def opcodeIterator(self, filter, ret=None):
        # We need to do this because python is dumb and won't let me use self in the lambda, which is ridiculous.
        if ret == None:
            ret = lambda op: {"name": op, "opcode": self.opcodes[op]}
        for op in self.opcodes.keys():
            if filter(self.opcodes[op]):
                yield ret(op)

    def toCpp(self, name):
        camelCase = re.sub(r'([^a-z0-9].)', lambda c: c.group(0)[1].upper(), name)
        CamelCase = camelCase[:1].upper() + camelCase[1:]
        return CamelCase


def isNormal(op):
    return op["category"] == "arithmetic" or op["category"] == "comparison" or op["category"] == "conversion"


def isUnary(op):
    return isNormal(op) and len(op["parameter"]) == 1


def isBinary(op):
    return isNormal(op) and len(op["parameter"]) == 2


def isSimple(op):
    return "b3op" in op


def memoryLog2Alignment(op):
    assert op["opcode"]["category"] == "memory"
    match = re.match(r'^[if]([36][24])\.[^0-9]+([0-9]+)?_?[us]?$', op["name"])
    memoryBits = int(match.group(2) if match.group(2) else match.group(1))
    assert 2 ** math.log(memoryBits, 2) == memoryBits
    return str(int(math.log(memoryBits / 8, 2)))
