150 likes | 513 Views
Chapter 3 Describing Syntax and Semantics. Programming Languages. 문법 (Syntax). 문법 (syntax) - 식 , 문장 , 프로그램 단위의 형태와 구조 의미 (semantics) - 식 , 문장 , 프로그램 단위의 의미 예 ) if(a==b) then a = a+1 누가 언어의 정의를 사용하는가 ? 다른 언어 설계자 : 설계시에 검증 단계를 거침
E N D
Chapter 3Describing Syntax and Semantics Programming Languages
문법 (Syntax) • 문법 (syntax) - 식, 문장, 프로그램 단위의 형태와 구조 • 의미 (semantics) - 식, 문장, 프로그램 단위의 의미 • 예) if(a==b) then a = a+1 • 누가 언어의 정의를 사용하는가? • 다른 언어 설계자 : 설계시에 검증 단계를 거침 • 구현하는 사람 : 프로그램의 구조와 실행후의 결과가 무엇인지를 명확하게 알 수 있어야 한다. • 프로그래머 (언어의 사용자) : 프로그래머가 알 수 있는 길은 language manual 뿐이므로 • 센텐스 (sentence) : 알파벳으로부터 만들어진 문자의 스트링 • 언어 (language) : 센텐스의 집합 • 렉심 (lexeme) : 언어의 최하위 문법 단위 (예 : *, sum, begin) • 토근 (token) : 렉심을 같은 종류로 묶어 놓은 것 (예 : 식별자) • 예) index := 2 * count + 17;
문법 기술 방법 • 문법을 기술하기 위한 형식적인 방법 • 인식기(recognizer) • 컴파일러에서 사용 • 주어진 프로그램이 언어에 속하는지 • 생성기(generator) • 언어의 sentence를 생성 • 생성된 언어의 효율성이 문제 • 읽고 이해하기 쉽다는 점에서 필요 • Context-Free Grammars • 1950년대 중반에 Noam Chomsky에 의해 개발 • 언어 생성기, 자연어의 문법을 표시하기 위해 사용 • 문맥 자유 언어(CFG)라 불리는 언어의 종류를 정의 • Backus Normal Form (1959) • Algol 58을 기술한 John Backus에 의해서 발견 • BNF는 CFG와 동등하다
문법 기술 방법 • 메타 언어(metalanguage) • 다른 언어를 기술하는데 사용되는 언어이다 • Abstractions : 문법 구조의 종류를 나타내는데 사용 • 문법 변수처럼 행동한다. • 예: <while_stmt> while <logic_expr> do <stmt> • 이것은 규칙(rule)이다 ; while 문장의 구조를 기술 • 규칙 : LHS(Left-hand side)와 RHS(right-hand side)를 가지며, 터미널 심볼과 논터미널 기호로 구성된다 • 문법 : 공집합이 아닌 규칙의 유한 집합 • Abstraction(혹은 논터미널 심볼)은 하나 이상의 RHS를 가질 수 있다 <stmt> <single_stmt> | begin <stmt_list> end • recursive rule • rule의 LHS가 RHS에 다시 나타나면 Syntactic lists는 recursion을 이용한 BNF로 기술된다 <ident_list> ident | ident, <ident_list>
문법 기술 방법 • Derivation • 시작 기호로부터 시작하여 규칙을 반복해서 적용하여 센텐스로 끝나도록 하는 과정 • 센텐스는 모두 터미널 심볼로 구성됨 • Grammar 예 <program> <stmts> <stmts> <stmt> | <stmt> ; <stmts> <stmt> <var> = <expr> <var> a | b | c | d <expr> <term> + <term> | <term> - <term> <term> <var> | const • Derivation 예: start symbol로부터 sentence를 만드는 과정 <program> => <stmts> => <stmt> => <var> = <expr> => a = <expr> => a = <term> + <term> => a = <var> + <term> => a = b + <term> => a = b + const
program <stmts> <stmt> <var> = <expr> a <term> + <term> <var> const b Derivation • sentential form : derivation 안에 있는 모든 기호의 스트링 • sentence : 터미널 기호로만 구성된 sentential form이다 • leftmost derivation • 각 sentential form에서 가장 왼쪽에 있는 non-terminal 기호가 확장되는 경우 derivation은 leftmost도 rightmost도 아닐 수 있다 • 파스트리(parse tree) : derivation을 계층적으로 나타낸 것 문법이 둘 또는 그 이상의 parse-tree를 가지는 sentential form을 만들 경우 애매하다(ambiguous)고 한다
<expr> <expr> <op> <expr> <expr> <op> <expr> <expr> <expr> <expr> <op> <expr> <expr> <op> const - const / const const - const / const 애매한 수식 문법 • 애매한 수식 문법: <expr> <expr> <op> <expr> | const <op> / | - • 만약 연산자의 우선 순위를 지정하기 위하여 파스 트리를 사용한다면, 애매성이 없어진다
<expr> - <expr> <term> <term> <term> const / const const <expr> + <expr> const <expr> + const const 애매하지 않은 수식 문법 • 애매하지 않은 수식 문법: <expr> <expr> - <term> | <term> <term> <term> / const | const • 다른 parse-tree가 생성되지 않는다! <expr> => <expr> - <term> => <term> - <term> => const - <term> => const - <term> / const => const - const / const • 연산자의 결합도(associativity)도 문법에 의해 지정할 수 있다 <expr> <expr> + <expr> | const (ambiguous) <expr> <expr> + const | const (unambiguous) • 결합법칙이 성립하지 않는 경우 • 21/2 + 1/8 + 1/8 • left to right : 2(5/8) (10.101) = 01101010 (2(2/1))+ 1/8 • right to left : ¼ (.01) = 00111000 + 1/8
BNF vs. EBNF • 확장 BNF: • 옵션 부분을 brackets ([]) 안에 둔다 <proc_call> ident [ ( <expr_list>)] • | 기호와 괄호를 이용하여 선택하여야 할 RHSs를 표시한다 <term> <term> (+ | -) const • 반복을 braces ({})를 사용하여 표시한다 <ident> letter {letter | digit} • BNF: <expr> <expr> + <term> | <expr> - <term> | <term> <term> <term> * <factor> | <term> / <factor> | <factor> • EBNF: <expr> <term> {(+ | -) <term>} <term> <factor> {(* | /) <factor>}
type_identifier identifier ( ) , constant constant .. Syntax Graph • Syntax graph • 터미널 기호는 원 혹은 타원형에, 논터미널 기호는 사각형에 넣고 이들을 화살표로 연결한다
Recursive Descent Parsing • 파싱은 주어진 입력 스트링에 대해 파스 트리를 만들거나 추적하는 과정이다 • 파서는 lexeme을 분석하지 않는다 • 그것은 파서가 호출하는 lexical analyzer가 한다 • recursive descent parser는 파스 트리를 하향식으로 처리한다(top-down parser) • 문법에 있는 nonterminal은 그것과 관계된 서브프로그램을 가진다 • 그 서브프로그램이 위의 nonterminal이 생성할 수 있는 모든 sentential forms을 파싱한다 • recursive descent parsing 서브프로그램은 문법 규칙으로부터 직접 만들어진다 • Recursive descent parser는 다른 top-down parser 처럼left-recursive 문법으로부터 만들 수 없다 • 문법예 : <term> <factor> {(* | /) <factor>} void term () { factor (); /* parse the first factor*/ while (next_token == ast_code || next_token == slash_code) { lexical (); /* get next token */ factor (); /* parse the next factor */ } }
속성 문법 • 정적 의미(Static semantics) - 수행중의 의미와는 무관 • Context-free; 귀찮다 (예 ; 타입 검사) • Noncontext-free (예; 변수는 사용되기 전에 반드시 선언되어야 한다) • Ada에서 end subprogram name이 head와 일치해야 한다. • 속성 문법(Attribute Grammars; AGs) (Knuth, 1968) • Cfgs는 프로그래밍 언어의 모든 문법을 기술할 수 없다 • 파스 트리에 의미 정보를 가지고 다닐 수 있도록 • AG의 중요성: • Static semantics를 지정한다 • Compiler 설계시 사용가능(static semantics 검사) • 정의:속성 문법은 cfg G = (S, N, T, P)에 다음과 같은 것을 추가로 가진다: • 각 문법 기호 x에 대해 속성 값의 집합 A(x)를 가진다 • 각 규칙은 규칙안에 있는 모든 nonterminal의 속성을 정의하는 함수의 집합을 가진다 • 각 규칙은 속성의 일관성을 검사하기 위한 술어의 집합을 가진다(공집합일 수도 있다)
속성 문법 • X0 -> X1 ... Xn 을 규칙이라 하고 • 함수 S(X0) = f(A(X1), ... A(Xn)) 은 synthesized attributes를 정의 • 함수 I(Xj) = f(A(X0), ... , A(Xn)), (i ≤ j ≤ n), inherited attribute를 정의 • 처음에 리프 노드에 intrinsic attributes가 존재 • 예 :식 id + id • id는 int_type 혹은 real_type이 될 수 있다 • 두 id는 모두 같은 타입이어야 한다 • 식의 타입은 그것이 기대되는 타입과 일치하여야 한다 • BNF: <expr> <var> + <var> <var> id • 속성: • actual_type - synthesized for <var> and <expr> • expected_type - inherited for <expr> • env - inherited for <expr> and <var>
속성 문법 • Attribute Grammar: • syntax rule: <expr> <var>[1] + <var>[2] • semantic rules: <var>[1].env <expr>.env <var>[2].env <expr>.env <expr>.actual_type <var>[1].actual_type • predicate: <var>[1].actual_type == <var>[2].actual_type <expr>.expected_type == <expr>.actual_type • syntax rule: <var> id • semantic rule: <var>.actual_type lookup (id, <var>.env) • 어떻게 속성 값이 계산되는가? • 만약 모든 속성이 inherited이면 트리는 하향식으로 장식된다 • 만약 모든 속성이 synthesized이면 트리는 상향식으로 장식된다 • 만약 두 가지 종류의 속성이 모두 다 사용되면 상향식과 하향식이 조합된다
속성 문법 • <expr>.env inherited from parent <expr>.expected_type inherited from parent • <var>[1].env <expr>.env <var>[2].env <expr>.env • <var>[1].actual_type lookup (A, <var>[1].env) <var>[2].actual_type lookup (B, <var>[2].env) <var>[1].actual_type =? <var>[2].actual_type • <expr>.actual_type <var>[1].actual_type <expr>.actual_type =? <expr>.expected_type