這邊實現簡單的 Parser,其中一個重要的概念不可變 Immutable + 結構共享 Structural Sharing,減少複製、簡化快取一致性。
成本:更多小物件 GC 壓力 -> 以 Pool + Lazy + table-driven 緩解。
其中的 Syntax Trees,Lexer 掃描 SourceText 建立 token,Parser 使用遞迴下降,帶表驅動優化建立樹。
using System;
using System.Collections.Generic;
namespace MiniRoslyn.Syntax
{
// 節點基類 - 模仿 Green Node 概念(不可變)
public abstract record Node;
public record NumberNode(int Value) : Node;
public record BinaryNode(string Op, Node Left, Node Right) : Node;
public static class MiniParser
{
// 極簡:只處理 整數 + 加減乘
public static Node Parse(string text)
{
var tokens = Tokenize(text);
int pos = 0;
return ParseExpr();
Node ParseExpr()
{
Node node = ParseTerm();
while (Match("+") || Match("-"))
{
string op = tokens[pos - 1];
var right = ParseTerm();
node = new BinaryNode(op, node, right);
}
return node;
}
Node ParseTerm()
{
Node node = ParseFactor();
while (Match("*"))
{
string op = tokens[pos - 1];
var right = ParseFactor();
node = new BinaryNode(op, node, right);
}
return node;
}
Node ParseFactor()
{
if (Match("("))
{
var e = ParseExpr();
Expect(")");
return e;
}
if (int.TryParse(Peek(), out var v))
{
pos++;
return new NumberNode(v);
}
throw new Exception("Unexpected token: " + Peek());
}
bool Match(string t)
{
if (Peek() == t) { pos++; return true; }
return false;
}
void Expect(string t)
{
if (!Match(t)) throw new Exception("Expect " + t);
}
string Peek() => pos < tokens.Count ? tokens[pos] : "<eof>";
}
static List<string> Tokenize(string text)
{
var list = new List<string>();
for (int i = 0; i < text.Length; i++)
{
char c = text[i];
if (char.IsWhiteSpace(c)) continue;
if (char.IsDigit(c))
{
int j = i;
while (j < text.Length && char.IsDigit(text[j])) j++;
list.Add(text[i..j]);
i = j - 1;
continue;
}
if ("+-*()".IndexOf(c) >= 0)
{
list.Add(c.ToString());
continue;
}
throw new Exception("Invalid char: " + c);
}
return list;
}
}
}