@@ -47,120 +47,3 @@ Please submit fixes and features by:
47
47
48
48
For new features it is often better to open an issue first to see if we agree
49
49
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 ``.
0 commit comments