@@ -6,38 +6,113 @@ for implementers. It documents how variable stacks are implemented.
6
6
## Foreword
7
7
8
8
LibSass 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
10
10
Sass has proven to be a bit more dynamic than static optimizers like, therefore we
11
11
had to adopt the principle a little to accommodate the edge-cases due to this.
12
12
13
13
There 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
15
15
the 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.
17
19
18
20
Also for regular sass code and style-rules we wouldn't need this setup, but
19
21
it 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 ` .
21
23
22
24
## Overview
23
25
24
26
The 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
27
29
we 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.
29
67
30
68
## Basic example
31
69
32
70
Let's assume we have the following scss code:
33
71
34
72
``` scss
35
- $a : 1 ;
73
+ $a : a ;
36
74
b {
37
- $a : 2 ;
75
+ $a : b ;
38
76
}
39
77
```
40
78
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
+
41
116
This will allocate two independent variables on the stack. For easier reference
42
117
we can think of them as variable 0 and variable 1. So let's see what happens if
43
118
we introduce some VariableExpressions:
@@ -59,7 +134,7 @@ As you may have guesses, the `a0` expression will reference variable 0 and the
59
134
variable 0 again. Given this easy example this might seem overengineered, but
60
135
let's see what happens if we introduce a loop:
61
136
62
- ```
137
+ ``` scss
63
138
$a : 1 ;
64
139
b {
65
140
@for $x from 1 through 2 {
0 commit comments