emerald-lang 1.0.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 +7 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.rubocop.yml +17 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +18 -0
- data/Rakefile +12 -0
- data/bin/console +14 -0
- data/bin/emerald +7 -0
- data/bin/setup +7 -0
- data/circle.yml +13 -0
- data/config/pre_commit.yml +5 -0
- data/emerald-logo.png +0 -0
- data/emerald.gemspec +33 -0
- data/lib/emerald.rb +75 -0
- data/lib/emerald/grammar.rb +90 -0
- data/lib/emerald/grammar/emerald.tt +93 -0
- data/lib/emerald/grammar/scopes.tt +48 -0
- data/lib/emerald/grammar/tokens.tt +57 -0
- data/lib/emerald/grammar/variables.tt +10 -0
- data/lib/emerald/index.emr +33 -0
- data/lib/emerald/nodes/attribute_list.rb +13 -0
- data/lib/emerald/nodes/attributes.rb +16 -0
- data/lib/emerald/nodes/base_scope_fn.rb +11 -0
- data/lib/emerald/nodes/binary_expr.rb +17 -0
- data/lib/emerald/nodes/boolean_expr.rb +11 -0
- data/lib/emerald/nodes/comment.rb +12 -0
- data/lib/emerald/nodes/each.rb +40 -0
- data/lib/emerald/nodes/given.rb +16 -0
- data/lib/emerald/nodes/line.rb +13 -0
- data/lib/emerald/nodes/nested.rb +17 -0
- data/lib/emerald/nodes/node.rb +11 -0
- data/lib/emerald/nodes/pair_list.rb +23 -0
- data/lib/emerald/nodes/root.rb +14 -0
- data/lib/emerald/nodes/scope.rb +12 -0
- data/lib/emerald/nodes/scope_fn.rb +12 -0
- data/lib/emerald/nodes/tag_statement.rb +78 -0
- data/lib/emerald/nodes/text_literal.rb +26 -0
- data/lib/emerald/nodes/unary_expr.rb +16 -0
- data/lib/emerald/nodes/unless.rb +16 -0
- data/lib/emerald/nodes/value_list.rb +18 -0
- data/lib/emerald/nodes/variable.rb +14 -0
- data/lib/emerald/nodes/variable_name.rb +21 -0
- data/lib/emerald/nodes/with.rb +14 -0
- data/lib/emerald/preprocessor.rb +145 -0
- data/lib/emerald/version.rb +3 -0
- data/sample.emr +45 -0
- metadata +245 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
#
|
2
|
+
# Context free grammar of tokens (terminal
|
3
|
+
# nodes) for the Emerald language.
|
4
|
+
#
|
5
|
+
grammar Scopes
|
6
|
+
include Variables
|
7
|
+
|
8
|
+
rule scope_fn
|
9
|
+
(given / unless / each / with) "\n"
|
10
|
+
<ScopeFn>
|
11
|
+
end
|
12
|
+
|
13
|
+
rule given
|
14
|
+
"given" space* boolean_expr <Given>
|
15
|
+
end
|
16
|
+
|
17
|
+
rule unless
|
18
|
+
"unless" space* boolean_expr <Unless>
|
19
|
+
end
|
20
|
+
|
21
|
+
rule with
|
22
|
+
"with" space* variable_name <With>
|
23
|
+
end
|
24
|
+
|
25
|
+
rule each
|
26
|
+
"each" space* collection:variable_name
|
27
|
+
space* "as" space* val_name:variable_name
|
28
|
+
indexed:(',' space* key_name:variable_name)?
|
29
|
+
<Each>
|
30
|
+
end
|
31
|
+
|
32
|
+
rule boolean_expr
|
33
|
+
binary_expr / unary_expr
|
34
|
+
end
|
35
|
+
|
36
|
+
rule binary_expr
|
37
|
+
lhs:unary_expr
|
38
|
+
space+ op:("and" / "or") space*
|
39
|
+
rhs:boolean_expr
|
40
|
+
<BinaryExpr>
|
41
|
+
end
|
42
|
+
|
43
|
+
rule unary_expr
|
44
|
+
negated:("not" space+)?
|
45
|
+
(val:variable_name / '(' space* val:boolean_expr space* ')')
|
46
|
+
<UnaryExpr>
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
#
|
2
|
+
# Context free grammar of tokens (terminal
|
3
|
+
# nodes) for the Emerald language.
|
4
|
+
#
|
5
|
+
grammar Tokens
|
6
|
+
rule base_keyword
|
7
|
+
'images' / 'metas' / 'scripts' / 'styles'
|
8
|
+
end
|
9
|
+
|
10
|
+
rule special_keyword
|
11
|
+
'images' / 'scripts' / 'styles'
|
12
|
+
end
|
13
|
+
|
14
|
+
rule tag
|
15
|
+
[a-z] [a-z1-9]*
|
16
|
+
end
|
17
|
+
|
18
|
+
rule attr
|
19
|
+
[a-z\-_]+
|
20
|
+
end
|
21
|
+
|
22
|
+
rule event
|
23
|
+
'onabort' / 'onclick' / 'onhover' / 'onbeforeprint' / 'onbeforeunload'
|
24
|
+
end
|
25
|
+
|
26
|
+
rule equals
|
27
|
+
'='
|
28
|
+
end
|
29
|
+
|
30
|
+
rule comma
|
31
|
+
','
|
32
|
+
end
|
33
|
+
|
34
|
+
rule lparen
|
35
|
+
'('
|
36
|
+
end
|
37
|
+
|
38
|
+
rule rparen
|
39
|
+
')'
|
40
|
+
end
|
41
|
+
|
42
|
+
rule lbrace
|
43
|
+
"{"
|
44
|
+
end
|
45
|
+
|
46
|
+
rule rbrace
|
47
|
+
"}"
|
48
|
+
end
|
49
|
+
|
50
|
+
rule space
|
51
|
+
' '
|
52
|
+
end
|
53
|
+
|
54
|
+
rule newline
|
55
|
+
[\n] <Node>
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
html
|
2
|
+
head
|
3
|
+
styles
|
4
|
+
"public/css/main.emr"
|
5
|
+
|
6
|
+
scripts
|
7
|
+
"public/js/main.js"
|
8
|
+
|
9
|
+
body
|
10
|
+
nav
|
11
|
+
a Emerald
|
12
|
+
a Docs
|
13
|
+
a Github
|
14
|
+
|
15
|
+
header
|
16
|
+
h1 Emerald!
|
17
|
+
h2 A templating engine for multiple languages.
|
18
|
+
|
19
|
+
main
|
20
|
+
section
|
21
|
+
h3 About
|
22
|
+
p Emerald is a templating engine designed for use in multiple languages.
|
23
|
+
p Emerald may be used as a templating engine for node, python, ruby, or go web applications.
|
24
|
+
h1 ->
|
25
|
+
this is some literal text, not an actual tag
|
26
|
+
with an indent
|
27
|
+
and multiple lines
|
28
|
+
and an e$cape character
|
29
|
+
p and a tag
|
30
|
+
p this is an actual tag now
|
31
|
+
|
32
|
+
footer
|
33
|
+
p Copyright Emerald Language 2016
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'treetop'
|
5
|
+
require_relative 'node'
|
6
|
+
|
7
|
+
# elements[2] is the attributes
|
8
|
+
# call to_html() on them.
|
9
|
+
class AttributeList < Node
|
10
|
+
def to_html(context)
|
11
|
+
elements[4].to_html(context)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'treetop'
|
5
|
+
require_relative 'node'
|
6
|
+
|
7
|
+
# The attributes hash for an element
|
8
|
+
class Attributes < Node
|
9
|
+
def to_html(context)
|
10
|
+
output = ''
|
11
|
+
elements.each do |e|
|
12
|
+
output += " #{e.elements[0].text_value}=\"#{e.elements[2].to_html(context)}\""
|
13
|
+
end
|
14
|
+
output
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'treetop'
|
5
|
+
require_relative 'boolean_expr'
|
6
|
+
|
7
|
+
# A boolean expression with two children and an operator
|
8
|
+
class BinaryExpr < BooleanExpr
|
9
|
+
def truthy?(context)
|
10
|
+
case op.text_value
|
11
|
+
when 'and'
|
12
|
+
lhs.truthy?(context) && rhs.truthy?(context)
|
13
|
+
when 'or'
|
14
|
+
lhs.truthy?(context) || rhs.truthy?(context)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'treetop'
|
5
|
+
require_relative 'node'
|
6
|
+
|
7
|
+
# Prints out the value of the comment's text
|
8
|
+
class Comment < Node
|
9
|
+
def to_html(context)
|
10
|
+
"<!-- #{text_content.to_html(context)} -->\n"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'treetop'
|
5
|
+
require_relative 'base_scope_fn'
|
6
|
+
|
7
|
+
# Function to map over elements in the context
|
8
|
+
class Each < BaseScopeFn
|
9
|
+
def to_html(body, context)
|
10
|
+
vars = collection.content(context)
|
11
|
+
key_name = indexed.text_value.length.positive? ? indexed.key_name : nil
|
12
|
+
|
13
|
+
# TODO: clean up somehow
|
14
|
+
if vars.is_a? Hash
|
15
|
+
vars
|
16
|
+
.map do |var, key|
|
17
|
+
new_ctx = context.clone
|
18
|
+
new_ctx[val_name.text_value] = var
|
19
|
+
new_ctx[key_name.text_value] = key if key_name
|
20
|
+
|
21
|
+
body.to_html(new_ctx)
|
22
|
+
end
|
23
|
+
.join("\n")
|
24
|
+
elsif vars.is_a? Array
|
25
|
+
vars
|
26
|
+
.map.with_index do |var, idx|
|
27
|
+
new_ctx = context.clone
|
28
|
+
new_ctx[val_name.text_value] = var
|
29
|
+
new_ctx[key_name.text_value] = idx if key_name
|
30
|
+
|
31
|
+
body.to_html(new_ctx)
|
32
|
+
end
|
33
|
+
.join("\n")
|
34
|
+
elsif vars.nil?
|
35
|
+
''
|
36
|
+
else
|
37
|
+
raise 'bad variable type :('
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'treetop'
|
5
|
+
require_relative 'base_scope_fn'
|
6
|
+
|
7
|
+
# Renders if a condition is met
|
8
|
+
class Given < BaseScopeFn
|
9
|
+
def to_html(body, context)
|
10
|
+
if boolean_expr.truthy?(context)
|
11
|
+
body.to_html(context)
|
12
|
+
else
|
13
|
+
''
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'treetop'
|
5
|
+
require_relative 'node'
|
6
|
+
|
7
|
+
#
|
8
|
+
# Appends compiled html for nested rule to the output.
|
9
|
+
# elements[0] is the tag statement, elements[4] is any nested html.
|
10
|
+
#
|
11
|
+
class Nested < Node
|
12
|
+
def to_html(context)
|
13
|
+
elements[0].opening_tag(context) +
|
14
|
+
elements[4].to_html(context) +
|
15
|
+
elements[0].closing_tag(context)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'treetop'
|
5
|
+
require_relative 'node'
|
6
|
+
|
7
|
+
# Base rule for lists of images, metas, styles, and scripts
|
8
|
+
class PairList < Node
|
9
|
+
def to_html(context)
|
10
|
+
list_items.elements.map do |e|
|
11
|
+
attrs = e.pairs.elements.map do |j|
|
12
|
+
"#{j.attr.text_value}=\"#{j.literal.to_html(context)}\""
|
13
|
+
end.join(' ')
|
14
|
+
|
15
|
+
case keyword.text_value
|
16
|
+
when 'images' then "<img #{attrs}/>"
|
17
|
+
when 'metas' then "<meta #{attrs}>"
|
18
|
+
when 'styles' then "<link #{attrs}/>"
|
19
|
+
when 'scripts' then "<script #{attrs}></script>"
|
20
|
+
end
|
21
|
+
end.join("\n")
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'treetop'
|
5
|
+
require_relative 'node'
|
6
|
+
|
7
|
+
# A base piece of an Emerald file
|
8
|
+
class Root < Node
|
9
|
+
def to_html(context)
|
10
|
+
elements
|
11
|
+
.map { |e| e.to_html(context) }
|
12
|
+
.join("\n")
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'treetop'
|
5
|
+
require_relative 'node'
|
6
|
+
|
7
|
+
# A tag
|
8
|
+
class TagStatement < Node
|
9
|
+
# http://w3c.github.io/html/syntax.html#void-elements
|
10
|
+
VOID_TAGS = (
|
11
|
+
"area base br col embed hr img input link " +
|
12
|
+
"menuitem meta param source track wbr"
|
13
|
+
).split(/\s+/)
|
14
|
+
|
15
|
+
def to_html(context)
|
16
|
+
if void_tag?
|
17
|
+
opening_tag(context)
|
18
|
+
else
|
19
|
+
opening_tag(context) +
|
20
|
+
(
|
21
|
+
if !body.empty?
|
22
|
+
body.to_html(context)
|
23
|
+
else
|
24
|
+
''
|
25
|
+
end
|
26
|
+
) +
|
27
|
+
closing_tag(context)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def class_attribute
|
32
|
+
unless classes.empty?
|
33
|
+
class_names = classes
|
34
|
+
.elements
|
35
|
+
.map{ |c| c.name.text_value }
|
36
|
+
.join(' ')
|
37
|
+
|
38
|
+
" class=\"#{class_names}\""
|
39
|
+
else
|
40
|
+
''
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def id_attribute
|
45
|
+
unless identifier.empty?
|
46
|
+
" id=\"#{identifier.name.text_value}\""
|
47
|
+
else
|
48
|
+
''
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def opening_tag(context)
|
53
|
+
"<#{tag.text_value}" +
|
54
|
+
id_attribute +
|
55
|
+
class_attribute +
|
56
|
+
(
|
57
|
+
if !attributes.empty?
|
58
|
+
' ' + attributes.to_html(context)
|
59
|
+
else
|
60
|
+
''
|
61
|
+
end
|
62
|
+
) + (
|
63
|
+
if void_tag?
|
64
|
+
' />'
|
65
|
+
else
|
66
|
+
'>'
|
67
|
+
end
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
def void_tag?
|
72
|
+
VOID_TAGS.include? tag.text_value
|
73
|
+
end
|
74
|
+
|
75
|
+
def closing_tag(_context)
|
76
|
+
"</#{tag.text_value}>"
|
77
|
+
end
|
78
|
+
end
|