Theory of computation · A level · AQA 7517 4.4.3.1, Eduqas A500QS 1.8 · about 25 min
Production rules, syntax diagrams, a grammar for robot programs, recursive descent, and why BNF can describe what a regex cannot.
[1 mark]In BNF, what does ::= mean?
[1 mark]Using <integer> ::= <digit> | <digit><integer> and <move> ::= <direction><integer> with <direction> ::= F | B | L | R, which are valid moves?
Tick every answer that is true.
[1 mark]Why can BNF describe some languages that regular expressions cannot?
[1 mark]In a syntax diagram, what does a path that loops back round a box show?
[1 mark]Given <s> ::= ab | a<s>b, how many times must the rule be used to produce aaabbb?
[1 mark]What does this program print?
def s(text, i):
if text[i:i + 2] == "ab":
return i + 2
if text[i:i + 1] == "a":
j = s(text, i + 1)
if j != -1 and text[j:j + 1] == "b":
return j + 1
return -1
for t in ["aabb", "aab", "abb"]:
print(t, s(t, 0))aabb 4 aab -1 abb 2
aabb matches all 4 characters. aab fails with -1. abb matches only ab, returning 2, so the whole string is not valid.
Write a recursive descent checker for the robot command language above and use it on the strings in tests.
- Write one function per rule you need, each taking (text, i) and returning the position after the match, or -1 if it does not match. At least program, command and integer.
- A string is valid only if program matches all of it.
- Check the nesting with your functions. Do not use the re module: no regex can check matched brackets.
- For each string in tests, in order, print the string, a space, then valid or invalid. For example F20R90 valid. Eight lines in all.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
tests = ["F20R90", "[4F20R90]", "[2F10[3L5]]", "F20R", "[4F20", "4F20", "[3]", "F1]"]
def integer(text, i):
return -1The hint students can ask for: Follow the BNF: each function tries its rule's alternatives at position i. A command is a move if the character there is a direction letter, or a repeat if it is an opening bracket. After one command, a program carries on only if the next character could start another command. A repeat must end with its own closing bracket.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
tests = ["F20R90", "[4F20R90]", "[2F10[3L5]]", "F20R", "[4F20", "4F20", "[3]", "F1]"]
def integer(text, i):
# <integer> ::= <digit> | <digit><integer>
if text[i:i + 1].isdigit():
j = integer(text, i + 1)
return j if j != -1 else i + 1
return -1
def command(text, i):
# <command> ::= <move> | <repeat>
ch = text[i:i + 1]
if ch != "" and ch in "FBLR":
return integer(text, i + 1)
if ch == "[":
j = integer(text, i + 1)
if j == -1:
return -1
k = program(text, j)
if k != -1 and text[k:k + 1] == "]":
return k + 1
return -1
def program(text, i):
# <program> ::= <command> | <command><program>
j = command(text, i)
if j == -1:
return -1
nxt = text[j:j + 1]
if nxt != "" and nxt in "FBLR[":
return program(text, j)
return j
for text in tests:
print(text, "valid" if program(text, 0) == len(text) else "invalid")
Any program that meets the task's checks is marked correct in the simulator; this is one way, not the only way.