Skip to content

Commit

Permalink
Linked many files in ARCHITECTURE.
Browse files Browse the repository at this point in the history
  • Loading branch information
amyjko committed Oct 26, 2023
1 parent 0e012a5 commit 1f98a90
Showing 1 changed file with 15 additions and 15 deletions.
30 changes: 15 additions & 15 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,40 +26,40 @@ Here are the major components of Svelte, and how they interact with each other.

### Database

`Database.ts` is exactly what it's named: an interface to all data persistence. It keeps a snapshot of creator configuration settings, creator projects, and creator authentication information. It decides which state to persist in a browser's `localStorage` (because it's device specific) and which to keep in Firebase (because it's account specific).
[Database.ts](https://github.com/wordplaydev/wordplay/blob/main/src/db/Database.ts) is exactly what it's named: an interface to all data persistence. It keeps a snapshot of creator configuration settings, creator projects, and creator authentication information. It decides which state to persist in a browser's `localStorage` (because it's device specific) and which to keep in Firebase (because it's account specific).

The database also relies heavily on [Svelte stores](https://svelte.dev/docs/svelte-store), offering granular access to settings to the user interface. For example, it keeps a store for the current list of locales selected, and exposes it globally, so that user interfaces can access the current locale or locales, and change the interface based on them, automatically updating whenever the language is changed.

The database should generally be fairly opaque; it shouldn't matter to code using the Database's methods how or where data is stored. It's currently backed by Firebase, but that could change, and no other part of the application should have to care.

### Abstract Syntax Trees (ASTs)

All Wordplay code starts as strings and is converted to an _abstract syntax tree_ by `Parser.ts`. Parser first tokenizes the strings using `Tokenizer.ts` to segment the text into a sequence of `Token` nodes. Parser then translates the sequence of `Token` nodes into a tree. Root nodes of programs are `Source` nodes, and then inside `/src/nodes` are all of the different types of abstract syntax tree nodes that can appear in a Wordplay program.
All Wordplay code starts as strings and is converted to an _abstract syntax tree_ by [parseProgram](https://github.com/wordplaydev/wordplay/blob/main/src/parser/parseProgram.ts). Parser first tokenizes the strings using [Tokenizer.ts](https://github.com/wordplaydev/wordplay/blob/main/src/parser/Tokenizer.ts) to segment the text into a sequence of [Token](https://github.com/wordplaydev/wordplay/blob/main/src/nodes/Token.ts) nodes. Parser then translates the sequence of `Token` nodes into a tree. Root nodes of programs are [Source](https://github.com/wordplaydev/wordplay/blob/main/src/nodes/Source.ts) nodes, and then inside [/src/nodes](https://github.com/wordplaydev/wordplay/tree/main/src/nodes) are all of the different types of abstract syntax tree nodes that can appear in a Wordplay program.

Abstract syntax tree nodes follow a common interface defined by `Node.ts`. Some of the key concepts are that all nodes have a list of child nodes, and a grammar that defines their order, names, and whitespace rules, and other metadata. This metadata is used extensively in editing. `Node` also provides many interfaces for managing lexical scoping, edits to the tree, and localized descriptions of the node, connecting to the localization components.
Abstract syntax tree nodes follow a common interface defined by [Node.ts](https://github.com/wordplaydev/wordplay/blob/main/src/nodes/Node.ts). Some of the key concepts are that all nodes have a list of child nodes, and a grammar that defines their order, names, and whitespace rules, and other metadata. This metadata is used extensively in editing. `Node` also provides many interfaces for managing lexical scoping, edits to the tree, and localized descriptions of the node, connecting to the localization components.

Some nodes add additional interfaces, especially `Expression.ts` and `Type.ts`. Expression defines interfaces for compiling expressions to evaluable steps, for getting the type of the expression, and for providing localized descriptions of their evaluation. `Type` defines all of the different types of values that can exist and the rules for how they can be computed upon.
Some nodes add additional interfaces, especially [Expression.ts](https://github.com/wordplaydev/wordplay/blob/main/src/nodes/Expression.ts) and [Type.ts](https://github.com/wordplaydev/wordplay/blob/main/src/nodes/Type.ts). Expression defines interfaces for compiling expressions to evaluable steps, for getting the type of the expression, and for providing localized descriptions of their evaluation. `Type` defines all of the different types of values that can exist and the rules for how they can be computed upon.

One important note about AST nodes: they are all **immutable**. This has a few implications:

- They should never have state can can be modified, so all of their fields are `readonly`, unless they are a temporary cache of some derived value (e.g., an expression's type).
- They do not know their parent. This is the parser builds the tree from the bottom up; nodes have to be created before they can become part of other nodes, and so each node's parent doesn't exist until after it's created. However, this is also because immutable nodes can be reused, since they cannot change. One node might appear in many trees.

To work around the lack of a parent, we have `Root.ts`, which represents the root of an AST, and manages all of the parent information, offering facilities for figuring out the structure of an AST.
To work around the lack of a parent, we have [Root.ts](https://github.com/wordplaydev/wordplay/tree/main/src/nodes), which represents the root of an AST, and manages all of the parent information, offering facilities for figuring out the structure of an AST.

A Wordplay Project is a list of `Source`, with a name, ID, and other metadata.

Overall, it's best to think of the nodes as the center of everything: they define a program's structure, behavior, description, and more, and so most other things in Wordplay rely on nodes and trees to do their work.

### Type Checking

All nodes that are subclasses of `Expression` have a _type_. If you're not familiar with types in programming languags, they represent what kinds of values some symbol might store. They're a central idea in TypeScript and also a central idea in Wordplay.
All nodes that are subclasses of [Expression.ts](https://github.com/wordplaydev/wordplay/blob/main/src/nodes/Expression.ts) have a _type_. If you're not familiar with types in programming languags, they represent what kinds of values some symbol might store. They're a central idea in TypeScript and also a central idea in Wordplay.

Wordplay allows for types to be declared explicitly, but also to be inferred from context. To enable this, each `Expression` node has a `computeType()` function that computes what type the expression has, either from its declared type, or its implicit semantics (e.g., a Boolean literal has a Boolean type, by definition), or inferred from context. To see what kinds of types an expression has and what kinds of type inference it does, check it's `computeType()`.
Wordplay allows for types to be declared explicitly, but also to be inferred from context. To enable this, each [Expression.ts](https://github.com/wordplaydev/wordplay/blob/main/src/nodes/Expression.ts) node has a `computeType()` function that computes what type the expression has, either from its declared type, or its implicit semantics (e.g., a Boolean literal has a Boolean type, by definition), or inferred from context. To see what kinds of types an expression has and what kinds of type inference it does, check it's `computeType()`.

To enable type inference, and to prevent infinite cycles (e.g., a variable referencing itself), we have `Context.ts`, which is a place to cache information about an AST while it's being traversed and analyzed. This cache stores type information, remembers paths through trees during analysis to prevent cycles, gets roots of nodes, and remembers definitions in scope. It generally exists to make program analysis possible and efficient.

There are many types, each defined as a subclass of `Type.ts`. Many of these represent values, some represent unknown values. Each defines a function `acceptsAll()`, which takes a set of types and verifies that all of the types in the set are okay to assign to the type in question. These various implementations of `acceptsAll()` defines the semantics of Wordplay's type system.
There are many types, each defined as a subclass of [Type.ts](https://github.com/wordplaydev/wordplay/blob/main/src/nodes/Type.ts). Many of these represent values, some represent unknown values. Each defines a function `acceptsAll()`, which takes a set of types and verifies that all of the types in the set are okay to assign to the type in question. These various implementations of `acceptsAll()` defines the semantics of Wordplay's type system.

Note that all types are subclasses of `Node` and are therefore immutable. This is because nodes can be explicitly stated in code, and are therefore must be AST nodes. But we use the very same nodes to represent types that are inferred; they just happen to not live in an AST.

Expand All @@ -75,25 +75,25 @@ Once an AST is built for a Wordplay program, it's not necessarily analyzed for c

### Evaluation

Wordplay programs are _evaluated_, in that they are purely functional. A Wordplay program is one big function, composed of smaller functions, and every Wordpaly program evaluates to a single `Value`. Values can be as simple as a `Bool` or a `Text`, or as complex as a `Structure` with 17 properties, one of which is a `List` of other `Structure` values. The most interesting values that a Wordplay program evaluates to are `Stage` structures, which define the arrangement and appearance of `Phrase`es.
Wordplay programs are _evaluated_, in that they are purely functional. A Wordplay program is one big function, composed of smaller functions, and every Wordpaly program evaluates to a single [Value](https://github.com/wordplaydev/wordplay/blob/main/src/values/Value.ts). Values can be as simple as a [BoolValue](https://github.com/wordplaydev/wordplay/blob/main/src/values/BoolValue.ts) or a [Text](https://github.com/wordplaydev/wordplay/blob/main/src/values/TextValue.ts), or as complex as a [Structure](https://github.com/wordplaydev/wordplay/blob/main/src/values/StructureValue.ts) with 17 properties, one of which is a `List` of other `Structure` values. The most interesting values that a Wordplay program evaluates to are [Stage](https://github.com/wordplaydev/wordplay/blob/main/src/output/Stage.ts) structures, which define the arrangement and appearance of [Phrase](https://github.com/wordplaydev/wordplay/blob/main/src/output/Phrase.ts)es.

Only `Expression` nodes are evaluable. Each one defines a `compile()` function that converts the node and its children into a series of `Step`. There are fewer than a dozen types of steps; most do things like bind values to a name in scope, start a function evaluation, jump past some step based on some condition, or do some other low level operation. Every Wordplay `Source` therefore compiles down to a sequence of `Step`s that are evaluated one at a time.
Only `Expression` nodes are evaluable. Each one defines a `compile()` function that converts the node and its children into a series of [Step](https://github.com/wordplaydev/wordplay/blob/main/src/runtime/Step.ts). There are fewer than a dozen types of steps; most do things like bind values to a name in scope, start a function evaluation, jump past some step based on some condition, or do some other low level operation. Every Wordplay `Source` therefore compiles down to a sequence of `Step`s that are evaluated one at a time.

The component that evaluates steps is `Evaluator.ts`. It takes a `Project`, and compiles its `Source`, and evaluates each sequence of steps according to the rules of each step. As it does this, it maintains a stack of function evaluations, and for each evaluation, a stack of values, and named scope of key/`Value` bindings. As each step evaluates, values are pushed and popped onto the value stack, bound to names in memory, and passed as inputs to function evaluations. If any expression every evaluates to an `Exception` value, the `Evaluator` halts and evaluates to the exception.
The component that evaluates steps is [Evaluator.ts](https://github.com/wordplaydev/wordplay/blob/main/src/runtime/Evaluator.ts). It takes a [Project](https://github.com/wordplaydev/wordplay/blob/main/src/models/Project.ts), and compiles its `Source`, and evaluates each sequence of steps according to the rules of each step. As it does this, it maintains a stack of function evaluations, and for each evaluation, a stack of values, and named scope of key/`Value` bindings. As each step evaluates, values are pushed and popped onto the value stack, bound to names in memory, and passed as inputs to function evaluations. If any expression every evaluates to an [ExceptionValue](https://github.com/wordplaydev/wordplay/blob/main/src/values/ExceptionValue.ts) value, the `Evaluator` halts and evaluates to the exception.

A key aspect of Wordplay is that some of it's values are `Stream`s, which change over time. Streams are sequences of values that are input by the external world, including things like time, mouse buttons, keyboard presses, and other events. Every time a stream has a new value, `Evaluator` reevaluates the `Source` that references it. This is what creates interactivity; every time there is some input, the program gets a chance to respond to it be reevaluating.
A key aspect of Wordplay is that some of it's values are [StreamValues](https://github.com/wordplaydev/wordplay/tree/main/src/values), which change over time. Streams are sequences of values that are input by the external world, including things like time, mouse buttons, keyboard presses, and other events. Every time a stream has a new value, `Evaluator` reevaluates the `Source` that references it. This is what creates interactivity; every time there is some input, the program gets a chance to respond to it be reevaluating.

### Localization

Wordplay defines a locale schema in `Locale`, which is basically one big JSON data structure of named string values. Some of these strings are constant, others are templates which can be given inputs and rendered with concrete values. Wordplays many nodes and user interfaces generally make deep links into this data structure to get a string or tempate, and render appropriate text.
Wordplay defines a locale schema in [Locale](https://github.com/wordplaydev/wordplay/blob/main/src/locale/Locale.ts), which is basically one big JSON data structure of named string values. Some of these strings are constant, others are templates which can be given inputs and rendered with concrete values. Wordplays many nodes and user interfaces generally make deep links into this data structure to get a string or tempate, and render appropriate text.

Localization is intimately connected to accessibility, as many of the localization strings are templated descriptions of nodes, values, and other content.

`Database` keeps track of which languages and regions are selected, loads the appropriate locale files with the strings and templates, and exposes them as a Svelte store for the user interface and language implementation to use to render localized descriptions of things. When the database receives a request to change languages and regions, these are propagated to all interfaces that depend on the selected locales. All projects are also revised to have the new locales as well.

### Output

All output is rendered in the `OutputView` component. It renders `Exception`s, and other arbitrary values using all of the Svelte views defined in `valueToView.ts`, which maps `Value` instances onto views. If a Value corresponds to a `Phrase`, `Group`, or `Stage`, then it is rendered as typographic output. These typographic values are generally converted into classes that provide convenience functions for reasoning about the output without having to use the low level interface of `Structure` values.
All output is rendered in the [OutputView](https://github.com/wordplaydev/wordplay/blob/main/src/components/output/OutputView.svelte) component. It renders `Exception`s, and other arbitrary values using all of the Svelte views defined in `valueToView.ts`, which maps `Value` instances onto views. If a Value corresponds to a `Phrase`, `Group`, or `Stage`, then it is rendered as typographic output. These typographic values are generally converted into classes that provide convenience functions for reasoning about the output without having to use the low level interface of `Structure` values.

The typographic output is defined by `Stage`. It's responsible for managing any typographic outputs that are animating, for tracking outputs that have entered stage, or existed, or moved, and for rendering the output in a way that respects various settings, such as the Stage's place (i.e., it's zoom level, rotation, etc.). It's also responsible for monitoring for inputs from input devices and passing them to the `Evaluator`'s streams, causing reevaluation.

Expand Down Expand Up @@ -131,7 +131,7 @@ Most documentation is written in `Locale`, as all of it needs to be localized. B

### Project View

`ProjectView.svelte` defines a set of `Tile` that represent source files, documentation windows, palettes, output, and other project level settings. It's basically a window manager and global context store. It also reacts to project revisions, pushing the revised project down to its views to update its appearance. It relies heavily on Svelte to make these updates minimal and fast.
[ProjectView.svelte](https://github.com/wordplaydev/wordplay/blob/main/src/components/project/ProjectView.svelte) defines a set of [Tile](https://github.com/wordplaydev/wordplay/blob/main/src/components/project/Tile.ts) that represent source files, documentation windows, palettes, output, and other project level settings. It's basically a window manager and global context store. It also reacts to project revisions, pushing the revised project down to its views to update its appearance. It relies heavily on Svelte to make these updates minimal and fast.

## Immutability

Expand Down

0 comments on commit 1f98a90

Please sign in to comment.