The 2026 assessment specification states that Formal Languages questions may cover:
Regular expressions and their use in pattern matching and validation
Simple finite-state automata and their role in recognising regular languages
Context-free grammars and their applications in programming languages
The relationship between formal languages, compilers, and computational complexity
Students must also be prepared to apply their knowledge to unfamiliar contexts rather than relying on one rehearsed example.
Formal languages are precisely defined systems of symbols and rules. They allow computers to recognise patterns, validate input, understand program structure, and translate human-written instructions into actions.
Unlike natural language, a formal language aims to remove ambiguity. A computer must be able to determine whether an input follows the rules of the language.
Alphabet: The permitted symbols in a language.
String: A sequence of symbols from the alphabet.
Language: The set of all strings accepted by a particular set of rules.
Regular expression: A pattern used to describe or recognise strings.
Regular language: A language that can be described by a regular expression or recognised by a finite-state automaton.
Finite-state automaton: A model that reads one input symbol at a time and moves between a fixed number of states.
Context-free grammar: A set of production rules used to describe structured or nested languages.
Tokenising: Breaking source text into tokens such as keywords, identifiers, numbers, operators, and punctuation.
Parsing: Checking whether a sequence of tokens follows the grammar of the language.
Syntax tree: A tree representing the structure of a valid expression, statement, or program.
The Computer Science Field Guide Formal Languages chapter provides interactive explanations of finite-state automata, regular expressions, grammars, parsing, and how these mechanisms work together.
Formal languages solve an important computer science problem:
How can a computer reliably decide whether an input is valid and determine what that input means?
Natural languages rely heavily on context and human judgement. Computers instead require rules that can be applied consistently.
Regular-expression matching
Finite-state transitions
Lexical analysis and tokenising
Grammar derivation
Parsing and syntax checking
Parse trees and abstract syntax trees
Compiling and interpreting
Error detection and recovery
Regular expressions and finite-state automata describe the same class of regular languages. Regular expressions are often easier for humans to write, while an automaton provides a simple mechanism for processing the input. Context-free grammars are more powerful and can describe nested structures such as brackets, expressions, blocks, and statements.
Checking form entries and identification codes
Searching documents and log files
Recognising tokens in programming languages
Checking programming-language syntax
Processing configuration and data files
Designing network protocols
Controlling menus, appliances, and embedded systems
Syntax highlighting and code completion
Low-code and scripting platforms
Validating AI-generated code
Strictness: Precise rules support reliable processing, but small mistakes may cause the whole input to be rejected.
Ambiguity: Poorly designed rules may allow more than one interpretation.
Expressiveness: A simple language is easier to process but may restrict what users can express.
Error feedback: A technically correct parser may still provide confusing or unhelpful messages.
Performance: Complicated grammars or poorly designed regular expressions may take longer to process.
Accessibility: Syntax, terminology, punctuation, and error messages can make a language easier or harder for different users.
Syntax versus meaning: An input can follow the grammar but still be logically incorrect or unsafe.
Compiler developers may value precise syntax, predictable parsing, and efficient processing.
End users may value readability, flexibility, useful examples, and clear error messages.
Organisations may value reliability, safety, compatibility, and reduced training costs.
These priorities can conflict. A highly strict language may be easy for a computer to process but difficult for a beginner to use. The current AS91908 evidence expectations therefore require students to connect technical mechanisms to people, compare perspectives, recommend improvements, and draw justified conclusions.
Computers require exact syntax. A missing bracket, incorrect capital letter, or misplaced symbol can prevent valid intentions from being processed.
This precision helps identify errors before a program runs, but it can also frustrate users when the mistake is minor or the error message is unclear.
A finite-state automaton has a fixed number of states. It can remember limited information, such as whether it has seen an odd or even number of symbols.
However, it cannot generally count without limit or match arbitrarily nested brackets.
Programming languages contain expressions inside expressions and blocks inside other blocks.
Regular expressions and finite-state automata are not generally suitable for arbitrary nesting. Context-free grammars and parsers are used for these structures.
A grammar is ambiguous when the same string can be interpreted in more than one way.
This can create:
Multiple possible parse trees
Unpredictable program behaviour
More complicated parsers
Difficult error messages
Disagreement over the intended meaning
A statement may follow every grammar rule but still contain a logical or semantic problem.
For example:
divide total by zero
The sentence may be syntactically valid, but its requested operation is invalid.
A deterministic FSA normally reads one symbol and performs one transition at each step, making it efficient for token recognition.
Parsing cost depends on the parser and grammar. A carefully designed grammar can be processed efficiently, while ambiguity and complicated alternatives can make parsing and error recovery more difficult.
Some backtracking regular-expression engines can also experience severe slowdowns when patterns are poorly designed. Engines such as RE2 deliberately restrict some features to provide stronger efficiency guarantees.
Formal languages connect several areas of computer science:
Theory of computation: Understanding what different models can and cannot recognise.
Algorithms: Processing strings, traversing states, and constructing parse trees.
Compiler construction: Converting source code into a form that a computer can execute.
Software engineering: Designing languages and tools that remain reliable and maintainable.
Human-computer interaction: Making syntax, editors, and error messages understandable.
Cybersecurity: Rejecting malicious or malformed input and avoiding unsafe interpretation.
Artificial intelligence: Checking whether generated code or structured output follows required rules.
A compiler or interpreter normally processes source code through a sequence of connected stages.
The user enters a program or command as a sequence of characters.
IF temperature > 30 THEN SHOW warning
The source text is divided into tokens.
IF
IDENTIFIER
>
NUMBER
THEN
SHOW
IDENTIFIER
Regular expressions can describe patterns for identifiers, numbers, keywords, and symbols. Finite-state automata can implement the recognition of these patterns.
Whitespace and comments may be removed, leaving an organised sequence of tokens.
A parser uses a context-free grammar to determine whether the token sequence forms a valid statement.
The parser creates a tree representing the structure of the statement.
IF statement
├── condition
│ ├── temperature
│ ├── >
│ └── 30
└── action
└── SHOW warning
The system checks meaning that cannot be confirmed by grammar alone.
It might check whether:
temperature has been defined
The comparison uses compatible data types
warning is a valid display item
The user has permission to perform the action
A compiler translates the program into another form before it runs.
An interpreter processes and executes the program directly or one section at a time.
The CS Field Guide describes regular expressions as useful for recognising identifiers, keywords, numbers, and similar tokens, while context-free grammars describe nested program structures.
A regular expression (regex) is a pattern used to search for or validate text. For example, it could check whether a code contains two letters followed by four numbers.
Regular expressions describe simple text patterns.
A finite-state automaton (FSA) reads one symbol at a time and moves between a limited number of states. It accepts or rejects the input depending on the state where it finishes.
Finite-state automata recognise regular languages using states and transitions.
Regular expressions and FSAs can recognise the same types of simple patterns, known as regular languages. A regex describes the pattern in text, while an FSA represents it using states and transitions.
Context-free grammars describe more complex or nested structures.
A context-free grammar (CFG) defines rules for more complex or nested structures, such as programming statements and matching brackets. A parser applies these rules to determine whether an input is correctly structured and may create a parse tree.
Parsers apply grammar rules to check and interpret the structure of an input.
Identify the most suitable mechanism for each task.
Task
Best-fitting mechanism
Find dates inside a large text file
Check whether a binary message contains an even number of 1s
Recognise identifiers and numbers during tokenising
Check matching and nested brackets
Model the screens and button presses of a ticket machine
Check whether a complete programming statement follows the required structure
Which tasks can be completed using a regular expression?
Which tasks can be represented using an FSA?
Which tasks require a context-free grammar and parser?
Why can regular expressions and FSAs often perform similar tasks?
Why is a parser still required after tokenising?
What can syntax checking not prove about a program?
Task Best-fitting mechanism
Find dates inside a large text file Regular expression
Check whether a binary message contains an even number of 1s FSA
Recognise identifiers and numbers during tokenising Regular expression or FSA
Check matching and nested brackets CFG and parser
Model ticket-machine screens and buttons FSA
Check a complete programming statement CFG and parser
Dates and token patterns can usually be described using regular expressions.
Fixed-state patterns and interface states can be represented using FSAs.
Nested brackets and complete program structures require grammars and parsing.
Every regular expression can be represented by an equivalent FSA.
Tokens may be individually valid but placed in an invalid order.
Syntax checking cannot prove that the program is useful, safe, or logically correct.
RegExr allows students to construct and test regular expressions. Matches update as the expression is edited, and the tool explains individual parts of a pattern.
Open RegExr and leave the expression type set to JavaScript.
Delete the example text from the Text area.
Paste these test values, one per line:
RK:2048/A
AQ:0192/C
rK:2048/A
RK:248/A
RK:2048/AB
XX:9999/Z
A valid sensor identifier must contain:
Two uppercase letters
A colon
Exactly four digits
A forward slash
One uppercase letter
Write a regular expression that matches the valid identifier.
Build a pattern (above where is says TEXT | TESTS)
How Many from the list were highlighted
Explain the purpose of each part.
Identify which examples are accepted and rejected.
Explain how the expression could be used for validation.
Explain how it could be used during tokenising.
Give one strength and one limitation of this approach.
^[A-Z]{2}:[0-9]{4}/[A-Z]$
^ marks the start.
[A-Z]{2} requires two uppercase letters.
: matches the colon.
[0-9]{4} requires four digits.
/ matches the forward slash.
[A-Z] requires one uppercase letter.
$ marks the end.
Accepted:
RK:2048/A
AQ:0192/C
XX:9999/Z
Rejected:
rK:2048/A
RK:248/A
RK:2048/AB
The expression can prevent incorrectly formatted identifiers from being entered into a system. During tokenising, it could recognise a complete sensor identifier as one token.
Its strength is fast, consistent pattern recognition. Its limitation is that it only checks format. It cannot confirm that the sensor actually exists.
Hover over each part of the expression to view RegExr’s explanation.
Change one valid identifier at a time to test what happens when you add a lowercase letter, remove a digit, or add an extra letter.
Here's a map of a commuter train system for the town of Trainsylvania. The trouble is, it doesn't show where the trains go – all you know is that there are two trains from each station, the A-train and the B-train. The inhabitants of Trainsylvania don't seem to mind this – it's quite fun choosing trains at each station, and after a while you usually find yourself arriving where you intended.
You can travel around Trainsylvania yourself using the following interactive. You're starting at the Airport station, and you need to find your way to Harbour station. At each station you can choose either the A-train or the B-train – press the button to find out where it will take you. But, like the residents of Trainsylvania, you'll probably want to start drawing a map of the railway, because later you might be asked to find your way somewhere else.
Use the CS Field Guide Trainsylvania interactive. You begin at Airport Station and select either Train A or Train B for each transition. Use the Planner to test complete sequences.
Shortest route: What is the shortest sequence of A and B trains that takes you from Airport Station to Harbour Station? How many transitions does it require?
Trace a sequence: Enter BBABBA into the Planner. Which station do you finish at? Record each station visited along the route.
Alternative routes: Find two different sequences that finish at Harbour Station. One must be the shortest route and the other must contain at least 12 train rides. Explain how a repeated state or loop allows the longer sequence to reach the same destination.
Shortest route to Harbour:
ABBAB — 5 train rides
Airport → West → Central → Midway → North → Harbour.
Where does BBABBA finish?
It finishes back at Airport Station:
Airport → South → Factory → Airport → South → Factory → Airport.
Two routes to Harbour:
Shortest: ABBAB
Longer route: BABABABAABBAB — 13 train rides
BABA forms a loop that returns to Airport. Repeating the loop twice and then taking ABBAB still finishes at Harbour.
Download JFLAP 7.1 from the official JFLAP website. It downloads as a file called JFLAP7.1.jar. JFLAP recommends version 7.1 and requires Java 8 or newer.
Check that Java is installed by opening Terminal or Command Prompt and entering:
java -version
Open your Downloads folder and double-click JFLAP7.1.jar.
If macOS blocks it:
Right-click the file.
Select Open.
Select Open again.
Alternatively, open Terminal and enter:
java -jar JFLAP7.1.jar
If double-clicking does not work, open Command Prompt and enter:
java -jar JFLAP7.1.jar
Once JFLAP opens, select Finite Automaton to begin creating the FSA. The .jar file is the program itself; it does not need to be installed separately.
finsm.io is a free browser-based tool for creating and testing finite-state automata without installing software. Students can draw states and transitions, choose start and accepting states, enter test strings, and step through each symbol to see how the automaton moves and whether the input is accepted or rejected. It is a simple online alternative to JFLAP for learning how FSAs work.
Create a finite-state automaton over the alphabet:
{a, b}
The automaton must accept a string only when it contains an even number of b symbols.
Open JFLAP and select Finite Automaton. This opens the editor containing tools for creating states, transitions, and initial or accepting states.
Select the State Creator tool and click twice on the workspace.
JFLAP will create:
q0 — an even number of bs has been read
q1 — an odd number of bs has been read
Select the Attribute Editor tool and right-click q0.
Mark q0 as:
Initial
Final
It is the initial state because the automaton begins with zero bs, and zero is even. A final state appears with a double circle.
Do not mark q1 as final.
Select the Transition Creator tool and add these transitions:
From Input To Reason
q0 a q0 Reading a does not change the number of bs
q1 a q1 Reading a does not change the number of bs
q0 b q1 One more b changes even to odd
q1 b q0 One more b changes odd to even
To create a loop, select the transition tool and click the state itself. To connect two states, drag from the first state to the second, type the transition symbol, and press Enter.
Your finished FSA should follow this structure:
b
┌─────────────┐
▼ │
→ ((q0: even)) (q1: odd)
↺ a ↺ a
│ ▲
└──────b──────┘
q0 is both the start and accepting state.
Select Input → Multiple Run.
Enter each test string on a separate row.
Use Enter Lambda to enter the empty string.
Select Run Inputs.
JFLAP will display Accept or Reject for each string.
Input Number of bs Expected result
Empty string "" 0 Accept
a 0 Accept
b 1 Reject
bb 2 Accept
ababa 2 Accept
aab 1 Reject
bbabb 4 Accept
To watch a string being processed one symbol at a time, select Input → Step with Closure, enter the string, and repeatedly select Step. JFLAP highlights the state reached after each input symbol.
Create a finite-state automaton over the alphabet:
{a, b}
The automaton must accept a string only when it contains an even number of b symbols.
Open finsm.io in Build mode.
Hold Shift and click an empty space to create the first state.
Repeat to create a second state.
Shift-click each state label and rename them:
q0 – even
q1 – odd
In finsm.io, states are created with Shift-click, moved by dragging, and renamed by Shift-clicking their labels.
Click q0 once to select it, then press F.
It should now appear as a double circle. q0 is accepting because the machine has read an even number of bs. At the beginning it has read zero bs, and zero is even.
Do not make q1 an accepting state.
Hover over the edge of a state until a + appears. Drag from that state to the destination state. Shift-click the arrow label to enter its input symbol. To create a loop, drag the arrow back onto the same state.
Create these four transitions:
From Input To Reason
q0 a q0 Reading a does not change the number of bs
q1 a q1 Reading a does not change the number of bs
q0 b q1 Another b changes even to odd
q1 b q0 Another b changes odd to even
Your automaton should follow this pattern:
b
┌─────────────────┐
▼ │
→ ((q0: even)) (q1: odd)
↺ a ↺ a
│ ▲
└────────b────────┘
Select Simulate mode and choose DFA at the top left.
Click q0 to make it the start state. In DFA mode, clicking a state makes it the single starting state.
At the bottom of Simulate mode:
Select + to create a new input tape.
Select the pencil beside the tape.
Enter a test string and press Enter.
Select the tape and press the right-arrow key to process one symbol at a time.
The active state is highlighted as the input is processed.
For the empty string, create a tape but leave it blank. Because the automaton starts in accepting state q0, the empty string is accepted.
Input Number of bs Result
Empty string "" 0 Accept
a 0 Accept
b 1 Reject
bb 2 Accept
ababa 2 Accept
aab 1 Reject
bbabb 4 Accept
For example, ababa follows:
q0 → q0 → q1 → q1 → q0 → q0
a b a b a
It finishes in q0, so it is accepted.
Design an FSA over the alphabet:
{a, b}
""
a
b
ab
aab
aba
bbab
abba
What is the input alphabet?
What does each state represent?
Which state is the start state?
Which state should be accepting?
What should happen when the automaton reads a from the start state?
What should happen when it then reads b?
Trace the input bbab.
Why are three states required?
What is one limitation of this FSA?
The alphabet is {a, b}.
One state represents no useful ending, one represents that the latest symbol is a, and one represents that the string currently ends in ab.
The “no useful ending” state is the start state.
The state representing “ends in ab” is the accepting state.
Reading a moves the automaton to the state showing that the latest symbol is a.
Reading b then moves it to the accepting state because the string now ends in ab.
bbab finishes in the accepting state, so it is accepted.
Three states are needed to remember whether the input has no useful ending, ends in a, or ends in ab.
It only checks the final two symbols. It does not understand the meaning of the string or recognise nested structures.
From state Input To state
Start a Ends in a
Start b Start
Ends in a a Ends in a
Ends in a b Ends in ab
Ends in ab a Ends in a
Ends in ab b Start
RoboRoute controls a small delivery robot.
<program> → <command> | <command> ; <program>
<command> → MOVE <number>
| TURN <direction>
| REPEAT <number> { <program> }
<direction> → LEFT | RIGHT
<number> → 1 | 2 | 3 | 4 | 5
Example programs:
MOVE 3
MOVE 2 ; TURN LEFT
REPEAT 2 { MOVE 1 ; TURN RIGHT }
Identify the start symbol.
Identify the non-terminals.
Give four examples of terminals.
What do the production rules describe?
Explain why MOVE 3 ; TURN LEFT is valid.
Explain why REPEAT { MOVE 2 } is invalid.
Why can this grammar describe nested REPEAT blocks?
Why is a CFG more suitable than a basic regular expression here?
What would a parser produce after accepting the program?
The start symbol is <program>.
The non-terminals are <program>, <command>, <direction>, and <number>.
Terminals include MOVE, TURN, LEFT, RIGHT, REPEAT, {, }, and ;.
Production rules describe how each non-terminal can be expanded.
It matches <command> ; <program>.
REPEAT must be followed by a number before the opening brace.
<program> can contain a <command>, and a REPEAT command contains another <program>.
The grammar can describe recursive and arbitrarily nested structures.
The parser would produce a parse tree or abstract syntax tree representing the program’s structure.
Context-free grammars are designed for hierarchical structures in which expressions or statements may contain more expressions or statements.
AST Explorer allows students to enter Python code and inspect the abstract syntax tree (AST) created by the parser.
Enter:
total = price + tax
Then try:
total = price * tax
Finally, remove the expression after the equals sign:
total =
Which tokens can you identify in the first statement?
Which tree node represents the assignment?
How is the addition operation represented?
What changes when + is replaced by *?
What happens when the expression is removed?
Why is an AST more useful than storing only the original code?
Does producing a valid AST prove that price and tax have values?
Tokens include total, =, price, +, and tax.
An Assign node represents the assignment statement.
The addition is represented by a BinOp node containing price, the Add operator, and tax.
The operator changes from Add to Mult, while the overall tree structure remains similar.
The parser reports a SyntaxError because an expression is required after =.
An AST clearly represents the structure of the code, making it easier to analyse, check, translate, or modify.
No. The AST only shows that the syntax is valid. Checking whether price and tax exist happens later during execution or semantic analysis.
Formal-language design is not only about machine correctness. It shapes whether humans can successfully express their intentions.
Consider these two messages:
SyntaxError: unexpected token at 1:24
Expected THEN after the condition.
Add THEN before SHOW on line 1.
Both messages report the same underlying problem, but the second provides more useful support.
Strict rules make interpretation reliable but may reject reasonable variations.
A small grammar is easier to learn and process but may not support complex tasks.
Syntax that looks natural to humans may contain ambiguity that is difficult for a parser to resolve.
Stopping at the first error is technically simple. Recovering and identifying several useful errors may provide a much better user experience.
Changing a grammar can make a language clearer, but existing programs may stop working.
Strong validation may prevent unsafe actions, but excessive restrictions can prevent legitimate work.
AI systems can generate programs, queries, configuration files, and structured data. However, an AI-generated output may be:
Grammatically invalid
Syntactically valid but unsafe
Based on unsupported functions
Ambiguous to a human reviewer
Inconsistent with an organisation’s rules
Formal-language tools can help by:
Validating generated output
Parsing it before execution
Restricting output to an approved grammar
Identifying unsafe structures
Producing clear error locations
Requiring human review before sensitive actions
A less obvious implication is that formal languages may become more important as people write less code directly. Even when AI generates the text, someone must define and enforce the rules that determine what the computer is allowed to accept.
Use an original scripting language, validation system, control interface, or digital service as your context.
Explain:
Alphabet
Symbols and strings
Regular expressions
Regular languages
States and transitions
Start and accepting states
Context-free grammars
Terminals and non-terminals
Production rules and start symbols
Connect each concept to the system you have chosen.
Explain:
Tokenising or lexical analysis
How regular expressions identify tokens
How FSAs recognise regular patterns
How a parser uses a CFG
Syntax checking
Parse trees or abstract syntax trees
Compiling or interpreting
How grammar design may affect processing complexity
A strong response should show the complete sequence rather than discussing each mechanism separately.
Analyse:
Strict syntax
Ambiguity
Expressiveness
Parsing efficiency
Syntax versus semantic errors
Error messages
Learnability
Accessibility
Reliability and safety
Connect each technical decision to a consequence for people.
For example:
Requiring exact punctuation makes parsing predictable, but users may be unable to correct a mistake when the error message reports only an unexpected token.
Compare at least two perspectives, such as:
Compiler developers and beginner programmers
Platform owners and non-specialist users
Organisations and accessibility advocates
AI-tool developers and software reviewers
System designers and people affected by automated decisions
Recommend improvements such as:
Clearer grammar rules
Better error locations
Plain-language explanations
Syntax highlighting
Autocomplete
Guided templates
Parser error recovery
Validation before execution
Grammar-aware AI guardrails
Human review of sensitive actions
Finish with a justified conclusion about how formal-language design affects successful and safe use.
Formal languages are not merely sets of rules for computers. They form the interface between human intention and machine action.
Precise syntax supports correctness, reliability, and automation, but precision alone does not create a usable system. Formal-language systems are most effective when their grammars are unambiguous, their processing is efficient, their limitations are understood, and their tools help people recognise and recover from mistakes.