Chapter 4: Syntax analysis • Syntax analysis is done by the parser. – Detects whether the program is written following the grammar rules and reports syntax errors. – Produces a parse tree from which intermediate code can be generated. token Source program Lexical analyzer parser Request for token Symbol table Parse tree Rest of front end Int. code • The syntax of a programming language is described by a context-free grammar (Backus-Naur Form (BNF)). – Similar to the languages specified by regular expressions, but more general. – A grammar gives a precise syntactic specification of a language. – From some classes of grammars, tools exist that can automatically construct an efficient parser. These tools can also detect syntactic ambiguities and other problems automatically. – A compiler based on a grammatical description of a language is more easily maintained and updated. • A grammar G = (N, T, P, S) – N is a finite set of non-terminal symbols – T is a finite set of terminal symbols – P is a finite subset of ( N T ) * N ( N T ) * ( N T ) * • An element ( , ) P is written as – S is a distinguished symbol in N and is called the start symbol. • Language defined by a grammar – We say “aAb derives awb in one step”, denoted as “aAb=>awb”, if A->w is a production and a and b are arbitrary strings of terminal or nonterminal symbols. * – We say a1 derives am if a1=>a2=>…=>am, written as a1=>am – The languages L(G) defined by G are the set of strings of the * terminals w such that S=>w. • Example: A->aA A->bA A->a A->b • Chomsky Hierarchy (classification of grammars) • A grammar is said to be – regular if it is • right-linear, where each production in P has the form, A wB or A w . Here, A and B are non-terminals and w is a terminal • or left-linear – context-free if each production in P is of the form A , where A N and ( N T ) * – context sensitive if each production in P is of the form where | || | – unrestricted if each production in P is of the form where • Languages specified by different types of grammars: – Language1 = {a, aa, aaa, aaaa, ….} – Language2 = {ab, aabb, aaabbb, aaaabbbb, …} – Language3 = {abc, aabbcc, aaabbbccc, …} • Context-free grammar is sufficient to describe most programming languages. • Example: a grammar for arithmetic expressions. <expr> -> <expr> <op> <expr> <expr> -> ( <expr> ) <expr> -> - <expr> <expr> -> id <op> -> + | - | * | / derive -(id) from the grammar: <expr> => -<expr> => - (<expr>) =>-(id) sentence: a strings of terminals that can be derived from S sentential form: a strings of terminals or none terminals that can be derived from S. – derive id + id * id from the grammar: E=>E+E=>E+E*E=>E+E*id=>E+id*id=>id+id*id – leftmost/rightmost derivation -- each step replaces leftmost/rightmost non-terminal. E=>E+E=>id+E=>id+E*E=>id+id*E=>id+id*id – Parse tree: • A parse tree pictorially shows how the start symbol of a grammar derives a specific string in the language. Given a context-free grammar, a parse tree has the following properties: – – – – The root is labeled by the start symbol Each leaf is labeled by a token or the empty string Each interior node is labeled by a nonterminal If A is a non-terminal labeling some interior node and abcdefg..z are the labels of the children of that node from left to right, then A->abcdefg..z is a production of the grammar. – The leaves of the parse tree read from left to right is called “yield” of the parse tree. It is equivalent to the string derived from the nonterminal at the root of the parse tree. – An ambiguous grammar is one that can generate two or more parse trees that yield the same string. – E.G string -> string + string string->string - string string ->0|1|2|3|4|5|6|7|8|9 string=>string + string =>string - string + string => 9 -5 + 2 string=>string - string=>string - string + string =>9-5+2