emerald-lang 1.0.0

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