본문 바로가기

Concepts of Programming Languages

4장 Lexical and Syntax Analysis 1(lecture 9)

 

 

우리가 high-level language로 작성한 코드(=program)들은 실제로 컴퓨터에서 실행 되기 위해선

 

일련의 과정이 필요한데 이 과정의 차이에 따라 아래와 같이 3가지 방식으로 나뉜다.

 

 

 

프로그래밍 언어의 실행(implementation) 방식

 

 

1. 컴파일 (compilation) 

 

 프로그래밍 언어 코드(high level language) ------------------------------->  기계코드 -----> 컴파일된 파일을 실행

                                                                              컴파일러가 컴파일(translate)

 

처음에 처리를 통째로 한 상태라서 실행파일이 효율적이고 , 해석해나가며 실행하는 것보다 빠른 실행가능 

(규모가 큰 어플리케이션에서 유용)

 

C++, COBOL 언어들이 취하는 방식임

 

 

2. 순수 인터프리터(pure interpretation)

 

프로그래밍 언어 코드(high level language) ---------> 인터프리터라는 프로그램이 한줄씩 해석해나가면서 실행 (no translation but interpret)    

 

//translation과 interpretation을 다르게 얘기하는데 거의 비슷한 뜻아닌가 싶었는데, 비유를 들어 생각해보면 쉽다

 영어 원서가 하나 있다고 생각하면 translation은 이걸 번역본 만들어서 한국 출판사에서 출판한거고, 

  interpret는 내 머릿속(interpretor)에서 하나하나 해석하면서 영어 원서를 읽어나간다는 차이.

 

아무래도 미리 처리한 컴파일러보다 실행시간이 오래걸릴 수 밖에.

(규모가 작거나 실행시간이 중요하지 않으면 이 방식을 선택)

 

HTML, javascript 같은 언어들이 취하는 방식(스크립트 언어들에서 많이 사용한다고함)                                                          

 

 

3. 하이브리드 (컴파일방식과 인터프리테이트 방식의 짬뽕된 형태 , hybrid implementation)

 

프로그래밍 언어 코드(high level language) ----------> 중간형태의 형태의 코드(intermediate form)-----> 이 언어를 해석(interpret)해서 실행하는 방식

                                                                                     translate

이 하이브리드 방식은 원래 컴파일방식에 비해 아주 느렸음, 근데 JIT컴파일러의 등장으로 인해 널리 퍼기게 되었다. (특히 자바와

마이크로닷넷 시스템에서)

- JIT(just-in-time) 컴파일러

JIT에서 특징 적인 것은 intermediate code를 machine code로 translate 하는 것.

실행 시간에 기계어로 번역을 하며, 해당 번역을 하면서 그 코드를 캐싱함으로써 같은 함수가 여러번 불릴 때 기계어코드가 또 생성되는 것을

방지해준다. 

 이것은 실행시간의 효율성을 높여주어, 하이브리드 방식을 조금은 더딘 컴파일방식의 수준으로 높였다고 보면 된다.

 

 

이 세 방식 모두 코드를 분석해야 하고, 그 접근 방식에 상관 없이 lexical analyzer 과 syntax analyzer(parser)을 이용해야함.

 

//pure interpreter에서는 그림에 안나타나 있지만 실제로 해석과정에서 두 analyzer가 필요함. 

 

//참고로 일반적인 언어(영어, 한글)를 생각했을 때, lexical은 어휘와 관련된거고 , syntax는 문장의 구조와 관련된 것.

 

 

anlayzer를 굳이 하나로 통합해 쓰지 않고 lexical적인 것과 syntax적인 부분으로 나누어 사용하는 이유?

 

 

책에서 간결성(simplicity), 효율성(effeciency), 범용성(portability) 측면에서 둘을 분리하는 것의 장점을 설명한다.

 

들어가기에 앞서 상식적으로 생각해봐도 어휘적인 판단, apple이 영어단어에 포함되는가라는 문제보다 문장이 문법에 맞는가를 검사하는게 어렵다는 걸

 

기억해두자. 

 

 

간결성

-둘의 분리는 어휘 분석을 간단하게 한다. //상식적으로 ㅇㅇ

-저차원에서 상세히 어휘분석을 하는 것을 문장구조 분석에서 분리시키는 것이 문장구조 분석기를 덜 복잡하고 작게 만들어준다.

 

효율성

-어휘 분석을 하는게 컴파일 시간에 상당한 부분을 차지함.(근데 어휘분석은 따로 떼어놓으면 쉬우니 분석시간을 효과적으로 줄이겠군)

-어휘 분석은 최적화(optimization)가 가능함. (어휘 분석 간단하니까 뭔가 중복되는 것들을 한번에 처리한다든가 해서 최적화가 가능하다는 얘기일 듯)

-문장구조 분석에서 최적화를 하는건 그 노력에 비해 별로 성과가 없음. (복잡하니까 최적화도 힘들고 한다고 해서 효과가 별로 없다는 얘기인 듯 )

-둘을 분리함으로써 최적화가 가능해짐. (둘을 한번에 처리하면서 최적화를 하는건 힘들지)

 

범용성, 이식가능성, 적용성 .. 알아서 해석하시길

-어휘 분석은 해당 플랫폼에 아주 종속적이지만 문장구조는 플랫폼에 독립적임

(내 생각 : 어떤 언어든 표현하는 단어는 다를 수 있지만, 새로운 언어를 개발할 때 개발자가 기존의 프로그램 언어구조들과 크게 다르지 않게 설계하나봄)

-소프트웨어 시스템에서 기계에 종속적인 부분을 따로 떼어놓는게 좋다.(이건 뭔말인지 잘 모르겠음)

 

 

 

아 쓰다보니 너무 시간이 낭비된다. 이제 이해한 것들은 머리에 들어간거니까 굳이 주절주절 쓰지말고 간단하게 포인트만 쓰겠다.

 

주목적은 공부하는거지 예쁘게 정리하는게 아니니까..

 

 

 

이전에 알 것. lexemes과 token 그리고 pattern.  // lexemes는 표현이고 그것의 상위 카테고리가 token

 

c언어 코드

 

a = result + 7 ;

 

 

있다고 하면

 

 

 lexemes

 token

 a , result

 identifier

 =

 assign operator

 ... 이하 생략 귀찮

 ...이하 생략 귀찮..

 

 

pattern은 token이 어떻게 형성되는가에 대한 규칙

 

indentifier ->letter(letter | digit | _ )  // 식별자는 문자, 숫자, _ 로 이루어진다는 뜻임

 

 

*Lexical Analysis

 

 

lexical 분석기는 심볼 테이블을 이용한다는 것이 포인트, Parser(=Syntax analyzer)와 심볼테이블이 연결된 이유는 user-defined names(사용자 정의한 명칭?)들을 해당 심볼테이블에 포함시키기 때문.

 

*Symbol-table이 포함하는 것.

 

-키워드

-표준 식별자(standard identifiers)

-숫자나 문자 이외 특수문자

-사용자 정의 데이터 타입

-사용자 정의 변수

 

반드시 포함해야 하는것

-token class   // 토큰도 단계적인가 보다 , operator가 조금 더 높은 class고 그 아래 assign-opreator, compare-oprator 가 있고,  말단에는 lexemeㄴ 

-lexemes

-scope  // 대충 알거 같은데 말로 설명은 못하겠음. (지역변수, 전역변수 쓸 때 그 의미의 scope와 관련된 내용인거 같음)

-types

-pointer to other symbol entries (필요로 할 때만)  //대충 알거 같은데 말로 설명은 못하겠음

 

 

cf. lexical 분석기서 검출된 오류는  compile error? run-time error?  --------->실행파일 실행중이 아니고 실행파일 만드는 중이니까 당연히 compile error.

 

 

 

Lexical analyzer 만들기      

 

-lexical analyzer는 syntax analyzer가 작동중에 다음 token을 필요로 할 때  불려지는 기능을 가짐

 

3가지 접근 방식

 

방식 1. 해당 언어를 표현하는데 쓰이는 token에 대한 formal한 기술(description)을 한다. 해당 기술은 lexical analyzer를 자동적으로 가동시키는 소프트웨어에 input으로 이용됨.

 

방식 2. token에 대해 기술한 state diagram이란걸 설계하고, 그 state diagram을 실행하는 프로그램을 작성하는 것.

 

방식 3. 방식 2에서와 똑같이 state diagram을 설계하지만, 그 state diagram을 테이블을 이용하여 실행되게 수기 작성하는 것 // 이건 잘 모르겠다..

 

 

 

자 그럼 state (transition) diagram이 뭐냐

-방향성이 있는 graph 방식임

-node는 state name이 표시되어 있음

-선은 state간에 이동(transition)에 영향을 미치는 input character이 표시되어 있음

-선은 이동이 발생할 때 lexical analyzer가 실행해야 되는 action들이 표시되기도 함

-lexical analyzer에 이용되는 state diagram은 finite automata를 나타낸다.

 

cf. finite automata // automata가 수학적으로 추상화된 기계 모델이고 , finite는 유한하다는 거니까---> 입출력이나 상태들이 유한한 수학적 기계모델 이라고 이해하면 될 듯.

 

간단화를 거쳐 만든 state diagram 

 

아래 내용은 위에 쓰인 것들에 대한 간단한 설명.

 

 

 

 

 

참고도서 : concepts of programming language 10th edition - sebesta 저.