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 java.util.Deque;
013import java.util.LinkedList;
014import java.util.concurrent.atomic.AtomicLong;
015
016/**
017 * Name generator for synthetic objects.
018 * <p>
019 * The generated name follows the patter {@code __$$_<name>_<counter>}.
020 * The default name is {@code symbol}.
021 * <p>
022 * The generator maintains a stack of scope names, to generate hierarchical names.
023 * <pre class="listing"><code class="lang-java" data-lang="java">
024 * SymbolGenerator sym = new SymbolGenerator("closure");
025 * sym.next(); // __$$_closure_0
026 * sym.next(); // __$$_closure_1
027 * sym.enter("scope");
028 * sym.next(); // __$$_closure_scope_2
029 * sym.enter("subscope");
030 * sym.next(); // __$$_closure_scope_subscope_3
031 * sym.exit().exit();
032 * sym.next(); // __$$_closure_4
033 * </code></pre>
034 * <p>
035 * Since the counter maintains uniqueness, the name and scopes only purpose is to give
036 * somewhat readable names to help debugging.
037 * <p>
038 * Be warned that the uniqueness is only preserved in the context of a single generator.
039 * Two independent generators with the same name (and scope) can produce identical names.
040 */
041public final class SymbolGenerator {
042  private static final String FORMAT = "__$$_%s_%d";
043  private static final String DEFAULT_NAME = "symbol";
044  private final AtomicLong counter = new AtomicLong();
045  private final Deque<String> prefixes = new LinkedList<>();
046
047  public SymbolGenerator(String name) {
048    this.prefixes.addLast(name == null ? DEFAULT_NAME : name);
049  }
050
051  public SymbolGenerator() {
052    this.prefixes.addLast(DEFAULT_NAME);
053  }
054
055  private String name(String localName) {
056    String name = String.join("_", prefixes);
057    if (localName != null && !"".equals(localName)) {
058      name += "_" + localName;
059    }
060    return name;
061  }
062
063  public String next() {
064    return next(null);
065  }
066
067  public String next(String name) {
068    return String.format(FORMAT, name(name), counter.getAndIncrement());
069  }
070
071  public SymbolGenerator exit() {
072    if (this.prefixes.size() == 1) {
073      return this;
074    }
075    this.prefixes.removeLast();
076    return this;
077  }
078
079  public SymbolGenerator enter(String scopeName) {
080    this.prefixes.addLast(scopeName);
081    return this;
082  }
083
084}