明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 1546|回复: 2

[其它] 用C#实现Lisp解释器

[复制链接]
发表于 2024-9-9 09:06:12 | 显示全部楼层 |阅读模式
本帖最后由 你有种再说一遍 于 2024-9-17 22:41 编辑

# 用C#实现Lisp解释器
想Lisp转译成C#怎么进行呢?
转译编程语言本质上就是切割字符串,大家也不需要想得太高大上.

# 名词解释
CST具体语法树:
拥有全部信息,这一步利用分词器产生有序的tokens.

AST抽象语法树:
抛弃很多无用信息,例如空格/注释/语法糖/死代码消除,通过树形图获取简化的运行结构,类似于"a + b"就会变成"+ a b"函数在前.
用它的目的是为了语言和语言之间的简化转换.
c语言函数需要顺序声明才能够低访问高,而现代语言都不需要函数顺序声明,那么这一步其实构造了函数名map.

LLVM:
苹果公司发现GCC在某些方面难以满足他们的需求,包括对Objective-C的支持和集成到Xcode开发环境中的困难,所以苹果主导的一个开源工程LLVM.
它的意义在于编程语言和硬件层之间加入一个IR层(类似汇编的中间语言)进行转换,这样不同的编程语言就不用关心硬件指令的实现(硬件会更换指令地址,包括X86/X64/ARM/苹果定制ARM的实现).
梦想是美好的,实际上不同系统平台有各自的特性,所以编程语言开发者还会利用已有的C++库,例如vector的扩容大小不同,ARM没有除法器需要优化.
这些种种问题在IR层是处理不了的.
不过我们其实不会用到它,因为我们转译只需要对接微软的接口,在.net上面中间层叫MSIL.
如果是用AOT编译,那么微软其实会把MSIL转译成LLVM的IR上面.并且C#有了AOT编译之后,微软自举部分的编译代码.

LLVM的AST实际上长这样:
`-FunctionDecl 0x7fc02585a228 <test.c:1:1, line:3:1> line:1:5 main 'int ()'
`-CompoundStmt 0x7fc02585a340 <col:12, line:3:1>
`-ReturnStmt 0x7fc02585a330 <line:2:5, col:12>
`-IntegerLiteral 0x7fc02585a310 <col:12> 'int' 0
想知道含义的可以去粘贴问问AI.
参考:https://zhuanlan.zhihu.com/p/161626997

# 0x01参考项目-mal
使用75种语言编写一个Lisp解释器:
https://www.jianshu.com/p/8764dd49ac2d
直接进入它的C#项目:
https://github.com/kanaka/mal/tree/master/impls/cs

一个参考的C语言实现的例子:
https://ksco.gitbooks.io/build-your-own-lisp/content/Parsing.html

# 0x02参考项目-JS文章
参考这篇JS文章很容易就知道语言转译顺序了.
https://zhuanlan.zhihu.com/p/140889954
整体流程就是:
LispCode->LispTokens->LispAST
->CSharpAST->CSharpCode
->MSIL(dll/exe)->LLVM_IR(无GC的dll/exe)

具体步骤:
1,Lisp代码进入分词器,构造一个有序tokens数组.

2,顺序遍历tokens,构造Lisp的AST (Lisp几乎等价于AST,不然为什么说它除了汇编之外最简单呢).

3,LispAST可以很少差异地转换为其他语言的AST,也就是转换LispAST为C#AST.

3.1,链接器
生成C#AST之前执行:
`mal`和`JS文章`都缺少处理一些AutoLisp的setq/宏等符号需要补充.
遍历LispAST每个节点,标记函数签名(函数签名是包含参数),并加入对应容器:LispFuncMap/CsFuncMap/CFuncMap/ComMap.
其实此时只会知道两种签名,一种是Lisp函数,一种是未知,假设未知都是调度外部函数(FFI).
我们可以提前导出FFI载入Map,就可以分离出哪些是真正的未知,然后报错.

COM(COM+/ActiveX)是跨进程的,可以调用html和excel,所以无法导出全部的COM函数表.
分为两步:
a,制作acad专用的vla/vlax用tlbexp.exe工具导出,然后映射成后绑.就可以加入FFI Map进行函数提示.
工具链接:https://learn.microsoft.com/zh-c ... pe-library-exporter
b,直接提供后绑app.invoke函数,这也是vlax的功能.

3.2,优化器
优化器既需要在LispAST做,也需要在C#AST做,这取决于转换复杂性.
a,生成C#AST之前执行:
宏(小撇')展开转换为Lisp函数.
提供特殊符号给循环continue和break.
死代码消除
b,生成C#AST之后执行:
常量折叠,循环展开,内联函数,SIMD扩展.
c,加入新的语法规则,
函数头上面加入注解
;[此函数系统变量改了后末尾自动恢复,系统变量()]
;[栈帧变量自动捕获并释放(全局)]
Jig模式补充??
加入反射自身注解...嘿嘿...似乎会诞生一门方言.
提供array/hashset/map/有序结构.
要不弄个一键转原生Lisp.

**动态语言特性,类型推断**
一个语言可以是弱类型又可以是动态类型的.
弱类型:类型隐式转换,例如JS的: "A"+1 == "A1".
动态类型:运行时检查.这样会令简单的A+B面临各种检查.
为了降低这种检查,人们发明了JIT特化,例如运行5次之后收集了运行时间,JIT将构造一个函数进行调用,以提高命中率加速时间.https://b23.tv/EFYy1Kg

把Lisp转为C#就会面临动态转静态的难题.
https://stackoverflow.org.cn/questions/53425328
要先进行类型推导,然后标记类型,然后每个类型生成函数(不能生成模板而是直接展开,像C语言一样直接生成对应的函数).
这个工作可以参考开源项目.
实际上类型推断大部分是失败的,成为了object了,只能运行时求类型.
例如:同一个变量改为表之后可以改为数值,LispAST可以看见上面描述的行为.
分情况处理:
a:变量有类型改变.
如果变量是分离的,然后入参之后再改变.那么可以把第二个变量改名**
b:变量没有类型改变.
可以进行类型标记.
如果根据入参确定,Lisp入参是支持动态类型的.动态类型是运行时确定的,无法标记,
即使追溯到原生函数的参数类型标记,然后把过程链上面每个都标记.如果遇到已有标记,那么就需要生成新的函数,实现重载参数.
如果是动态那么它原生也是动态啊.

**泛型**
数值类型的类型提升:
python整数类型它是无限精度的BigInteger,它是字符串记录的,用时间换空间...如果要做优化,可以命中Int64运算,如果溢出再回到BigInteger,用基础数值类型提高效率.https://b23.tv/EFYy1Kg
Lisp没有,只是Int32/double.采用截断吗?还是报溢出呢?
AOT编译要确定类型的啊,岂不是每次运行之后都要判断是否溢出?然后执行数值类型提升?

4.C#的AST转为C#代码.
`JS文章`是C语言例子,只是调换了函数名出现的位置.
我们实际上需要用Roslyn提供的类去构造C#的AST和转为Code,但它本质就是一个巨大的字符串而已.

5,编译C#.
5.1,JIT动态编译,根据编译选项,导出文件就是.net的exe/dll了,也可以不导出直接在acad内存里执行.
5.2,AOT编译:利用AOT语法树进行编译,那么可以生成没有GC的exe/dll.

优化建议:
能不能并行SIMD分词呢?
学十亿行天文台数据挑战,直接返回token下标.
Java版本思路描述更好:
https://segmentfault.com/a/1190000044679461
c#版本:
https://www.cnblogs.com/InCerry/ ... aster-than-java-cpp
SimdJson项目:它在处理微小Json数据2048字节是通过栈帧内存进行的,并且还用了一些零拷贝/内存映射的方法加速.
https://www.zhihu.com/question/313943804

以下是`JS文章`代码翻译成CSharp,仅为原理参考,不保证编译能够通过.
## 调用例子
```c#
public class Program {
    public static void Main() {
        string lispCode = "(add 2 (\"string\" 42)";
        // 分词
        var tz = new Tokenizer();
        tokenizer.Tokenize(lispCode);
        // code to AST
        var parser = new Parser(tz.Tokens.ToArray());
        var lispAst = parser.Parse();
        // LispAST to CSharpAST
        var transformer = new Transformer();
        var csharpAst = transformer.Transform(lispAst);
        // 生成c#代码
        var codeGenerator = new CodeGenerator();
        string csharpCode = codeGenerator.GenerateCode(csharpAst);
        // 打印结果验证
        Console.WriteLine(csharpCode);
    }
}
```

## CST具体语法树
分词器把LispCode转Tokens
```c#
// 定义接口
public interface IToken {
    string Type { get; }
    string Value { get; }
}

// 行号,列号,为了并行构造tokens,需要它们进行排序.并且需要提示语法错误位置
public class Token : IToken {
    public string Type {get;} // 节点类型
    public string Value {get;}// 节点值
    public List<Node> Children; // 子节点列表
    public Node Parent; // 父节点
    public int Line; // 行号
    public int Column;// 列号
    public Dictionary<string, object> Attributes;// 其他属性

    public Token (string type, string value) {
        Type = type;
        Value = value;
        Children = new ();
        Attributes = new ();
    }
    public override string ToString() {
        return $"行:{Line} 列{Column} Type: {Type} Value: {Value}";
    }
}

public class Tokenizer {
   // 有序结构,存储分词结果的列表
    public List<IToken> Tokens = new();

    // 分词器,输入lisp代码
    public void Tokenize(string input/*lispcode*/) {
        // 遍历位置指针
        int current = 0;
        while (current < input.Length) {
            char ch = input[current]; // 当前字符
            // 处理左括号
            if (ch == '(') {
                Tokens.Add(new Token("paren", "("));
                current++; continue; }
            // 处理右括号
            if (ch == ')') {
                Tokens.Add(new Token("paren", ")"));
                current++; continue; }
            // 跳过无用字符
            if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') { current++; continue; }

            // 处理数字,数字字面量0x 0b 1e-10..负数
           var num = GetNumberRange(input,ref current);
           if(mum is not null) {
              Tokens.Add("number",mum);
               current++; continue; }

            // 处理字符串,包括转义字符
            if (ch == '"') {
                StringBuilder value = new();
                ch = input[++current]; // 跳过开头的双引号
                while (current < input.Length && ch != '"') {
                    if (ch == '\\' && input[current + 1] == '"') {
                        value.Append('\\');
                        value.Append('"');
                        current += 2; // 跳过转义
                        continue;
                    }
                    value.Append(ch);
                    ch = input[++current];
                }
                Tokens.Add(new Token("string", value.ToString()));
                current++; // 跳过结尾的双引号
                continue;
            }

// 处理宏
// 分词时候不处理,AST把它作为函数或者数组,给前面的表达式.岂不是传递函数指针?
// 这里很复杂: '((1 .0)) 要把后面整个表取走?
// 还有什么地方呢?
            if(ch=='\''){
             Tokens.Add(new Token("quote","'"));
            }

// 处理标识符和关键字,可以添加不同的前缀
// 控制流关键字 if while progn
// 用户定义的函数
            if ((char.IsLetter(ch) || ch == '_') && current < input.Length) {
                StringBuilder value = new();
                while (current < input.Length && (char.IsLetterOrDigit(ch) || ch == '_')) {
                    value.Append(ch);
                    ch = input[++current];
                }
                Tokens.Add(new Token("name", value.ToString()));
                continue;
            }

            // 无法识别的字符
            throw new Exception($"分词器错误,异常符号: {ch}");
        }
    }
}

public class NumberParser {
    // 获取数字字面量
    public static string? GetNumberRange(char[] input, ref int current) {
        StringBuilder numberRange = new StringBuilder();
        if (TryParseNumber(input, ref current, numberRange))
            return numberRange.ToString();
       return null;
    }

    private static bool TryParseNumber(char[] input, ref int index, StringBuilder numberRange) {
        if (index >= input.Length) return false;

        if (input[index] == '-') {
            numberRange.Append('-');
            index++;
            if (index >= input.Length) return false;
        }

        if (input[index] == '0') {
            index++;
            if (index < input.Length && (input[index] == 'x' || input[index] == 'X')) {
                index++;
                if (!ParseHex(input, ref index, numberRange)) return false;
            }
            else if (index < input.Length && (input[index] == 'b' || input[index] == 'B')) {
                index++;
                if (!ParseBinary(input, ref index, numberRange)) return false;
            }
            else
            {
                if (!ParseDecimal(input, ref index, numberRange)) return false;
            }
        }
        else
        {
            if (!ParseDecimal(input, ref index, numberRange)) return false;
        }

        return numberRange.Length > 0;
    }

    private static bool ParseHex(char[] input, ref int index, StringBuilder numberRange) {
        while (index < input.Length) {
            char c = input[index++];
            if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) { index--; break; }
            numberRange.Append(c);
        }
        return numberRange.Length > 0;
    }

    private static bool ParseBinary(char[] input, ref int index, StringBuilder numberRange) {
        while (index < input.Length) {
            char c = input[index++];
            if (c != '0' && c != '1') { index--; break; }
            numberRange.Append(c);
        }
        return numberRange.Length > 0;
    }

    private static bool ParseDecimal(char[] input, ref int index, StringBuilder numberRange) {
        while (index < input.Length) {
            char c = input[index++];
            if (!char.IsDigit(c) && c != '.' && (c != 'e' && c != 'E' || (index == 1 && numberRange.Length == 0))) { index--; break; }
            if (c == 'e' || c == 'E') {
                if (index < input.Length && (input[index] == '+' || input[index] == '-')) index++;
                while (index < input.Length && char.IsDigit(input[index])) index++;
                index--;
            }
            numberRange.Append(c);
        }
        return numberRange.Length > 0;
    }
}

// 单元测试
class Program {
    static void Main() {
        char[] input = "-0x1A3E".ToCharArray();
        int current = 0;
        string result = NumberParser.GetNumberRange(input, ref current);
        Console.WriteLine(result); // 输出: -0x1A3E
        Console.WriteLine("Current index after parsing: " + current); // 输出解析后的当前索引
    }
}
```

## Tokens转Lisp的AST
节点定义,为什么同类型容器呢?这样就不用写名字了吧.
```c#
// 定义抽象语法树节点的接口
public interface IASTNode {
    string Type { get; }
}

// 数字字面量的节点
public class NumberLiteral : IASTNode {
    public string Type { get; } = "NumberLiteral";
    public object Value { get; }
    public NumberLiteral(object value) {
        Value = value;
    }
}

// 字符串字面量的节点
public class StringLiteral : IASTNode {
    public string Type { get; } = "StringLiteral";
    public object Value { get; }
    public StringLiteral(object value) {
        Value = value;
    }
}

// 调用表达式的节点
public class CallExpression : IASTNode {
    public string Type { get; } = "CallExpression";
    public string Name { get; }
    public List<IASTNode> Params { get; } = new();
    public CallExpression(string name) {
        Name = name;
    }
}

// 变量赋值的节点,用于表示LISP中的变量赋值操作
public class Assignment : IASTNode {
    public string Type { get; } = "Assignment";
    public string Variable { get; }  // 变量名
    public IASTNode Value { get; }  // 赋值的值
    public Assignment(string variable, IASTNode value) {
        Variable = variable;
        Value = value;
    }
}

// 函数定义节点,用于表示LISP中的函数定义
public class FunctionDefinition : IASTNode {
    public string Type { get; } = "FunctionDefinition";
    public string Name { get; }  // 函数名
    public List<string> Parameters { get; }  // 函数参数列表
    public IASTNode Body { get; }  // 函数体
    public FunctionDefinition(string name, List<string> parameters, IASTNode body) {
        Name = name;
        Parameters = parameters;
        Body = body;
    }
}

// AST抽象语法树的根节点
// ...怎么不是左右节点形成树呢? eval实现:https://www.cnblogs.com/lsdsjy/p/licp-the-interpreter.html

public class Program : IASTNode {
    public string Type { get; } = "Program";
    public List<IASTNode> Body { get; } = new();
}
```

```c#
// 解析器类
public class Parser {
    private readonly IToken[] tokens;
    private int current = 0;

    public Parser(IToken[] tokens) {
        this.tokens = tokens;
    }

    private IASTNode Walk() {
        var token = tokens[current++];
        // 数字
        if (token.Type == "number") {
            return new NumberLiteral(token.Value);
        }
        // 字符串
        if (token.Type == "string") {
            return new StringLiteral(token.Value);
        }
        // 带括号的表达式
        if (token.Type == "paren" && token.Value.Equals('(')) {
            token = tokens[current++];
            var node = new CallExpression(token.Value.ToString());

            token = tokens[current++];
            while (token.Type != "paren" || (token.Type == "paren" && token.Value.Equals('('))) {
                // 进一步遍历
                node.Params.Add(Walk());
                token = tokens[current];
            }
            current++;
            return node;
        }
        throw new InvalidOperationException($"Unexpected token type: {token.Type}");
    }

   // 生成Lisp的AST,返回根节点
    public Program Parse() {
        var ast = new Program();
        while (current < tokens.Length) {
            ast.Body.Add(Walk());
        }
        return ast;
    }
}
```

## 将LISP的AST转译为C#的AST
两种语言存在语法差别,需要转换.

```c#
public class Transformer {
    public static Program Transform(IASTNode lispAst) {
        var newAst = new Program();
        TraverseNode(lispAst, null, newAst);
        return newAst;
    }

    // 递归遍历Lisp AST节点,并转译为C# AST节点(文章是c语言,所以要需要修改)
    private static void TraverseNode(IASTNode node, IASTNode parent, Program newAst) {
        switch (node.Type) {
            case "NumberLiteral":
                newAst.Body.Add(new NumberLiteral(((NumberLiteral)node).Value));
                break;
            case "StringLiteral":
                newAst.Body.Add(new StringLiteral(((StringLiteral)node).Value));
                break;
            case "CallExpression":
                var callExpr = (CallExpression)node;
                var csharpCallExpr = new CallExpression(callExpr.Name) {
                    Params = callExpr.Params.Select(param => (IASTNode)TraverseNode(param, node, newAst)).ToList()
                };
                newAst.Body.Add(csharpCallExpr);
                break;
            case "Assignment":
                var assignment = (Assignment)node;
                newAst.Body.Add(new Assignment(assignment.Variable, TraverseNode(assignment.Value, node, newAst)));
                break;
            case "FunctionDefinition":
                var function = (FunctionDefinition)node;
                var csharpFunction = new FunctionDefinition(function.Name, function.Parameters, TraverseNode(function.Body, node, newAst));
                newAst.Body.Add(csharpFunction);
                break;
            default:
                throw new InvalidOperationException($"Unknown node type: {node.Type}");
        }
    }
}
```

## 用AST生成c#代码,这一步应该用动态编译,或者AOT编译
public class CodeGenerator {
    public static string GenerateCode(Program ast) {
        StringBuilder code = new();
        foreach (var node in ast.Body) {
            switch (node.Type) {
                case "NumberLiteral":
                    code.AppendLine($"int number = {((NumberLiteral)node).Value};");
                    break;
                case "StringLiteral":
                    code.AppendLine($"string text = \"{((StringLiteral)node).Value}\";");
                    break;
                case "CallExpression":
                    var callExpr = (CallExpression)node;
                    code.AppendLine($"{callExpr.Name}({string.Join(", ", callExpr.Params.Select(p => p.ToString()))});");
                    break;
                case "Assignment":
                    var assignment = (Assignment)node;
                    code.AppendLine($"{assignment.Variable} = {assignment.Value};");
                    break;
                case "FunctionDefinition":
                    var function = (FunctionDefinition)node;
                    code.AppendLine($"void {function.Name}({string.Join(", ", function.Parameters)}) {{");
                    code.AppendLine($"    // Function body");
                    code.AppendLine("}");
                    break;
                default:
                    throw new InvalidOperationException($"Unknown node type: {node.Type}");
            }
        }
        return code.ToString();
    }
}
```

# CSharp的AST例子
微软参考链接:
https://learn.microsoft.com/en-u ... ted/syntax-analysis
C#的处理不是code to tokens to AST,然后逆转.
而是code to SyntaxTree(CST具体语法树),就可以to其他了.

在C#中,使用Roslyn编译器平台可以分析和操作语法树.
语法树是由各种语法节点组成的,这些节点代表了代码中的不同结构.
以下是一些常见的Roslyn语法树节点类型及其含义:
1.CompilationUnit:整个源文件的根节点.
2.NamespaceDeclaration:命名空间声明.
3.ClassDeclaration:类声明.
4.MethodDeclaration:方法声明.
5.PropertyDeclaration:属性声明.
6.FieldDeclaration:字段声明.
7.VariableDeclaration:变量声明.
8.UsingDirective:导入using指令.
9.SyntaxToken:独立的关键词/标识符/操作符/标点.
10.SyntaxTrivia:语法上不重要的信息,例如空白/预处理指令和注释.
这些节点构成了语法树的结构,允许开发者进行代码分析/重构/生成等操作.
例如,可以通过遍历语法树来查找特定的代码模式,或者生成新的代码结构.
在Roslyn中,语法树的节点是通过继承自SyntaxNode的类来表示的.每个节点都可以包含子节点,这些子节点可以是其他SyntaxNode对象,或者是SyntaxToken和SyntaxTrivia对象.
这样的结构使得语法树能够详细地表示源代码的结构.
Roslyn还提供了语义模型(SemanticModel),
它与语法树配合使用,可以提供关于代码元素的语义信息,例如类型信息/符号信息等.这使得开发者能够进行更深入的代码分析和操作.

## 例子一
代码转CST,再转其他
```c#
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace RoslynExample {
    class Program {
        static void Main(string[] args) {
            // 原始的C#源代码字符串
            string originalCode = @"
                using System;
                namespace HelloWorld
                {
                    public class Program
                    {
                        public static void Main(string[] args)
                        {
                            Console.WriteLine(""Hello, World!"");
                        }
                    }
                }
            ";

            // 解析源代码字符串为CST
            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(originalCode);

            // CST中获取所有的Tokens
            IEnumerable<SyntaxToken> tokens = syntaxTree.GetRoot().DescendantTokens();
            Console.WriteLine("Tokens:");
            foreach (var token in tokens)
            {
                Console.WriteLine($"Token: {token.Text}, Type: {token.Kind()}");
            }

            // 获取AST的根节点
            CompilationUnitSyntax compilationUnit = syntaxTree.GetCompilationUnitRoot();

            // 将AST转换回源代码字符串
            string generatedCode = compilationUnit.ToFullString();
            Console.WriteLine("\nGenerated Code:");
            Console.WriteLine(generatedCode);

            Console.ReadKey();
        }
    }
}
```

## 例子二
AST语法树转代码,或者转CST再转其他.
```c#
using System;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace RoslynReverseExample {
    class Program {
        static void Main(string[] args) {
            // 创建一个命名空间节点,名称为"HelloWorld"
            var namespaceDeclaration = SyntaxFactory.NamespaceDeclaration(
                SyntaxFactory.Identifier("HelloWorld"));

            // 创建一个公共类Program的声明
            var classDeclaration = SyntaxFactory.ClassDeclaration("Program")
                .WithModifiers(
                    // 设置类为public
                    SyntaxFactory.TokenList(
                        SyntaxFactory.Token(SyntaxKind.PublicKeyword)));

            // 创建一个Main方法的声明
            var methodDeclaration = SyntaxFactory.MethodDeclaration(
                // 方法返回类型为void
                SyntaxFactory.PredefinedType(
                    SyntaxFactory.Token(SyntaxKind.VoidKeyword)),
                "Main")
                .WithModifiers(
                    // 设置方法为static
                    SyntaxFactory.TokenList(
                        SyntaxFactory.Token(SyntaxKind.StaticKeyword)))
                .WithParameterList(
                    // 添加一个参数args,类型为string数组
                    SyntaxFactory.ParameterList(
                        SyntaxFactory.SingletonSeparatedList(
                            SyntaxFactory.Parameter(
                                SyntaxFactory.Identifier("args"))
                            .WithType(
                                SyntaxFactory.ArrayType(
                                    SyntaxFactory.PredefinedType(
                                        SyntaxFactory.Token(SyntaxKind.StringKeyword)),
                                    SyntaxFactory.SingletonList(
                                        SyntaxFactory.ArrayRankSpecifier(
                                            new [] { SyntaxFactory.OmittedArraySizeExpression() }))))))));

            // 创建一个Console.WriteLine的调用表达式
            var writeLineExpression = SyntaxFactory.InvocationExpression(
                SyntaxFactory.MemberAccessExpression(
                    SyntaxKind.SimpleMemberAccessExpression,
                    SyntaxFactory.IdentifierName("Console"),
                    SyntaxFactory.IdentifierName("WriteLine")))
                .WithArgumentList(
                    SyntaxFactory.ArgumentList(
                        SyntaxFactory.SingletonSeparatedList(
                            SyntaxFactory.Argument(
                                SyntaxFactory.LiteralExpression(
                                    SyntaxKind.StringLiteralExpression,
                                    SyntaxFactory.Literal("Hello, World!"))))));

            // 创建一个表达式语句,将writeLineExpression包装成语句
            var writeLineStatement = SyntaxFactory.ExpressionStatement(writeLineExpression);

            // 为Main方法添加方法体,包含writeLineStatement
            var methodWithBody = methodDeclaration.WithBody(SyntaxFactory.Block(writeLineStatement));

            // 将Main方法添加到Program类中
            var classWithMainMethod = classDeclaration.WithMembers(SyntaxFactory.SingletonList<MemberDeclarationSyntax>(methodWithBody));

            // 将Program类添加到命名空间中
            var namespaceWithClass = namespaceDeclaration.WithMembers(
SyntaxFactory.SingletonList<NamespaceMemberSyntax>(classWithMainMethod));

            // 将AST节点转换为源代码
            var code = namespaceWithClass.NormalizeWhitespace().ToFullString();
            // 打印生成的源代码
            Console.WriteLine(code);
            Console.WriteLine("===========");


// 如果要转为CST
// SyntaxFactory.CompilationUnit是编译单元,表示一个代码文件.
// 创建语法树的根节点
var compilationUnit = SyntaxFactory.CompilationUnit()
    .WithMembers(namespaceWithClass) // 添加命名空间声明到编译单元
    .NormalizeWhitespace(); // 标准化空白字符

// 转换为CST的SyntaxTree
SyntaxTree syntaxTree = CSharpSyntaxTree.Create(compilationUnit);

// 从SyntaxTree获取源代码字符串
string generatedCode = syntaxTree.GetCompilationUnitRoot().ToFullString();
            // 打印生成的源代码
            Console.WriteLine(generatedCode);

            // 这样就能对上了
            // 获取AST的根节点
            // CompilationUnitSyntax compilationUnit = syntaxTree.GetCompilationUnitRoot();
            // 将AST转换回源代码字符串
            // string generatedCode = compilationUnit.ToFullString();

            Console.ReadKey();
        }
    }
}
```
(完)

评分

参与人数 1明经币 +1 收起 理由
tranque + 1 主打一个听不懂

查看全部评分

发表于 2024-9-9 10:24:59 | 显示全部楼层
你咋啥都会啊,,,
发表于 2024-9-10 09:56:53 | 显示全部楼层
惊佬牛逼,谢谢大佬
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-11-22 20:42 , Processed in 0.188492 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表