Skip to content

Jinja Template for Weaving [Completed]

S.Lott edited this page Jul 16, 2022 · 1 revision

Planned for https://github.com/slott56/py-web-tool/milestone/2

The essence of weaving seems to be rendering the following kinds of chunks

  • Chunk base class for anonymous chunks of the file

  • NamedChunk class for defined names, created with @d name @{...@}

  • OutputChunk class for output files, created with @o file @{...@}

  • NamedDocumentChunk class @d name @[...@]. A special case of a named chunk. It's only woven when referenced; the definition is silenced. This lets us defined repeated words, phrases or values. They're never tangled into code, and are expanded in-place when weaving.

These define the various "major commands", @d name @{...@}, @o name @{...@}, and anonymous text.

Within each Chunk are a series of commands of the following classes:

  • TextCommand class to contain a document text block. Woven without change.

  • CodeCommand class to contain a program source code block. Woven with quoting for symbols that might mess up the publication chain.

  • ReferenceCommand class for chunk references. A @<name@> reference to a @d name @{...@} block of code or a @d name @[...@] block of text. Option 1 is to expand these prior to weaving. Option 2 is to use a property for the value which expands at weaving time.

  • XrefCommand superclass for the cross-reference summary commands.

    • FileXrefCommand class for an output file cross-reference. The @f command.

    • MacroXrefCommand class for a named chunk cross-reference. The @m command.

    • UserIdXrefCommand class for a user identifier cross-reference. The @u command.

Note that any kind of Chunk can have a @<...@> ReferenceCommand reference in it. There are two cases:

  • A @[...@] definition is expanded in-line. This can be recursive. Ideally, it's done in advance. Loops -- of course -- are invalid.
  • A @{...@} definition gets a hyperlink reference.

The outline of weaving is (in part) a nested set of {% for %} and {% if %} statements. For the Named Chunks and Output Chunks, we insert a begin_code(chunk) and code_end(chunk) to provide the wrapper around a code example.

    {% for chunk in web.chunks -%}
        {%- if chunk.typeid.OutputChunk or chunk.typeid.NamedChunk -%}{{begin_code(chunk)}}{%- endif -%}
        {% for command in chunk.commands -%}
            {%- if command.typeid.TextCommand %}{{text(command)}}
            {%- elif command.typeid.CodeCommand %}{{code(command)}}
            {%- elif command.typeid.ReferenceCommand %}{{ref(command)}}
            {%- elif command.typeid.FileXrefCommand %}{{file_xref(command)}}
            {%- elif command.typeid.MacroXrefCommand %}{{macro_xref(command)}}
            {%- elif command.typeid.UserIdXrefCommand %}{{userid_xref(command)}}
            {% endif -%}
        {%- endfor %}
        {%- if chunk.typeid.OutputChunk or chunk.typeid.NamedChunk -%}{{end_code(chunk)}}{%- endif -%}
    {%- endfor %}

The various {{text(command)}}, {{code(command)}}, and {{ref(command)}}, etc., constructs are macros that are provided by the tool, and can be modified by the user.

{%- macro text(command) -%}
{{command.text}}
{%- endmacro -%}
{% macro code(command) -%}
..  _`{{command.seq}}`::
..  rubric:: {{command.name}} ({{command.seq}}) =
..  parsed-literal::

    {% for line in command.lines.splitlines() -%}
    {{line | quote_rules}}
    {% endfor %}

{% endmacro -%}
{% macro file_xref(command) -%}
{% for file in web.files -%}
:{{file.name}}:
    {{ref(file.seq)}}
{%- endfor %}
{%- endmacro -%}
{% macro macro_xref(command) -%}
{% for macro in web.macros -%}
:{{macro.full_name}}:
    {{ref_list(macro.seq_list)}}
{%- endfor %}
{%- endmacro -%}
{% macro userid_xref(command) -%}
{% for userid in web.userids -%}
:{{userid.userid}} {{ref(userid.seq)}}:
    {{ref_list(userid.used_list)}}
{%- endfor %}
{%- endmacro -%}
{% macro ref(id) -%}
→\ `{{id}}`_
{%- endmacro -%}
{% macro ref_list(refs) -%}
{% for r in refs -%}{{ref(r)}}{% if loop.last %}{% else %}, {% endif %}{%- endfor %}
{%- endmacro -%}

Here's a mock of the Web -> Chunk -> Command structure.

web = SimpleNamespace(
    chunks=[
        [SimpleNamespace(type=SimpleNamespace(TextCommand=True), text="Title\n=====\n\n\n")],  # Chunk
        [SimpleNamespace(type=SimpleNamespace(CodeCommand=True), lines='print("Hello, World!")\n', name="hw.py", seq=1)], # OutputChunk
        [SimpleNamespace(type=SimpleNamespace(TextCommand=True), text="Conclusion...\n\nMore thoughts.\n\n")],  # Chunk
        [SimpleNamespace(type=SimpleNamespace(TextCommand=True), text="Appendices\n==========\n\n")],  # Chunk
        [SimpleNamespace(type=SimpleNamespace(TextCommand=True), text="Files\n-----\n\n")],  # Chunk
        [SimpleNamespace(type=SimpleNamespace(FileXrefCommand=True))],  # Chunk
        [SimpleNamespace(type=SimpleNamespace(TextCommand=True), text="\n\nMacros\n------\n\n")],  # Chunk
        [SimpleNamespace(type=SimpleNamespace(MacroXrefCommand=True))],  # Chunk
        [SimpleNamespace(type=SimpleNamespace(TextCommand=True), text="\n\nNames\n------\n\n")],  # Chunk
        [SimpleNamespace(type=SimpleNamespace(UserIdXrefCommand=True))],  # Chunk
    ],
    files=[SimpleNamespace(name="hw.py", seq="1")],
    macros=[SimpleNamespace(full_name="example code", seq_list=["2"])],
    userids=[SimpleNamespace(userid="print", seq=["2"], used_list=["3", "4"])]
)