MYC 컴 파일 러 소스 코드 생 성

7343 단어
앞에서 문법 에 대한 해석 을 말 한 후에 코드 생 성 은 매우 간단 해 졌 다.my c 는 간단 한 예제 컴 파일 러 이지 만 해석 하 는 과정 에서 작은 문법 트 리 를 만 들 었 습 니 다. 이 문법 트 리 는 exe 실행 가능 한 파일 과 il 소스 코드 를 만 드 는 과정 에 사 용 될 것 입 니 다.
컴 파일 러 가 분석 할 때 emit 류 를 사용 하여 중간 문법 트 리 를 만 들 고 문법 트 리 의 데이터 구조 와 조작 방법 은 iasm 이라는 유형 에서 이 루어 집 니 다. 소스 프로그램의 문법 해석 이 끝 난 후에 Exe 와 Asm 두 가지 유형 이 각각 생 성 된 문법 트 리 를 옮 겨 다 니 며 최종 코드 를 만 듭 니 다.
우 리 는 몇 가지 코드 의 예 를 살 펴 보 겠 습 니 다. 다음 표 의 함수 Parser. program 에서 함수 시작 과 끝 에 각각 prolog 와 epilog 두 함 수 를 호출 했 습 니 다. 이 두 함수 의 목적 은 문법 해석 전후 에 준비 와 마무리 작업 을 수행 하 는 것 입 니 다.컴 파일 과정의 시작 단계 에서. net assembly 의 요구 에 따라 모듈 (module) 과 클래스 (class) 를 만 듭 니 다. c 언어 는 과정 을 대상 으로 하 는 언어 이지 만. net 은 대상 을 대상 으로 하 는 환경 이 므 로 모든 코드 는 하나의 클래스 에 저장 해 야 합 니 다.
public void program()
  {
  prolog();
  while (tok.NotEOF())
    {
    outerDecl();
    }
  if (Io.genexe && !mainseen)
    io.Abort("Generating executable with no main entrypoint");
  epilog();
  }

void prolog()
  {
  emit = new Emit(io);
  emit.BeginModule();       // need assembly module
  emit.BeginClass();
  }

그리고 emit 에서 BeginModule 과 BeginClass 두 함수 의 코드 는 다음 과 같 습 니 다.
public void BeginModule()
  {
  //    Exe      module,   .net ,  assembly   
  //   module  ,   C   ,    module    ,  
  //            IL       module。
  exe.BeginModule(io.GetInputFilename());
  }

public void BeginClass()
  {
  //    Exe    class,           ,      
  //              。
  exe.BeginClass(Io.GetClassname(), TypeAttributes.Public);
  //             ,          ,  
  //     IL class     。
  if (Io.genlist)
    io.Out(".class " + Io.GetClassname() + "{\r
"); }

. NET 에 서 는 반사 기술 을 사용 하여 assembly, 유형 과 함 수 를 생 성 할 수 있 습 니 다. 다음 표 는 Exe 류 의 BeginModule 함수 의 소스 코드 입 니 다.
public void BeginModule(string ifile)
  {
  // .net   assembly    ,   appdomain  
  appdomain = System.Threading.Thread.GetDomain();
  appname = getAssemblyName(filename);
  //   AppDomain.DefineDynamiceAssembly    Assembly,    
  //   ,      ,       。   ,.net  IronPython 
  //                 。
  appbuild = appdomain.DefineDynamicAssembly(appname,
                         AssemblyBuilderAccess.Save,
                         Io.genpath);
  //  .net ,                module 。
  emodule = appbuild.DefineDynamicModule(
        filename+"_module",
        Io.GetOutputFilename(),
        Io.gendebug);
  Guid g = System.Guid.Empty;
  if (Io.gendebug)
    srcdoc = emodule.DefineDocument(ifile, g, g, g);
  }

준비 작업 이 끝 난 후에 문법 트 리 를 생 성 할 수 있 습 니 다. 컴 파일 러 는 문법 을 해석 하 는 과정 에서 문법 트 리 에 요 소 를 계속 추가 합 니 다. 예 를 들 어 컴 파일 함수 과정 에서 while 순환 을 처리 하 는 것 을 예 로 들 면 (그 중의 호출 경 로 는: program -> outerDecl -> declFunc -> blockOuter -> fcWhile 입 니 다.
void fcWhile()
  {
  //     il       ,                   
  //       (label),                label   
  String label1 = newLabel();
  String label2 = newLabel();
 
  //          ,    IL            IL      
  CommentHolder();  /* mark the position in insn stream */
  //     ,              ,           ,
  //            ,      ,  label          
  //      ,                 
  emit.Label(label1);
  tok.scan();
  //        
  if (tok.getFirstChar() != '(')
    io.Abort("Expected '('");

  //                
  boolExpr();
  CommentFillPreTok();
  //      label
  emit.Branch("brfalse", label2);

  //         ,  blockInner         
  blockInner(label2, label1);   /* outer label, top of loop */
  //         ,            
  emit.Branch("br", label1);
  
  //     ,       
  emit.Label(label2);
  }

한편, emit 유형 에서 각 방법 은 분 석 된 문법 요 소 를 문법 트 리 에 추가 할 뿐 문법 트 리 의 노드, 데이터 구조 와 조작 방법 은 모두 IAsm 류 에서 정 의 됩 니 다. 다음 표 는 Branch 의 소스 코드 입 니 다.
public void Branch(String s, String lname)
  {             // this is the branch source
  NextInsn(1);
  //              Branch    
  icur.setIType(IAsm.I_BRANCH);
  //     
  icur.setInsn(s);
  //     
  icur.setLabel(lname);
  }

프로그램 컴 파일 이 완료 되면 Exe 클래스 와 Asm 클래스 는 각각 문법 트 리 를 옮 겨 다 니 며 최종 결 과 를 생 성 합 니 다. my c 컴 파일 러 의 소스 코드 에서 Parser. declFunc 함 수 는 Emit. IL 함 수 를 호출 하여 프로그램의 생 성 을 완성 합 니 다.
//   C             ,                ,
//           ,                     
void declFunc(Var e)
  {
#if DEBUG
  Console.WriteLine("declFunc token=["+tok+"]
"); #endif CommentHolder(); // start new comment // e.setName(tok.getValue()); /* value is the function name */ // main, - mainseen true // , if (e.getName().Equals("main")) { if (Io.gendll) io.Abort("Using main entrypoint when generating a DLL"); mainseen = true; } // , , // , , // staticvar.add(e); /* add function name to static VarList */ paramvar = paramList(); // track current param list e.setParams(paramvar); // and set it in func var // localvar = new VarList(); // track new local parameters CommentFillPreTok(); // prolog, ,this emit.FuncBegin(e); if (tok.getFirstChar() != '{') io.Abort("Expected ‘{'"); // blockOuter(null, null); emit.FuncEnd(); // , emit.IL(); // IL , LIST IL if (Io.genlist) emit.LIST(); emit.Finish(); }

emit. IL 함 수 는 전체 문법 트 리 를 Exe 형식 으로 옮 겨 다 니 며 결과 프로그램 을 만 드 는 것 입 니 다.
public void IL()
  {
  IAsm a = iroot;
  IAsm p;

  //          
  while (a != null)
    {
    //                      
    switch (a.getIType())
      {
      case IAsm.I_INSN:
    exe.Insn(a);
    break;
      case IAsm.I_LABEL:
    exe.Label(a);
    break;
      case IAsm.I_BRANCH:
    exe.Branch(a);
    break;
      //       
      default:
    io.Abort("Unhandled instruction type " + a.getIType());
    break;
      }
    p = a;
    a = a.getNext();
    }
  }

그리고 Exe 형식 은 실제 코드 생 성 을 수행 합 니 다. 예 를 들 어 앞의 IL 함수 와 같이 I 를 만 나 고 있 습 니 다.BRANCH 형식의 노드 를 호출 할 때 Exe. Branch 함 수 를 동적 Assembly (DynamicAssemby) 에서 코드 를 생 성 합 니 다.
public void Branch(IAsm a)
  {
  Object o = opcodehash[a.getInsn()];
  if (o == null)
    Io.ICE("Instruction branch opcode (" + a.getInsn() + ") not found in hash”);
  //    ILGenerator      IL  。
  il.Emit((OpCode) o, (Label) getILLabel(a));
  }

그리고 Asm 류 도 비슷 한 방법 으로 IL 소스 코드 를 생 성 한다.
마지막 으로 myc 컴 파일 러 에 도 일부 의미 처리 가 있 습 니 다. 예 를 들 어 앞에서 말 한 함수 호출 시 호출 된 함수 가 정의 되 지 않 으 면 이상 한 상황 을 던 져 야 합 니 다. Parser. statement (즉, 실제 C 문 구 를 컴 파일 하 는 함수) 에 나타 납 니 다.
void statement()
  {
  Var e;
  String vname = tok.getValue();

  CommentHolder();  /* mark the position in insn stream */
  switch (io.getNextChar())
    {
    case '(':           /* this is a function call */
      //              
     tok.scan();        /* move to next token */
     //                  ,        
   //                ,      ,        
      e = staticvar.FindByName(vname); /* find the symbol (e cannot be null) */
      emit.Call(e);
     
      //        
   }
  if (tok.getFirstChar() != ';')
    io.Abort("Expected ';'");
  tok.scan();
  }

좋은 웹페이지 즐겨찾기