【问题标题】:Build symbol table from grammar [closed]从语法构建符号表[关闭]
【发布时间】:2013-03-13 09:05:13
【问题描述】:

我正在尝试使用 Eclipse 从我的grammar(使用 antlr 完成)构建符号表。但是我不知道从什么开始。我想我在某处读到过,您需要 antlr 生成的解析器和词法分析器才能做到这一点。有人知道一个简单的例子,以便我能理解它是如何工作的吗?

【问题讨论】:

  • 去阅读任何关于编译器符号表的标准文本。在那之后,它只是(一堆)汗水。 ANTLR 没有为您提供任何具体的帮助;它是一个解析器生成器,在那里做得非常好,然后它就停止了。
  • 是的,我目前正在检查它们。似乎您可以对 antlr 生成的词法分析器执行 getTree 并对其进行处理。

标签: eclipse antlr grammar symbol-table


【解决方案1】:

符号表只是 id 到值的版本映射。这是一种解决方案,使用范围的推送和弹出作为版本控制机制——在范围定义规则的进入时推送范围并在退出时弹出。

package net.certiv.metal.symbol;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;

import net.certiv.metal.types.ScopeType;
import net.certiv.metal.util.Strings;

public class Scope {

    public final int genId;

    public ScopeType type;
    public Scope enclosingScope;
    protected Map<String, Symbol> symbolMap = new LinkedHashMap<String, Symbol>();

    public Scope(ScopeType type, final int genId, Scope enclosingScope) {
        this.type = type;
        this.genId = genId;
        this.enclosingScope = enclosingScope;
    }

    /** 
     * Define a new variable in the current scope 
     * This is the entry point for adding new variables
     */
    public void define(String name, ArrayList<String> parameters) {
        String params = Strings.asString(parameters, true, ".");
        Symbol symbol = new Symbol(null, name + params, null);
        define(symbol);
    }

    /** Define a symbol in the current scope */
    private void define(Symbol symbol) {
        symbol.setScope(this);
        symbolMap.put(symbol.name, symbol);
    }

    /**
     * Look up the symbol name in this scope and, if not found, 
     * progressively search the enclosing scopes. 
     * Return null if not found in any applicable scope.
     */
    private Symbol resolve(String name) {
        Symbol symbol = symbolMap.get(name);
        if (symbol != null) return symbol;
        if (enclosingScope != null) return enclosingScope.resolve(name);
        return null; // not found
    }
    /**
     * Lookup a variable starting in the current scope.
     * This is the entry point for lookups
     */
    public Symbol resolve(String name, ArrayList<String> parameters) {
        String params = Strings.asString(parameters, true, ".");
        return resolve(name + params);
    }

    /** Where to look next for symbols */
    public Scope enclosingScope() {
        return enclosingScope;
    }

    public String toString() {
        return symbolMap.keySet().toString();
    }
}


package net.certiv.metal.types;

public enum ScopeType {
    GLOBAL,
    LOCAL;
}



package net.certiv.metal.symbol;

import net.certiv.metal.converter.BaseDescriptor;
import net.certiv.metal.types.ValueType;

public class Symbol {

    protected Scope scope; // the owning scope
    protected BaseDescriptor descriptor;
    protected String name;
    protected ValueType type;

    public Symbol(BaseDescriptor descriptor, String name, ValueType type) {
        this.descriptor = descriptor;
        this.name = name;
        this.type = type;
    }

    public BaseDescriptor getDescriptor() {
        return descriptor;
    }

    public String getName() {
        return name;
    }

    public ValueType getType() {
        return type;
    }

    public void setScope(Scope scope) {
        this.scope = scope;
    }

    public Scope getScope() {
        return scope;
    }

    public int genId() {
        return scope.genId;
    }

    public String toString() {
        if (type != null) return '<' + getName() + ":" + type + '>';
        return getName();
    }
}

package net.certiv.metal.symbol;

import java.util.ArrayList;
import java.util.Stack;

import net.certiv.metal.types.ScopeType;
import net.certiv.metal.util.Log;

public class SymbolTable {

    protected Stack<Scope> scopeStack;
    protected ArrayList<Scope> allScopes;
    protected int genId;

    public SymbolTable() {
        init();
    }

    protected void init() {
        scopeStack = new Stack<>();
        allScopes = new ArrayList<>();
        genId = 0;

        Scope globals = new Scope(ScopeType.GLOBAL, nextGenId(), null);
        scopeStack.push(globals);
        allScopes.add(globals);
    }

    public Scope pushScope() {
        Scope enclosingScope = scopeStack.peek();
        Scope scope = new Scope(ScopeType.LOCAL, nextGenId(), enclosingScope);
        scopeStack.push(scope);
        allScopes.add(scope);
        return scope;
    }

    public void popScope() {
        scopeStack.pop();
    }

    public Scope currentScope() {
        if (scopeStack.size() > 0) {
            return scopeStack.peek();
        }
        Log.error(this, "Unbalanced scope stack.");
        return allScopes.get(0);
    }

    public Scope getScope(int genId) {
        for (Scope scope : scopeStack) {
            if (scope.genId == genId) return scope;
        }
        return null;
    }

    private int nextGenId() {
        genId++;
        return genId;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (Scope scope : scopeStack.subList(0, scopeStack.size() - 1)) {
            sb.append(scope.toString());
        }
        return sb.toString();
    }
}

【讨论】:

  • 符号表实际上是每个标识符实例到该标识符的类型实例的描述符的映射。作用域是一种组织代码一部分中的符号如何映射到作用域中已知符号声明的方法。这个答案很简单:许多旧语言可以由一个简单的范围堆栈处理。具有命名空间和泛型的更现代的语言几乎没有那么简单,尽管地图的想法仍然存在。
  • 不知道是你读的太快还是什么,但答案是完全正确的。该代码作为“一个解决方案”给出,以说明使用符号表的机制,这是对 OP 问题的直接回答。任何存在限定都可以在 id 到值的映射上实现,但这不是 OP 问题的一个元素。此外,有些语言是无类型/单类型的,从 OP 的语法来看,OP 可能就是这种情况。命名空间和泛型均由版本控制形式处理,不涉及 OP 的语法。
  • 我并没有抱怨你的解决方案只是一个草图(注意:没有丁!),只是观察到为真正的语言构建符号表可能需要更多。也许他的语言很简单。他最好在开始实施之前做出决定,这是我在匆忙做某事之前详细阅读符号表的最初观点。
  • 抱歉回复晚了,谢谢两位的回答。 @GRosenberg 感谢您的代码,我目前仍在研究它,以便我能理解它,看看它是否适用于我的语法。但是,有一些导入的类我看不到它的来源:( util.Strings; util.Log; ValueType; )。
  • Strings.asString 应用规则(无论你想要什么)来规范 var 名称的表示。例如,将复杂的变量命名方案转换为具有适当唯一性的更简单的属性 (a.b.c) 编码字符串表示。 ValueType 可以是一个简单的枚举,用于标识 var 的正式类型。 BaseDescriptor 可以是一个类,其中包含您想与 var 关联的任何扩展数据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-26
  • 2015-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多