漫談IronPython 編譯器講述說(shuō)明
自 IronPython 正式發(fā)布以來(lái),由于對(duì) Python 語(yǔ)言的喜愛(ài)所驅(qū)使,同時(shí)我想藉此去了解一下編程語(yǔ)言的IronPython 編譯器,分析器等程序是什么原理,如何運(yùn)作的,于是就開(kāi)始進(jìn)行IronPython 編譯器的學(xué)習(xí)了。
但代碼也看了有一段時(shí)間了,之前是看一些實(shí)現(xiàn)細(xì)節(jié),結(jié)果越看越糊涂。現(xiàn)在我發(fā)現(xiàn)需要改變一下策略了,因?yàn)槲覀兞私庖粋€(gè)系統(tǒng)總是從對(duì)它的使用方法去開(kāi)始了解,如果直接去了解底層的運(yùn)作原理,則可能會(huì)迷失在代碼海洋里面。所以我也準(zhǔn)備采取自頂而下的分析方法,撿軟柿子捏,從簡(jiǎn)單的,宏觀的入手。至于具體的實(shí)現(xiàn)細(xì)節(jié),可以慢慢再深入研究。
直奔主題,我們看到 Compile() 方法,這是負(fù)責(zé)編譯的主控制方法。這個(gè)方法不難理解,我讀了一遍,注釋如下:
- /// <summary>
- /// 編譯
- /// </summary>
- public void Compile() {
- string fullPath = Path.GetFullPath(outputAssembly);
- string outDir = Path.GetDirectoryName(fullPath);
- string fileName = Path.GetFileName(outputAssembly);
- // Python 編譯器的接受池
- PythonCompilerSink sink = new PythonCompilerSink(compilerSink);
- // 程序集產(chǎn)生器
- assemblyGen = new AssemblyGen(
- Path.GetFileNameWithoutExtension(outputAssembly),
- outDir, fileName, includeDebugInformation, staticTypes, executable, machine
- );
- // 是否以設(shè)定入口點(diǎn)(entry point)
- bool entryPointSet = false;
- // 設(shè)定默認(rèn)的主文件(對(duì)非 DLL 的輸出文件類型而言)
- if (mainFile == null && sourceFiles.Count == 1 && targetKind != PEFileKinds.Dll) {
- mainFile = sourceFiles[0];
- }
- // 對(duì)每個(gè)源文件依次編譯
- foreach (string sourceFile in sourceFiles) {
- // 是否產(chǎn)生 Main 方法
- bool createMainMethod = sourceFile == mainFile;
- // 每個(gè)源代碼文件編譯為一個(gè)模塊
- CompilePythonModule(sourceFile, sink, createMainMethod);
- if (sink.Errors > 0) return;
- if (createMainMethod) {
- entryPointSet = true;
- }
- }
這段代碼中,調(diào)用到了 IronPython 編譯器自身的私有方法 CompilePythonModule() 來(lái)完成編譯模塊的功能。下面我們來(lái)看一下這個(gè)方法在做什么:
- // 依次將所有資源文件添加到程序集中
- if (resourceFiles != null) {
- foreach (ResourceFile rf in resourceFiles) {
- assemblyGen.AddResourceFile(rf.Name, rf.File, rf.PublicResource ? ResourceAttributes.Public : ResourceAttributes.Private);
- }
- }
- // 對(duì)非 DLL 的目標(biāo)文件,必須要求有一個(gè)入口點(diǎn)
- if (targetKind != PEFileKinds.Dll && !entryPointSet) {
- sink.AddError("", string.Format("Need an entry point for target kind {0}", targetKind), String.Empty, CodeSpan.Empty, -1, Severity.Error);
- }
- // 最終產(chǎn)生輸出的程序集
- assemblyGen.Dump();
- }
- 本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/inelm/archive/2006/10/09/4612996.aspx
在上述兩個(gè)方法中,我們看到,出現(xiàn)了幾個(gè)重要的類,它們將是我們下面接著分析的重點(diǎn)線索:
- // 編譯模塊
- private void CompilePythonModule(string fileName, PythonCompilerSink sink, bool createMain) {
- // 設(shè)定當(dāng)前要編譯的源文件
- assemblyGen.SetPythonSourceFile(fileName);
- // 創(chuàng)建編譯器環(huán)境對(duì)象
- CompilerContext context = new CompilerContext(fileName, sink);
- // 創(chuàng)建分析器
- Parser p = Parser.FromFile(state, context);
- // 調(diào)用分析器的分析方法,得到一個(gè)語(yǔ)句對(duì)象(語(yǔ)句應(yīng)該是利用了組合模式的一個(gè)嵌套的概念,這個(gè)語(yǔ)句代表整個(gè)文件里的一個(gè)大語(yǔ)句)
- Statement body = p.ParseFileInput();
- if (sink.Errors > 0) return;
- // 創(chuàng)建一個(gè)全局套件??有可能是指 globals() 這個(gè)字典對(duì)象。有待分析。。。
- // 這里面的 Binder 是干什么的也有待研究。
- GlobalSuite gs = Compiler.Ast.Binder.Bind(body, context);
- string moduleName = GetModuleFromFilename(fileName);
- // 這里看到了 TypeGen,該類代表一個(gè)類型產(chǎn)生器
- // tg 指向了一個(gè)模塊類型(IronPython 中,每一個(gè)模塊產(chǎn)生為一個(gè)對(duì)應(yīng)的類。)
- TypeGen tg = OutputGenerator.GenerateModuleType(moduleName, assemblyGen);
- // 編譯模塊的 __init__ 方法??(猜測(cè))
- CodeGen init = CompileModuleInit(context, gs, tg, moduleName);
到這里為止,我們大致上看到了 IronPython 編譯器的工作流程,從一系列源代碼文件,資源文件,以及其他一些配置屬性出發(fā),經(jīng)過(guò) Parser, 各種 Generator 的運(yùn)作,最終到達(dá) AssemblyGenerator 的 Dump() 方法,輸出編譯結(jié)果程序集。
【編輯推薦】