forked from Botanical/BotanJS
194 lines
3.6 KiB
Python
194 lines
3.6 KiB
Python
|
#!/usr/bin/env python3
|
||
|
|
||
|
import os;
|
||
|
import re;
|
||
|
import sys;
|
||
|
|
||
|
from xml.dom import minidom
|
||
|
from collections import defaultdict
|
||
|
|
||
|
RegEx_N = re.compile( r"""
|
||
|
.*
|
||
|
__namespace
|
||
|
\s*\(
|
||
|
\s*(['"])([^\1]+)\1
|
||
|
\s*\)
|
||
|
.*
|
||
|
""", re.X )
|
||
|
|
||
|
RegEx_I = re.compile( r"""
|
||
|
.*
|
||
|
__import
|
||
|
\s*\(
|
||
|
\s*(['"])([^\1]+)\1
|
||
|
\s*\)
|
||
|
.*
|
||
|
""", re.X )
|
||
|
|
||
|
RegEx_V = re.compile( r"""
|
||
|
.*
|
||
|
ns
|
||
|
\s*\[
|
||
|
\s*NS_INVOKE
|
||
|
\s*\]
|
||
|
\s*\(
|
||
|
\s*(['"])([^\1]+)\1
|
||
|
\s*\)
|
||
|
.*
|
||
|
""", re.X )
|
||
|
|
||
|
RegEx_E = re.compile( r"""
|
||
|
.*
|
||
|
ns
|
||
|
\s*\[
|
||
|
\s*NS_EXPORT
|
||
|
\s*\]
|
||
|
\s*\(
|
||
|
\s*EX_([A-Z_]+[A-Z])
|
||
|
\s*,
|
||
|
\s*(['"])([^\1]+)\2
|
||
|
\s*,
|
||
|
[^\)]+
|
||
|
\s*\)
|
||
|
.*
|
||
|
""", re.X )
|
||
|
|
||
|
def classMeta( cf ):
|
||
|
ns = ""
|
||
|
imps = list()
|
||
|
exps = list()
|
||
|
for line in open( cf, "r" ):
|
||
|
|
||
|
m = RegEx_N.match( line )
|
||
|
if m:
|
||
|
ns = m.group(2)
|
||
|
continue
|
||
|
|
||
|
m = RegEx_I.match( line )
|
||
|
if m:
|
||
|
imps.append( m.group(2) )
|
||
|
continue
|
||
|
|
||
|
m = RegEx_V.match( line )
|
||
|
if m:
|
||
|
imps.append( ns + "." + m.group(2) )
|
||
|
continue
|
||
|
|
||
|
m = RegEx_E.match( line )
|
||
|
if m:
|
||
|
exps.append( [ m.group(1), m.group(3) ] )
|
||
|
continue
|
||
|
|
||
|
return [ ns, imps, exps ]
|
||
|
|
||
|
def className( classFile ):
|
||
|
return ( os.path
|
||
|
.splitext( classFile )[0]
|
||
|
.replace( os.sep, "." )
|
||
|
.replace( "._this", "" )
|
||
|
.replace( "..BotanJS.", "" ) )
|
||
|
|
||
|
# __export types definition => nodeName
|
||
|
EX_CLASS = "class"
|
||
|
EX_FUNC = "method"
|
||
|
eDef = defaultdict( lambda: 'prop', { "CLASS": EX_CLASS, "FUNC": EX_FUNC } )
|
||
|
|
||
|
class ClassMap:
|
||
|
head = None
|
||
|
DOM = None
|
||
|
R = None
|
||
|
|
||
|
def __init__( self, BotanRoot ):
|
||
|
self.R = BotanRoot
|
||
|
self.DOM = minidom.parseString( "<BotanJS></BotanJS>" )
|
||
|
head = os.path.join( self.R, "_this.js" )
|
||
|
|
||
|
def getNode( self, name, t = EX_CLASS ):
|
||
|
paths = name.split( "." )
|
||
|
|
||
|
currentNode = self.DOM.firstChild
|
||
|
|
||
|
# Step down the path and create the path if necessary
|
||
|
for path in paths:
|
||
|
|
||
|
l = currentNode.childNodes
|
||
|
for i in l:
|
||
|
if i.getAttribute( "name" ) == path:
|
||
|
currentNode = i
|
||
|
break
|
||
|
|
||
|
if currentNode.getAttribute( "name" ) != path:
|
||
|
newNode = self.DOM.createElement( t )
|
||
|
newNode.setAttribute( "name", path )
|
||
|
currentNode.appendChild( newNode )
|
||
|
currentNode = newNode
|
||
|
|
||
|
return currentNode
|
||
|
|
||
|
|
||
|
|
||
|
def skipFile( self, cf ):
|
||
|
|
||
|
if os.path.splitext( cf )[1] == ".js":
|
||
|
if cf == self.head:
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
return True;
|
||
|
|
||
|
def drawMap( self, ns, ci, ce, cf ):
|
||
|
nsNode = self.getNode( ns )
|
||
|
|
||
|
# Source every:
|
||
|
# Since namespace may differ from file name
|
||
|
# ns.__export may export to a different level
|
||
|
# Source the exported val explicitly means
|
||
|
# exports only available when source file is imported
|
||
|
srcEvery = ( ns != className( cf ) )
|
||
|
|
||
|
cf = cf.replace( self.R, "" )
|
||
|
|
||
|
if not srcEvery:
|
||
|
nsNode.setAttribute( "src", cf )
|
||
|
|
||
|
for ex in ce:
|
||
|
_t = eDef[ ex[0] ]
|
||
|
cNode = self.getNode( ns + "." + ex[1], t = _t )
|
||
|
|
||
|
if srcEvery:
|
||
|
# The import is for the defined class
|
||
|
if _t == EX_CLASS:
|
||
|
for imp in ci:
|
||
|
impNode = self.DOM.createElement( "import" )
|
||
|
impNode.appendChild( self.DOM.createTextNode( imp ) )
|
||
|
cNode.appendChild( impNode )
|
||
|
|
||
|
cNode.setAttribute( "src", cf )
|
||
|
|
||
|
# the file dose not export classes
|
||
|
# Hence it import for itself
|
||
|
if not srcEvery:
|
||
|
for imp in ci:
|
||
|
impNode = self.DOM.createElement( "import" )
|
||
|
impNode.appendChild( self.DOM.createTextNode( imp ) )
|
||
|
nsNode.appendChild( impNode )
|
||
|
|
||
|
|
||
|
def build( self ):
|
||
|
for root, dirs, files in os.walk( self.R ):
|
||
|
|
||
|
if root == self.R:
|
||
|
dirs.remove("externs")
|
||
|
|
||
|
for name in files:
|
||
|
classFile = os.path.join( root, name )
|
||
|
|
||
|
if self.skipFile( classFile ):
|
||
|
continue
|
||
|
|
||
|
ns, ci, ce = classMeta( classFile )
|
||
|
classFile = classFile.replace( self.R + os.path.sep, "" )
|
||
|
self.drawMap( ns, ci, ce, classFile )
|
||
|
return self.DOM.toxml()
|
||
|
|