Skip to content

Commit b836674

Browse files
committed
Improve cross-language namebinding
Previously to enable cross-language scoping, we converted scoping types from one language to another. For example, when embedding a Python method into PHP, we renamed it to `function` so PHP's scoping rules could reference it. This was restricitive as not every scope type has a true representation in the other language. For example, in Python `method` can be referenced by `variables`, which is not possible in PHP. Thus converting `method` to `function` would disallow `method` to be referenced by a PHP `variable`. This commit changes how we treat namebinding information from language boxes. When embedding Python into PHP, for example, Python's scoping rules are merged into PHP's as is, keeping their specific types. We then extend PHP's scoping rules, allowing them to reference Python types. For example, a PHP `FunctionCall` now references `function` (PHP) and `method` (Python).
1 parent 763146e commit b836674

7 files changed

Lines changed: 48 additions & 41 deletions

File tree

lib/eco/astanalyser.py

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -239,49 +239,36 @@ def merge_lbox_data(self, node, path):
239239
analyser = self.get_lboxanalyser(root)
240240
analyser.analyse(root, self.parsers)
241241

242-
# convert variables, functions
243-
if node.symbol.name == "<Python + PHP>":
244-
# merge python into php
245-
original = "variable"
246-
dest = "function"
247-
max_path = 1
248-
elif node.symbol.name == "<PHP + Python>":
249-
# merge php into python
250-
original = "function"
251-
dest = "variable"
252-
max_path = 1
253-
else:
254-
return
255-
for method in analyser.data.get(original,[]):
256-
if len(method.path) > max_path:
242+
for kind in analyser.data:
243+
if kind in ["File", "reference"]:
257244
continue
258-
uri = self.convert_uri(dest, method, path)
259-
uri.nbrule = NBRule("none", [], {})
260-
self.add_uri(uri)
245+
for obj in analyser.data[kind]:
246+
uri = self.convert_uri(kind, obj, path)
247+
self.add_uri(uri)
261248

262249
# convert references
263250
for ref in analyser.data.get("reference",[]):
264251
# if reference couldn't be resolved, try in outer lbox
265252
if ref.node in analyser.errors:
266253
del analyser.errors[ref.node]
267254
uri = self.convert_uri("reference", ref, path)
268-
if uri.name.startswith("$"):
255+
if uri.name.startswith("$"): # XXX PHP hack
269256
uri.name = uri.name[1:]
270-
else:
271-
uri.name = "$" + uri.name
272-
uri.nbrule = NBRule("none", [], {"references":(["variable"], ["function"])})
257+
uri.nbrule = NBRule("none", [], {"references":(["variable", "function"], ["name"])})
273258
self.add_uri(uri)
274259
return
275260

276261
def convert_uri(self, newkind, prev, path):
277-
uri = URI()
278-
uri.kind = newkind
279-
uri.astnode = prev.astnode
280-
uri.node = prev.node
281-
uri.path = path
282-
uri.name = prev.name
283-
uri.vartype = prev.vartype
284-
uri.index = self.index
262+
uri = prev
263+
if len(prev.path) >= 1:
264+
# Replace language box's top level with current location
265+
uri.path.pop(0)
266+
for p in reversed(path):
267+
uri.path.insert(0, p)
268+
else:
269+
# Language box has no top-level (subgrammar)
270+
for p in reversed(path):
271+
uri.path.insert(0, p)
285272
return uri
286273

287274
def add_uri(self, uri):

lib/eco/eco.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,9 +1385,10 @@ def btReparse(self, selected_node=[]):
13851385
def updateASTOutline(self):
13861386
self.ui.tw_astoutline.clear()
13871387
aa = self.getEditor().tm.parsers[0][3]
1388-
for uri in aa.data["class"]:
1389-
if uri.path[0].name is None and len(uri.path) == 1:
1390-
self.addToASTOutline(aa, uri, self.ui.tw_astoutline)
1388+
if aa.data.has_key("class"):
1389+
for uri in aa.data["class"]:
1390+
if not uri.path or (uri.path and uri.path[0].name is None and len(uri.path) == 1):
1391+
self.addToASTOutline(aa, uri, self.ui.tw_astoutline)
13911392
self.ui.tw_astoutline.expandAll()
13921393

13931394
def addToASTOutline(self, aa, uri, parent):

lib/eco/grammars/grammars.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2020
# IN THE SOFTWARE.
2121

22+
import os
23+
2224
class Language(object):
2325

2426
def __init__(self, name, grammar, priorities, base=""):
@@ -51,6 +53,7 @@ def __init__(self, name, filename, base=""):
5153
self.extract = None
5254
self.auto_include = None
5355
self.auto_exclude = None
56+
self.nb_file = os.path.splitext(filename)[0] + ".nb"
5457

5558
def load(self, buildlexer=True):
5659
from grammar_parser.bootstrap import BootstrapParser
@@ -118,6 +121,9 @@ def set_auto_exclude(self, lang, tokentype):
118121
self.auto_exclude = {}
119122
self.auto_exclude[lang] = tokentype
120123

124+
def set_custom_nb(self, basename):
125+
self.nb_file = "{}/{}".format(os.path.dirname(self.filename), basename)
126+
121127
def auto_allows(self, lang, tokentype):
122128
if self.auto_include and self.auto_include.has_key(lang):
123129
return tokentype in self.auto_include[lang]
@@ -194,6 +200,7 @@ def __hash__(self):
194200
python_class.change_start("classdef")
195201

196202
phppython = EcoFile("PHP + Python", "grammars/php.eco", "Php")
203+
phppython.set_custom_nb("phppython.nb")
197204
pythonphp = EcoFile("Python + PHP", "grammars/python275.eco", "Python")
198205
phppython.add_alternative("top_statement", pythonphp)
199206
phppython.add_alternative("class_statement", pythonphp)

lib/eco/grammars/phppython.nb

1.7 KB
Binary file not shown.

lib/eco/grammars/python275.nb

352 Bytes
Binary file not shown.

lib/eco/nodeeditor.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,9 @@ def resizeEvent(self, event):
146146

147147
def analysis_timer(self):
148148
if self.getWindow().show_namebinding():
149-
self.tm.analyse()
150-
self.update()
151-
self.getWindow().updateASTOutline()
149+
if self.tm.analyse() is not False:
150+
self.update()
151+
self.getWindow().updateASTOutline()
152152
self.timer.stop()
153153

154154
# save swap

lib/eco/treemanager.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -544,10 +544,12 @@ def load_analyser(self, language):
544544
return
545545
if isinstance(lang, EcoFile):
546546
import os
547-
filename = os.path.splitext(lang.filename)[0] + ".nb"
548-
if os.path.exists(filename):
547+
if os.path.exists(lang.nb_file):
549548
from astanalyser import AstAnalyser
550-
return AstAnalyser(filename)
549+
return AstAnalyser(lang.nb_file)
550+
else:
551+
print("Namebinding file '%s' not found." % (lang.nb_file))
552+
551553

552554
def get_languagebox(self, node):
553555
root = node.get_root()
@@ -604,10 +606,20 @@ def get_error_presentation(self, node):
604606
return None
605607

606608
def analyse(self):
607-
if self.parsers[0][2] == "PHP + Python":
608-
self.parsers[0][3].analyse(self.parsers[0][0].previous_version.parent, self.parsers)
609+
# for now only do cross-scope analysing for certain grammars
610+
crossscope = ["PHP + Python", "Java + Python"]
611+
lang = self.parsers[0][2]
612+
parser = self.parsers[0][0]
613+
analyser = self.parsers[0][3]
614+
615+
if not analyser:
616+
return False
617+
618+
if lang in crossscope:
619+
analyser.analyse(parser.previous_version.parent, self.parsers)
609620
return
610621

622+
# analyse all parsers individually
611623
for p in self.parsers:
612624
if p[0].last_status:
613625
if p[3]:

0 commit comments

Comments
 (0)