emerald-lang 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|