dart-sass/differences.md

56 lines
3.0 KiB
Markdown
Raw Normal View History

2016-05-25 06:01:19 +02:00
# Differences from Ruby Sass
Dart Sass was created and architected by Natalie Weizenbaum, the lead designer
and developer of Ruby Sass. Its architecture is informed by lessons learned from
working on the Ruby implementation, and as such differs in a number of key ways.
This document is intended to record the differences and to act as a guide to
Dart Sass for developers familiar with Ruby Sass.
1. The biggest difference is that Dart Sass intentionally tries to minimize the
number of whole-stylesheet compilation phases. Ruby Sass loses a lot of time
to the raw mechanics of AST traversal, so minimizing that should produce
enough benefit to offset the more complex code.
The parse phase and the CSS serialization phase both still exist and do more
or less the same thing as in Ruby Sass. However, the perform, cssize, and
extend phases are now a single perform phase. This phase executed SassScript
and builds the final CSS syntax tree from the resulting information. Extends
and bubbling are applied as the tree is being created.
The nesting verification phases have been removed in favor of more thorough
parser-based checking for appropriate nesting, as well as dynamic
valid-parent checks in the perform phase where necessary.
2. Dart Sass uses entirely separate abstract syntax trees for the Sass input
than for the CSS output, rather than having some node types shared between
them. This better models the fact that the data being consumed from the user
is very different than the data being emitted. In particular, the input data
often has SassScript in places where the output needs to rely on plain CSS
for proper formatting.
3. The abstract syntax trees are all immutable. This is enabled in part by #2,
since there's no need to set resolved data on a node that was not previously
resolved. Immutability makes code dealing with the ASTs much easier to reason
about and consequently to refactor.
4. There's no distinction between the statement-level parser and the
expression-level parser. This distinction in Ruby Sass was an artifact of the
original indented-syntax-only implementation and didn't really provide any
utility.
5. The parser is character-based rather than regular-expression-based. This is
faster due to Dart's well-tuned support for integers, and it gives developers
finer control over the precise workings of the parser.
6. The parser is more switch-based and less recursion-based. The Ruby Sass
parser's methods returned a value or `nil`, and much of its logic was based
on trying to consume one production and moving on to another if the first
returned `nil`. This makes parsing tend towards `O(n)` in the number of
productions. The Dart Sass parser instead checks the first character (or
several characters if necessary) and chooses which production to consume
based on those.
2016-05-27 19:31:05 +02:00
7. The environment uses an array of maps to track variable (and eventually
function and mixin) definitions. This requires fewer allocations and produces
more cache locality.