@@ -6,38 +6,113 @@ for implementers. It documents how variable stacks are implemented.
66## Foreword
77
88LibSass uses an optimized stack approach similar to how C compilers have always
9- done it, by using a growable stack where we can push and pop items of . Unfortunately
9+ done it, by using a growable stack where we can push and pop items. Unfortunately
1010Sass has proven to be a bit more dynamic than static optimizers like, therefore we
1111had to adopt the principle a little to accommodate the edge-cases due to this.
1212
1313There are three different kind of entities on the stack during runtime, namely
14- variables, functions and mixins. Each has it's own dedicated stack to optimize
14+ variables, functions and mixins. Each has its own dedicated stack to optimize
1515the lookups. In this doc we will often only cover one case, but it should be
16- applicable to any other stack object (with some small differences).
16+ applicable to any other stack object (with some small differences). Variables
17+ are the most complicated ones, as functions and mixins can only be declared
18+ on the root scope, so loops or functions don't need to be considered for them.
1719
1820Also for regular sass code and style-rules we wouldn't need this setup, but
1921it becomes essential to correctly support mixins and functions, since those
20- can be called recursively. It is also vital for loops, like @for or @each .
22+ can be called recursively. It is also vital for loops, like ` @for ` or ` @each ` .
2123
2224## Overview
2325
2426The whole process is split into two main phases. In order to correctly support
25- @import we had to introduce the preloader phase, where all @use , @forward and
26- @import rules are loaded first, before any evaluation happens. This ensures that
27+ ` @import ` we had to introduce the preloader phase, where all ` @use ` , ` @forward ` and
28+ ` @import ` rules are loaded first, before any evaluation happens. This ensures that
2729we know all entities before the evaluation phase in order to correctly setup
28- all stack frames.
30+ all stack frames before populating them.
31+
32+ ### Parser/EnvFrame phase
33+
34+ During parsing every potential scope block creates an ` EnvFrame ` , which is
35+ stored at the corresponding ast-node (e.g. on a ` StyleRule ` ). The ` EnvFrame `
36+ is designed as a RAII stack object. It adds itself to the frame-stack on
37+ creation and removes itself once it goes out of scope. Additionally it
38+ creates an ` EnvRefs ` instance on the heap, which is later used by the
39+ compile/evaluation phase.
40+
41+ ### Compiler/EnvScope phase
42+
43+ The ` EnvScope ` is similar to the ` EnvFrame ` , as it is also designed to be a RAII
44+ stack object. On creation it will increase the stack for each entity and update
45+ the corresponding offset pointers and reverts it when it goes out of scope.
46+
47+ ### EnvRefs heap object
48+
49+ The ` EnvRefs ` object is the main object holding all the information about a
50+ block scope. It mainly holds three (flat) maps, one for every entity type.
51+ These maps are used to resolve a name to an integer offset. Whenever a new
52+ entity (e.g. variable assignment), the item is added to the map if it does
53+ not already exist there and the offset is simply increased (size of the map).
54+
55+ ### EnvRoot object
56+
57+ The ` EnvRoot ` is the main object where entities are stored. It mainly holds
58+ two stack vectors for every entity type. The actual stack vector and an
59+ additional vector holding the previous stack size (or offset position).
60+ Further it holds on to all created ` EnvRefs ` and all built-in entities.
61+
62+ ### Frame offset pointers
63+
64+ Every ` EnvRefs ` object get a unique number, increasing from 0 (which is
65+ reserved for the root scope block). The ` EnvRoot ` objects has a vector
66+ containing all ` EnvRefs ` which should correspond to that index offset.
2967
3068## Basic example
3169
3270Let's assume we have the following scss code:
3371
3472``` scss
35- $a : 1 ;
73+ $a : a ;
3674b {
37- $a : 2 ;
75+ $a : b ;
3876}
3977```
4078
79+ ### Parsing phase
80+
81+ The parser will first initialize the ` EnvRoot ` with the root ` EnvRefs ` .
82+ First it will parse the top variable assignment, which will create a new
83+ entry in the variable map of the ` EnvRefs ` heap object.
84+
85+ It will then parse the ` StyleRule ` and create a ` EnvFrame ` (which will also
86+ create and register a new ` EnvRefs ` heap object). It will then parse the inner
87+ assignment and create a new entry in the new ` EnvRefs ` of the ` StyleRule ` .
88+
89+ ### Evaluation phase
90+
91+ First the compiler will create an ` EnvScope ` stack object, which will increase
92+ the stack size of ` EnvRoot ` accordingly. In this case it will increase the size
93+ of ` varStack ` by one to accommodate the single local variable. Note that the
94+ variable object itself is still undefined at this point, but the slot on the
95+ stack exists now. The ` EnvScope ` will also remember that it has to decrease
96+ that stack vector by one, once it goes out of scope.
97+
98+ On the assignment the compiler knows that variable ` $a ` can be found at the
99+ local offset ` 0 ` (as stored within the ` EnvRefs ` map). Now it only needs to
100+ add the current ` EnvRefs ` position on the ` varStack ` to get the absolute
101+ address of the requested variable.
102+
103+
104+
105+ when it sees ` b ` , it will
106+ create and assign a new ` EnvFrame ` with the ` StyleRule ` . Each ` EnvRefs `
107+ will have one Variable ` $a ` with the offset ` 0 ` . On runtime the compiler will
108+ first evaluate the top assignment rule, thus assigning the string ` a ` to the
109+ variable at offset ` 0 ` at the current active scope (root). Then it evaluates
110+ the ruleset and creates a new ` EnvScope ` , which will push new instances onto
111+ the env-stack for all previously parsed entities (one variable in this case).
112+
113+ We now have two variables on the actual env-scope with the inner still undefined.
114+
115+
41116This will allocate two independent variables on the stack. For easier reference
42117we can think of them as variable 0 and variable 1. So let's see what happens if
43118we introduce some VariableExpressions:
@@ -59,7 +134,7 @@ As you may have guesses, the `a0` expression will reference variable 0 and the
59134variable 0 again. Given this easy example this might seem overengineered, but
60135let's see what happens if we introduce a loop:
61136
62- ```
137+ ``` scss
63138$a : 1 ;
64139b {
65140 @for $x from 1 through 2 {
0 commit comments