1 Architecture
Danila edited this page 2017-08-18 17:46:20 -07:00

The goal in designing Abacus was to separate core math logic from UI, and maintain a high degree of separation in the core itself. This would allow for parts of Abacus to be interchanged, while not affecting unrelated code. Contributors are strongly encouraged to maintain this separation of components, especially between modules.

Abacus Core

The "core" math code, contained in the core Gradle module, is the main part of Abacus - the calculating part. It defines parsing, tokenizing, and the NumberInterface. It is essentially responsible for converting input text into a tree and reducing that tree into a single number. However, the core itself consists of several pieces.

Tokenizer

The Tokenizer interface defines a class that converts a single string into a list of tokens. As a theoretical example, given the string "Hello, world!", it would produce the tokens Hello, ,, , world and !. By doing so, it groups important letters and characters together, and separates them from unrelated text. This makes it much easier to parse.

Parser

The Parser interface defines a class that can convert some list of tokens, of some type, into a tree. This deduces the grammatical structure of the text. This step is independent of the input string, and only depends on the tokens given to it. The tokens given to the parser tend to be produced by a Tokenizer.

TreeBuilder

There's a third class inside of the parsing package, called TreeBuilder. The tree builder serves as a kind of glue between the Tokenizer and Parser, and mostly serves to avoid generics. Let's take a look at an example:

// A tokenizer instance that produces a list of tokens of some type from a string.
SomeTokenizer<SomeToken> tokenizer = ...;
// A parser instance that produces a tree out of a list of tokens of the same type.
SomeParser<SomeToken> parser = ...;

String inputString = ...;
TreeNode producedTree = parser.constructTree(tokenizer.tokenizeString(inputString));

Note that the type of SomeToken is never actually used outside the Tokenizer and Parser. Once we have created a Tokenizer and Parser, there's no need to keep their type information around, as long as they match. For this purpose, a TreeBuilder takes a Tokenizer and Parser of the same type, and combines them. TreeBuilder itself doesn't provide any generic methods, and can be used without any generic arguments once it's been created. This way, although the tokenizer and parser are separate entities, they are combined together to perform a single task, and don't complicate matters in surrounding code.

Reducer

The reducer is the last step in evaluating a mathematical expression. A reducer walks a tree recursively, simplifying each child into a value, then simplifying a parent and its already simplified children into a value, etc, until it reaches the of the tree. This is used by the NumberReducer, which reduces TreeNodes into a single NumberInterface, the result of the expression. This is also separate from tokenizing and parsing, but depends on the Abacus main class for creating instances of NumberInterface.

FX

The fx module is a UI built around the Abacus core. It provides little functionality beyond interacting with the user and saving / loading configuration from disk.