#!/usr/bin/python # This file is part of Espruino, a JavaScript interpreter for Microcontrollers # # Copyright (C) 2013 Gordon Williams # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # # ---------------------------------------------------------------------------------------- # Scans files for comments of the form /*JSON......*/ and then builds a tree structure of ifs to # efficiently detect the symbols without using RAM. See common.py for formatting # ---------------------------------------------------------------------------------------- import subprocess; import re; import json; import sys; import os; sys.path.append("."); import common # Scans files for comments of the form /*JSON......*/ and then builds a tree structure of ifs to # efficiently detect the symbols without using RAM. See common.py for formatting jsondatas = common.get_jsondata(False) includes = common.get_includes_from_jsondata(jsondatas) # ------------------------------------------------------------------------------------------------------ def treewalk(tree, name, jsondata): if len(name)==0: tree[""] = jsondata else: firstchar = name[:1] if not firstchar in tree: tree[firstchar] = {} treewalk(tree[firstchar], name[1:], jsondata) # ------------------------------------------------------------------------------------------------------ # Creates something like 'name[0]=='s' && name[1]=='e' && name[2]=='t' && name[3]==0' def createStringCompare(varName, checkOffsets, checkCharacters): checks = [] # if we're doing multiple checks, batch up into int compare # NOTE: batching up into 64 bit compare doesn't help here # 4 byte check while len(checkOffsets)>3: checkOffset = checkOffsets.pop(0) checkOffsets.pop(0) checkOffsets.pop(0) checkOffsets.pop(0) checkWC = [checkCharacters.pop(0),checkCharacters.pop(0),checkCharacters.pop(0),checkCharacters.pop(0)] checks.append("CMP4("+varName+"["+str(checkOffset)+"],'"+("','".join(checkWC))+"')") # 3 byte check while len(checkOffsets)>2: checkOffset = checkOffsets.pop(0) checkOffsets.pop(0) checkOffsets.pop(0) checkWC = [checkCharacters.pop(0),checkCharacters.pop(0),checkCharacters.pop(0)] checks.append("CMP3("+varName+"["+str(checkOffset)+"],'"+("','".join(checkWC))+"')") # 2 byte check while len(checkOffsets)>1: checkOffset = checkOffsets.pop(0) checkOffsets.pop(0) checkWC = [checkCharacters.pop(0),checkCharacters.pop(0)] checks.append("CMP2("+varName+"["+str(checkOffset)+"],'"+("','".join(checkWC))+"')") # finish up with single checks while len(checkOffsets)>0: checkOffset = checkOffsets.pop(0) checkCharacter = checkCharacters.pop(0) # This check is a hack for when class names are exactly the same length as the data field in varData (8 chars) if not "varData" in varName or checkOffset < 8: checks.append(varName+"["+str(checkOffset)+"]=='"+checkCharacter+"'") return " && ".join(checks) # ------------------------------------------------------------------------------------------------------ def getTestFor(className, static): if static: #return 'jsvIsStringEqual(parentName, "'+className+'")' n = 0; # IMPORTANT - we expect built-in objects to have their name stored # as a string in the varData element checkOffsets = [] checkCharacters = [] for ch in className: checkOffsets.append(n) checkCharacters.append(ch) n = n + 1 checkOffsets.append(n) checkCharacters.append("\\0") return createStringCompare("parent->varData.str", checkOffsets, checkCharacters) else: if className=="String": return "jsvIsString(parent)" if className=="Pin": return "jsvIsPin(parent)" if className=="Integer": return "jsvIsInt(parent)" if className=="Double": return "jsvIsFloat(parent)" if className=="Number": return "jsvIsInt(parent) || jsvIsFloat(parent)" if className=="Object": return "parent" # we assume all are objects if className=="Array": return "jsvIsArray(parent)" if className=="ArrayBufferView": return "jsvIsArrayBuffer(parent) && parent->varData.arraybuffer.type!=ARRAYBUFFERVIEW_ARRAYBUFFER" if className=="Function": return "jsvIsFunction(parent)" n = 0 checkOffsets = [] checkCharacters = [] for ch in className: checkOffsets.append(n) checkCharacters.append(ch) n = n + 1 checkOffsets.append(n) checkCharacters.append("\\0") extra = "" if len(className) > 8: checkOffsets = checkOffsets[:8] checkCharacters = checkCharacters[:8] extra = " && jsvIsStringEqual(constructorName, \""+className+"\")" return createStringCompare("constructorName->varData.str", checkOffsets, checkCharacters)+extra exit(1) print "Building decision tree" tree = {} for jsondata in jsondatas: if "name" in jsondata: jsondata["static"] = not (jsondata["type"]=="property" or jsondata["type"]=="method") className = "!parent" if not jsondata["type"]=="constructor": if "class" in jsondata: className = getTestFor(jsondata["class"], jsondata["static"]) if not className in tree: print "Adding "+className+" to tree" tree[className] = {} treewalk(tree[className], jsondata["name"], jsondata) classTree = tree[className] # ------------------------------------------------------------------------------------------------------ #print json.dumps(tree, sort_keys=True, indent=2) # ------------------------------------------------------------------------------------------------------ print "Outputting decision tree" wrapperFile = open('gen/jswrapper.c', 'w') def codeOut(s): # print str(s) wrapperFile.write(s+"\n"); def getUnLockGetter(varType, name, funcName): if varType=="float": return "jsvGetFloatAndUnLock("+name+")" if varType=="int": return "jsvGetIntegerAndUnLock("+name+")" if varType=="int32": return "(int)jsvGetIntegerAndUnLock("+name+")" if varType=="bool": return "jsvGetBoolAndUnLock("+name+")" if varType=="pin": return "jshGetPinFromVarAndUnLock("+name+")" sys.stderr.write("ERROR: getUnLockGetter: Unknown type '"+varType+"' for "+funcName+":"+name+"\n") exit(1) def getGetter(varType, name, funcName): if varType=="float": return "jsvGetFloat("+name+")" if varType=="int": return "jsvGetInteger("+name+")" if varType=="int32": return "(int)jsvGetInteger("+name+")" if varType=="bool": return "jsvGetBool("+name+")" if varType=="pin": return "jshGetPinFromVar("+name+")" if varType=="JsVar" or varType=="JsVarName": return name sys.stderr.write("ERROR: getGetter: Unknown type '"+varType+"' for "+funcName+":"+name+"\n") exit(1) def getCreator(varType, value, funcName): if varType=="float": return "jsvNewFromFloat("+value+")" if varType=="int": return "jsvNewFromInteger("+value+")" if varType=="int32": return "jsvNewFromInteger((JsVarInt)"+value+")" if varType=="bool": return "jsvNewFromBool("+value+")" if varType=="JsVar": return value sys.stderr.write("ERROR: getCreator: Unknown type '"+varType+"'"+"' for "+funcName+"\n") exit(1) def toArgumentType(argName): if argName=="": return "JSWAT_VOID"; if argName=="JsVar": return "JSWAT_JSVAR"; if argName=="JsVarName": return "JSWAT_JSVARNAME"; if argName=="JsVarArray": return "JSWAT_ARGUMENT_ARRAY"; if argName=="bool": return "JSWAT_BOOL"; if argName=="pin": return "JSWAT_PIN"; if argName=="int32": return "JSWAT_INT32"; if argName=="int": return "JSWAT_JSVARINT"; if argName=="float": return "JSWAT_JSVARFLOAT"; sys.stderr.write("ERROR: toArgumentType: Unknown argument name "+argName+"\n") exit(1) def toCType(argName): if argName=="": return "void"; if argName=="JsVar": return "JsVar*"; if argName=="JsVarName": return "JsVar*"; if argName=="JsVarArray": return "JsVar*"; if argName=="bool": return "bool"; if argName=="pin": return "Pin"; if argName=="int32": return "int"; if argName=="int": return "JsVarInt"; if argName=="float": return "JsVarFloat"; sys.stderr.write("ERROR: toCType: Unknown argument name "+argName+"\n") exit(1) def hasThis(func): return func["type"]=="property" or func["type"]=="method" def getParams(func): params = [] if hasThis(func): params.append(["","JsVar",""]); if "params" in func: for param in func["params"]: params.append(param) return params def getResult(func): result = [ "", "Description" ] if "return" in func: result = func["return"] return result def getArgumentSpecifier(jsondata): params = getParams(jsondata) result = getResult(jsondata); s = [ toArgumentType(result[0]) ] n = 1 for param in params: s.append("("+toArgumentType(param[1])+" << (JSWAT_BITS*"+str(n)+"))"); n=n+1 return " | ".join(s); def getCDeclaration(jsondata): params = getParams(jsondata) result = getResult(jsondata); s = [ ] for param in params: s.append(toCType(param[1])); return toCType(result[0])+" (*)("+",".join(s)+")"; def codeOutFunctionObject(indent, obj): codeOut(indent+"// Object "+obj["name"]+" ("+obj["filename"]+")") if "#if" in obj: codeOut(indent+"#if "+obj["#if"]); codeOut(indent+"jspParseVariableName();") codeOut(indent+"return jspNewObject(\""+obj["name"]+"\", \""+obj["instanceof"]+"\");"); if "#if" in obj: codeOut(indent+"#endif //"+obj["#if"]); def codeOutFunction(indent, func): if func["type"]=="object": codeOutFunctionObject(indent, func) return name = "" if "class" in func: name = name + func["class"]+"."; name = name + func["name"] print name codeOut(indent+"// "+name+" ("+func["filename"]+")") if ("generate" in func): codeOut(indent+"return jsvNewNativeFunction((void (*)(void))"+func["generate"]+", "+getArgumentSpecifier(func)+");") elif ("generate_full" in func): argNames = ["a","b","c","d"]; params = [] if "params" in func: params = func["params"] if len(params)==0: if func["type"]=="variable" or common.is_property(func): codeOut(indent+"jspParseVariableName();") else: codeOut(indent+"jspParseEmptyFunction();") elif len(params)==1 and params[0][1]=="JsVarArray": codeOut(indent+"JsVar *"+params[0][0]+" = jspParseFunctionAsArray();") codeOut(indent+"if (!"+params[0][0]+") return 0; // if parse error") elif len(params)==1 and params[0][1]!="JsVarName": codeOut(indent+"JsVar *"+params[0][0]+" = jspParseSingleFunction();") elif len(params)<9: funcName = "jspParseFunction8" paramCount = 8 if len(params)<5: funcName = "jspParseFunction" paramCount = 4 paramDefs = [] paramPtrs = [] skipNames = "0" n = 0 letters = ["A","B","C","D","E","F","G","H"]; for param in params: paramDefs.append("*"+param[0]) paramPtrs.append("&"+param[0]) if param[1]=="JsVarName": skipNames = skipNames + "|JSP_NOSKIP_"+letters[n] n = n + 1 while len(paramPtrs)