CSwithZain
WhatsApp Join a class

A Level 9618 · Paper 3 · 16.2

Translation Software

How source code becomes a program the computer can run, and how a language's grammar is written down. Read the notes, try the past-paper questions, then check the mark scheme.

Past papers from June 2021 to June 202627 questions · 145 marks

    In this subtopic

    • How an interpreter runs a program without translating it first
    • The stages of compilation: lexical analysis, syntax analysis, code generation and optimisation
    • Syntax diagrams and Backus-Naur Form (BNF)
    • Reverse Polish Notation (RPN) for evaluating expressions

    16.2 · 1 of 21Book page Contents

    Compilers and interpreters

    A compiler translates the whole program into object code before it runs. An interpreter never produces a translated program. Instead, it translates and runs the source code one statement at a time:

    1. Read the next statement of the source code.
    2. Check it for errors and translate it.
    3. Run it straight away.
    4. Go back to step 1, until the program ends. If a statement has an error, the interpreter stops there and reports it.

    Nothing is saved, so the program is translated again every time it runs, and a statement inside a loop is translated every time round the loop. The interpreter must be on the computer that runs the program.

    CompilerInterpreter
    TranslatesThe whole program, before it runsOne statement at a time, while it runs
    OutputAn executable file that can be run again without translatingNo executable file
    ErrorsReports all the errors after translating the whole programStops at the first error
    SpeedRuns quickly, because it is already translatedRuns more slowly, because each statement is translated as it runs
    To run it you needOnly the executable file, so the source code stays privateThe interpreter and the source code
    Best forHanding out finished softwareWriting and testing a program, because each part can be run straight away

    16.2 · 2 of 21Book page Contents

    Stages of compilation

    The compilation process is a set of stages that turns source code into executable object code. The programmer first creates the source code, and then the compiler takes it through four stages in order:

    StageWhat happens
    1. Lexical analysisRemoves comments and white space, allocates tokens, produces a symbol table, optimises code
    2. Syntax analysisChecks the code tokens are in the correct order and follow the language rules, optimises code
    3. Semantic analysisChecks that the code is valid within a given context, optimises code
    4. Code generationCreates the object code, optimises code

    Note In the exam, Cambridge names the stages as lexical analysis, syntax analysis, code generation and optimisation, so learn optimisation as a stage. In practice, code can be optimised at every stage, and most of all during code generation.

    16.2 · 3 of 21Book page Contents

    Lexical analysis

    Lexical analysis is the first stage in the process of compilation. All unnecessary characters that the compiler does not need, such as white space and comments, are removed.

    Here is a small program before and after the unnecessary characters have been removed.

    Source program

    // My addition program Version 2
    DECLARE x, y, z : INTEGER
    OUTPUT "Please enter two numbers to add together" INPUT x, y
    z=x+ y
    OUTPUT "Answer is ", z

    Source program after unnecessary characters have been removed (the comment line has gone)

    DECLARE x, y, z : INTEGER
    OUTPUT "Please enter two numbers to add together" INPUT x, y
    z=x+ y
    OUTPUT "Answer is ", z

    16.2 · 4 of 21Book pages Contents

    Tokens, the keyword table and the symbol table

    Before translation, the source program must be converted to tokens. This process is called tokenisation. To tokenise the program, the compiler uses a keyword table that holds the tokens for all the reserved words and symbols of the programming language. In this example, every token is written as two hexadecimal digits.

    The keyword table stores all the reserved words and symbols that can be used in the language. The term fixed symbol table can also be used for the symbols stored. Every program being compiled uses the same keyword table. Part of a keyword table could look like this:

    Keyword or symbolToken
    =01
    +02
    :03
    ,04
    DECLARE31
    INTEGER32
    INPUT33
    OUTPUT34

    During lexical analysis, the variables, constants and other identifiers used in the program are also added to a symbol table. The symbol table is made during compilation specially for that one program. Part of the symbol table for the program above could be:

    SymbolToken valueToken typeData type
    x81variableinteger
    y82variableinteger
    z83variableinteger
    "Please enter two numbers to add together"84constantstring
    "Answer is "85constantstring

    The output from lexical analysis is a tokenised list, stored in main memory. This is the output for the first three lines of the program:

    31 81 04 82 04 83 03 32 34 84 33 81 04 82 83 01 81 02 82

    Reading the token list

    Check the tokenised list against the three lines of the program.

    1. Step 1
      DECLARE x, y, z : INTEGER becomes
      31 81 04 82 04 83 03 32
    2. Step 2
      OUTPUT "Please enter two numbers to add together" INPUT x, y becomes
      34 84 33 81 04 82
    3. Step 3
      z=x+ y becomes
      83 01 81 02 82

    16.2 · 5 of 21Book page Contents

    Syntax analysis

    Syntax analysis is the compilation stage that comes straight after lexical analysis. Once tokens have been given to the code elements, the compiler checks that the tokens are in the correct order and that they follow the rules of the programming language.

    In Python, the command print(user_name) is syntactically correct because it follows the rules for a print statement:

    1. print in lower case
    2. immediately followed by a left-hand bracket
    3. followed by an identifier
    4. closed by a right-hand bracket

    Natural languages like English work the same way. The sentence “he Moved wearily” breaks three syntax rules. A sentence must:

    1. start with a capital letter
    2. continue in lower case letters
    3. end with a full stop

    The syntax rules of a programming language are limited, but they must be followed.

    16.2 · 6 of 21Book page Contents

    Abstract syntax tree

    During syntax analysis, an abstract syntax tree (AST) is created. It maps the structure of the program, first dropping the brackets, semicolons and so on that the programmer used. If required tokens are missing from the tree, or are in the wrong place, the compiler reports an error.

    Every language produces a different AST, depending on its syntax. Below is a general example of an AST for this small piece of pseudocode:

    a = 5
    b = a + 2
    Abstract syntax tree for a = 5 and b = a + 2. The root is Module, with two Assign children. The first Assign has Variable (with a and Store) and Num 5. The second Assign has Variable (with b and Store) and Binary Operation, which has Variable (with a and Load), Add, and Num 2.

    16.2 · 7 of 21Book pages Contents

    Semantic analysis

    So far, lexical analysis has checked whether the tokens in the program are valid, and syntax analysis has checked whether the tokens are used according to the rules of the language. Another stage, semantic analysis, checks whether what has been written actually has a meaning within the language.

    Suppose you are writing rules for grammatically correct English sentences. One rule for a valid sentence could be:

    <pronoun> <verb> <plural noun>

    So “I eat crackers” and “They play video games” are valid sentences. But a computer that only uses syntax analysis would also accept “You swim cats” and “We bite movies”, even though these make no sense in English.

    Semantic analysis checks whether the code is valid within a given context. In this pseudocode, every token is valid and the assignment statement is formatted correctly:

    DECLARE Total : INTEGER
    Total ← "Karachi"

    However, line 2 gives a semantic error when the program is compiled, because assigning a string value to an identifier that has been declared as an integer is not valid.

    Here is another example, in Python:

    fruit = ["apple", "peach", "pear"]
    print(fruit[4])

    This code gives no error at the lexical analysis stage, because all the tokens are valid in the language. It gives no error at the syntax analysis stage, because the list declaration and the print statement are correctly formed. But it has a semantic error: index 4 is out of bounds for the fruit list. Python only reports this when the program runs, as below, but a compiler could flag it during semantic analysis.

    Traceback (most recent call last):
      File "main.py", line 2, in <module>
        print(fruit[4])
              ~~~~~^^^
    IndexError: list index out of range

    Semantic analysis flags errors that do not make sense in the context the code is used in.

    Types of semantic errors a compiler might find

    • a data type declaration mismatch
    • trying to access an array index that is out of bounds
    • using a variable before it has been declared
    • passing the wrong number of parameters to a subroutine
    • trying to access a variable that is out of scope

    16.2 · 8 of 21Book page Contents

    Code generation

    Code generation comes after lexical analysis, syntax analysis and semantic analysis. A separate program is created, distinct from the original source code. The generated code is a binary representation of the source code, known as object code. It is the executable version of the code, before linked libraries are included.

    Here is a “Hello World” program in Python:

    print("hello world")

    When this code is built, an executable file is generated. The executable file is a binary file meant to be read by the computer. If you open it in a text editor, it makes no sense: the editor has turned the binary into characters because it assumes the file holds text, which it does not.

    A text editor window showing the executable file as rows of meaningless hexadecimal groups such as cffa edfe 0c00 0001.
    The executable file opened in a text editor.

    Compiler or interpreter

    • Code generation is a major difference between compilation and interpretation: interpreters do not produce a separate executable file.

    16.2 · 9 of 21Book page Contents

    Code optimisation

    Code optimisation happens throughout the compilation process, and most of all during the code generation stage. The optimiser may find redundant or repeated code, and remove or rearrange it as needed.

    Note Exam wording: the purpose of optimisation is to make the program more efficient, by reducing its execution time and the memory it needs.

    This pseudocode displays the nine times table:

    FOR Number ← 1 TO 12
        Multiplier ← 9
        Result ← Number * Multiplier
        OUTPUT Result
    NEXT Number

    This code is inefficient because Multiplier is assigned the value 9 inside the loop, so the assignment happens twelve times. The value never changes, so the optimiser moves the statement to before the loop:

    Multiplier ← 9
    FOR Number ← 1 TO 12
        Result ← Number * Multiplier
        OUTPUT Result
    NEXT Number

    Here is another piece of code that the compiler would optimise:

    FUNCTION DoubleIt(Num : INTEGER) RETURNS INTEGER
        DECLARE Result : INTEGER
        Result ← Num * 2
        RETURN Result
        OUTPUT "The number doubled is ", Result
    ENDFUNCTION

    The OUTPUT statement is dead code, because it can never be executed. As soon as the function reaches the RETURN statement, it stops, and the program goes back to the place the function was called from. The optimiser can remove dead code.

    16.2 · 10 of 21Book page Contents

    Context-free languages

    Backus-Naur Form is used to describe a class of languages called context-free languages. The diagram shows how they fit inside the set of all languages.

    Nested sets: regular languages is a circle inside context-free languages, which is inside all languages.

    Most programming languages can be defined as context-free languages. To define the set of strings in a particular context-free language, you use a context-free grammar: a set of production rules that describe all the possible strings in that language. One example of a context-free grammar is Backus-Naur Form.

    16.2 · 11 of 21Book pages Contents

    Backus-Naur Form (BNF)

    Backus-Naur Form (BNF) is a context-free grammar that developers of programming languages often use to set out the syntax rules of a language. John Backus was a programming language designer who created a notation to document IAL (an early version of Algol). Peter Naur later worked on Backus’ ideas, and the notation is credited to both computer scientists.

    BNF uses a set of symbols to write production rules. A simple BNF production rule looks like this:

    <digit> ::= 0|1|2|3|4|5|6|7|8|9

    This means: a digit can be defined as 0, 1, 2, 3, 4, 5, 6, 7, 8 or 9.

    The chevrons < > mark a non-terminal symbol. If a non-terminal symbol appears on the right side of a production rule, there will be another production rule (or set of rules) that defines what it can be replaced by. Look at this rule:

    <fullname> ::= <title><name><name>

    This shows that a full name is made of a title, a name and another name. All three parts are non-terminal, so more production rules are needed. For example, a rule could define title:

    <title> ::= Mr|Mrs|Ms|Miss|Dr

    In this rule, Mr, Mrs, Ms, Miss and Dr are terminal symbols. They are not in chevrons, so they are the actual values allowed for a title. The pipe symbol | is a metacharacter that separates alternatives. Each production rule is applied strictly: you may know that other titles exist, such as Lord, but only the production rule decides what is valid in this formal language.

    BNF symbols

    • ::= means “is defined as”.
    • | means “or”: it separates the alternatives.
    • < > surround a non-terminal symbol, which has its own production rule.
    • Anything not in chevrons is a terminal symbol: an actual value.

    The production rules for something as complex as a whole programming language form a very large set of BNF statements that set out how every part of the language is defined. Whenever a non-terminal symbol appears on the right side of a production rule, there should be another rule with that symbol on the left side. This goes on until everything is defined in terms of terminal symbols.

    Here is a complete set of rules for a small part of a programming language:

    <addition> ::= <number>+<number>
    <number>   ::= <sign><integer>|<integer>
    <integer>  ::= <digit>|<digit><integer>
    <digit>    ::= 0|1|2|3|4|5|6|7|8|9
    <sign>     ::= +|-

    The terminal symbols are the digits 0 to 9 and the plus and minus signs. The plus sign appears twice: once as an operator and once as the sign of a number. So a valid addition can have a double plus sign, for example 23++6.

    Practise this: past-paper questions

    Show 9 more

    16.2 · 12 of 21Book pages Contents

    Recursion in BNF

    Recursion is used in BNF to write production rules for “one or more” of a symbol. For example, a number can be made of one or more digits. You might start by writing an ordinary production rule:

    <number> ::= <digit>|<digit><digit>|<digit><digit><digit>|...

    But where would you stop? How many digits can a number have? Recursion gives a neat solution:

    <number> ::= <digit>|<digit><number>
    <digit>  ::= 0|1|2|3|4|5|6|7|8|9

    Recursive rules

    Use the production rules to decide whether 2, 16 and 234 are numbers.

    1. Step 1
      2 is a digit, so it matches the first alternative, <digit>. It is a number.
    2. Step 2
      16 is not a single digit, so it does not match the first alternative. Try the second alternative, <digit><number>: 1 is a digit, so that part is fine. Is 6 a number? Yes: go back to the rule, and 6 matches the first alternative. So 16 is a number.
    3. Step 3
      234 has more than one digit, so the first alternative is ruled out straight away. Using the second alternative, 2 is a digit. Is the rest, 34, a number? Use the second alternative again: 3 is a digit. Is the rest, 4, a number? Yes, it matches the first alternative. So 234 is a number.

    Notice how you go back to the rule, taking each symbol one at a time, until the symbols run out (or you meet a symbol that means the rule cannot be satisfied).

    This recursive production rule has a base case (a single digit) and a general case (a digit followed by a number). The general case uses the recursive definition. The problem gets smaller and smaller until it reaches the base case: is 234 a number? Is 34 a number? Is 4 a number?

    Note In BNF, recursion is the only way to show “one or more” or a loop.

    Used later in

    19.2Recursionbase case, general case, recursion

    16.2 · 13 of 21Book pages Contents

    Parse trees

    A parse tree is a useful way to check whether a string satisfies a set of production rules. Parsing means breaking something down into its parts. Here are the production rules from before:

    <addition> ::= <number>+<number>
    <number>   ::= <sign><integer>|<integer>
    <integer>  ::= <digit>|<digit><integer>
    <digit>    ::= 0|1|2|3|4|5|6|7|8|9
    <sign>     ::= +|-

    Worked example

    Use a parse tree to check whether 24+54 is a valid addition.

    1. Step 1
      Put the production rule for addition at the top of the tree: <number> + <number>. It includes the terminal symbol +, and the string being checked has a +, so far so good. Highlight the + in the tree.
    2. Step 2
      The strings on either side of the + must satisfy <number> ::= <sign><integer>|<integer>: a sign followed by an integer, or just an integer. Neither 24 nor 54 starts with a sign, so each must be an integer. Write <integer> below <number> on both sides of the tree.
    3. Step 3
      Now show that 24 is an integer, using <integer> ::= <digit>|<digit><integer>. 24 is not a digit (a digit is a single numeric symbol), so to be valid it must be <digit><integer>. Write this on the next line of the tree. Do the same on the other side, because 54 works the same way.
    4. Step 4
      Is 24 a digit followed by an integer? 2 is definitely a digit, so write 2 below <digit>. In the same way, write 5 below <digit> on the right side. Highlight 2 and 5.
    5. Step 5
      Now show that 4 is an integer, on both sides. 4 is a digit, so write <digit> below <integer> and 4 below <digit>. Do the same on the other side, and highlight both digits.
    6. Step 6
      Every part of 24+54 has been matched to the rules, so the parse tree proves that 24+54 is a valid addition.
    The finished parse tree for 24+54. Two <number> nodes sit either side of a highlighted +. Under each is <integer>, then <digit><integer>. The digits 2 and 5 are highlighted under <digit>, and under each lower <integer> is a <digit> holding a highlighted 4.
    The finished parse tree for 24+54.

    16.2 · 14 of 21Book pages Contents

    Syntax diagrams

    As well as BNF, you can use a diagram that mirrors the formal grammar: a syntax diagram. When no key is given, you must be able to recognise the terminal and non-terminal symbols yourself. Here is a syntax diagram for this production rule:

    <special> ::= @|?|&|*
    Syntax diagram for special: one line splits into four stacked paths through the terminal symbols @, ?, & and *, which join again at the end.

    By following any one of the lines, you get a character that is a valid special. The alternatives are stacked under each other, and you can pick any of them as you follow the line through the diagram from start to end.

    Now a more complex rule:

    <date> ::= <day-name><month-name>|<day-name><month-name><year>

    This can be drawn as syntax diagrams like this:

    Syntax diagram for date: day-name then month-name, then either straight on to the end or down through year and back up. Below it, the syntax diagram for day-name: seven stacked paths for Monday to Sunday. A note says there would also be diagrams for month-name and year.

    Year is optional, as the production rule says. Syntax diagrams are always read from left to right, following the arrows. You can follow the top line, which leaves out the year, or drop down to include it. You cannot go back on yourself unless the arrows allow it.

    Recursion is shown by a loop back through the diagram (in BNF, the only way to show a loop is recursion). The rule

    <number> ::= <digit>|<digit><number>

    is drawn as a syntax diagram like this:

    Syntax diagram for number: a line passes through a digit box, with a loop from after the digit back to before it, so one or more digits can be taken.

    Reading a syntax diagram

    • Read from left to right, following the arrows.
    • Stacked paths are alternatives: take exactly one of them.
    • A path that goes around a box makes that part optional.
    • A loop back over a box means that part can be repeated (one or more times).

    Practise this: past-paper questions

    Show 26 more

    Used later in

    19.2Recursionrecursion

    16.2 · 15 of 21Book pages Contents

    Reverse Polish notation and infix notation

    Reverse Polish Notation (RPN) was invented as a way of simplifying mathematical expressions.

    RPN is older than modern computers. It became useful with the first electronic calculators, which were not good at handling complex expressions with brackets. RPN needs no brackets, because expressions are entered in an unambiguous postfix form.

    Early program translators converted expressions to RPN to evaluate them. Today only a few fairly specialised stack-based languages use RPN.

    When you write an arithmetic expression normally, you use infix notation: the operator is placed in between the operands. So 2+3 means “add together the values 2 and 3”.

    For more complex expressions, a set of agreed rules decides the order of evaluation. The mnemonic BODMAS (sometimes BIDMAS or PEDMAS) helps you remember the order of precedence of the operators:

    LettersRule
    BAnything in brackets is done first
    OPowers and roots are done next
    DMDivision and multiplication are next (equal precedence, worked left to right)
    ASAddition and subtraction are done last (equal precedence, worked left to right)

    Worked example

    Evaluate 7 + (8 * 32 + 4).

    1. Step 1
      First deal with the part in brackets: 8 * 32 + 4.
    2. Step 2
      Raise 3 to the power 2: 32 = 9.
    3. Step 3
      Multiply 9 by 8 = 72.
    4. Step 4
      Add 4: 72 + 4 = 76.
    5. Step 5
      Add 7: 7 + 76 = 83.

    16.2 · 16 of 21Book page Contents

    Postfix notation

    In postfix notation, or Reverse Polish Notation, the operator comes after its operands. The infix expression

    2 + 3

    is written in postfix notation as

    2 3 +

    Now take the more complex expression from before, 7 + (8 * 32 + 4). Using ^ for the power operator, it is written in postfix as:

    7 8 3 2 ^ * 4 + +

    There are no brackets. You also do not need a mnemonic to remember the order of precedence: the expression is simply evaluated from left to right.

    16.2 · 17 of 21Book pages Contents

    Evaluating RPN using a stack

    Builds on

    ASIntroduction to Abstract Data Types (ADT)10.4: stack16.2Postfix notationReverse Polish Notation (RPN)

    To evaluate a postfix (RPN) expression, use a stack. A stack is a LIFO (last in, first out) data structure. Work through the expression from left to right:

    • When you meet an operand, push it onto the stack.
    • When you meet an operator, pop two values from the stack, apply the operator to them, and push the result back onto the stack.
    • When you have been through the whole expression, pop the final answer from the stack.

    Here is the method applied to 7 8 3 2 ^ * 4 + +. The stack is written from bottom to top, so the last number in each row is the top of the stack.

    Still to readActionStack (bottom to top)
    7 8 3 2 ^ * 4 + +Push operand 7 onto the stack7
    8 3 2 ^ * 4 + +Push operand 8 onto the stack7, 8
    3 2 ^ * 4 + +Push operand 3 onto the stack7, 8, 3
    2 ^ * 4 + +Push operand 2 onto the stack7, 8, 3, 2
    ^ * 4 + +Pop 2 items (3 and 2), apply the operator: 3 ^ 2 = 9, push 9 back onto the stack7, 8, 9
    * 4 + +Pop 2 items (8 and 9), apply the operator: 8 * 9 = 72, push 72 back onto the stack7, 72
    4 + +Push operand 4 onto the stack7, 72, 4
    + +Pop 2 items (72 and 4), apply the operator: 72 + 4 = 76, push 76 back onto the stack7, 76
    +Pop 2 items (7 and 76), apply the operator: 7 + 76 = 83, push 83 back onto the stack83
    nothing leftPop the final result from the stack: 83empty

    This method is almost foolproof. The one thing to remember is that when you pop two items for an operator, the first item off the stack is the right operand and the second is the left operand. With addition and multiplication the order makes no difference, but with subtraction, division and powers it does. In the example above, working out 2 ^ 3 (= 8) instead of 3 ^ 2 (= 9) would give a different result. To check your order, make sure the operands always stay in the same left-to-right order as in the original expression.

    16.2 · 18 of 21Book page Contents

    Evaluating RPN by sliding

    Another way to evaluate an RPN expression, which you may find easier, is to “slide” the operators into place and evaluate. At each step:

    1. Take the leftmost operator.
    2. Slide it one place to the left.
    3. Apply the operator to the operands on either side.
    4. Substitute the result.

    Sliding

    Evaluate 7 8 3 2 ^ * 4 + + by sliding.

    1. Step 1
      The leftmost operator is ^. Slide it between 3 and 2: 3 ^ 2 = 9.
      7 8 3 2 ^ * 4 + +
      → 7 8 9 * 4 + +
    2. Step 2
      The leftmost operator is now *. Slide it between 8 and 9: 8 * 9 = 72.
      7 8 9 * 4 + +
      → 7 72 4 + +
    3. Step 3
      The leftmost operator is +. Slide it between 72 and 4: 72 + 4 = 76.
      7 72 4 + +
      → 7 76 +
    4. Step 4
      The last operator is +. Slide it between 7 and 76: 7 + 76 = 83.
      7 76 +
      → 83

    16.2 · 19 of 21Book page Contents

    Converting from infix to postfix

    A well-known algorithm for converting from infix to postfix notation is Dijkstra’s Shunting Yard Algorithm. It uses a queue and a stack to do the conversion, and is good programming practice when you study data structures.

    To do the conversion by hand, use the fully parenthesising method.

    Fully parenthesising

    Convert the infix expression 7 + 8 * 6 to postfix.

    1. Step 1
      Using the rules of operator precedence, add brackets around the whole expression, so that the inner brackets hold the part to be evaluated first.
      ( 7 + ( 8 * 6 ) )
    2. Step 2
      Move each operator to the end of its own pair of brackets, just before the right-hand bracket.
      ( 7 ( 8 6 * ) + )
    3. Step 3
      Finally, remove the brackets.
      7 8 6 * +

    The golden rule

    • When you convert from postfix to infix, or from infix to postfix, the order of the operands must not change.
    • Check your answer by evaluating it with the stack method: you must get the same answer as when you evaluate the infix expression.

    16.2 · 20 of 21Book pages Contents

    Converting from postfix to infix

    Worked example

    Convert the postfix expression 7 8 6 * + back to infix.

    1. Step 1
      Working from left to right, find the first operator, move it one place to the left, and put brackets around the infix expression this makes.
      7 ( 8 * 6 ) +
    2. Step 2
      Continue from left to right, moving each operator in turn one place to the left (a bracketed part counts as a single place). Put brackets around each infix expression this makes.
      ( 7 + ( 8 * 6 ) )
    3. Step 3
      It is common to end up with brackets you do not need, and they can be removed. Here the outer brackets are not needed.
      7 + ( 8 * 6 )

    Worked example

    Convert the longer postfix expression 7 8 3 2 ^ * 4 + + back to infix.

    1. Step 1
      Move ^ one place left.
      7 8 ( 3 ^ 2 ) * 4 + +
    2. Step 2
      Move * one place left.
      7 ( 8 * ( 3 ^ 2 ) ) 4 + +
    3. Step 3
      Move the first + one place left.
      7 ( ( 8 * ( 3 ^ 2 ) ) + 4 ) +
    4. Step 4
      Move the last + one place left.
      ( 7 + ( ( 8 * ( 3 ^ 2 ) ) + 4 ) )
    5. Step 5
      Remove the outer brackets, because the addition would be done last anyway.
      7 + ( ( 8 * ( 3 ^ 2 ) ) + 4 )
    6. Step 6
      Remove the next set of outer brackets, for the same reason.
      7 + ( 8 * ( 3 ^ 2 ) ) + 4
    7. Step 7
      Finally, double-check that you have not changed the order of the operands: 7, 8, 3, 2, 4.

    16.2 · 21 of 21Book page Contents

    Advantages of RPN

    Advantages of RPN

    • RPN expressions do not need brackets, and there is no need for rules of operator precedence.
    • RPN is simpler for a machine to evaluate (with a stack).
    • There is no need for backtracking during evaluation: the operators appear in the order needed for the calculation, so the expression can be evaluated from left to right.

    16.2

    Concepts in 16.2

    Every idea explained in 16.2, and where else in the notes it is used.

    ConceptIn one lineAlso used in
    abstract syntax treeA tree made during syntax analysis that maps the structure of the program.only here
    Backus-Naur Form (BNF)A notation of production rules using ::=, | and <non-terminals> to define the syntax of a language.16.2 Context-free languages, 16.2 Recursion in BNF, 16.2 Syntax diagrams
    BODMASThe order of precedence: brackets, powers, division and multiplication, then addition and subtraction.only here
    code generationThe compilation stage that creates the object code, a separate binary program.16.2 Stages of compilation, 16.2 Code optimisation
    code optimisationRemoving or rearranging redundant or repeated code so the program runs faster or is smaller.only here
    compilerA translator that turns the whole source program into object code before it runs.16.2 Stages of compilation, 16.2 Lexical analysis, 16.2 Tokens, the keyword table and the symbol table, 16.2 Syntax analysis, 16.2 Abstract syntax tree, 16.2 Semantic analysis, 16.2 Code generation, 16.2 Code optimisation
    context-free grammarA set of production rules that describe all the possible strings in a language.16.2 Backus-Naur Form (BNF)
    context-free languageA language whose strings can be described by a context-free grammar; most programming languages are.only here
    dead codeCode that can never be executed, such as a statement after RETURN.only here
    fully parenthesisingConverting infix to postfix by bracketing every operation, moving each operator to its closing bracket, then removing brackets.only here
    infix notationWriting the operator between its operands, as in 2 + 3.16.2 Postfix notation, 16.2 Converting from infix to postfix, 16.2 Converting from postfix to infix
    interpreterA translator that translates and runs a program one statement at a time, making no executable file.16.2 Code generation
    keyword tableA table of every reserved word and symbol in a language with its token, the same for all programs.only here
    lexical analysisThe first compilation stage: removes white space and comments and turns the code into tokens.16.2 Stages of compilation, 16.2 Tokens, the keyword table and the symbol table, 16.2 Syntax analysis, 16.2 Semantic analysis, 16.2 Code generation
    metacharacterA symbol with a special meaning in BNF, such as | for “or”.only here
    non-terminal symbolA symbol in chevrons that is defined by another production rule.16.2 Syntax diagrams
    object codeThe binary, executable version of a program made by code generation, before libraries are linked.16.2 Compilers and interpreters, 16.2 Stages of compilation
    parse treeA tree that breaks a string into parts to show it satisfies a set of production rules.only here
    production ruleA rule that defines what a symbol can be replaced by, such as <digit> ::= 0|1|...|9.16.2 Context-free languages, 16.2 Recursion in BNF, 16.2 Parse trees, 16.2 Syntax diagrams
    Reverse Polish Notation (RPN)Postfix notation: each operator comes after its operands, as in 2 3 +, with no brackets.16.2 Reverse Polish notation and infix notation, 16.2 Evaluating RPN using a stack, 16.2 Evaluating RPN by sliding, 16.2 Converting from infix to postfix, 16.2 Converting from postfix to infix, 16.2 Advantages of RPN
    semantic analysisThe compilation stage that checks whether valid code has a meaning in its context.16.2 Stages of compilation, 16.2 Code generation
    semantic errorCode that follows the syntax rules but makes no sense in context, such as a type mismatch.only here
    Shunting Yard AlgorithmDijkstra’s algorithm that uses a queue and a stack to convert infix to postfix.only here
    stages of compilationLexical analysis, syntax analysis, semantic analysis and code generation, with optimisation throughout.16.2 Code optimisation
    symbol tableA table made for one program during compilation, holding its identifiers with their tokens and types.16.2 Stages of compilation
    syntax analysisThe compilation stage that checks the tokens are in the right order and follow the language rules.16.2 Stages of compilation, 16.2 Abstract syntax tree, 16.2 Semantic analysis, 16.2 Code generation
    syntax diagramA diagram of a grammar rule, read left to right, with stacked alternatives and loops for repetition.only here
    terminal symbolAn actual value allowed by a rule, written without chevrons.16.2 Parse trees
    tokenisationReplacing each keyword, symbol and identifier in the source code with a token.only here

    When you want more than reading

    The notes are the front door. The class is where it clicks.

    Join a class →Ask on WhatsApp