a special operator for \\((r_1+)?\\), written as \\(r_1*\\).
So how does one go about checking if a regular expression matches a string? An efficient way is to
first construct a [state machine](https://en.wikipedia.org/wiki/Finite-state_machine). A type of state machine can be constructed from a regular expression
by literally translating each part of it to a series of states, one-to-one. This machine is called
a __Nondeterministic Finite Automaton__, or NFA for short. The "Finite" means that the number of
states in the state machine is, well, finite. For us, this means that we can store such
a machine on disk. The "Nondeterministic" part, though, is more complex: given a particular character
and a particular state, it's possible that an NFA has the option of transitioning into more
than one other state. Well, which state __should__ it pick? No easy way to tell. Each time
we can transition to more than one state, we exponentially increase the number of possible
states that we can be in. This isn't good - we were going for efficiency, remember?
What we can do is convert our NFA into another kind of state machine, in which for every character,
only one possible state transition is possible. This machine is called a __Deterministic Finite Automaton__,
or DFA for short. There's an algorithm to convert an NFA into a DFA, which I won't explain here.
Since both the conversion of a regex into an NFA and a conversion of an NFA into a DFA is done
by following an algorithm, we're always going to get the same DFA for the same regex we put in.
If we come up with the rules for our tokens once, we don't want to be building a DFA each time
our tokenizer is run - the result will always be the same! Even worse, translating a regular
expression all the way into a DFA is the inefficient part of the whole process. The solution is to
generate a state machine, and convert it into code to simulate that state machine. Then, we include
that code as part of our compiler. This way, we have a state machine "hardcoded" into our tokenizer,
and no conversion of regex to DFAs needs to be done at runtime.
#### The Practice
Creating an NFA, and then a DFA, and then generating C++ code are all cumbersome. If we had to
write code to do this every time we made a compiler, it would get very repetitive, very fast.
Fortunately, there exists a tool that does exactly this for us - it's called `flex`. Flex
takes regular expressions, and generates code that matches a string against those regular expressions.
It does one more thing in addition to that - for each regular expression it matches, flex
runs a user-defined action (which we write in C++). We can use this to convert strings that
represent numbers directly into numbers, and do other small tasks.
So, what tokens do we have? From our arithmetic definition, we see that we have integers.
Let's use the regex `[0-9]+` for those. We also have the operators `+`, `-`, `*`, and `/`.