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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +17 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +18 -0
  9. data/Rakefile +12 -0
  10. data/bin/console +14 -0
  11. data/bin/emerald +7 -0
  12. data/bin/setup +7 -0
  13. data/circle.yml +13 -0
  14. data/config/pre_commit.yml +5 -0
  15. data/emerald-logo.png +0 -0
  16. data/emerald.gemspec +33 -0
  17. data/lib/emerald.rb +75 -0
  18. data/lib/emerald/grammar.rb +90 -0
  19. data/lib/emerald/grammar/emerald.tt +93 -0
  20. data/lib/emerald/grammar/scopes.tt +48 -0
  21. data/lib/emerald/grammar/tokens.tt +57 -0
  22. data/lib/emerald/grammar/variables.tt +10 -0
  23. data/lib/emerald/index.emr +33 -0
  24. data/lib/emerald/nodes/attribute_list.rb +13 -0
  25. data/lib/emerald/nodes/attributes.rb +16 -0
  26. data/lib/emerald/nodes/base_scope_fn.rb +11 -0
  27. data/lib/emerald/nodes/binary_expr.rb +17 -0
  28. data/lib/emerald/nodes/boolean_expr.rb +11 -0
  29. data/lib/emerald/nodes/comment.rb +12 -0
  30. data/lib/emerald/nodes/each.rb +40 -0
  31. data/lib/emerald/nodes/given.rb +16 -0
  32. data/lib/emerald/nodes/line.rb +13 -0
  33. data/lib/emerald/nodes/nested.rb +17 -0
  34. data/lib/emerald/nodes/node.rb +11 -0
  35. data/lib/emerald/nodes/pair_list.rb +23 -0
  36. data/lib/emerald/nodes/root.rb +14 -0
  37. data/lib/emerald/nodes/scope.rb +12 -0
  38. data/lib/emerald/nodes/scope_fn.rb +12 -0
  39. data/lib/emerald/nodes/tag_statement.rb +78 -0
  40. data/lib/emerald/nodes/text_literal.rb +26 -0
  41. data/lib/emerald/nodes/unary_expr.rb +16 -0
  42. data/lib/emerald/nodes/unless.rb +16 -0
  43. data/lib/emerald/nodes/value_list.rb +18 -0
  44. data/lib/emerald/nodes/variable.rb +14 -0
  45. data/lib/emerald/nodes/variable_name.rb +21 -0
  46. data/lib/emerald/nodes/with.rb +14 -0
  47. data/lib/emerald/preprocessor.rb +145 -0
  48. data/lib/emerald/version.rb +3 -0
  49. data/sample.emr +45 -0
  50. 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,10 @@
1
+ # Variable support in Emerald
2
+ grammar Variables
3
+ rule variable
4
+ '|' variable_name '|' <Variable>
5
+ end
6
+
7
+ rule variable_name
8
+ [a-zA-Z0-9_]+ ('.' variable_name)* <VariableName>
9
+ end
10
+ 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,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'treetop'
5
+
6
+ # Base class for scope functions
7
+ class BaseScopeFn < Treetop::Runtime::SyntaxNode
8
+ def to_html(_body, _context)
9
+ raise 'not implemented :('
10
+ end
11
+ 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,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'treetop'
5
+
6
+ # The boolean condition for a logic statement
7
+ class BooleanExpr < Treetop::Runtime::SyntaxNode
8
+ def truthy?(context)
9
+ elements.first.truthy?(context)
10
+ end
11
+ 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,13 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'treetop'
5
+ require_relative 'node'
6
+
7
+ # Either a tag or text
8
+ class Line < Node
9
+ def to_html(context)
10
+ e = elements[0]
11
+ e.to_html(context)
12
+ end
13
+ 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,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'treetop'
5
+
6
+ # Base class for all Emerald syntax nodes
7
+ class Node < Treetop::Runtime::SyntaxNode
8
+ def to_html(_context)
9
+ raise 'not implemented :('
10
+ end
11
+ 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,12 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'treetop'
5
+ require_relative 'node'
6
+
7
+ # Block that modifies the context
8
+ class Scope < Node
9
+ def to_html(context)
10
+ fn.to_html(body, context)
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'treetop'
5
+ require_relative 'base_scope_fn'
6
+
7
+ # Base class for scope functions
8
+ class ScopeFn < BaseScopeFn
9
+ def to_html(body, context)
10
+ elements[0].to_html(body, context)
11
+ end
12
+ 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