Update development log
This commit is contained in:
1 parent
cdaf2e7013
commit
03a17aa4a0
1 file changed
+50
@@ -143,3 +143,53 @@ I was then able to create a demo program by defining a dummy evaluator
|
||||
that simply returns the input expression unmodified, and running a
|
||||
REPL using that, connected to stdin and stdout. This demonstrated a
|
||||
full round trip through the parsing and printing procedures.
|
||||
|
||||
## Evaluator
|
||||
|
||||
Rather than trying to handle everything in one complex evaluator, I
|
||||
decided to implement a multi-pass evaluator. This essentially means
|
||||
that the expressions run through multiple different passes that apply
|
||||
different transformations to them, thereby keeping the complexity of
|
||||
each pass more managable. This is a common method used when
|
||||
implementing compilers and interpreters.
|
||||
|
||||
### Normalisation
|
||||
|
||||
The first preliminary pass I implemented was a normalisation pass.
|
||||
This transforms things like `x - y` into `x + (-1)y`, and `x/y` into
|
||||
`xy^(-1)`. The main benefit of this is to make it easier for the
|
||||
later differentiation pass as it will only have to handle sums,
|
||||
products and exponents (not differences and quotients). I decided to
|
||||
leave quotients of integers as-is since these will be untouched by the
|
||||
differentiation process anyway, and "1/2" reads far better than
|
||||
"1*2^(-1)".
|
||||
|
||||
### Constant Folding
|
||||
|
||||
The purpose of this pass is to combine constants together as much as
|
||||
possible, such as transforming `1 + 2*3` into `6`, or `x + 1 + 2` into
|
||||
`x + 3`. This is mostly a nice-to-have rather than an essential
|
||||
feature, though it will make the output much more readable.
|
||||
|
||||
Implementing this pass did get a little hairy, as the binary tree
|
||||
structure I used for the AST made handling cases like `x + 1 + 2`
|
||||
(which is represented in the AST as `(x + 1) + 2` quite complex. The
|
||||
module implementing this therefore ended up being somewhat messy,
|
||||
especially since Rust's move/borrow semantics made flattening trees of
|
||||
sums and products more complex than it would otherwise be. However, I
|
||||
decided to move on rather than spend a great deal of time tidying this
|
||||
up since, as previously mentioned, this was more of a nice-to-have
|
||||
feature rather than core functionality.
|
||||
|
||||
### Evaluator Composition
|
||||
|
||||
I then implemented a mechanism to compose evaluators together, and
|
||||
build a pipeline of arbitrarily many of them. This was simple to do
|
||||
since I was implementing the `Evaluator` trait for each pass, so
|
||||
supporting composition simply meant creating a struct containing two
|
||||
evaluators, and implementing the `Evaluator` trait for this such that
|
||||
the evaluators were run in sequence on any input expression.
|
||||
|
||||
To combine more than two evaluators, I created a `pipeline` macro that
|
||||
constructs nested compositions of evaluators with Rust's `macro_rules`
|
||||
system.
|
||||
Reference in new issue
Block a user