deadfire 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +2 -2
- data/Gemfile.lock +1 -1
- data/README.md +33 -35
- data/benchmarks/basic_benchmark.rb +8 -5
- data/benchmarks/tailwind_parser.rb +23 -0
- data/bin/console +16 -3
- data/changelog.md +21 -1
- data/lib/deadfire/ast_printer.rb +58 -0
- data/lib/deadfire/configuration.rb +7 -2
- data/lib/deadfire/css_generator.rb +66 -0
- data/lib/deadfire/error_reporter.rb +24 -0
- data/lib/deadfire/errors.rb +28 -0
- data/lib/deadfire/filename_helper.rb +29 -0
- data/lib/deadfire/front_end/apply_node.rb +44 -0
- data/lib/deadfire/front_end/at_rule_node.rb +19 -0
- data/lib/deadfire/front_end/base_node.rb +11 -0
- data/lib/deadfire/front_end/block_node.rb +21 -0
- data/lib/deadfire/front_end/comment_node.rb +17 -0
- data/lib/deadfire/front_end/newline_node.rb +17 -0
- data/lib/deadfire/front_end/parser.rb +156 -0
- data/lib/deadfire/front_end/ruleset_node.rb +18 -0
- data/lib/deadfire/front_end/scanner.rb +266 -0
- data/lib/deadfire/front_end/selector_node.rb +52 -0
- data/lib/deadfire/front_end/stylesheet_node.rb +21 -0
- data/lib/deadfire/front_end/token.rb +20 -0
- data/lib/deadfire/interpreter.rb +88 -0
- data/lib/deadfire/parser.rb +160 -289
- data/lib/deadfire/parser_engine.rb +41 -0
- data/lib/deadfire/spec.rb +136 -0
- data/lib/deadfire/version.rb +1 -1
- data/lib/deadfire.rb +27 -5
- metadata +23 -4
- data/lib/deadfire/transformers/transformer.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 570d2b607521baef50439b60e78f17aafcc9a6a53a258c2248967c6308964d27
|
4
|
+
data.tar.gz: ad3dc3a1ff0b925408cf64d73f41d659f7de3fbd2014d6d73ef55829d6246e68
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d23aee619e6429b4e3dc964a833f46ba8cba5275aea228d3945a6a240308366e067563a1fea45310a13e69414280f3d831a0e7f298596172f1fd5da44ad67235
|
7
|
+
data.tar.gz: 911f68ad428ed00b46a9062cd96af4ff50fb2459aa2fff1be796fac7651b77e88f0e405c2da8b30d38a8d21a01ae5d0c0ace7daa245deac032e6450747f08c76
|
data/.github/workflows/ci.yml
CHANGED
@@ -6,7 +6,7 @@ jobs:
|
|
6
6
|
strategy:
|
7
7
|
fail-fast: false
|
8
8
|
matrix:
|
9
|
-
ruby: [head, 3.1, 3.0, 2.7]
|
9
|
+
ruby: [head, 3.2, 3.1, 3.0, 2.7]
|
10
10
|
steps:
|
11
11
|
- uses: actions/checkout@v3
|
12
12
|
- name: Set up Ruby
|
@@ -15,4 +15,4 @@ jobs:
|
|
15
15
|
ruby-version: ${{ matrix.ruby }}
|
16
16
|
bundler-cache: true
|
17
17
|
- name: Run tests
|
18
|
-
run: bundle exec rake
|
18
|
+
run: bundle exec rake
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
A lightweight CSS preprocessor.
|
4
4
|
|
5
|
-
Use plain ol' CSS with a little bit of @import
|
5
|
+
Use plain ol' CSS with a little bit of @import and @apply.
|
6
6
|
|
7
7
|
CSS is a staple technology when building web applications. With the introduction of LESS, SASS, SCSS it made CSS easier to maintain. However, most of these tools are no longer supported, maintained or have far too many features (wait... that's a bad thing?).
|
8
8
|
|
@@ -14,7 +14,6 @@ Deadfire can be used with or without a CSS framework.
|
|
14
14
|
|
15
15
|
- [x] @import
|
16
16
|
- [x] [@apply](https://tabatkins.github.io/specs/css-apply-rule/)
|
17
|
-
- [x] [nesting](https://drafts.csswg.org/css-nesting-1)
|
18
17
|
|
19
18
|
### @import
|
20
19
|
|
@@ -55,20 +54,16 @@ Output;
|
|
55
54
|
|
56
55
|
The CSS apply rule was [proposed to be included into CSS](https://tabatkins.github.io/specs/css-apply-rule/) however it was abandoned. Mixins simplify applying existing css to a new class.
|
57
56
|
|
58
|
-
All mixins must be declared on the `:root` element or preloaded via the `Deadfire.mixins` method. Using a mixin before it's declared will raise an `EarlyApplyException`. Ideally the `:root` element should appear near the top of the document.
|
59
|
-
|
60
57
|
Let's see an example of how to declare mixins and use the @apply directive.
|
61
58
|
|
62
59
|
```CSS
|
63
|
-
:
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
text-align: center;
|
71
|
-
}
|
60
|
+
.font-bold: {
|
61
|
+
font-weight: bold;
|
62
|
+
}
|
63
|
+
|
64
|
+
.btn: {
|
65
|
+
padding-bottom: 10px;
|
66
|
+
text-align: center;
|
72
67
|
}
|
73
68
|
```
|
74
69
|
|
@@ -76,34 +71,19 @@ How can we use mixins? Using @apply...
|
|
76
71
|
|
77
72
|
```CSS
|
78
73
|
.btn-blue {
|
79
|
-
@apply
|
74
|
+
@apply .btn .font-bold;
|
80
75
|
}
|
81
76
|
|
82
77
|
.homepage-hero {
|
83
|
-
@apply
|
78
|
+
@apply .font-bold;
|
84
79
|
}
|
85
80
|
```
|
86
81
|
|
87
|
-
###
|
88
|
-
|
89
|
-
Nesting adds the ability to nest one style rule inside another.
|
82
|
+
### Fault tolerant
|
90
83
|
|
91
|
-
|
84
|
+
When Deadfire encounters an error, such as a missing mixin or other issues, it does not immediately raise an error that would halt the execution. Instead, it continues processing the CSS code and collects the encountered errors. These errors are then reported through the ErrorReporter class, allowing you to handle or display them as needed.
|
92
85
|
|
93
|
-
|
94
|
-
/* & can be used on its own */
|
95
|
-
.btn {
|
96
|
-
color: blue;
|
97
|
-
& > .homepage { color: red; }
|
98
|
-
}
|
99
|
-
```
|
100
|
-
|
101
|
-
This is expanded to:
|
102
|
-
|
103
|
-
```CSS
|
104
|
-
.btn { color: blue; }
|
105
|
-
.btn > .homepage { color: red; }
|
106
|
-
```
|
86
|
+
By adopting this fault-tolerant approach, Deadfire aims to provide more flexibility and resilience when dealing with CSS code that may contain errors or inconsistencies. It allows you to gather information about the encountered issues and take appropriate actions based on the reported errors.
|
107
87
|
|
108
88
|
## Installation
|
109
89
|
|
@@ -123,7 +103,25 @@ Or install it yourself as:
|
|
123
103
|
|
124
104
|
## Deadfire + Ruby on Rails
|
125
105
|
|
126
|
-
After adding Deadfire gem to your rails application, open the file `config/initializers/assets.rb`
|
106
|
+
After adding Deadfire gem to your rails application, open the file `config/initializers/assets.rb` or create a new initializer `config/initializers/deadfire.rb`.
|
107
|
+
|
108
|
+
### Propshaft
|
109
|
+
|
110
|
+
To setup Propshaft to use Deadfire as a preprocessor:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
# config/initializers/assets.rb
|
114
|
+
class DeadfireCompiler < Propshaft::Compiler
|
115
|
+
def compile(logical_path, input)
|
116
|
+
Deadfire.parse(input, root_path: Rails.root.join("app", "assets", "stylesheets"))
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
Rails.application.config.assets.compilers << ["text/css", DeadfireCompiler]
|
121
|
+
```
|
122
|
+
### Sprockets
|
123
|
+
|
124
|
+
To setup Sprocket to use Deadfire as a preprocessor:
|
127
125
|
|
128
126
|
```ruby
|
129
127
|
# config/initializers/assets.rb
|
@@ -156,4 +154,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
156
154
|
|
157
155
|
## Code of Conduct
|
158
156
|
|
159
|
-
Everyone interacting in the Deadfire project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/hahmed/deadfire/blob/master/CODE_OF_CONDUCT.md).
|
157
|
+
Everyone interacting in the Deadfire project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/hahmed/deadfire/blob/master/CODE_OF_CONDUCT.md).
|
@@ -13,7 +13,7 @@ gemfile(true) do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
css = <<~CSS
|
16
|
-
|
16
|
+
/* My very first css file! */
|
17
17
|
body {
|
18
18
|
font-family: helvetica, arial, sans-serif;
|
19
19
|
font-size: calc(1.3em + 0.5vw);
|
@@ -27,7 +27,10 @@ body {
|
|
27
27
|
h1 {
|
28
28
|
font-size: 40px;
|
29
29
|
}
|
30
|
-
|
30
|
+
/* Just
|
31
|
+
a
|
32
|
+
random
|
33
|
+
comment */
|
31
34
|
a {
|
32
35
|
font-size: 1em;
|
33
36
|
vertical-align: baseline;
|
@@ -41,9 +44,7 @@ a {
|
|
41
44
|
text-decoration-width: 0.1rem
|
42
45
|
}
|
43
46
|
|
44
|
-
.button--block {
|
45
|
-
min-width:100% !important
|
46
|
-
}
|
47
|
+
.button--block { min-width:100% !important }
|
47
48
|
|
48
49
|
code {
|
49
50
|
font-family: Roboto Mono;
|
@@ -51,6 +52,8 @@ code {
|
|
51
52
|
color: gray;
|
52
53
|
}
|
53
54
|
|
55
|
+
/* I like code blocks!!!!!======= */
|
56
|
+
|
54
57
|
.banner {
|
55
58
|
border: 1px solid #ccc;
|
56
59
|
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "bundler/inline"
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
gemfile(true) do
|
5
|
+
source "https://rubygems.org"
|
6
|
+
|
7
|
+
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
8
|
+
|
9
|
+
gem "deadfire", path: "./" #github: "hahmed/deadfire", branch: "main"
|
10
|
+
end
|
11
|
+
|
12
|
+
css = File.read("./benchmarks/tailwind.css")
|
13
|
+
|
14
|
+
puts css.inspect
|
15
|
+
puts "---"
|
16
|
+
|
17
|
+
|
18
|
+
time = Benchmark.measure do
|
19
|
+
parser = Deadfire::ParserEngine.new(css)
|
20
|
+
parser.parse
|
21
|
+
end
|
22
|
+
|
23
|
+
puts time.real
|
data/bin/console
CHANGED
@@ -6,9 +6,22 @@ require "deadfire"
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
8
8
|
|
9
|
-
#
|
10
|
-
|
11
|
-
|
9
|
+
# simple css test
|
10
|
+
css = <<-CSS
|
11
|
+
@page :first { margin: 1cm; }
|
12
|
+
CSS
|
13
|
+
|
14
|
+
# @charset "UTF-8";
|
15
|
+
# @viewport { width: device-width; }
|
16
|
+
# @font-face {
|
17
|
+
# font-family: "Open Sans";
|
18
|
+
# }
|
19
|
+
|
20
|
+
puts css.inspect
|
21
|
+
|
22
|
+
parser = Deadfire::ParserEngine.new(css)
|
23
|
+
parser.print_ast
|
24
|
+
puts parser.parse
|
12
25
|
|
13
26
|
require "irb"
|
14
27
|
IRB.start(__FILE__)
|
data/changelog.md
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
## Changelog
|
2
|
+
### 0.3.0 (15 November 2023)
|
3
|
+
|
4
|
+
- Redo the parser by splitting up the tokenizer, parser, interpreter and generator phases which makes each step simpler. It's still faster than sassc but much slower than it was previously which is something I hope to address soon.
|
5
|
+
- Key thing to note is that Deadfire now only handles @imports and mixins. Nesting is now a feature of CSS, which means Deadfire is much simpler as a result.
|
6
|
+
- In the next version I will drop the old parser and it's tests.
|
7
|
+
- Add support for Ruby 3.2
|
8
|
+
- Updated docs and added an example on how to get Deadfire working with Propshaft.
|
9
|
+
|
10
|
+
### 0.2.0 (25 October 2022)
|
11
|
+
|
12
|
+
- Added build for ruby 3.0
|
13
|
+
- StringIO is now hidden and only visible on the buffer class.
|
14
|
+
- Fixed a bug with a css ruleset after a nested block was being ignored, example;
|
15
|
+
```
|
16
|
+
.title {
|
17
|
+
color: blue;
|
18
|
+
& .text { padding: 3px; }
|
19
|
+
}
|
20
|
+
.image { padding: 2px; }
|
21
|
+
```
|
2
22
|
|
3
23
|
### 0.1.0 (17 October 2022)
|
4
24
|
|
5
|
-
Initial release
|
25
|
+
Initial release
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deadfire
|
4
|
+
class AstPrinter # :nodoc:
|
5
|
+
def initialize
|
6
|
+
@indentation = 0
|
7
|
+
end
|
8
|
+
|
9
|
+
def print(node)
|
10
|
+
node.accept(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def visit_stylesheet_node(node)
|
14
|
+
puts "StylesheetNode"
|
15
|
+
node.statements.each do |statement|
|
16
|
+
# something
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_at_rule_node(node)
|
21
|
+
puts "AtRuleNode"
|
22
|
+
puts " AtKeyword: #{node.at_keyword.lexeme}"
|
23
|
+
node.value.each do |value|
|
24
|
+
puts " Value: #{value}"
|
25
|
+
end
|
26
|
+
if node.block
|
27
|
+
visit_block_node(node.block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def visit_block_node(node)
|
32
|
+
puts "BlockNode"
|
33
|
+
node.declarations.each do |declaration|
|
34
|
+
case declaration
|
35
|
+
when FrontEnd::Token
|
36
|
+
puts " Declaration: #{declaration.lexeme}"
|
37
|
+
when FrontEnd::AtRuleNode
|
38
|
+
visit_at_rule_node(declaration)
|
39
|
+
when FrontEnd::RulesetNode
|
40
|
+
visit_ruleset_node(declaration)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def visit_ruleset_node(node)
|
46
|
+
puts "RulesetNode"
|
47
|
+
puts " Selector: #{node.selector}"
|
48
|
+
if node.block
|
49
|
+
visit_block_node(node.block)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def visit_comment_node(node)
|
54
|
+
puts "CommentNode"
|
55
|
+
puts " Comment: #{node.comment.lexeme}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -2,12 +2,13 @@
|
|
2
2
|
|
3
3
|
module Deadfire
|
4
4
|
class Configuration
|
5
|
-
attr_reader :directories, :root_path, :keep_comments
|
5
|
+
attr_reader :directories, :root_path, :keep_comments, :keep_whitespace
|
6
6
|
|
7
7
|
def initialize
|
8
8
|
@directories = []
|
9
9
|
@root_path = ""
|
10
10
|
@keep_comments = true
|
11
|
+
@keep_whitespace = true
|
11
12
|
end
|
12
13
|
|
13
14
|
def root_path=(value)
|
@@ -20,5 +21,9 @@ module Deadfire
|
|
20
21
|
def keep_comments=(value)
|
21
22
|
@keep_comments = value
|
22
23
|
end
|
24
|
+
|
25
|
+
def keep_whitespace=(value)
|
26
|
+
@keep_whitespace = value
|
27
|
+
end
|
23
28
|
end
|
24
|
-
end
|
29
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Frozen_string_literal: true
|
2
|
+
require "stringio"
|
3
|
+
|
4
|
+
module Deadfire
|
5
|
+
class CssGenerator # :nodoc:
|
6
|
+
def initialize(tree)
|
7
|
+
@tree = tree
|
8
|
+
@output = StringIO.new # TODO: write to file instead of string buffer in temp folder
|
9
|
+
end
|
10
|
+
|
11
|
+
def generate
|
12
|
+
@tree.accept(self)
|
13
|
+
@output.string
|
14
|
+
end
|
15
|
+
|
16
|
+
def visit_stylesheet_node(node)
|
17
|
+
node.statements.each { |child| child.accept(self) }.join("\n")
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_at_rule_node(node)
|
21
|
+
@output << node.at_keyword.lexeme
|
22
|
+
@output << " "
|
23
|
+
node.value.each do |value|
|
24
|
+
@output << value.lexeme
|
25
|
+
end
|
26
|
+
|
27
|
+
if node.block
|
28
|
+
visit_block_node(node.block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def visit_ruleset_node(node)
|
33
|
+
@output << node.selector.selector
|
34
|
+
@output << " "
|
35
|
+
|
36
|
+
visit_block_node(node.block)
|
37
|
+
end
|
38
|
+
|
39
|
+
def visit_block_node(node)
|
40
|
+
node.declarations.each do |declaration|
|
41
|
+
case declaration
|
42
|
+
when ApplyNode
|
43
|
+
visit_apply_node(declaration)
|
44
|
+
when FrontEnd::BlockNode
|
45
|
+
visit_block_node(declaration)
|
46
|
+
when FrontEnd::AtRuleNode
|
47
|
+
visit_at_rule_node(declaration)
|
48
|
+
else
|
49
|
+
@output << declaration.lexeme
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def visit_newline_node(node)
|
55
|
+
@output << node.text
|
56
|
+
end
|
57
|
+
|
58
|
+
def visit_apply_node(node)
|
59
|
+
@output << node.node.lexeme
|
60
|
+
end
|
61
|
+
|
62
|
+
def visit_comment_node(node)
|
63
|
+
@output << node.comment.lexeme if Deadfire.configuration.keep_comments
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deadfire
|
4
|
+
class ErrorReporter # :nodoc:
|
5
|
+
attr_reader :errors
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@errors = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def error(line, message)
|
12
|
+
@errors << Error.new(line, message)
|
13
|
+
end
|
14
|
+
|
15
|
+
def errors?
|
16
|
+
@errors.any?
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# create error struct with line and message
|
22
|
+
Error = Struct.new(:line, :message)
|
23
|
+
end
|
24
|
+
end
|
data/lib/deadfire/errors.rb
CHANGED
@@ -39,4 +39,32 @@ module Deadfire
|
|
39
39
|
super(msg)
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
class SyntaxError < StandardError
|
44
|
+
def initialize(message = "", lineno = "", original_line = "")
|
45
|
+
msg = if message
|
46
|
+
"#{original_line}\nline: #{lineno}: #{message}"
|
47
|
+
else
|
48
|
+
"Syntax "
|
49
|
+
end
|
50
|
+
|
51
|
+
super(msg)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class ErrorsList
|
56
|
+
attr_reader :errors
|
57
|
+
|
58
|
+
def initialize
|
59
|
+
@errors = []
|
60
|
+
end
|
61
|
+
|
62
|
+
def add(message:, lineno:, original_line:)
|
63
|
+
@errors << SyntaxError.new(message, lineno, original_line)
|
64
|
+
end
|
65
|
+
|
66
|
+
def empty?
|
67
|
+
@errors.empty?
|
68
|
+
end
|
69
|
+
end
|
42
70
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deadfire
|
4
|
+
class FilenameHelper
|
5
|
+
class << self
|
6
|
+
def resolve_import_path(line, lineno = 0)
|
7
|
+
path = normalize_path(line)
|
8
|
+
unless path.end_with?(Parser::CSS_FILE_EXTENSION)
|
9
|
+
path += Parser::CSS_FILE_EXTENSION
|
10
|
+
end
|
11
|
+
import_path = File.join(Deadfire.configuration.root_path, path)
|
12
|
+
|
13
|
+
unless File.exist?(import_path)
|
14
|
+
raise Deadfire::ImportException.new(import_path, lineno)
|
15
|
+
end
|
16
|
+
|
17
|
+
import_path
|
18
|
+
end
|
19
|
+
|
20
|
+
def normalize_path(line)
|
21
|
+
path = line.split.last
|
22
|
+
path.gsub!("\"", "")
|
23
|
+
path.gsub!("\'", "")
|
24
|
+
path.gsub!(";", "")
|
25
|
+
path
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deadfire
|
4
|
+
class ApplyNode
|
5
|
+
attr_reader :node, :mixin_names
|
6
|
+
|
7
|
+
def initialize(node, mixin_names)
|
8
|
+
# TODO: mixin name can be single or multiple names, separated by a comma
|
9
|
+
@node = node
|
10
|
+
@mixin_names = fetch_mixin_name_from(mixin_names)
|
11
|
+
end
|
12
|
+
|
13
|
+
def accept(visitor)
|
14
|
+
visitor.visit_apply_node(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def lineno
|
18
|
+
node.lineno
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def fetch_mixin_name_from(tokens)
|
24
|
+
@_cached_mixin_name ||= begin
|
25
|
+
names = []
|
26
|
+
current = []
|
27
|
+
tokens.each do |token|
|
28
|
+
case token.type
|
29
|
+
when :comma
|
30
|
+
names << current.join("")
|
31
|
+
current = []
|
32
|
+
current << token.lexeme
|
33
|
+
when :whitespace
|
34
|
+
# ignore whitespace
|
35
|
+
else
|
36
|
+
current << token.lexeme
|
37
|
+
end
|
38
|
+
end
|
39
|
+
names << current.join("")
|
40
|
+
names
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deadfire
|
4
|
+
module FrontEnd
|
5
|
+
class AtRuleNode < BaseNode
|
6
|
+
attr_reader :at_keyword, :value, :block
|
7
|
+
|
8
|
+
def initialize(at_keyword, value, block)
|
9
|
+
@at_keyword = at_keyword
|
10
|
+
@value = value
|
11
|
+
@block = block
|
12
|
+
end
|
13
|
+
|
14
|
+
def accept(visitor)
|
15
|
+
visitor.visit_at_rule_node(self)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deadfire
|
4
|
+
module FrontEnd
|
5
|
+
class BlockNode < BaseNode
|
6
|
+
attr_reader :declarations
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@declarations = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def <<(node)
|
13
|
+
@declarations << node
|
14
|
+
end
|
15
|
+
|
16
|
+
def accept(visitor)
|
17
|
+
visitor.visit_block_node(self)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deadfire
|
4
|
+
module FrontEnd
|
5
|
+
class CommentNode < BaseNode
|
6
|
+
attr_reader :comment
|
7
|
+
|
8
|
+
def initialize(comment)
|
9
|
+
@comment = comment
|
10
|
+
end
|
11
|
+
|
12
|
+
def accept(visitor)
|
13
|
+
visitor.visit_comment_node(self)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deadfire
|
4
|
+
module FrontEnd
|
5
|
+
class NewlineNode < BaseNode
|
6
|
+
attr_reader :text
|
7
|
+
|
8
|
+
def initialize(text)
|
9
|
+
@text = text
|
10
|
+
end
|
11
|
+
|
12
|
+
def accept(visitor)
|
13
|
+
visitor.visit_newline_node(self)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|