Skip to content

Optimizations and refactors#67

Open
hugopl wants to merge 16 commits into
jeromegn:masterfrom
hugopl:main
Open

Optimizations and refactors#67
hugopl wants to merge 16 commits into
jeromegn:masterfrom
hugopl:main

Conversation

@hugopl
Copy link
Copy Markdown

@hugopl hugopl commented May 8, 2026

I plan to use this shard in a project, so last year I started doing some refactoring to prepare the land for my final target: Be able to use Slang at compile time or at runtime (like Liquid).

2025 patches done by humans, 2026 patches done by AI under supervision of humans. Slang still only working with compile time rendering, but the output now is optimized and do a lot less allocations.

Results rendering the basic.slang file found in test fixtures:

  • Generated io << call count for basic.slang: 324 → 57.
    • Consecutive literal strings amended (so the fewer io << calls).
  • ~7% throughput improvement at runtime.
    • HTML.escape and .gsub done at compile time.

hugopl added 14 commits July 21, 2025 11:43
Code from nodes `to_s` methods were just moved to respective `visit`
methods.
Buffer static string fragments in @pending_static and flush them as a
single io << before any dynamic write or control-flow statement.
Reduces generated code for basic.slang from 324 to 132 lines (~59%
fewer io << calls) and yields ~7% throughput improvement at runtime.
For Text-type tokens (| and ' syntax) whose value is a plain string
literal with no escape sequences and no #{} interpolation, resolve the
content and any HTML escaping at codegen time via emit_static instead
of emitting a runtime HTML.escape(...).to_s call. This folds literal
text directly into the surrounding static io << buffer.
Introduce Token::AttributeValue record to carry the raw value and a
literal flag alongside each non-class attribute. An attribute is
literal when its value was a quoted template string with no #{} or
backslash escape sequences.

In codegen, literal attributes skip the unless-false/unless-true guards
and the runtime .gsub call entirely: the &quot; escaping is done at
codegen time and the result folded into the static io << buffer.
Dynamic attributes (variables, expressions, #{} interpolation) keep
the existing runtime path unchanged.
…mment

The pending_static buffer only needs flushing immediately before a direct
write to str. Every such write already calls flush_static itself, so the
explicit flush after emit_static(">") in visit(Element) and before
visit_children in visit(Comment) was redundant. Removing them lets
consecutive all-static subtrees accumulate into a single io << call.

Generated io << call count for basic.slang: 324 → 57.
…codegen

Improves readability of generated code when debugging: instead of a single
long inspect string, each HTML line appears on its own line in the output.
Runs Ameba linter on Linux and tests on Linux, Windows, and macOS using the latest Crystal version, triggered on every push and pull request.
@hugopl
Copy link
Copy Markdown
Author

hugopl commented May 8, 2026

@jeromegn

hugopl added 2 commits May 8, 2026 16:27
Fix consume_text to strip exactly one separator space (Slim-compatible)
instead of all leading whitespace, and use rstrip instead of strip.
This preserves relative indentation in multi-line | text blocks, which
matters for content like JSON inside <script type="importmap">.

Add spec/importmap_spec.cr (was untracked) and fix its expected output
to match the corrected whitespace behavior.

Add 9 new tests covering previously untested features: comments
(invisible, visible, children, conditional IE), anonymous div shorthands
(#id, .class, #id.class), trailing-space ' text prefix, and css: alias.
p prints to stdout during tests; passthrough returns the argument unchanged without printing.
@jeromegn
Copy link
Copy Markdown
Owner

jeromegn commented May 9, 2026

Looks like your GHA workflow doesn't pass: https://github.com/hugopl/slang/actions/runs/25576449648

Can you fix that first? Either your code doesn't work or there's something wrong with the workflow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants