/******************************************************************************* * Copyright (c) 2009, 2017 xored software, Inc. and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * xored software, Inc. - initial API and Implementation (Alex Panchenko) *******************************************************************************/ package org.eclipse.dltk.itcl.internal.core.parser.structure; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.dltk.compiler.IElementRequestor.FieldInfo; import org.eclipse.dltk.compiler.IElementRequestor.MethodInfo; import org.eclipse.dltk.compiler.IElementRequestor.TypeInfo; import org.eclipse.dltk.itcl.internal.core.IIncrTclModifiers; import org.eclipse.dltk.itcl.internal.core.parser.structure.model.IClass; import org.eclipse.dltk.itcl.internal.core.parser.structure.model.IMember; import org.eclipse.dltk.itcl.internal.core.parser.structure.model.IMember.Visibility; import org.eclipse.dltk.itcl.internal.core.parser.structure.model.IMethod; import org.eclipse.dltk.itcl.internal.core.parser.structure.model.IMethod.MethodKind; import org.eclipse.dltk.itcl.internal.core.parser.structure.model.IVariable; import org.eclipse.dltk.itcl.internal.core.parser.structure.model.IVariable.VariableKind; import org.eclipse.dltk.itcl.internal.core.parser.structure.model.impl.ClassImpl; import org.eclipse.dltk.itcl.internal.core.parser.structure.model.impl.Method; import org.eclipse.dltk.itcl.internal.core.parser.structure.model.impl.Variable; import org.eclipse.dltk.tcl.ast.Script; import org.eclipse.dltk.tcl.ast.TclArgument; import org.eclipse.dltk.tcl.ast.TclCommand; import org.eclipse.dltk.tcl.structure.AbstractTclCommandModelBuilder; import org.eclipse.dltk.tcl.structure.ITclModelBuildContext; import org.eclipse.dltk.tcl.structure.ITclTypeHandler; import org.eclipse.dltk.tcl.structure.ITclTypeResolver; import org.eclipse.dltk.tcl.structure.TclModelProblem; public class IncrTclClass extends AbstractTclCommandModelBuilder { @Override public boolean process(TclCommand command, ITclModelBuildContext context) throws TclModelProblem { if (command.getArguments().size() != 2) { return false; } final TclArgument className = command.getArguments().get(0); if (!isSymbol(className)) { return false; } final ClassImpl clazz = new ClassImpl(); // TODO parse without processors final Script classBody = context.parse(command.getArguments().get(1), ITclModelBuildContext.NO_TRAVERSE); processContent(clazz, classBody.getCommands(), context); final TypeInfo ti = new TypeInfo(); ti.declarationStart = command.getStart(); ti.nameSourceStart = className.getStart(); ti.nameSourceEnd = className.getEnd() - 1; ti.modifiers = IIncrTclModifiers.AccIncrTcl; ti.superclasses = clazz.getSuperClasses(); ITclTypeHandler resolvedType = context.get(ITclTypeResolver.class).resolveType(ti, command.getEnd(), asSymbol(className)); context.enterNamespace(resolvedType); clazz.setName(resolvedType.getNamespace()); IncrTclNames.create(context).addType(clazz); for (IMember member : clazz.getMembers()) { if (member instanceof IMethod) { final IMethod method = (IMethod) member; MethodInfo mi = new MethodInfo(); mi.name = member.getName(); mi.modifiers = method.getModifiers(); mi.isConstructor = method.getKind() == MethodKind.CONSTRUCTOR; mi.nameSourceStart = member.getNameStart(); mi.nameSourceEnd = member.getNameEnd(); mi.declarationStart = member.getStart(); fillParameters(mi, method.getParameters()); context.getRequestor().enterMethodRemoveSame(mi); for (TclArgument body : method.getBodies()) { context.parse(body); } context.getRequestor().exitMethod(member.getEnd()); } else if (member instanceof IVariable) { final IVariable variable = (IVariable) member; final FieldInfo fi = new FieldInfo(); fi.name = member.getName(); fi.modifiers = variable.getModifiers(); fi.nameSourceStart = variable.getNameStart(); fi.nameSourceEnd = variable.getNameEnd(); fi.declarationStart = variable.getStart(); if (context.getRequestor().enterFieldCheckDuplicates(fi)) { context.getRequestor().exitField(variable.getEnd()); } } } context.leaveNamespace(resolvedType); return false; } private void processContent(IClass clazz, List<TclCommand> bodyCommands, ITclModelBuildContext context) { for (TclCommand cmd : bodyCommands) { processContent(clazz, new CommandImpl(cmd), context); } } private void processContent(IClass clazz, ICommand cmd, ITclModelBuildContext context) { TclArgument cmdName = cmd.getName(); if (isSymbol(cmdName)) { Handler handler = handlers.get(asSymbol(cmdName)); if (handler != null) { handler.handle(clazz, cmd, context); } } } private interface Handler { void handle(IClass clazz, ICommand command, ITclModelBuildContext context); } private Map<String, Handler> handlers = new HashMap<>(); { handlers.put("inherit", (clazz, command, context) -> handleInherit(clazz, command)); handlers.put("constructor", (clazz, command, context) -> handleConstructor(clazz, command)); handlers.put("destructor", (clazz, command, context) -> handleDestructor(clazz, command)); handlers.put("method", (clazz, command, context) -> handleProc(clazz, command, MethodKind.METHOD)); handlers.put("proc", (clazz, command, context) -> handleProc(clazz, command, MethodKind.PROC)); handlers.put("variable", (clazz, command, context) -> handleVariable(clazz, command, VariableKind.VARIABLE)); handlers.put("common", (clazz, command, context) -> handleVariable(clazz, command, VariableKind.COMMON)); handlers.put("public", (clazz, command, context) -> handleVisibility(clazz, command, context, Visibility.PUBLIC)); handlers.put("protected", (clazz, command, context) -> handleVisibility(clazz, command, context, Visibility.PROTECTED)); handlers.put("private", (clazz, command, context) -> handleVisibility(clazz, command, context, Visibility.PRIVATE)); } /** * @param clazz * @param command */ protected void handleInherit(IClass clazz, ICommand command) { if (command.getArgumentCount() == 0) { return; } for (TclArgument argument : command.getArguments()) { if (isSymbol(argument)) { clazz.addSuperclass(asSymbol(argument)); } } } /** * @param clazz * @param command * @param kind */ protected void handleVariable(IClass clazz, ICommand command, VariableKind kind) { if (command.getArgumentCount() == 0) { return; } final TclArgument varName = command.getArgument(0); if (isSymbol(varName)) { final IVariable variable = new Variable(); variable.setRange(command); variable.setNameRange(varName); variable.setName(asSymbol(varName)); variable.setKind(kind); variable.setVisibility(clazz.peekVisibility()); clazz.addMember(variable); } } /** * @param clazz * @param command * @param kind */ protected void handleProc(IClass clazz, ICommand command, MethodKind kind) { if (command.getArgumentCount() == 0) { return; } final TclArgument procName = command.getArgument(0); if (isSymbol(procName)) { final IMethod method = new Method(); method.setRange(command); method.setNameRange(procName); method.setName(asSymbol(procName)); method.setKind(kind); method.setVisibility(clazz.peekVisibility()); if (command.getArgumentCount() >= 2) { parseRawParameters(command.getArgument(1), method.getParameters()); } if (command.getArgumentCount() == 3) { method.addBody(command.getArgument(2)); } clazz.addMember(method); } } /** * @param clazz * @param command */ protected void handleDestructor(IClass clazz, ICommand command) { if (command.getArgumentCount() == 0) { return; } final IMethod method = new Method(); method.setRange(command); method.setNameRange(command.getName()); method.setName(asSymbol(command.getName())); method.setKind(MethodKind.DESTRUCTOR); method.setVisibility(clazz.peekVisibility());// FIXME check if (command.getArgumentCount() == 1) { method.addBody(command.getArgument(0)); } clazz.addMember(method); } /** * @param clazz * @param command */ protected void handleConstructor(IClass clazz, ICommand command) { if (command.getArgumentCount() < 2) { return; } final IMethod method = new Method(); method.setRange(command); method.setNameRange(command.getName()); method.setName(asSymbol(command.getName())); method.setKind(MethodKind.CONSTRUCTOR); method.setVisibility(clazz.peekVisibility());// FIXME check parseRawParameters(command.getArgument(0), method.getParameters()); method.addBody(command.getArgument(1)); if (command.getArgumentCount() == 3) { method.addBody(command.getArgument(2)); } clazz.addMember(method); } /** * @param clazz * @param command * @param visibility */ protected void handleVisibility(IClass clazz, ICommand command, ITclModelBuildContext context, Visibility visibility) { if (command.getArgumentCount() == 0) { return; } clazz.pushVisibility(visibility); if (command.getArgumentCount() == 1) { TclArgument bodyArg = command.getArgument(0); processContent(clazz, context.parse(bodyArg).getCommands(), context); } else { processContent(clazz, new PrefixedCommandImpl(command), context); } clazz.popVisibility(); } }