前言 本章对应官方教程第8章 。本章介绍如何将语言编译为目标文件。
教程如下:
教你使用swift写编译器玩具(0) 
教你使用swift写编译器玩具(1) 
教你使用swift写编译器玩具(2) 
教你使用swift写编译器玩具(3) 
教你使用swift写编译器玩具(4) 
教你使用swift写编译器玩具(5) 
教你使用swift写编译器玩具(6) 
教你使用swift写编译器玩具(7) 
教你使用swift写编译器玩具(8) 
仓库在这 
开始 因为我们之前用了JIT,但是我们现在也要生成目标文件,两者只能选其一,所以我们现在把封装一下JIT以及main文件。
首先是封装JIT,我这里封装为类CodeRunner。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class  CodeRunner   {          private  let  jit: JIT           private  typealias  fnPr =  @convention(c)  () -> Double           init (machine: TargetMachine ) {         jit =  JIT (machine: machine)     }          public  func  run (module: Module)   {         do  {             let  handle =  try  jit.addEagerlyCompiledIR(module, { (_ ) -> JIT .TargetAddress  in                  return  JIT .TargetAddress ()             })             let  addr =  try  jit.address(of: "__anon_expr" )             let  fn =  unsafeBitCast (addr, to: fnPr.self )             print ("\(fn()) " )             try  jit.removeModule(handle)         } catch  {             fatalError ("Adds the IR from a given module failure." )         }     }      }
 
接着是main文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 let  isUseJIT =  false func  readFile (_  path: String)   -> String ? {     var  path =  path     if  path.hasSuffix("\n " ) {         path.removeLast()     }     guard  path.split(separator: "." ).last!  ==  "k"  else  {         print ("Expected file is *.k." )         return  nil      }     do  {         return  try  String (contentsOfFile: path, encoding: .utf8)     } catch  {         print ("Read file \(path)  failure." )         return  nil      } }func  main ()   {          initModule()          let  parser =  Parser ()          if  let  path =  String (data: FileHandle .standardInput.availableData, encoding: .utf8) {         if  let  str =  readFile(path) {             parser.parse(str)           	             theModule.targetTriple =  .default             do  {                                  let  targetMachine =  try  TargetMachine (triple: .default,                                                       cpu: "x86-64" ,                                                       features: "" ,                                                       optLevel: .default,                                                       relocations: .default,                                                       codeModel: .default)               	                 theModule.dataLayout =  targetMachine.dataLayout               	                 let  pass =  PassPipeliner (module: theModule)                 pass.execute()                 if  isUseJIT {                     let  runner =  CodeRunner (machine: targetMachine)                     runner.run(module: theModule)                 } else  {                                          let  path =  "填你自己的路径"                    	                     try  targetMachine.emitToFile(module: theModule, type: .object, path: path)                     print ("Wrote \(path) " )                 }             } catch  {                 print ("\(error) " )             }         }     } } main()
 
测试 我们新建.k文件average.k。
1 def average(x  y ) (x  + y ) * 0.5 ;
 
我们运行代码生成目标文件output.o,我们可以用下面命令看一下目标文件是否生成了符号表。
 
会输出类似的以下信息。
1 2 3 4 output .o:	file  format  Mach-O 64-bit x86-64 SYMBOL TABLE : 0000000000000000 g     F __TEXT,__text	_average
 
生成完目标文件我们需要写一段C++代码进行调用。
1 2 3 4 5 6 7 8 9 10 #include  <iostream>  extern  "C"  {     double  average (double , double )  ; }int  main ()   {     std ::cout  << "average of 3.0 and 4.0: "  << average(3.0 , 4.0 ) << std ::endl ; }
 
将程序链接到output.o并查看结果
1 2 3 4 5 clang++ main.cpp output.o -o main ./main //输出 average of 3.0 and 4.0: 3.5
 
完整代码请参考仓库 。
可能遇到的问题 macOS下’wchar.h’ File Not Found
参考回答macOS ‘wchar.h’ File Not Found 
1 open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg
 
Mojava下安装头文件包即可。