Skip to content

Commit 5fbb4b8

Browse files
committed
Moved architecture docs to own file, with fixes
1 parent cbb8836 commit 5fbb4b8

File tree

6 files changed

+120
-123
lines changed

6 files changed

+120
-123
lines changed

ARCHITECTURE.rst

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
Architecture of fluent-compiler
2+
-------------------------------
3+
4+
The following is a brief, very high-level overview of what fluent-compiler does
5+
and the various layers.
6+
7+
Our basic strategy is that we take an FTL file like this::
8+
9+
hello-user = Hello { $username }!
10+
11+
welcome = { hello-user } Welcome to { -app-name }.
12+
13+
-app-name = Acme CMS
14+
15+
16+
and compile it to Python functions, something roughly like this:
17+
18+
.. code-block:: python
19+
20+
def hello_user(args, errors):
21+
try:
22+
username = args['username']
23+
except KeyError:
24+
username = "username"
25+
errors.append(FluentReferenceError("Unknown external: username"))
26+
return f"Hello {username}"
27+
28+
29+
def welcome(args, errors):
30+
return f"{hello_user(args, errors)} Welcome to Acme CMS."
31+
32+
33+
We then need to store these message functions in some dictionary-like object,
34+
to allow us to call them.
35+
36+
.. code-block:: python
37+
38+
message_functions = {
39+
'hello-user': hello_user,
40+
'welcome': welcome,
41+
}
42+
43+
To actually format a message we have to do something like:
44+
45+
.. code-block:: python
46+
47+
errors = []
48+
formatted_message = message_functions['hello-user']({'username': 'guest'}, errors)
49+
return formatted_message, errors
50+
51+
Note a few things:
52+
53+
* Each message becomes a Python function.
54+
* Message references are handled by calling other message functions.
55+
* We do lots of optimizations at compile time to heavily simplify the
56+
expressions that are evaluated at runtime, including things like inlining
57+
terms.
58+
* We have to handle possible errors in accordance with the Fluent philosophy.
59+
Where possible we detect errors at compile time, in addition to the runtime
60+
handling shown above.
61+
62+
We do not, in fact, generate Python code as a string, but instead generate AST
63+
which we can convert to executable Python functions using the builtin functions
64+
`compile <https://docs.python.org/3/library/functions.html#compile>`_ and `exec
65+
<https://docs.python.org/3/library/functions.html#exec>`_.
66+
67+
Layers
68+
~~~~~~
69+
70+
The highest level code, which can be used as an entry point by users, is in
71+
``fluent_compiler.bundle``. The interface provided here, however, is meant
72+
mainly for demonstration purposes, since it is expected that in many
73+
circumstances the next level down will be used. For example, `django-ftl
74+
<https://github.com/django-ftl/django-ftl>`_ by-passes this module and uses the
75+
next layer down.
76+
77+
The next layer is ``fluent_compiler.compiler``, which handles actual
78+
compilation, converting FTL expressions (i.e. FTL AST nodes) into Python code.
79+
The bulk of the FTL specific logic is found here. See especially the comments
80+
on ``compile_expr``.
81+
82+
For generating Python code, it uses the classes provided by the
83+
``fluent_compiler.codegen`` module. These are simplified versions of various
84+
Python constructs, with an interface that makes it easy for the ``compiler``
85+
module to construct correct code without worrying about lower level details.
86+
87+
The classes in the ``codegen`` module eventually need to produce AST objects
88+
that can be passed to Python’s builtin `compile
89+
<https://docs.python.org/3/library/functions.html?highlight=compile#compile>`_
90+
function. The stdlib `ast <https://docs.python.org/3/library/ast.html>`_ module
91+
has incompatible differences between different Python versions, so we abstract
92+
over these in ``fluent_compiler.ast_compat`` which allows the ``codegen`` module
93+
to almost entirely ignore the differences in AST for different Python.
94+
95+
In addition to these modules, there are some runtime functions and types that
96+
are needed by the generated Python code, found in ``fluent_compiler.runtime``.
97+
98+
The ``fluent_compiler.types`` module contains types for handling number/date
99+
formatting - these are used directly by users of ``fluent_compiler``, as well as
100+
internally for implementing things like the ``NUMBER`` and ``DATETIME`` builtin
101+
FTL functions.
102+
103+
Other related level classes for the user are provided in
104+
``fluent_compiler.resource`` and ``fluent_compiler.escapers``.
105+
106+
Tests
107+
~~~~~
108+
109+
The highest level tests are in ``tests/format/``. These are essentially
110+
functional tests that ensure we produce correct output at runtime.
111+
112+
In addition we have many tests of the lower layers of code. These include
113+
a lot of tests for our optimizations, many of which work at the level of
114+
examining the generated Python code.
115+
116+
We also have benchmarking tests in ``tools``.

CONTRIBUTING.rst

Lines changed: 0 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -47,120 +47,3 @@ Please submit fixes and features by:
4747

4848
For new features it is often better to open an issue first to see if we agree
4949
that the feature is a good idea, before spending a lot of time implementing it.
50-
51-
Architecture
52-
------------
53-
54-
The following is a brief, very high-level overview of what fluent-compiler does
55-
and the various layers.
56-
57-
Our basic strategy is that we take an FTL file like this::
58-
59-
hello-user = Hello { $username }!
60-
61-
-app-name = Acme CMS
62-
63-
welcome = { hello-user } Welcome to { -app-name }.
64-
65-
66-
and compile it to Python functions, something roughly like this:
67-
68-
.. code-block:: python
69-
70-
def hello_user(args, errors):
71-
try:
72-
username = args['username']
73-
except KeyError:
74-
username = "username"
75-
errors.append(FluentReferenceError("Unknown external: username"))
76-
return f"Hello {username}"
77-
78-
79-
def welcome(args, errors):
80-
return f"{hello_user(args, errors)} Welcome to Acme CMS."
81-
82-
83-
We then need to store these message functions in some dictionary-like object,
84-
to allow us to call them.
85-
86-
.. code-block:: python
87-
88-
message_functions = {
89-
'hello-user': hello_user,
90-
'welcome': welcome,
91-
}
92-
93-
To actually format a message with name ``'msg-name'`` and external arguments
94-
``args``, we have to do something like:
95-
96-
.. code-block:: python
97-
98-
errors = []
99-
formatted_message = message_functions['msg-name'](args, errors)
100-
return formatted_message, errors
101-
102-
Note a few things:
103-
104-
* Each message becomes a Python function.
105-
* Message references are handled by calling other message functions.
106-
* We do lots of optimizations at compile time to heavily simplify the
107-
expressions that are evaluated at runtime, including things like inlining
108-
terms.
109-
* We have to handle possible errors in accordance with the Fluent philosophy.
110-
Where possible we detect errors at compile time, in addition to the runtime
111-
handling shown above.
112-
113-
We do not, in fact, generate Python code as a string, but instead generate AST
114-
which we can convert to executable Python functions using the builtin functions
115-
`compile <https://docs.python.org/3/library/functions.html#compile>`_ and `exec
116-
<https://docs.python.org/3/library/functions.html#exec>`_.
117-
118-
Layers
119-
~~~~~~
120-
121-
The highest level code, which can be used as an entry point by users, is in
122-
``fluent_compiler.bundle``. The interface provided here, however, is meant
123-
mainly for demonstration purposes, since it is expected that in many
124-
circumstances the next level down will be used. For example, `django-ftl
125-
<https://github.com/django-ftl/django-ftl>`_ by-passes this module and uses the
126-
next layer down.
127-
128-
The next layer is ``fluent_compiler.compiler``, which handles actual
129-
compilation, converting FTL expressions (i.e. FTL AST nodes) into Python code.
130-
The bulk of the FTL specific logic is found here. See especially the comments
131-
on ``compile_expr``.
132-
133-
For generating Python code, it uses the classes provided by the
134-
``fluent_compiler.codegen`` module. These are simplified versions of various
135-
Python constructs, with an interface that makes it easy for the ``compiler``
136-
module to construct correct code without worrying about lower level details.
137-
138-
The classes in the ``codegen`` module eventually need to produce AST objects
139-
that can be passed Python’s builtin ``compile`` function. The stdlib `ast
140-
<https://docs.python.org/3/library/ast.html>`_ module has incompatible
141-
differences between different Python versions, so we abstract over these in
142-
``fluent_compiler.ast_compat`` which allows the ``codegen`` module to almost
143-
entirely ignore the differences in AST for different Python.
144-
145-
In addition to these modules, there are some runtime functions and types that
146-
are needed by the generated Python code, found in ``fluent_compiler.runtime``.
147-
148-
The ``fluent_compiler.types`` module contains types for handling number/date
149-
formatting - these are used directly by users of ``fluent_compiler``, as well as
150-
internally for implementing things like the ``NUMBER`` and ``DATETIME`` builtin
151-
FTL functions.
152-
153-
Other related level classes for the user are provided in
154-
``fluent_compiler.resource`` and ``fluent_compiler.escapers``.
155-
156-
Tests
157-
~~~~~
158-
159-
The highest level tests are in ``tests/format/``. These are essentially
160-
functional tests that ensures we produce correct output at runtime.
161-
162-
In addition we have many tests of the lower layers of code. These include
163-
a lot of tests for our optimizations, many of which work at the level of
164-
examining the generated Python code.
165-
166-
We also have benchmarking tests in ``tools``.

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ incompatible changes easily.
5858

5959
See the `issues list <https://github.com/django-ftl/fluent-compiler/issues>`_
6060
for planned features, and `CONTRIBUTING.rst <CONTRIBUTING.rst>`_ for information
61-
about how to contribute.
61+
about how to contribute, and the `architecture docs <ARCHITECTURE.rst>`_
6262

6363
Background
6464
----------

docs/contributing.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
.. include:: ../CONTRIBUTING.rst
2+
3+
.. include:: ../ARCHITECTURE.rst

docs/index.rst

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
.. fluent_compiler documentation master file, created by
2-
sphinx-quickstart on Thu Jan 24 15:45:23 2019.
3-
You can adapt this file completely to your liking, but it should at least
4-
contain the root `toctree` directive.
51

62
Welcome to fluent_compiler's documentation!
73
===========================================

src/fluent_compiler/compiler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# The heart of the FTL -> Python compiler. See the architecture docs in
2-
# CONTRIBUTING.rst for the big picture, and comments on compile_expr below.
2+
# ARCHITECTURE.rst for the big picture, and comments on compile_expr below.
33
from __future__ import absolute_import, unicode_literals
44

55
import contextlib

0 commit comments

Comments
 (0)