001/*
002 * Copyright (c) 2012-2017 Institut National des Sciences Appliquées de Lyon (INSA-Lyon)
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 */
009
010package org.eclipse.golo.compiler.ir;
011
012import org.eclipse.golo.compiler.PackageAndClass;
013import org.eclipse.golo.compiler.parser.GoloASTNode;
014
015import java.util.*;
016
017import static java.util.Collections.unmodifiableSet;
018import static java.util.Collections.unmodifiableCollection;
019
020public final class GoloModule extends GoloElement implements FunctionContainer {
021
022  private final PackageAndClass packageAndClass;
023  private final ReferenceTable globalReferences;
024  private final Set<ModuleImport> imports = new LinkedHashSet<>();
025  private final Set<GoloFunction> functions = new LinkedHashSet<>();
026  private final Map<PackageAndClass, Augmentation> augmentations = new LinkedHashMap<>();
027  private final Set<NamedAugmentation> namedAugmentations = new LinkedHashSet<>();
028  private final Set<Struct> structs = new LinkedHashSet<>();
029  private final Set<Union> unions = new LinkedHashSet<>();
030  private final Set<LocalReference> moduleState = new LinkedHashSet<>();
031  private GoloFunction moduleStateInitializer = null;
032  private boolean hasMain = false;
033
034  public static final Set<ModuleImport> DEFAULT_IMPORTS = unmodifiableSet(new LinkedHashSet<>(Arrays.asList(
035    new ModuleImport(PackageAndClass.fromString("gololang.Predefined"), true),
036    new ModuleImport(PackageAndClass.fromString("gololang.StandardAugmentations"), true),
037    new ModuleImport(PackageAndClass.fromString("gololang"), true),
038    new ModuleImport(PackageAndClass.fromString("java.lang"), true)
039  )));
040
041  public static final String MODULE_INITIALIZER_FUNCTION = "<clinit>";
042
043  public GoloModule(PackageAndClass packageAndClass) {
044    this(packageAndClass, new ReferenceTable());
045  }
046
047  public GoloModule(PackageAndClass name, ReferenceTable references) {
048    super();
049    this.packageAndClass = name;
050    this.globalReferences = references;
051  }
052
053  @Override
054  public GoloModule ofAST(GoloASTNode n) {
055    super.ofAST(n);
056    return this;
057  }
058
059  public PackageAndClass getPackageAndClass() {
060    return packageAndClass;
061  }
062
063  public Set<ModuleImport> getImports() {
064    Set<ModuleImport> imp = new LinkedHashSet<>();
065    if (!structs.isEmpty() || !unions.isEmpty()) {
066      imp.add(new ModuleImport(this.getPackageAndClass().createSubPackage("types"), true));
067    }
068    imp.addAll(imports);
069    if (this.packageAndClass.hasPackage()) {
070      imp.add(new ModuleImport(this.packageAndClass.parentPackage(), true));
071    }
072    imp.addAll(DEFAULT_IMPORTS);
073    return imp;
074  }
075
076  public Collection<Augmentation> getAugmentations() {
077    return unmodifiableCollection(augmentations.values());
078  }
079
080  public Set<NamedAugmentation> getNamedAugmentations() {
081    return unmodifiableSet(namedAugmentations);
082  }
083
084  public void addImport(ModuleImport moduleImport) {
085    imports.add(moduleImport);
086    makeParentOf(moduleImport);
087  }
088
089  @Override
090  public Set<GoloFunction> getFunctions() {
091    return unmodifiableSet(functions);
092  }
093
094  @Override
095  public void addFunctions(Collection<GoloFunction> functions) {
096    for (GoloFunction f : functions) {
097      this.addFunction(f);
098    }
099  }
100
101  @Override
102  public boolean hasFunctions() {
103    return !functions.isEmpty();
104  }
105
106  public boolean hasMain() {
107    return hasMain;
108  }
109
110  @Override
111  public void addFunction(GoloFunction function) {
112    function.relinkTopLevel(globalReferences);
113    functions.add(function);
114    if (function.isMain()) {
115      hasMain = true;
116    }
117    makeParentOf(function);
118  }
119
120  public void addNamedAugmentation(NamedAugmentation augment) {
121    namedAugmentations.add(augment);
122    makeParentOf(augment);
123  }
124
125  // TODO: refactor to not return the value...
126  public Augmentation addAugmentation(Augmentation augment) {
127    if (augment.hasLocalTarget()) {
128      augment.setTargetPackage(packageAndClass + ".types");
129    }
130    if (augmentations.containsKey(augment.getTarget())) {
131      augmentations.get(augment.getTarget()).merge(augment);
132    } else {
133      augmentations.put(augment.getTarget(), augment);
134    }
135    makeParentOf(augment);
136    return augmentations.get(augment.getTarget());
137  }
138
139  public void addStruct(Struct struct) {
140    structs.add(struct);
141    makeParentOf(struct);
142    struct.setModuleName(getPackageAndClass());
143  }
144
145  public GoloElement getSubtypeByName(String name) {
146    for (Struct s : structs) {
147      if (s.getName().equals(name)) {
148        return s;
149      }
150    }
151    for (Union u : unions) {
152      if (u.getName().equals(name)) {
153        return u;
154      }
155    }
156    return null;
157  }
158
159  public void addUnion(Union union) {
160    unions.add(union);
161    makeParentOf(union);
162    union.setModuleName(this.getPackageAndClass());
163    this.addImport(new ModuleImport(union.getPackageAndClass(), true));
164  }
165
166  public void addModuleStateInitializer(AssignmentStatement assignment) {
167    addLocalState(assignment.getLocalReference());
168    if (moduleStateInitializer == null) {
169      moduleStateInitializer = Builders.functionDeclaration()
170        .name(MODULE_INITIALIZER_FUNCTION).synthetic()
171        .block(Builders.block().ref(globalReferences));
172      functions.add(moduleStateInitializer);
173    }
174    moduleStateInitializer.getBlock().addStatement(assignment);
175  }
176
177  private void addLocalState(LocalReference reference) {
178    moduleState.add(reference);
179    makeParentOf(reference);
180  }
181
182  @Override
183  public void accept(GoloIrVisitor visitor) {
184    visitor.visitModule(this);
185  }
186
187  @Override
188  public void walk(GoloIrVisitor visitor) {
189    for (ModuleImport moduleImport : getImports()) {
190      moduleImport.accept(visitor);
191    }
192    for (Union union : unions) {
193      union.accept(visitor);
194    }
195    for (Struct struct : structs) {
196      struct.accept(visitor);
197    }
198    for (Augmentation augment :augmentations.values()) {
199      augment.accept(visitor);
200    }
201    for (NamedAugmentation augmentation : namedAugmentations) {
202      augmentation.accept(visitor);
203    }
204    for (LocalReference moduleState : moduleState) {
205      moduleState.accept(visitor);
206    }
207    for (GoloFunction function : new LinkedList<GoloFunction>(functions)) {
208      function.accept(visitor);
209    }
210  }
211
212  @Override
213  protected void replaceElement(GoloElement original, GoloElement newElement) {
214    throw cantReplace();
215  }
216
217}