iT邦幫忙

2025 iThome 鐵人賽

DAY 11
0

Roslyn 語意分析與符號系統

Roslyn 的語法樹只是第一步,真正的編譯需要進行語意分析,建立符號表和語意模型。

符號系統架構

using System;
using System.Collections.Generic;
using System.Linq;

namespace MiniRoslyn.Symbols
{
    // 符號基類 - 代表程式中的實體(類別、方法、變數等)
    public abstract record Symbol(string Name, SymbolKind Kind);
    
    public enum SymbolKind
    {
        Type, Method, Field, Property, Parameter, Local
    }

    public record TypeSymbol(string Name, TypeKind TypeKind, List<Symbol> Members) 
        : Symbol(Name, SymbolKind.Type);
        
    public record MethodSymbol(string Name, TypeSymbol ReturnType, List<ParameterSymbol> Parameters) 
        : Symbol(Name, SymbolKind.Method);
        
    public record ParameterSymbol(string Name, TypeSymbol Type) 
        : Symbol(Name, SymbolKind.Parameter);

    public enum TypeKind
    {
        Class, Struct, Interface, Enum, Delegate
    }

    // 語意模型 - 提供語法節點的語意資訊查詢
    public class SemanticModel
    {
        private readonly Dictionary<SyntaxNode, Symbol> _nodeToSymbol = new();
        private readonly Dictionary<SyntaxNode, TypeSymbol> _nodeToType = new();
        private readonly SymbolTable _symbolTable;

        public SemanticModel(SymbolTable symbolTable)
        {
            _symbolTable = symbolTable;
        }

        public Symbol? GetSymbolInfo(SyntaxNode node)
        {
            return _nodeToSymbol.GetValueOrDefault(node);
        }

        public TypeSymbol? GetTypeInfo(SyntaxNode node)
        {
            return _nodeToType.GetValueOrDefault(node);
        }

        internal void BindSymbol(SyntaxNode node, Symbol symbol)
        {
            _nodeToSymbol[node] = symbol;
        }

        internal void BindType(SyntaxNode node, TypeSymbol type)
        {
            _nodeToType[node] = type;
        }
    }

    // 符號表 - 管理作用域與符號解析
    public class SymbolTable
    {
        private readonly Stack<Dictionary<string, Symbol>> _scopes = new();
        
        public SymbolTable()
        {
            PushScope(); // 全域作用域
        }

        public void PushScope()
        {
            _scopes.Push(new Dictionary<string, Symbol>());
        }

        public void PopScope()
        {
            if (_scopes.Count > 1) // 保留全域作用域
                _scopes.Pop();
        }

        public void DeclareSymbol(string name, Symbol symbol)
        {
            _scopes.Peek()[name] = symbol;
        }

        public Symbol? LookupSymbol(string name)
        {
            foreach (var scope in _scopes)
            {
                if (scope.TryGetValue(name, out var symbol))
                    return symbol;
            }
            return null;
        }
    }
}

綁定過程

Binding 是將語法節點與符號系統連結的過程:

namespace MiniRoslyn.Binding
{
    using MiniRoslyn.Syntax;
    using MiniRoslyn.Symbols;

    // 綁定器 - 執行語意分析
    public class Binder
    {
        private readonly SymbolTable _symbolTable;
        private readonly SemanticModel _semanticModel;
        private readonly Dictionary<string, TypeSymbol> _builtinTypes;

        public Binder()
        {
            _symbolTable = new SymbolTable();
            _semanticModel = new SemanticModel(_symbolTable);
            
            // 內建類型
            _builtinTypes = new Dictionary<string, TypeSymbol>
            {
                ["int"] = new TypeSymbol("int", TypeKind.Struct, new List<Symbol>()),
                ["string"] = new TypeSymbol("string", TypeKind.Class, new List<Symbol>()),
                ["void"] = new TypeSymbol("void", TypeKind.Struct, new List<Symbol>())
            };
            
            foreach (var (name, type) in _builtinTypes)
            {
                _symbolTable.DeclareSymbol(name, type);
            }
        }

        public SemanticModel Analyze(SyntaxNode root)
        {
            BindNode(root);
            return _semanticModel;
        }

        private void BindNode(SyntaxNode node)
        {
            switch (node)
            {
                case NumberNode number:
                    _semanticModel.BindType(number, _builtinTypes["int"]);
                    break;
                    
                case BinaryNode binary:
                    BindNode(binary.Left);
                    BindNode(binary.Right);
                    
                    var leftType = _semanticModel.GetTypeInfo(binary.Left);
                    var rightType = _semanticModel.GetTypeInfo(binary.Right);
                    
                    // 簡化:假設都是 int 運算
                    if (leftType?.Name == "int" && rightType?.Name == "int")
                    {
                        _semanticModel.BindType(binary, _builtinTypes["int"]);
                    }
                    break;
            }
        }
    }
}

IL 生成過程

Roslyn 的最終階段是將語意分析後的程式碼轉換為 IL:

using System;
using System.Collections.Generic;
using System.Reflection.Emit;
using System.Text;

namespace MiniRoslyn.CodeGen
{
    using MiniRoslyn.Syntax;
    using MiniRoslyn.Symbols;

    // IL 生成器
    public class ILGenerator
    {
        private readonly StringBuilder _il = new();
        private int _labelCounter = 0;

        public string Generate(SyntaxNode node, SemanticModel semanticModel)
        {
            _il.Clear();
            EmitPreamble();
            EmitNode(node);
            EmitPostamble();
            return _il.ToString();
        }

        private void EmitPreamble()
        {
            _il.AppendLine(".assembly extern mscorlib {}");
            _il.AppendLine(".assembly Sample {}");
            _il.AppendLine(".class public Program extends [mscorlib]System.Object");
            _il.AppendLine("{");
            _il.AppendLine("  .method public static void Main() cil managed");
            _il.AppendLine("  {");
            _il.AppendLine("    .entrypoint");
        }

        private void EmitPostamble()
        {
            _il.AppendLine("    call void [mscorlib]System.Console::WriteLine(int32)");
            _il.AppendLine("    ret");
            _il.AppendLine("  }");
            _il.AppendLine("}");
        }

        private void EmitNode(SyntaxNode node)
        {
            switch (node)
            {
                case NumberNode number:
                    EmitLoadConstant(number.Value);
                    break;
                    
                case BinaryNode binary:
                    EmitNode(binary.Left);
                    EmitNode(binary.Right);
                    EmitBinaryOperation(binary.Op);
                    break;
            }
        }

        private void EmitLoadConstant(int value)
        {
            _il.AppendLine($"    ldc.i4 {value}");
        }

        private void EmitBinaryOperation(string op)
        {
            switch (op)
            {
                case "+":
                    _il.AppendLine("    add");
                    break;
                case "-":
                    _il.AppendLine("    sub");
                    break;
                case "*":
                    _il.AppendLine("    mul");
                    break;
                default:
                    throw new NotSupportedException($"Operator {op} not supported");
            }
        }

        private string NewLabel()
        {
            return $"L{_labelCounter++}";
        }
    }
}

上一篇
簡單的 Parser
下一篇
Green/Red Tree 架構
系列文
新 .NET & Azure & IoT & AI 開源技術實戰手冊 (含深入官方程式碼講解) 12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言