#!/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/. # # ---------------------------------------------------------------------------------------- # Builds HTML documentation for functions from the JSON included in comments in # jswrap_*.c files # ---------------------------------------------------------------------------------------- # Needs: # pip install markdown # pip install markdown-urlize # # See common.py -> get_jsondata for command line options import subprocess; import re; import json; import sys; import os; import common import urllib2 import markdown import htmlentitydefs sys.path.append("."); scriptdir = os.path.dirname(os.path.realpath(__file__)) basedir = scriptdir+"/../" sys.path.append(basedir+"scripts"); sys.path.append(basedir+"boards"); # Scans files for comments of the form /*JSON......*/ and then writes out an HTML file describing # all the functions # htmldev default is False # if htmldev is set to True indicating 'html development mode', script: # - creates standalone html version for temporary html generation development by: # --- inserting a searchbox text input to test top link function # --- skipping MDN links dump and validation to complete quicker htmldev = False jsondatas = common.get_jsondata(True) classes = [] # list of class names libraries = [] for jsondata in jsondatas: if "class" in jsondata: if not jsondata["class"] in classes: classes.append(jsondata["class"]) if jsondata["type"]=="library": if not jsondata["class"] in libraries: libraries.append(jsondata["class"]) # Load list of 'uses' in EspruinoDocs code_uses = [] referenceFile = "../EspruinoDocs/references.json" if os.path.isfile(referenceFile): print("Found references.json - using this to link to examples") code_uses = json.loads(open(referenceFile, "r").read()) # Load list of MDN URLs (to speed up processing) valid_mdn_urls = { 'valid' : [], 'invalid' : [] }; mdnURLFile = "MDN_URLS.txt" if os.path.isfile(mdnURLFile): valid_mdn_urls = json.loads(open(mdnURLFile, "r").read()) # start writing htmlFile = open('functions.html', 'w') def html(s): htmlFile.write(s+"\n"); def htmlify(d,current): d = markdown.markdown(d, extensions=['urlize']) # replace with newlines with pre idx = d.find("") end = d.find("", idx) while idx>=0 and end>idx: codeBlock = d[idx+6:end] # search for known links in code if codeBlock[-2:]=="()" and codeBlock[:-2] in links: codeBlock = ""+codeBlock+""; elif codeBlock in links: codeBlock = ""+codeBlock+""; # ensure multi-line code is handled correctly codeBlock = ""+codeBlock+""; if codeBlock.find("\n")>=0: codeBlock = "
"+codeBlock+"
" d = d[0:idx]+codeBlock+d[end+7:]; # search again idx = d.find("", end) end = d.find("", idx) return d; def html_description(d,current): if isinstance(d, list): d = "\n".join(d) d = htmlify(d,current) html("
\n" + d + "\n
\n") def get_prefixed_name(jsondata): s="" if "class" in jsondata: s=s+jsondata["class"]+"." s=s+jsondata["name"] return s def get_fullname(jsondata): s = common.get_prefix_name(jsondata) if s!="": s = s + " " if jsondata["type"]!="constructor": if "class" in jsondata: s=s+jsondata["class"]+"." s=s+jsondata["name"] return s def get_arguments(jsondata): if common.is_property(jsondata): return "" args = []; if "params" in jsondata: for param in jsondata["params"]: args.append(param[0]); if param[1]=="JsVarArray": args.append("..."); return "("+", ".join(args)+")" def get_surround(jsondata): s = common.get_prefix_name(jsondata) if s!="": s = s + " " if jsondata["type"]!="constructor": if "class" in jsondata: if jsondata["class"] in libraries: s=s+"require(\""+jsondata["class"]+"\")." else: s=s+jsondata["class"]+"." s=s+jsondata["name"] if jsondata["type"]!="object": s=s+get_arguments(jsondata) return s def get_code(jsondata): if jsondata["type"]=="event": return jsondata["class"]+".on('"+jsondata["name"]+"', function"+get_arguments(jsondata)+" { ... });"; if jsondata["type"]=="constructor": return "new "+jsondata["name"]+get_arguments(jsondata); return get_surround(jsondata) def get_link(jsondata): s="l_"; if "class" in jsondata: s=s+jsondata["class"]+"_" else: s=s+"_global_" s=s+jsondata["name"] return s def replace_with_ifdef_description(m): contents = m.group(1) return common.get_ifdef_description(contents) def html_escape(text): escaped_chars = "" for c in text: if (ord(c) < 32) or (ord(c) > 126): c = '&{};'.format(htmlentitydefs.codepoint2name[ord(c)]) escaped_chars = escaped_chars + c return escaped_chars # If MDN doesn't 404 then include a link to it def insert_mdn_link(jsondata): if "class" in jsondata and "name" in jsondata: url = "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/"+jsondata["class"] if jsondata["type"]!="constructor": url = url +"/"+jsondata["name"] if url in valid_mdn_urls['valid']: code = 200 elif url in valid_mdn_urls['invalid']: code = 404 else: print("Checking URL "+url) try: connection = urllib2.urlopen(url) code = connection.getcode() connection.close() except urllib2.HTTPError, e: code = e.getcode() if code==200: valid_mdn_urls['valid'].append(url) else: valid_mdn_urls['invalid'].append(url) if code==200: html("

View MDN documentation

") # Remove duplicates existingSymbols = {} unduplicatedjsondatas = [] for jsondata in jsondatas: duplicate = False if "name" in jsondata: n = get_fullname(jsondata) if n in existingSymbols: print("WARNING: Duplicate symbol "+n); duplicate = True existingSymbols[n] = jsondata if not duplicate: unduplicatedjsondatas.append(jsondata) jsondatas = unduplicatedjsondatas html("") html(" ") html(" Espruino Reference") html(" ") html(" ") html(" ") html(" ") html("

Espruino Software Reference

") html("

Version "+common.get_version()+"

") if htmldev == True: html("
") html(" "); html("
") detail = [] links = {} def add_link(jsondata): if not "no_create_links" in jsondata: link = get_prefixed_name(jsondata); if link!="global": links[link] = get_link(jsondata) jsondatas = sorted(jsondatas, key=lambda s: common.get_name_or_space(s).lower()) html('
') html("

Contents

") html("
    ") html("
  • Globals
  • ") for jsondata in jsondatas: if "name" in jsondata and not "class" in jsondata: add_link(jsondata) detail.append(jsondata) for className in sorted(classes, key=lambda s: s.lower()): html("
  • "+className+"
  • ") links[className] = className for jsondata in jsondatas: if "name" in jsondata and "class" in jsondata and jsondata["class"]==className: add_link(jsondata) detail.append(jsondata) html("
") html('
') html(" "); #html("

Detail

") lastClass = "XXX" for jsondata in detail: className = "" niceName = "" linkName = "" if "class" in jsondata: className=jsondata["class"] if className in libraries: niceName=className+" Library" else: niceName=className+" Class" linkName=className else: className="" niceName="Globals" linkName="_global" # If we're on to a different class now, put up the heading for it if className!=lastClass: lastClass=className html("

"+niceName+"

") html("

(top)

") for j in jsondatas: if (j["type"]=="class" or j["type"]=="library") and j["class"]==className and "description" in j: html_description(j["description"], className) instances = [] for j in jsondatas: if "instanceof" in j and j["instanceof"]==className: instances.append(j) if len(instances)>0: html("

Instances

") text = "" for j in instances: text = text + " * [`"+j["name"]+"`](#l__global_"+j["name"]+")"; if "description" in j: text = text + " " + j["description"].split("\n")[0] text = text + "\n" html_description(text, "") html("

Methods and Fields

") html(" ") # Otherwise just output detail link = get_link(jsondata) html("

"+get_fullname(jsondata)+"") #html(""); if "githublink" in jsondata: html(''); html("

") insert_mdn_link(jsondata); html("

(top)

") if jsondata["type"]!="object": html("

Call type:

") html("
"+get_code(jsondata)+"
") elif "instanceof" in jsondata: html("

Instance of "+jsondata["instanceof"]+"") if "params" in jsondata: html("

Parameters

") for param in jsondata["params"]: desc = "" if len(param)>2: desc=param[2] if isinstance(desc, list): desc = '
'.join(desc) extra = "" if param[1]=="JsVarArray": extra = ", ..."; html("
"+htmlify("`"+param[0]+extra+"` - "+desc,"")+"
") if "return" in jsondata: html("

Returns

") desc = "" if len(jsondata["return"])>1: desc=jsondata["return"][1] if desc=="": desc="See description above" html("
"+htmlify(desc,"")+"
") if "description" in jsondata: html("

Description

") desc = jsondata["description"] if not isinstance(desc, list): desc = [ desc ] if "ifdef" in jsondata: desc.append("\n\n**Note:** This is only available in "+common.get_ifdef_description(jsondata["ifdef"])); if "ifndef" in jsondata: desc.append("\n\n**Note:** This is not available in "+common.get_ifdef_description(jsondata["ifndef"])); if "#if" in jsondata: d = jsondata["#if"].replace("||", " and ").replace("&&", " with ") d = re.sub('defined\((.+?)\)', replace_with_ifdef_description, d) d = re.sub('(.*)_COUNT>=(.*)', "devices with more than \\2 \\1 peripherals", d) desc.append("\n\n**Note:** This is only available in "+d); html_description(desc, jsondata["name"]) url = "http://www.espruino.com/Reference#"+get_link(jsondata) if url in code_uses: uses = code_uses[url] html("

Examples

") html("

This function is used in the following places in Espruino's documentation

") html(" ") html(" ") html("") # -------------------------------------------------------------------------- # Write/create keywords # -------------------------------------------------------------------------- keywords = {} for j in jsondatas: jkeywords = [] if ("name" in j): item = { "title" : get_surround(j), "path": "/Reference#"+get_link(j) }; jkeywords = [ j["name"] ] if get_prefixed_name(j)!=j["name"]: jkeywords.append(get_prefixed_name(j)) if "class" in j: jkeywords.append(j["class"]) elif j["type"]=="library": item = { "title" : j["class"]+" Library", "path": "/Reference#"+j["class"] }; jkeywords = [ j["class"] ] elif j["type"]=="class": item = { "title" : j["class"]+" Class", "path": "/Reference#"+j["class"] }; jkeywords = [ j["class"] ] for k in jkeywords: k = k.lower() if not k in keywords: keywords[k] = [ item ] else: keywords[k].append(item) #print(json.dumps(keywords, sort_keys=True, indent=2)) keywordFile = open('function_keywords.js', 'w') keywordFile.write(json.dumps(keywords, sort_keys=True, indent=2)); # ---------------------------- Just random helper stuff builtins = [] for jsondata in jsondatas: if jsondata["type"]=="staticmethod" or jsondata["type"]=="staticproperty" or jsondata["type"]=="class": if not jsondata["class"] in builtins: builtins.append(jsondata["class"]) elif jsondata["type"]=="function" or jsondata["type"]=="variable" or jsondata["type"]=="class": if not jsondata["name"] in builtins: builtins.append(jsondata["name"]) print("------------------------------------------------------") print('Global classes and functions: '+' '.join(builtins)); print("------------------------------------------------------") # Writing MDN URL file if htmldev == False: open(mdnURLFile, "w").write(json.dumps(valid_mdn_urls, sort_keys=True, indent=2))