Skip to content

Commit 13e22fe

Browse files
committed
First commit: a broken pom.xml, and a working java file with a main for testing
the parsing of test jython code.
1 parent 904b90f commit 13e22fe

2 files changed

Lines changed: 308 additions & 0 deletions

File tree

pom.xml

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<parent>
6+
<groupId>org.scijava</groupId>
7+
<artifactId>pom-scijava</artifactId>
8+
<version>29.2.1</version>
9+
<relativePath />
10+
</parent>
11+
12+
<artifactId>jython-autocompletion</artifactId>
13+
<version>0.0.1-SNAPSHOT</version>
14+
15+
<name>Jython Autocompletion</name>
16+
<description>Autocompletion for the jython language in the Script Editor</description>
17+
<url>https://github.com/fiji/jython-autocompletion</url>
18+
<inceptionYear>2020</inceptionYear>
19+
<organization>
20+
<name>SciJava</name>
21+
<url>https://scijava.org/</url>
22+
</organization>
23+
<licenses>
24+
<license>
25+
<name>GNU General Public License v3+</name>
26+
<url>http://www.gnu.org/licenses/gpl.html</url>
27+
<distribution>repo</distribution>
28+
</license>
29+
</licenses>
30+
31+
<developers>
32+
<developer>
33+
<id>acardona</id>
34+
<name>Albert Cardona</name>
35+
<url>https://imagej.net/User:Albertcardona</url>
36+
<roles>
37+
<role>lead</role>
38+
<role>developer</role>
39+
<role>debugger</role>
40+
<role>reviewer</role>
41+
<role>support</role>
42+
<role>maintainer</role>
43+
</roles>
44+
</developer>
45+
</developers>
46+
<contributors>
47+
<contributor>
48+
<name>Albert Cardona</name>
49+
<roles><role>developer</role></roles>
50+
<properties><id>acardona</id></properties>
51+
</contributor>
52+
</contributors>
53+
54+
<mailingLists>
55+
<mailingList>
56+
<name>Image.sc Forum</name>
57+
<archive>https://forum.image.sc/tags/jython</archive>
58+
</mailingList>
59+
</mailingLists>
60+
61+
<scm>
62+
<connection>scm:git:git://github.com/fiji/jython-autocompletion</connection>
63+
<developerConnection>scm:git:git@github.com:fiji/jython-autocompletion</developerConnection>
64+
<tag>HEAD</tag>
65+
<url>https://github.com/fiji/jython-autocompletion</url>
66+
</scm>
67+
<issueManagement>
68+
<system>GitHub Issues</system>
69+
<url>https://github.com/fiji/jython-autocompletion/issues</url>
70+
</issueManagement>
71+
<ciManagement>
72+
<system>Travis CI</system>
73+
<url>https://travis-ci.org/fiji/jython-autocompletion</url>
74+
</ciManagement>
75+
76+
<properties>
77+
<package-name>org.fiji.jython.autocompletion</package-name>
78+
<license.licenseName>GPL_3</license.licenseName>
79+
<license.copyrightOwners>Albert Cardona</license.copyrightOwners>
80+
81+
<!-- NB: Deploy releases to the SciJava Maven repository. -->
82+
<releaseProfiles>deploy-to-scijava</releaseProfiles>
83+
<jython-slim.version>2.7.2</jython-slim.version>
84+
</properties>
85+
86+
<repositories>
87+
<repository>
88+
<id>scijava.public</id>
89+
<url>https://maven.scijava.org/content/groups/public</url>
90+
</repository>
91+
</repositories>
92+
93+
<dependencies>
94+
<!-- SciJava dependencies -->
95+
<dependency>
96+
<groupId>org.scijava</groupId>
97+
<artifactId>script-editor</artifactId>
98+
</dependency>
99+
<!-- Third-party dependencies -->
100+
<dependency>
101+
<groupId>org.python</groupId>
102+
<artifactId>jython-slim</artifactId>
103+
<version>${jython-slim.version}</version>
104+
</dependency>
105+
</dependencies>
106+
107+
</project>
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
package org.fiji.jython.autocompletion;
2+
3+
import java.util.ArrayList;
4+
import java.util.HashMap;
5+
import java.util.List;
6+
import java.util.Map;
7+
8+
import org.python.antlr.PythonTree;
9+
import org.python.antlr.ast.Assign;
10+
import org.python.antlr.ast.Call;
11+
import org.python.antlr.ast.ClassDef;
12+
import org.python.antlr.ast.FunctionDef;
13+
import org.python.antlr.ast.ImportFrom;
14+
import org.python.antlr.ast.Name;
15+
import org.python.antlr.ast.Tuple;
16+
import org.python.antlr.base.mod;
17+
import org.python.core.CompileMode;
18+
import org.python.core.CompilerFlags;
19+
import org.python.core.ParserFacade;
20+
21+
public class AutoCompletionUtil {
22+
23+
static public String testCode = String.join("\n",
24+
"from ij import IJ, ImageJ as IJA, VirtualStack",
25+
"from ij.process import ByteProcessor",
26+
"imp = IJ.getImage()",
27+
"width, height = imp.getWidth(), imp.getHeight()",
28+
"imp2 = imp",
29+
"class Volume(VirtualStack):",
30+
" def getProcessor(self, index):",
31+
" return ByteProcessor(512, 512)",
32+
" def getSize(self):",
33+
" return 10",
34+
"def setRoi(an_imp):",
35+
" ip = an_imp.getStack().getProcessor(3)",
36+
" pixels = ip.");
37+
38+
static public class Scope {
39+
final Scope parent;
40+
final List<Scope> children = new ArrayList<>();
41+
final HashMap<String, String> imports = new HashMap<>();
42+
final HashMap<String, String> vars = new HashMap<>();
43+
44+
public Scope(final Scope parent) {
45+
this.parent = parent;
46+
if (null != parent) {
47+
this.imports.putAll(parent.imports);
48+
this.vars.putAll(parent.vars);
49+
}
50+
}
51+
52+
public Scope getLast() {
53+
if (children.isEmpty()) return this;
54+
return children.get(children.size() -1).getLast();
55+
}
56+
57+
public void print(final String indent) {
58+
if ("" == indent) {
59+
for (final Map.Entry<String, String> e: imports.entrySet())
60+
System.out.println(indent + "import :: " + e.getKey() + " --> " + e.getValue());
61+
System.out.println("scope global:");
62+
}
63+
for (final Map.Entry<String, String> e: vars.entrySet()) {
64+
if (null != parent && parent.vars.containsKey(e.getKey())) continue; // to print only the newly added ones
65+
System.out.println(indent + "var :: " + e.getKey() + " = " + e.getValue());
66+
}
67+
68+
int i = 0;
69+
for (final Scope child: children) {
70+
System.out.println(indent + "scope[" + (i++) + "]:");
71+
child.print(indent + " ");
72+
}
73+
}
74+
}
75+
76+
/**
77+
* Returns the top-level Scope.
78+
*/
79+
static public Scope parseAST(final String code) {
80+
// The code includes from beginning of the file until the point at which an autocompletion is requested.
81+
// Therefore, remove the last line, which would fail to parse because it is incomplete
82+
final int lastLineBreak = code.lastIndexOf("\n");
83+
final String codeToParse = code.substring(0, lastLineBreak);
84+
final mod m = ParserFacade.parse(codeToParse, CompileMode.exec, "<none>", new CompilerFlags());
85+
86+
return parseNode(m.getChildren(), null);
87+
}
88+
89+
static public Scope parseNode(final List<PythonTree> children, final Scope parent) {
90+
91+
final Scope scope = new Scope(parent);
92+
if (null != parent)
93+
parent.children.add(scope);
94+
95+
for (final PythonTree child : children) {
96+
print(child.getClass());
97+
98+
if (child instanceof ImportFrom)
99+
scope.imports.putAll(parseImportStatement( (ImportFrom)child ));
100+
else if (child instanceof Assign)
101+
scope.vars.putAll(parseAssignStatement( (Assign)child, scope ));
102+
else if (child instanceof FunctionDef)
103+
scope.vars.put(parseFunctionDef( (FunctionDef)child, scope), null); // no value: no class. TODO return the list of arguments, for autocompletion.
104+
else if (child instanceof ClassDef)
105+
scope.vars.put(parseClassDef( (ClassDef)child, scope), null); // TODO no value, but should have one, too look into its methods and implemented interfaces or superclasses
106+
}
107+
108+
return scope;
109+
// Prints the top code blocks
110+
// class org.python.antlr.ast.ImportFrom
111+
// class org.python.antlr.ast.ImportFrom
112+
// class org.python.antlr.ast.Assign
113+
// class org.python.antlr.ast.Assign
114+
// class org.python.antlr.ast.ClassDef
115+
// class org.python.antlr.ast.FunctionDef
116+
}
117+
118+
/**
119+
* Parse import statements, considering aliases.
120+
* @param im
121+
* @return
122+
*/
123+
static public Map<String, String> parseImportStatement(final ImportFrom im) {
124+
final Map<String, String> classes = new HashMap<>();
125+
final String module = im.getModule().toString();
126+
for (int i=0; i<im.getNames().__len__(); ++i) {
127+
final String alias = im.getInternalNames().get(i).getAsname().toString(); // alias: as name
128+
final String simpleClassName = im.getInternalNames().get(i).getInternalName(); // class name
129+
classes.put("None" == alias ? simpleClassName : alias, module + "." + simpleClassName);
130+
}
131+
return classes;
132+
}
133+
134+
static public Map<String, String> parseAssignStatement(final Assign assign, final Scope scope) {
135+
final Map<String, String> assigns = new HashMap<>();
136+
//final expr right = assign.getInternalValue(); // strangely this works
137+
final PythonTree right = assign.getChildren().get(1);
138+
if (right instanceof Tuple || right instanceof org.python.antlr.ast.List) { // TODO are there any other possible?
139+
final PythonTree left = assign.getChildren().get(0);
140+
for (int i=0; i<right.getChildren().size(); ++i) {
141+
final String name = left.getChildren().get(i).getNode().toString();
142+
final String val = parseRight(right.getChildren().get(i), scope);
143+
if (null != val) assigns.put(name, val); // scope.vars.put(name, val);
144+
}
145+
} else {
146+
final String name = assign.getInternalTargets().get(0).getNode().toString();
147+
final String val = parseRight(right, scope);
148+
assigns.put(name, val);
149+
}
150+
151+
return assigns;
152+
}
153+
154+
/**
155+
* Adds a child Scope to the given parent Scope, and also a variable to the parent scope
156+
* with no class (just for the name). Then populates the child scope.
157+
*/
158+
static public String parseFunctionDef(final FunctionDef fn, final Scope parent) {
159+
final String name = fn.getInternalName();
160+
print("function name: " + name);
161+
parseNode(fn.getChildren(), parent);
162+
return name;
163+
}
164+
165+
static public String parseClassDef(final ClassDef c, final Scope parent) {
166+
final String name = c.getInternalName();
167+
parseNode(c.getChildren(), parent);
168+
return name;
169+
}
170+
171+
/** Discover the class of the right statement in an assignment.
172+
*
173+
* @param right
174+
*/
175+
static public String parseRight(final Object right, final Scope scope) {
176+
if (right instanceof Name) {
177+
return scope.vars.getOrDefault( ((Name)right).toString(), null);
178+
}
179+
if (right instanceof Call) {
180+
// TODO recursive, to parse e.g. imp.getProcessor().getPixels()
181+
// to figure out what class is imp (if known), what class getProcessor returns,
182+
// and then what class getPixels returns.
183+
// And to handle also IJ.getImage()
184+
}
185+
return null;
186+
}
187+
188+
static public final void print(Object s) {
189+
System.out.println(s);
190+
}
191+
192+
static public final void main(String[] args) {
193+
try {
194+
parseAST(testCode).print("");
195+
} catch (Exception e) {
196+
e.printStackTrace();
197+
if (null != e.getCause())
198+
e.getCause().printStackTrace();
199+
}
200+
}
201+
}

0 commit comments

Comments
 (0)