Presentation is loading. Please wait.

Presentation is loading. Please wait.

Implementation, Syntax, and Semantics

Similar presentations


Presentation on theme: "Implementation, Syntax, and Semantics"— Presentation transcript:

1 Implementation, Syntax, and Semantics

2 Implementation Methods
Compilation Translate high-level program to machine code Slow translation Fast execution Compiler Source Program Target Target Program Input Output

3 Compiler Compilation Process

4 Implementation Methods
Pure interpretation No translation Slow execution Becoming rare Interpreter Source Program Output Input

5 Implementation Methods
Hybrid implementation systems Small translation cost Medium execution speed Translator Source Program Intermediate Examples: Java -> Java bytecode (intermediate language) C/C++ -> assembler (intermediate representation) Virtual Machine Intermediate Program Output Input

6 Hybrid Implementation System
Translator Hybrid Implementation System

7 Programming Environments
The collection of tools used in software development UNIX An older operating system and tool collection Borland JBuilder An integrated development environment for Java Microsoft Visual Studio.NET A large, complex visual environment Used to program in C#, Visual BASIC.NET, Jscript, J#, or C++

8 Describing Syntax Lexemes: lowest-level syntactic units
Tokens: categories of lexemes sum = x + 2 – 3 Lexemes: sum, =, x, +, 2, -, 3 Tokens: identifier, equal_sign, plus_op, integer_literal, minus_op

9 Formal Method for Describing Syntax
Backus-Naur form (BNF) Also equivalent to context-free grammars, developed by Noam Choamsky (a linguist) BNF is a meta-language a language used to describe another language Consists of a collection of rules (or productions) Example of a rule: <assign>  < var > = < expression > LHS: the abstraction being defined RHS: contains a mixture of tokens, lexemes, and references to other abstractions Abstractions are called non-terminal symbols Lexemes and tokens are called terminal symbols Also contains a special non-terminal symbol called the start symbol

10 Example of a grammar in BNF
<program>  begin <stmt_list> end <stmt_list>  <stmt> | <stmt>; <stmt_list> <stmt>  <var> = <expression> <var>  A | B | C | D <expression>  <var> + <var> | <var> - <var> | <var>

11 Derivation The process of generating a sentence begin A = B – C end
Derivation: <program> (start symbol) => begin <stmt_list> end => begin <stmt> end => begin <var> = <expression> end => begin A = <expression> end => begin A = <var> - <var> end => begin A = B - <var> end => begin A = B - C end

12 BNF Leftmost derivation:
the replaced non-terminal is always the leftmost non-terminal Rightmost derivation the replaced non-terminal is always the rightmost non-terminal Sentential forms Each string in the derivation, including <program>

13 Derivation begin A = B + C; B = C end
Rightmost: <program> (start symbol) => begin <stmt_list> end => begin <stmt>; <stmt_list> end => begin <stmt>; <stmt> end => begin <stmt>; <var> = <expression> end => begin <stmt>; <var> = <var> end => begin <stmt>; <var> = C end => begin <stmt>; B = C end => begin <var> = <expression>; B = C end => begin <var> = <var> + <var>; B = C end => begin <var> = <var> + C; B = C end => begin <var> = B + C; B = C end => begin A = B + C; B = C end

14 Parse Tree A hierarchical structure that shows the derivation process
Example: <assign>  <id> = <expr> <id>  A | B | C | D <expr>  <id> + <expr> | <id> - <expr> | ( <expr> ) | <id>

15 Parse Tree A = B * (A + C) <assign> <id> = <expr>
A = <expr> A = <id> * <expr> A = B * <expr> A = B * ( <expr> ) A = B * ( <id> + <expr> ) A = B * ( A + <expr> ) A = B * ( A + <id> ) A = B * ( A + C )

16 Ambiguous Grammar A grammar that generates a sentence for which there are two or more distinct parse trees is said to be ambiguous Example: <assign>  <id> + <expr> <id>  A | B | C | D <expr>  <expr> + <expr> | <expr> * <expr> | ( <expr> ) | <id> Draw two different parse trees for A = B + C * A

17 Ambiguous Grammar

18 Ambiguous Grammar Is the following grammar ambiguous?
<if_stmt>  if <logic_expr> then <stmt> | if <logic_expr> then <stmt> else <stmt>

19 Operator Precedence A = B + C * A
How to force “*” to have higher precedence over “+”? Answer: add more non-terminal symbols Observe that higher precedent operator reside at “deeper” levels of the trees

20 Operator Precedence A = B + C * A Before: After:
<assign>  <id> + <expr> <id>  A | B | C | D <expr>  <expr> + <expr> | <expr> * <expr> | ( <expr> ) | <id> After: <assign>  <id> + <expr> <id>  A | B | C | D <expr>  <expr> + <term> | <term> <term>  <term> * <factor> | <factor> <factor>  ( <expr> ) | <id>

21 Operator Precedence A = B + C * A

22 Associativity of Operators
A = B + C – D * F / G Left-associative Operators of the same precedence evaluated from left to right C++/Java: +, -, *, /, % Right-associative Operators of the same precedence evaluated from right to left C++/Java: unary -, unary +, ! (logical negation), ~ (bitwise complement) How to enforce operator associativity using BNF?

23 Associativity of Operators
<assign>  <id> = <expr> <id>  A | B | C | D <expr>  <expr> + <term> | <term> <term>  <term> * <factor> | <factor> <factor>  ( <expr> ) | <id> Left-recursive rule Left-associative

24 Associativity of Operators
<assign>  <id> = <factor> <factor>  <exp> ^ <factor> | <exp> <exp>  (<expr>) | <id> <id>  A | B | C | D Right-recursive rule Exercise: Draw the parse tree for A = B^C^D (use leftmost derivation)

25 Extended BNF BNF rules may grow unwieldy for complex languages
Provide extensions to “abbreviate” the rules into much simpler forms Does not enhance descriptive power of BNF Increase readability and writability

26 Extended BNF Optional parts are placed in brackets ([ ])
<select_stmt>  if ( <expr> ) <stmt> [ else <stmt> ] Put alternative parts of RHSs in parentheses and separate them with vertical bars <term>  <term> (+ | -) const Put repetitions (0 or more) in braces ({ }) <id_list>  <id> { , <id> }

27 Extended BNF (Example)
<expr>  <expr> + <term> | <expr> - <term> | <term> <term>  <term> * <factor> | <term> / <factor> | <factor> <factor>  <exp> ^ <factor> | <exp> <exp>  ( <expr> ) | <id> EBNF: <expr>  <term> {(+|-) <term>} <term><factor>{(*|/)<factor>} <factor> <exp>{^ <exp>} <exp>  ( <expr> ) | <id>

28 Compilation

29 Lexical Analyzer A pattern matcher for character strings
The “front-end” for the parser Identifies substrings of the source program that belong together => lexemes Lexemes match a character pattern, which is associated with a lexical category called a token Example: sum = B –5; Lexeme Token sum ID (identifier) = ASSIGN_OP B ID - SUBTRACT_OP 5 INT_LIT (integer literal) ; SEMICOLON

30 Lexical Analyzer Functions:
Extract lexemes from a given input string and produce the corresponding tokens, while skipping comments and blanks Insert lexemes for user-defined names into symbol table, which is used by later phases of the compiler Detect syntactic errors in tokens and report such errors to user How to build a lexical analyzer? Create a state transition diagram first A state diagram is a directed graph Nodes are labeled with state names One of the nodes is designated as the start node Arcs are labeled with input characters that cause the transitions

31 State Diagram (Example)
Letter  A | B | C |…| Z | a | b | … | z Digit  0 | 1 | 2 | … | 9 id  Letter{(Letter|Digit)} int  Digit{Digit} main () { int sum = 0, B = 4; sum = B - 5; }

32 Lexical Analyzer Need to distinguish reserved words from identifiers e.g., reserved words: main and int identifiers: sum and B Use a table lookup to determine whether a possible identifier is in fact a reserved word To determine whether id is a reserved word

33 Lexical Analyzer Useful subprograms in the lexical analyzer: lookup
determines whether the string in lexeme is a reserved word (returns a code) getChar reads the next character of input string, puts it in a global variable called nextChar, determines its character class (letter, digit, etc.) and puts the class in charClass addChar Appends nextChar to the current lexeme

34 Lexical Analyzer int lex() { switch (charClass) { case LETTER:
addChar(); getChar(); while (charClass == LETTER || charClass == DIGIT) { addChar(); } return lookup(lexeme); break; case DIGIT: while (charClass == DIGIT) { return INT_LIT; } /* End of switch */ } /* End of function lex */

35 Parsers (Syntax Analyzers)
Goals of a parser: Find all syntax errors Produce parse trees for input program Two categories of parsers: Top down produces the parse tree, beginning at the root Uses leftmost derivation Bottom up produces the parse tree, beginning at the leaves Uses the reverse of a rightmost derivation Context-free grammarFrom Wikipedia, the free encyclopedia.(Redirected from Context-free)In linguistics and computer science, a context-free grammar (CFG) is a formal grammar in which every production rule is of the formV → wwhere V is a non-terminal symbol and w is a string consisting of terminals and/or non-terminals. The term "context-free" comes from the fact that the non-terminal V can always be replaced by w, regardless of the context in which it occurs. A formal language is context-free if there is a context-free grammar that generates it.Context-free grammars are powerful enough to describe the syntax of most programming languages; in fact, the syntax of most programming languages are specified using context-free grammars. On the other hand, context-free grammars are simple enough to allow the construction of efficient parsing algorithms which, for a given string, determine whether and how it can be generated from the grammar. An Earley parser is an example of such an algorithm, while LR and LL parsers only deal with more restrictive subsets of context-free grammars.BNF (Backus-Naur Form) is the most common notation used to express context-free grammars.Not all formal languages are context-free ム a well-known counterexample is . This particular language can be generated by a parsing expression grammar, which is a relatively new formalism that is particularly well-suited to programming languages. Useful web site (Chomsky hierarchy):

36 Recursive Descent Parser
A top-down parser implementation Consists of a collection of subprograms A recursive descent parser has a subprogram for each non-terminal symbol If there are multiple RHS for a given nonterminal, parser must make a decision which RHS to apply first A  x… | y…. | z…. | … The correct RHS is chosen on the basis of the next token of input (the lookahead)

37 Recursive Descent Parser
void expr() { term();    while ( nextToken ==PLUS_CODE || nextToken == MINUS_CODE ) { lex();      term();    } } <expr>  <term> {(+|-) <term>} <term>  <factor> {(*|/) <factor>} <factor>  id | ( <expr> ) lex() is the lexical analyzer function. It gets the next lexeme and puts its token code in the global variable nextToken All subprograms are written with the convention that each one leaves the next token of input in nextToken Parser uses leftmost derivation

38 Recursive Descent Parser
void factor() { /* Determine which RHS */ if (nextToken == ID_CODE)      lex(); else if (nextToken == LEFT_PAREN_CODE) {       lex(); expr();     if (nextToken == RIGHT_PAREN_CODE) lex(); else error(); } error(); /* Neither RHS matches */ <expr>  <term> {(+|-) <term>} <term>  <factor> {(*|/) <factor>} <factor>  id | ( <expr> ) Yellow text: look ahead portion

39 Recursive Descent Parser
Problem with left recursion A  A + B (direct left recursion) A  B c D (indirect left recursion) B  A b A grammar can be modified to remove left recursion Inability to determine the correct RHS on the basis of one token of lookahead Example: A  aC | Bd B  ac C  c

40 LR Parsing LR Parsers are almost always table-driven
Uses a big loop to repeatedly inspect 2-dimen table to find out what action to take Table is indexed by current input token and current state Stack contains record of what has been seen SO FAR (not what is expected/predicted to see in future) PDA: Push down automata: State diagram looks just like a DFA state diagram Arcs labeled with <input symbol, top-of-stack symbol>

41 PDAs LR PDA: is a recognizer Builds a parse tree bottom up
States keep track of which productions we “might” be in the middle of.

42 Example read A <pgm> -> <stmt list> $$ read B
<stmt list>-> <stmt list> <stmt> | <stmt> <stmt> -> id := <expr> | read id | write <expr> <expr> -> <term> | <expr> <add op> <term> <term> -> <factor> | <term> <mult op> <factor> <factor> -> ( <expr> ) | id | literal <add op> -> + | - <mult op> -> * | / read A read B sum := A + B write sum write sum / 2

43 STOP

44 Static Semantics BNF cannot describe all of the syntax of PLs
Examples: All variables must be declared before they are referenced The end of an Ada subprogram is followed by a name, that name must match the name of the subprogram Procedure Proc_example (P: in Object) is begin …. end Proc_example Static semantics Rules that further constrain syntactically correct programs In most cases, related to the type constraints of a language Static semantics are verified before program execution (unlike dynamic semantics, which describes the effect of executing the program) BNF cannot describe static semantics

45 Attribute Grammars (Knuth, 1968)
A BNF grammar with the following additions: For each symbol x there is a set of attribute values, A(x) A(X) = S(X)  I(X) S(X): synthesized attributes – used to pass semantic information up a parse tree I(X): inherited attributes – used to pass semantic information down a parse tree Each grammar rule has a set of functions that define certain attributes of the nonterminals in the rule Rule: X0  X1 … Xj … Xn S(X0) = f (A(X1), …, A(Xn)) I(Xj) = f (A(X0), …, A(Xj-1)) A (possibly empty) set of predicate functions to check whether static semantics are violated Example: S(Xj ) = I (Xj ) ?

46 Attribute Grammars (Example)
Procedure Proc_example (P: in Object) is begin …. end Proc_example Syntax rule: <Proc_def>  Procedure <proc_name>[1] <proc_body> end <proc_name>[2] Semantic rule: <proc_name>[1].string = <proc_name>[2].string attribute

47 Attribute Grammars (Example)
Expressions of the form <var> + <var> var's can be either int_type or real_type If both var’s are int, result of expr is int If at least one of the var’s is real, result of expr is real BNF <assign>  <var> = <expr> (Rule 1) <expr>  <var> + <var> (Rule 2) | <var> (Rule 3) <var>  A | B | C (Rule 4) Attributes for non-terminal symbols <var> and <expr> actual_type - synthesized attribute for <var> and <expr> expected_type - inherited attribute for <expr>

48 Attribute Grammars (Example)
Syntax rule: <assign>  <var> = <expr> Semantic rule: <expr>.expected_type  <var>.actual_type Syntax rule: <expr>  <var>[2] + <var>[3] Semantic rule: <expr>.actual_type  if ( <var>[2].actual_type = int) and <var>[3].actual_type = int) then int else real end if Predicate: <expr>.actual_type = <expr>.expected_type Syntax rule: <expr>  <var> Semantic rule: <expr>.actual_type  <var>.actual_type Predicate: <expr>.actual_type = <expr>.expected_type Syntax rule: <var>  A | B | C Semantic rule: <var>.actual_type  lookup(<var>.string) Note: Lookup function looks up a given variable name in the symbol table and returns the variable’s type

49 Parse Trees for Attribute Grammars
A = A + B <assign> <expr> <var> var[2] var[3] A = A B How are attribute values computed? 1. If all attributes were inherited, the tree could be decorated in top-down order. 2. If all attributes were synthesized, the tree could be decorated in bottom-up order. 3. If both kinds of attributes are present, some combination of top-down and bottom-up must be used.

50 Parse Trees for Attribute Grammars
A = A + B <assign>  <var> = <expr> <expr>.expected_type  <var>.actual_type <expr>  <var>[2] + <var>[3] <expr>.actual_type  if ( <var>[2].actual_type = int) and <var>[3].actual_type = int) then int else real end if Predicate: <expr>.actual_type = <expr>.expected_type <expr>  <var> <expr>.actual_type  <var>.actual_type Predicate: <expr>.actual_type = <expr>.expected_type <var>  A | B | C <var>.actual_type  lookup(<var>.string) <var>.actual_type  lookup(A) (Rule 4) <expr>.expected_type  <var>.actual_type (Rule 1) <var>[2].actual_type  lookup(A) (Rule 4) <var>[3].actual_type  lookup(B) (Rule 4) <expr>.actual_type  either int or real (Rule 2) <expr>.expected_type = <expr>.actual_type is either TRUE or FALSE (Rule 2)

51 Parse Trees for Attribute Grammars
5 Predicate test 2 4 3 1

52 Attribute Grammar Implementation
Determining attribute evaluation order is a complex problem, requiring the construction of a dependency graph to show all attribute dependencies Difficulties in implementation The large number of attributes and semantic rules required make such grammars difficult to write and read Attribute values for large parse trees are costly to evaluate Less formal attribute grammars are used by compiler writers to check static semantic rules

53 Describing (Dynamic) Semantics
<for_stmt>  for (<expr1>; <expr2>; <expr3>) <assign_stmt>  <var> = <expr>; What is the meaning of each statement? dynamic semantics How do we formally describe the dynamic semantics?

54 Describing (Dynamic) Semantics
There is no single widely acceptable notation or formalism for describing dynamic semantics Three formal methods: Operational Semantics Axiomatic Semantics Denotational Semantics

55 Operational Semantics
Describe the meaning of a program by executing its statements on a machine, either simulated or actual. The change in the state of the machine (memory, registers, etc.) defines the meaning of the statement. Execute Statement Initial State: (i1,v1), (i2,v2), … Final State: (i1,v1’), (i2,v2’), …

56 Operational Semantics
To use operational semantics for a high-level language, a virtual machine in needed. A hardware pure interpreter would be too expensive A software pure interpreter also has problems: 1. The detailed characteristics of the particular computer would make actions difficult to understand 2. Such a semantic definition would be machine- dependent.

57 Operational Semantics
Approach: use a complete computer simulation Build a translator (translates source code to the machine code of an idealized computer) Build a simulator for the idealized computer Example: C Statement: Operational Semantics: for (expr1; expr2; expr3) { expr1; … loop: if expr2 = 0 goto out } … expr3; goto loop out: …

58 Operational Semantics
Valid statements for the idealized computer: iden = var iden = iden + 1 iden = iden – 1 goto label if var relop var goto label Evaluation of Operational Semantics: Good if used informally (language manuals, etc.) Extremely complex if used formally (e.g., VDL)

59 Axiomatic Semantics Based on formal logic (first order predicate calculus) Approach: Each statement is preceded and followed by a logical expression that specifies constraints on program variables The logical expressions are called predicates or assertions Define axioms or inference rules for each statement type in the language to allow transformations of expressions to other expressions

60 Axiomatic Semantics {P} A = B + 1 {Q} where P: precondition
Q: postcondition Precondition: an assertion before a statement that states the relationships and constraints among variables that are true at that point in execution Postcondition: an assertion following a statement A weakest precondition is the least restrictive precondition that will guarantee the postcondition Example: A = B + 1 {A > 1} Postcondition: A > 1 One possible precondition: {B > 10} Weakest precondition: {B > 0}

61 Axiomatic Semantics Program proof process:
The postcondition for the whole program is the desired results. Work back through the program to the first statement. If the precondition on the first statement is the same as the program spec, the program is correct. An axiom for assignment statements {P} x = E {Q} Axiom: P = Qx  E (P is computed with all instances of x replaced by E) Example: a = b / 2 – 1 {a < 10} Weakest precondition: b/2 – 1 < => b < 22 Axiomatic Semantics for assignment: {Qx  E } x = E {Q}

62 Axiomatic Semantics Inference rule for Sequences:
{P1} S1 {P2}, {P2} S2 {P3} {P1} S1; S2 {P3} Example: Y = 3 * X + 1; X = Y + 3; {X < 10} Precondition for second statement: {Y < 7} Precondition for first statement: {X < 2} {X < 2} Y = 3 * X + 1; X = Y + 3; {X < 10}

63 Axiomatic Semantics: Axioms
An inference rule for logical pretest loops {P} while B do S end {Q} where I is the loop invariant (the inductive hypothesis)

64 Axiomatic Semantics: Axioms
Characteristics of the loop invariant: I must meet the following conditions: P => I the loop invariant must be true initially {I} B {I} evaluation of the Boolean must not change the validity of I {I and B} S {I} -- I is not changed by executing the body of the loop (I and (not B)) => Q if I is true and B is false, is implied The loop terminates

65 Loop Invariant The loop invariant I is a weakened version of the loop postcondition, and it is also a precondition. I must be weak enough to be satisfied prior to the beginning of the loop, but when combined with the loop exit condition, it must be strong enough to force the truth of the postcondition

66 Evaluation of Axiomatic Semantics
Developing axioms or inference rules for all of the statements in a language is difficult Utility: It is a good tool for correctness proofs, and an excellent framework for reasoning about programs, it is not as useful for language users and compiler writers Its usefulness in describing the meaning of a programming language is limited for language users or compiler writers

67 Denotational Semantics
Based on recursive function theory The most abstract semantics description method Originally developed by Scott and Strachey (1970)

68 Denotational Semantics (cont’d)
The process of building a denotational specification for a language Define a mathematical object for each language entity Define a function that maps instances of the language entities onto instances of the corresponding mathematical objects The meaning of language constructs are defined by only the values of the program's variables

69 Denotation vs Operational Semantics
In operational semantics, the state changes are defined by coded algorithms In denotational semantics, the state changes are defined by rigorous mathematical functions

70 Denotational Sem.: Program State
The state of a program is the values of all its current variables s = {<i1, v1>, <i2, v2>, …, <in, vn>} Let VARMAP be a function that, when given a variable name and a state, returns the current value of the variable VARMAP(ij, s) = vj

71 Denotational Semantics
Based on recursive function theory The meaning of language constructs are defined by the values of the program's variables The process of building a denotational specification for a language: Define a mathematical object for each language entity Define a function that maps instances of the language entities onto instances of the corresponding mathematical objects

72 Denotational Semantics
Decimal Numbers The following denotational semantics description maps decimal numbers as strings of symbols into numeric values Syntax rule: <dec_num>  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <dec_num> (0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9) Denotational Semantics: Mdec('0') = 0, Mdec ('1') = 1, …, Mdec ('9') = 9 Mdec (<dec_num> '0') = 10 * Mdec (<dec_num>) Mdec (<dec_num> '1’) = 10 * Mdec (<dec_num>) + 1 Mdec (<dec_num> '9') = 10 * Mdec (<dec_num>) + 9 Note: Mdec is a semantic function that maps syntactic objects to a set of non-negative decimal integer values

73 Expressions Map expressions onto Z  {error}
We assume expressions are decimal numbers, variables, or binary expressions having one arithmetic operator and two operands, each of which can be an expression

74 3.5 Semantics (cont.) Me(<expr>, s) = case <expr> of <dec_num> => Mdec(<dec_num>, s) <var> => if VARMAP(<var>, s) == undef then error else VARMAP(<var>, s) <binary_expr> => if (Me(<binary_expr>.<left_expr>, s) == undef OR Me(<binary_expr>.<right_expr>, s) = undef) else if (<binary_expr>.<operator> == ‘+’ then Me(<binary_expr>.<left_expr>, s) + Me(<binary_expr>.<right_expr>, s) else Me(<binary_expr>.<left_expr>, s) * ...

75 Assignment Statements
Maps state sets to state sets Ma(x := E, s) = if Me(E, s) == error then error else s’ = {<i1’,v1’>,<i2’,v2’>,...,<in’,vn’>}, where for j = 1, 2, ..., n, vj’ = VARMAP(ij, s) if ij <> x = Me(E, s) if ij == x

76 Logical Pretest Loops Maps state sets to state sets
Ml(while B do L, s) = if Mb(B, s) == undef then error else if Mb(B, s) == false then s else if Msl(L, s) == error else Ml(while B do L, Msl(L, s))

77 Loop Meaning The meaning of the loop is the value of the program variables after the statements in the loop have been executed the prescribed number of times, assuming there have been no errors In essence, the loop has been converted from iteration to recursion, where the recursive control is mathematically defined by other recursive state mapping functions Recursion, when compared to iteration, is easier to describe with mathematical rigor

78 Evaluation of Denotational Semantics
Can be used to prove the correctness of programs Provides a rigorous way to think about programs Can be an aid to language design Has been used in compiler generation systems Because of its complexity, they are of little use to language users

79 Summary BNF and context-free grammars are equivalent meta-languages
Well-suited for describing the syntax of programming languages An attribute grammar is a descriptive formalism that can describe both the syntax and the semantics of a language Three primary methods of semantics description Operational, axiomatic, denotational


Download ppt "Implementation, Syntax, and Semantics"

Similar presentations


Ads by Google