PageRenderTime 156ms CodeModel.GetById 62ms app.highlight 54ms RepoModel.GetById 1ms app.codeStats 0ms

/bundles/plugins-trunk/RubyPlugin/src/org/jedit/ruby/parser/RubyNodeVisitor.java

#
Java | 587 lines | 489 code | 70 blank | 28 comment | 95 complexity | c379726397598f5b78e7790b57bcfe94 MD5 | raw file
  1/*
  2 * RubyNodeVisitor.java - 
  3 *
  4 * Copyright 2005 Robert McKinnon
  5 *
  6 * This program is free software; you can redistribute it and/or
  7 * modify it under the terms of the GNU General Public License
  8 * as published by the Free Software Foundation; either version 2
  9 * of the License, or any later version.
 10 *
 11 * This program is distributed in the hope that it will be useful,
 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14 * GNU General Public License for more details.
 15 *
 16 * You should have received a copy of the GNU General Public License
 17 * along with this program; if not, write to the Free Software
 18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 19 */
 20package org.jedit.ruby.parser;
 21
 22import org.jruby.ast.visitor.AbstractVisitor;
 23import org.jruby.ast.*;
 24import org.jruby.lexer.yacc.SyntaxException;
 25import org.jruby.lexer.yacc.ISourcePosition;
 26import org.jruby.evaluator.Instruction;
 27import org.jedit.ruby.ast.*;
 28import org.jedit.ruby.RubyPlugin;
 29
 30import java.util.List;
 31import java.util.LinkedList;
 32import java.util.ArrayList;
 33import java.util.Iterator;
 34
 35/**
 36 * @author robmckinnon at users.sourceforge.net
 37 */
 38final class RubyNodeVisitor extends AbstractVisitor {
 39
 40    private static final String CLASS = "class";
 41    private static final String MODULE = "module";
 42
 43    private final List<String> namespaceNames;
 44    private final List<String> compositeNamespaceNames;
 45    private final LinkedList<Member> currentMember;
 46    private int methodIndex;
 47
 48    private final List<Member> methods;
 49    private final List<RubyParser.WarningListener> problemListeners;
 50    private final LineCounter lineCounter;
 51    private final NameVisitor nameVisitor;
 52    private MethodCallWithSelfAsAnImplicitReceiver methodCall;
 53    private boolean inIfNode;
 54    private boolean underModuleNode;
 55    private Root root;
 56
 57    public RubyNodeVisitor(LineCounter lineCounts, List<Member> methodMembers, List<RubyParser.WarningListener> listeners) {
 58        inIfNode = false;
 59        underModuleNode = false;
 60        lineCounter = lineCounts;
 61        namespaceNames = new ArrayList<String>();
 62        compositeNamespaceNames = new ArrayList<String>();
 63        currentMember = new LinkedList<Member>();
 64        root = new Root(RubyPlugin.getEndOfFileOffset());
 65        currentMember.add(root);
 66        nameVisitor = new NameVisitor();
 67        problemListeners = listeners;
 68        methods = methodMembers;
 69        methodCall = null;
 70        methodIndex = 0;
 71    }
 72
 73    public final List<Member> getMembers() {
 74        return currentMember.getFirst().getChildMembersAsList();
 75    }
 76
 77    protected final Instruction visitNode(Node node) {
 78        if (printNode()) {
 79            String name = node.getClass().getName();
 80            int index = name.lastIndexOf('.');
 81            ISourcePosition position = node.getPosition();
 82            if (position != null) {
 83                System.out.println("Line " + position.getStartLine() +"-"+ position.getEndLine() +
 84                        ": " + position.getStartOffset() + "-" + position.getEndOffset() +
 85                        " " + name.substring(index + 1));
 86            } else {
 87                System.out.print("          Node: " + name.substring(index + 1));
 88            }
 89        }
 90        return null;
 91    }
 92
 93    private static boolean printNode() {
 94//        return !(node instanceof NewlineNode);
 95        return false;
 96    }
 97
 98    private void visitNodeIterator(Iterator iterator) {
 99        while (iterator.hasNext()) {
100            Node node = (Node) iterator.next();
101            visitNode(node);
102            if (printNode()) {
103                RubyPlugin.log("", getClass());
104            }
105            node.accept(this);
106        }
107    }
108
109    public final Instruction visitBlockNode(BlockNode node) {
110        visitNode(node);
111        RubyPlugin.log("", getClass());
112        visitNodeIterator(node.childNodes().iterator());
113        return null;
114    }
115
116    public final Instruction visitNewlineNode(NewlineNode node) {
117        visitNode(node);
118        node.getNextNode().accept(this);
119        return null;
120    }
121
122    public final Instruction visitModuleNode(ModuleNode module) {
123//        System.out.print("[");
124        addParentNode(MODULE, module, module, module.getBodyNode());
125//        System.out.print("]");
126        return null;
127    }
128
129    public Instruction visitSClassNode(SClassNode selfClassNode) {
130        selfClassNode.getBodyNode().accept(this);
131        return null;
132    }
133
134    public final Instruction visitClassNode(ClassNode classNode) {
135        boolean tempUnderModuleNode = underModuleNode;
136        underModuleNode = false;
137        Member member = addParentNode(CLASS, classNode, classNode, classNode.getBodyNode());
138        Node superNode = classNode.getSuperNode();
139
140        if (superNode != null) {
141            superNode.accept(nameVisitor);
142            StringBuffer name = new StringBuffer();
143            for (String namespace : nameVisitor.namespaces) {
144                name.append(namespace).append("::");
145            }
146            nameVisitor.namespaces.clear();
147            name.append(nameVisitor.name);
148            ((ClassMember)member).setSuperClassName(name.toString());
149        }
150
151        underModuleNode = tempUnderModuleNode;
152        return null;
153    }
154
155    private Member addParentNode(String memberType, Node node, IScopingNode scopeNode, Node bodyNode) {
156        visitNode(node);
157        scopeNode.getCPath().accept(nameVisitor);
158        String name = nameVisitor.name;
159
160        Member member;
161        if (memberType == MODULE) {
162            member = new Module(name);
163        } else {
164            member = new ClassMember(name);
165        }
166
167        member = populateOffsets(member, scopeNode.getCPath().getPosition(), node.getPosition(), memberType);
168
169        int colonNameCount = nameVisitor.namespaces.size();
170        for (String namespace : nameVisitor.namespaces) {
171            compositeNamespaceNames.add(namespace);
172            namespaceNames.add(namespace);
173        }
174        nameVisitor.namespaces.clear();
175        populateNamespace(member);
176        compositeNamespaceNames.clear();
177
178        namespaceNames.add(name);
179        Member parent = currentMember.getLast();
180        parent.addChildMember(member);
181        currentMember.add(member);
182
183        if (memberType == MODULE) {
184            underModuleNode = true;
185        }
186        tranverseChildren(bodyNode, node);
187        if (memberType == MODULE) {
188            underModuleNode = false;
189        }
190
191        namespaceNames.remove(name);
192        while (colonNameCount > 0) {
193            namespaceNames.remove(namespaceNames.size() - 1);
194            colonNameCount--;
195        }
196        currentMember.removeLast();
197        return member;
198    }
199
200    private void tranverseChildren(Node bodyNode, Node node) {
201        if (bodyNode == null) {
202            if (node.childNodes() != null) {
203                for (Object child : node.childNodes()) {
204                    if (child instanceof ArgumentNode) {
205                        // do nothing
206                    } else if (child instanceof Node) {
207                        Node childNode = (Node) (child);
208                        childNode.accept(this);
209                    }
210                }
211            }
212        } else {
213            bodyNode.accept(this);
214        }
215    }
216
217    public Instruction visitArgsCatNode(ArgsCatNode node) {
218        visitNode(node);
219        return null;
220    }
221
222    public Instruction visitArgsNode(ArgsNode node) {
223        visitNode(node);
224//        node.accept(this);
225        return null;
226    }
227
228    public final Instruction visitDefnNode(DefnNode node) {
229        visitNode(node);
230        Member method;
231        try {
232            method = getMember("def", methodIndex, methods, node.getName(), node.getNameNode().getPosition(), node.getPosition());
233        } catch (IndexAdjustmentException e) {
234            methodIndex = e.getIndex();
235            method = getMethodNoCheckedException(methodIndex, methods, node.getName(), node.getPosition());
236        }
237        methodIndex++;
238        Member parent = currentMember.getLast();
239        parent.addChildMember(method);
240        return null;
241    }
242
243    private Member getMethodNoCheckedException(int index, List<Member> members, String memberName, ISourcePosition position) {
244        try {
245            return getMember("def", index, members, memberName, position, position);
246        } catch (IndexAdjustmentException e) {
247            return throwCantFindException("def", memberName, position);
248        }
249    }
250
251    public final Instruction visitDefsNode(DefsNode node) {
252        visitNode(node);
253
254        Method method = (Method)methods.get(methodIndex++);
255        populateReceiverName(method, node);
256        populateOffsets(method, node.getPosition(), node.getPosition(), "def");
257
258        currentMember.getLast().addChildMember(method);
259        currentMember.add(method);
260
261        tranverseChildren(node.getBodyNode(), node);
262
263        currentMember.removeLast();
264        return null;
265    }
266
267    private void populateReceiverName(Method method, DefsNode node) {
268        String methodName = node.getName();
269        Node receiverNode = node.getReceiverNode();
270        
271        if (receiverNode instanceof ConstNode) {
272            ConstNode constNode = (ConstNode)receiverNode;
273            method.setReceiver(constNode.getName(), methodName);
274        } else if (receiverNode instanceof SelfNode) {
275            method.setReceiverToSelf(methodName);
276        }
277        RubyPlugin.log(": " + method.getFullName(), getClass());
278    }
279
280    private void populateNamespace(Member member) {
281        StringBuffer namespace = new StringBuffer();
282        if (namespaceNames.size() > 0) {
283            for (String name : namespaceNames) {
284                namespace.append(name).append("::");
285            }
286            member.setNamespace(namespace.toString());
287        }
288        if (compositeNamespaceNames.size() > 0) {
289            namespace = new StringBuffer();
290            for (String name : compositeNamespaceNames) {
291                namespace.append(name).append("::");
292            }
293            member.setCompositeNamespace(namespace.toString());
294        }
295    }
296
297    private Member getMember(String memberType, int index, List<Member> members, String memberName, ISourcePosition startPosition, ISourcePosition endPosition) throws IndexAdjustmentException {
298        Member member;
299        try {
300            member = members.get(index);
301            String shortName = member.getShortName();
302            if (!memberName.equals(shortName)) {
303                index++;
304                while(index < members.size()) {
305                    member = members.get(index);
306                    if (memberName.equals(member.getShortName())) {
307                        throw new IndexAdjustmentException(index);
308                    } else {
309                        index++;
310                    }
311                }
312                throw new Exception();
313            }
314        } catch (Exception e) {
315            if (e instanceof IndexAdjustmentException) {
316                throw (IndexAdjustmentException)e;
317            } else {
318                return throwCantFindException(memberType, memberName, endPosition);
319            }
320        }
321        return populateOffsets(member, startPosition, endPosition, memberType);
322    }
323
324    private Member populateOffsets(Member member, ISourcePosition position, ISourcePosition endPosition, String memberType) {
325        member.setStartOffset(getStartOffset(position, member));
326        member.setEndOffset(getEndOffset(endPosition));
327        member.setStartOuterOffset(getOuterOffset(member, memberType+" "));
328        return member;
329    }
330
331    private Member throwCantFindException(String memberType, String memberName, ISourcePosition position) {
332        String message = "parser can't find " + memberType + " " + memberName;
333        for (RubyParser.WarningListener listener : problemListeners) {
334            listener.error(position, message);
335        }
336        throw new SyntaxException(position, message);
337    }
338
339    public Instruction visitRootNode(RootNode node) {
340        visitNode(node);
341        RubyPlugin.log("",getClass());
342        if (node.getBodyNode() != null) {
343            node.getBodyNode().accept(this);
344        }
345        return null;
346    }
347
348    public final Instruction visitIfNode(IfNode node) {
349        visitNode(node);
350        if (node.getThenBody() != null) {
351            inIfNode = true;
352            node.getThenBody().accept(this);
353            inIfNode = false;
354        }
355        return null;
356    }
357
358    public final Instruction visitIterNode(IterNode node) {
359        visitNode(node);
360        RubyPlugin.log("", getClass());
361        if (node.getBodyNode() != null) {
362            node.getBodyNode().accept(this);
363        }
364        return null;
365    }
366
367    public Instruction visitArrayNode(ArrayNode node) {
368        if (isMethodCall()) {
369            for (int i = 0; i < node.size(); i++) {
370                node.get(i).accept(this);
371            }   
372        }
373        return null;
374    }
375
376    private boolean isMethodCall() {
377        return methodCall != null;
378    }
379
380    public Instruction visitConstNode(ConstNode node) {
381        if (isMethodCall()) {
382            methodCall.addArgument(node.getName());
383        }
384        return null;
385    }
386
387    public Instruction visitSymbolNode(SymbolNode node) {
388        if (isMethodCall()) {
389            methodCall.addArgument(":" + node.getName());
390        }
391        return null;
392    }
393
394    public Instruction visitStrNode(StrNode node) {
395        if (isMethodCall()) {
396            methodCall.addArgument("'" + node.getValue().toString() + "'");
397        }
398        return null;
399    }
400
401    public Instruction visitHashNode(HashNode node) {
402        if (isMethodCall()) {
403            node.getListNode().accept(this);
404        }
405        return null;
406    }
407
408    public final Instruction visitFCallNode(FCallNode node) {
409        visitNode(node);
410        String name = node.getName();
411        RubyPlugin.log(": " + name, getClass());
412        Member parent = currentMember.getLast();
413
414        if (parent instanceof Root ||
415                parent instanceof ClassMember ||
416                parent instanceof Module ||
417                (isRspecMethodName(parent.getName()) && isRspecMethodName(name)) ||
418                (isRakeMethodName(parent.getName()) && isRakeMethodName(name)) ) {
419            MethodCallWithSelfAsAnImplicitReceiver call = new MethodCallWithSelfAsAnImplicitReceiver(name);
420            call.setStartOuterOffset(getStartOffset(node.getPosition(), call));
421            call.setStartOffset(call.getStartOuterOffset() + name.length() + 1);
422            call.setEndOffset(getEndOffset(node.getPosition()));
423
424            parent.addChildMember(call);
425            currentMember.add(call);
426            methodCall = call;
427            if (node.getArgsNode() != null) {
428                node.getArgsNode().accept(this);
429            }
430            if (node.getIterNode() != null) {
431                node.getIterNode().accept(this);
432            }
433            methodCall = null;
434            currentMember.removeLast();
435        }
436        return null;
437    }
438
439    private boolean isRakeMethodName(String name) {
440        // ignore 'desc' as always next to something else
441        return name.equals("directory") ||
442                name.equals("file") ||
443                name.equals("file_create") ||
444                name.equals("import") ||
445                name.equals("multitask") ||
446                name.equals("namespace") ||
447                name.equals("rule") ||
448                name.equals("task");
449    }
450
451    private boolean isRspecMethodName(String name) {
452        return name.equals("describe") ||
453                name.equals("it") ||
454                name.equals("before") ||
455                name.equals("after") ||
456                name.equals("shared_examples_for") ||
457                name.equals("it_should_behave_like") ||
458                name.equals("fixtures") ||
459                name.equals("context") ||
460                name.equals("controller_name") ||
461                name.equals("integrate_views");
462    }
463
464    public final Instruction visitClassVarDeclNode(ClassVarDeclNode node) {
465        visitNode(node);
466        RubyPlugin.log(": " + node.getName(), getClass());
467        return null;
468    }
469
470    public final Instruction visitClassVarAsgnNode(ClassVarAsgnNode node) {
471        visitNode(node);
472        RubyPlugin.log(": " + node.getName(), getClass());
473        return null;
474    }
475
476    private int getStartOffset(ISourcePosition position, Member member) {
477        if (inIfNode ||
478                underModuleNode ||
479                (member instanceof Method && currentMember.getLast() == root) ||
480                (member instanceof Method && !member.getName().equals(member.getFullName())) ) {
481            int startOffset = position.getStartOffset();
482            int index = lineCounter.getLineAtOffset(startOffset);
483            String line = lineCounter.getLine(index);
484            int offset = line.indexOf(member.getShortName());
485            if (offset != -1) {
486                return offset + lineCounter.getStartOffset(index);
487            } else {
488                return position.getStartOffset();
489            }
490        } else {
491            return position.getStartOffset();
492        }
493    }
494
495    private int getOuterOffset(Member member, String keyword) {
496        int startOffset = member.getStartOffset();
497        int index = lineCounter.getLineAtOffset(startOffset);
498        String line = lineCounter.getLineUpTo(index, startOffset);
499        return line.lastIndexOf(keyword) + lineCounter.getStartOffset(index);
500    }
501
502    private int getEndOffset(ISourcePosition position) {
503        int end = position.getEndOffset();
504        if (lineCounter.charAt(end - 3) == 'e'
505                && lineCounter.charAt(end - 2) == 'n'
506                && lineCounter.charAt(end - 1) == 'd') {
507            return end;
508        }
509        if (lineCounter.charAt(end - 4) == 'e'
510                && lineCounter.charAt(end - 3) == 'n'
511                && lineCounter.charAt(end - 2) == 'd') {
512            return end - 1;
513        }
514
515        char endChar = lineCounter.charAt(end);
516        if (((int)endChar) != 65535) {
517            switch (endChar) {
518                case 'd': return end + 1;
519                case 'n': return end + 2;
520                case 'e': return end + 3;
521            }
522            int endLine = position.getEndLine();
523            String line = lineCounter.getLine(endLine);
524            int start = lineCounter.getStartOffset(endLine);
525            int beginIndex = end - start;
526            String text = line.substring(beginIndex);
527            if (text.indexOf("end") != -1) {
528                return text.indexOf("end") + 3 + beginIndex;
529            } else {
530                return position.getEndOffset();
531            }
532        } else {
533            return position.getEndOffset();
534        }
535    }
536
537    private static final class IndexAdjustmentException extends Exception {
538        private final int index;
539
540        public IndexAdjustmentException(int index) {
541            this.index = index;
542        }
543
544        public final int getIndex() {
545            return index;
546        }
547    }
548
549    private static final class NameVisitor extends AbstractVisitor {
550        private final List<String> namespaces;
551        private String name;
552        private int visits;
553
554        public NameVisitor() {
555            namespaces = new ArrayList<String>();
556            visits = 0;
557        }
558
559        protected final Instruction visitNode(Node node) {
560            return null;
561        }
562
563        public final Instruction visitColon2Node(Colon2Node node) {
564            visits++;
565            if (node.getLeftNode() != null) {
566                node.getLeftNode().accept(this);
567            }
568            visits--;
569
570            if (visits == 0) {
571                name = node.getName();
572            } else {
573                namespaces.add(node.getName());
574            }
575            return null;
576        }
577
578        public Instruction visitConstNode(ConstNode node) {
579            if (visits == 0) {
580                name = node.getName();
581            } else {
582                namespaces.add(node.getName());
583            }
584            return null;
585        }
586    }
587}