template-ruby 0.2.4 → 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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/Gemfile +2 -1
  4. data/Gemfile.lock +6 -1
  5. data/bin/code +51 -0
  6. data/bin/template +6 -2
  7. data/code-ruby.gemspec +22 -0
  8. data/docs/euler/4.template +0 -1
  9. data/docs/fibonnaci.template +14 -0
  10. data/docs/precedence.template +6 -31
  11. data/lib/code/node/code.rb +7 -1
  12. data/lib/code/node/list.rb +7 -7
  13. data/lib/code/node/name.rb +6 -1
  14. data/lib/code/node/string.rb +11 -6
  15. data/lib/code/node/string_characters.rb +13 -0
  16. data/lib/code/node/string_component.rb +23 -0
  17. data/lib/code/node/string_interpolation.rb +13 -0
  18. data/lib/code/object/argument.rb +1 -1
  19. data/lib/code/object/decimal.rb +22 -1
  20. data/lib/code/object/dictionnary.rb +24 -0
  21. data/lib/code/object/function.rb +2 -1
  22. data/lib/code/object/integer.rb +18 -20
  23. data/lib/code/object/list.rb +4 -0
  24. data/lib/code/object/string.rb +2 -2
  25. data/lib/code/object.rb +7 -0
  26. data/lib/code/parser/and_operator.rb +4 -4
  27. data/lib/code/parser/call.rb +3 -3
  28. data/lib/code/parser/code.rb +3 -4
  29. data/lib/code/parser/defined.rb +2 -2
  30. data/lib/code/parser/dictionnary.rb +1 -1
  31. data/lib/code/parser/equality.rb +4 -4
  32. data/lib/code/parser/function.rb +3 -2
  33. data/lib/code/parser/group.rb +1 -1
  34. data/lib/code/parser/if.rb +3 -3
  35. data/lib/code/parser/list.rb +1 -1
  36. data/lib/code/parser/not_keyword.rb +2 -2
  37. data/lib/code/parser/statement.rb +1 -1
  38. data/lib/code/parser/string.rb +19 -4
  39. data/lib/code/parser/ternary.rb +3 -3
  40. data/lib/code-ruby.rb +12 -0
  41. data/lib/code.rb +10 -9
  42. data/lib/template/version.rb +1 -1
  43. data/lib/template-ruby.rb +3 -1
  44. data/lib/template.rb +14 -8
  45. data/spec/code/error/type_error_spec.rb +0 -2
  46. data/spec/code/parser/dictionnary_spec.rb +5 -32
  47. data/spec/code/parser/string_spec.rb +15 -16
  48. data/spec/code_spec.rb +12 -0
  49. data/spec/template_spec.rb +6 -0
  50. metadata +10 -4
  51. data/TODO.md +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 477a0d0d70a09f5118391d5288031811436a520c8faac5794da2ff3dcee996ad
4
- data.tar.gz: e3e24d79c50c1058f619b63d09154b823acc3f0cd7b3c7ac75029c86df9e1bee
3
+ metadata.gz: 0d53d39478e69a97de7680a29e4a47634804ed0bb82badf8ec255ef70ca32968
4
+ data.tar.gz: 431593c814bf56bcecc3f28e6564b6e3799e674183fdf5b298b4e913b8604d43
5
5
  SHA512:
6
- metadata.gz: ca7d9deac26c98d399d190a11192b8448a1c2bec8119a1dbfae6cca905554fd25d9bdfd91ffdbd2ad8ac73f743c07d30d5f97fab81d5d7d508ac7c8a09832217
7
- data.tar.gz: d6da812be0c57c0c943ef44a46a7e0df3dd45f687251305c9cba5ada1d1f997dd57723405b129e5e2458139c9f2ef3baec31d9b66f4a850d01b5b8b1320cca37
6
+ metadata.gz: b11d8a9daf795dfd5c90eaed1246f85951b76c417e7b4775fe55997b88255a290bd17ed0427c7d7c4d927b20b537cc5353e8a91f76bc2d442527415b209a812b
7
+ data.tar.gz: 8266b19ec1b8d73a35490264ec593ef8c6609229335d99c30bc196b7719455791f240f08ea56890a40d4f0e0a6dea1973a4043ff40f3308f0b96d4ef07e61f28
data/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## 0.3.0 / ?
9
+
10
+ - `bin/template` accepts `--timeout` (or `-t`) parameter
11
+ - Adds `bin/code` with same options as `bin/template`
12
+ - Prevent loose syntax like `{ a: }`, `[1,,,]` and `()`
13
+ - Change precedence of defined? (to allow `defined?(name) ? name : nothing`)
14
+ - Updates parsers to allow `while false end == nothing`
15
+ - String interpolations like `"1 + 1 = {1 + 1}"`
16
+ - `context(:name)` to get a function without calling it for instance
17
+ - `.to_string` on all objects
18
+ - `1 + "a"` and `"a" + 1.0` for instance now convert to strings
19
+ - `Dictionnary#each` e.g. `{ a: 1 }.each { |k, v| print(k) }`
20
+ - Fix context duplication issue that was preventing implementation of recursive
21
+ functions like Fibonacci
22
+
8
23
  ## 0.2.4 / 2022-08-02
9
24
 
10
25
  - Add method `String#*`, e.g. `{"Dorian " \* 2}" -> "Dorian Dorian "
data/Gemfile CHANGED
@@ -1,3 +1,4 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gemspec
3
+ gemspec name: "template-ruby"
4
+ gemspec name: "code-ruby"
data/Gemfile.lock CHANGED
@@ -1,7 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- template-ruby (0.2.3)
4
+ code-ruby (0.2.4)
5
+ activesupport (~> 7)
6
+ parslet (~> 2)
7
+ zeitwerk (~> 2.6)
8
+ template-ruby (0.2.4)
5
9
  activesupport (~> 7)
6
10
  parslet (~> 2)
7
11
  zeitwerk (~> 2.6)
@@ -62,6 +66,7 @@ PLATFORMS
62
66
  arm64-darwin-21
63
67
 
64
68
  DEPENDENCIES
69
+ code-ruby!
65
70
  prettier (~> 3)
66
71
  rspec (~> 3)
67
72
  template-ruby!
data/bin/code ADDED
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "optparse"
4
+ require_relative "../lib/template-ruby"
5
+
6
+ options = { timeout: 0 }
7
+
8
+ OptionParser.new do |opts|
9
+ opts.banner = "Usage: bin/code [options]"
10
+
11
+ opts.on(
12
+ "-i INPUT",
13
+ "--input=INPUT",
14
+ "Input in the code language (String or File)"
15
+ ) do |input|
16
+ if File.exists?(input)
17
+ input = File.read(input)
18
+ end
19
+
20
+ options[:input] = input
21
+ end
22
+
23
+ opts.on(
24
+ "-c CONTEXT",
25
+ "--context=CONTEXT",
26
+ "Context in the code language (String or File)"
27
+ ) do |context|
28
+ if File.exists?(context)
29
+ context = File.read(context)
30
+ end
31
+
32
+ options[:context] = context
33
+ end
34
+
35
+ opts.on("-p", "--parse", "Get parser results for input") do |parse|
36
+ options[:parse] = parse
37
+ end
38
+
39
+ opts.on("-t TIMEOUT", "--timeout=TIMEOUT", "Set timeout in seconds") do |timeout|
40
+ options[:timeout] = timeout.to_f
41
+ end
42
+ end.parse!
43
+
44
+ input = options.fetch(:input, "")
45
+ context = options.fetch(:context, "")
46
+
47
+ if options[:parse]
48
+ pp ::Code::Parser::Code.new.parse(input)
49
+ else
50
+ print Code.evaluate(input, context, io: $stdout, timeout: options[:timeout]).to_s
51
+ end
data/bin/template CHANGED
@@ -3,7 +3,7 @@
3
3
  require "optparse"
4
4
  require_relative "../lib/template-ruby"
5
5
 
6
- options = {}
6
+ options = { timeout: 0 }
7
7
 
8
8
  OptionParser.new do |opts|
9
9
  opts.banner = "Usage: template [options]"
@@ -27,6 +27,10 @@ OptionParser.new do |opts|
27
27
  opts.on("-p", "--parse", "Get parser results for input") do |parse|
28
28
  options[:parse] = parse
29
29
  end
30
+
31
+ opts.on("-t TIMEOUT", "--timeout=TIMEOUT", "Set timeout in seconds") do |timeout|
32
+ options[:timeout] = timeout.to_f
33
+ end
30
34
  end.parse!
31
35
 
32
36
  input = options.fetch(:input, "")
@@ -35,5 +39,5 @@ context = options.fetch(:context, "")
35
39
  if options[:parse]
36
40
  pp ::Template::Parser::Template.new.parse(input)
37
41
  else
38
- Template.render(input, context, io: $stdout)
42
+ Template.render(input, context, io: $stdout, timeout: options[:timeout])
39
43
  end
data/code-ruby.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ require_relative "lib/template/version"
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "code-ruby"
5
+ s.version = ::Template::Version
6
+ s.summary = "A programming language"
7
+ s.description = 'A programming language, like Code.evaluate("1 + 1") # => 2'
8
+ s.authors = ["Dorian Marié"]
9
+ s.email = "dorian@dorianmarie.fr"
10
+ s.files = `git ls-files`.split($/)
11
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
12
+ s.require_paths = ["lib"]
13
+ s.homepage = "https://github.com/dorianmariefr/template-ruby"
14
+ s.license = "MIT"
15
+
16
+ s.add_dependency "activesupport", "~> 7"
17
+ s.add_dependency "parslet", "~> 2"
18
+ s.add_dependency "zeitwerk", "~> 2.6"
19
+
20
+ s.add_development_dependency "prettier", "~> 3"
21
+ s.add_development_dependency "rspec", "~> 3"
22
+ end
@@ -3,7 +3,6 @@
3
3
  min = 1
4
4
  max = 999
5
5
 
6
-
7
6
  (min..max).map do |i|
8
7
  ((min..max).to_list.reverse.detect do |j|
9
8
  (i * j).to_string.reverse == (i * j).to_string
@@ -0,0 +1,14 @@
1
+ {
2
+ fibonnaci = (n) => {
3
+ if n < 2
4
+ n
5
+ else
6
+ fibonnaci(n - 1) + fibonnaci(n - 2)
7
+ end
8
+ }
9
+
10
+ (0..20).each do |i|
11
+ puts("fibonnaci({i}) = {fibonnaci(i)}")
12
+ end
13
+
14
+ nothing
@@ -2,58 +2,33 @@
2
2
  - {a = "Go" a += "od" a}
3
3
  - Statement
4
4
  - {a = "Good"}
5
- - {
6
- a = 0
7
- while a < 10
8
- a += 1
9
- "Good"
10
- end
11
- }
5
+ - Equality
6
+ - {1 == 1 ? "Good"}
12
7
  - While
13
- - {
14
- a = 0
15
- until a > 10
16
- a += 1
17
- "Good"
18
- end
19
- }
20
- - {
21
- a = 0
22
- while
23
- if a > 10
24
- false
25
- else
26
- true
27
- end
28
- a += 1
29
- "Good"
30
- end
31
- }
8
+ - {a = 0
9
+ while a < 10 a += 1 "Good" end}
32
10
  - If
33
11
  - {if true "Good" end}
34
- - {if true if false "Bad" else "Good" end}
35
12
  - IfModifier
36
13
  - {"Good" if true}
37
14
  - OrKeyword
38
15
  - {false or "Good"}
39
16
  - NotKeyword
40
17
  - {not false and "Good"}
41
- - Defined
42
- - {a = 1 "Good" if defined?(a)}
43
18
  - Equal
44
19
  - {a = "Good"}
45
20
  - Rescue
46
21
  - {0 > "String" rescue "Good"}
47
22
  - Ternary
48
23
  - {nothing ? "Bad" : "Good"}
24
+ - Defined
25
+ - {a = 1 "Good" if defined?(a)}
49
26
  - Range
50
27
  - {("Good".."Bad").first}
51
28
  - OrOperator
52
29
  - {false || "Good"}
53
30
  - AndOperator
54
31
  - {"Bad" && "Good"}
55
- - Equality
56
- - {1 == 1 ? "Good"}
57
32
  - GreaterThan
58
33
  - {2 > 1 ? "Good"}
59
34
  - BitwiseOr
@@ -9,7 +9,13 @@ class Code
9
9
  end
10
10
 
11
11
  def evaluate(**args)
12
- @statements.map { |statement| statement.evaluate(**args) }.last
12
+ last = ::Code::Object::Nothing.new
13
+
14
+ @statements.each do |statement|
15
+ last = statement.evaluate(**args)
16
+ end
17
+
18
+ last
13
19
  end
14
20
  end
15
21
  end
@@ -2,13 +2,13 @@ class Code
2
2
  class Node
3
3
  class List < Node
4
4
  def initialize(codes)
5
- @codes =
6
- codes
7
- .map do |code|
8
- code.fetch(:code).presence &&
9
- ::Code::Node::Code.new(code.fetch(:code))
10
- end
11
- .compact
5
+ if codes.to_s.blank?
6
+ @codes = []
7
+ else
8
+ @codes = codes.map do |code|
9
+ ::Code::Node::Code.new(code.fetch(:code))
10
+ end
11
+ end
12
12
  end
13
13
 
14
14
  def evaluate(**args)
@@ -33,8 +33,13 @@ class Code
33
33
  end
34
34
  elsif name == "puts"
35
35
  arguments.each { |argument| io.puts argument.value }
36
-
37
36
  ::Code::Object::Nothing.new
37
+ elsif name == "print"
38
+ arguments.each { |argument| io.print argument.value }
39
+ ::Code::Object::Nothing.new
40
+ elsif name == "context"
41
+ return ::Code::Object::Nothing.new if arguments.size != 1
42
+ context[arguments.first&.value] || ::Code::Object::Nothing.new
38
43
  else
39
44
  raise ::Code::Error::Undefined.new("#{name} undefined")
40
45
  end
@@ -2,16 +2,21 @@ class Code
2
2
  class Node
3
3
  class String < Node
4
4
  def initialize(string)
5
- @string = string
5
+ if string.to_s.blank?
6
+ @string = []
7
+ elsif string.is_a?(Array)
8
+ @string = string.map do |component|
9
+ ::Code::Node::StringComponent.new(component)
10
+ end
11
+ else
12
+ @string = [::Code::Node::StringCharacters.new(string)]
13
+ end
6
14
  end
7
15
 
8
16
  def evaluate(**args)
9
- ::Code::Object::String.new(string.to_s)
17
+ string = @string.map { |component| component.evaluate(**args) }.map(&:to_s).join
18
+ ::Code::Object::String.new(string)
10
19
  end
11
-
12
- private
13
-
14
- attr_reader :string
15
20
  end
16
21
  end
17
22
  end
@@ -0,0 +1,13 @@
1
+ class Code
2
+ class Node
3
+ class StringCharacters < Node
4
+ def initialize(characters)
5
+ @characters = characters
6
+ end
7
+
8
+ def evaluate(**args)
9
+ ::Code::Object::String.new(@characters.to_s)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ class Code
2
+ class Node
3
+ class StringComponent < Node
4
+ def initialize(component)
5
+ if component.key?(:characters)
6
+ @component = ::Code::Node::StringCharacters.new(
7
+ component.fetch(:characters)
8
+ )
9
+ elsif component.key?(:interpolation)
10
+ @component = ::Code::Node::StringInterpolation.new(
11
+ component.fetch(:interpolation)
12
+ )
13
+ else
14
+ raise NotImplementedError.new(component.inspect)
15
+ end
16
+ end
17
+
18
+ def evaluate(**args)
19
+ @component.evaluate(**args)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ class Code
2
+ class Node
3
+ class StringInterpolation < Node
4
+ def initialize(interpolation)
5
+ @interpolation = ::Code::Node::Code.new(interpolation)
6
+ end
7
+
8
+ def evaluate(**args)
9
+ @interpolation.evaluate(**args)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -30,7 +30,7 @@ class Code
30
30
  end
31
31
 
32
32
  def to_s
33
- "argument"
33
+ "<Argument #{value.inspect}>"
34
34
  end
35
35
 
36
36
  def inspect
@@ -19,10 +19,14 @@ class Code
19
19
  operator = args.fetch(:operator, nil)
20
20
  arguments = args.fetch(:arguments, [])
21
21
 
22
- if %w[% - + / * **].detect { |o| operator == o }
22
+ if %w[% - / * **].detect { |o| operator == o }
23
23
  number_operation(operator.to_sym, arguments)
24
24
  elsif %w[< <= > >=].detect { |o| operator == o }
25
25
  comparaison(operator.to_sym, arguments)
26
+ elsif %w[<< >> & | ^].detect { |o| operator == o }
27
+ integer_operation(operator.to_sym, arguments)
28
+ elsif operator == "+"
29
+ plus(arguments)
26
30
  else
27
31
  super
28
32
  end
@@ -44,11 +48,28 @@ class Code
44
48
  ::Code::Object::Decimal.new(raw.public_send(operator, other.raw))
45
49
  end
46
50
 
51
+ def integer_operation(operator, arguments)
52
+ sig(arguments, ::Code::Object::Number)
53
+ other = arguments.first.value
54
+ ::Code::Object::Integer.new(raw.to_i.public_send(operator, other.raw.to_i))
55
+ end
56
+
47
57
  def comparaison(operator, arguments)
48
58
  sig(arguments, ::Code::Object::Number)
49
59
  other = arguments.first.value
50
60
  ::Code::Object::Boolean.new(raw.public_send(operator, other.raw))
51
61
  end
62
+
63
+ def plus(arguments)
64
+ sig(arguments, ::Code::Object)
65
+ other = arguments.first.value
66
+
67
+ if other.is_a?(::Code::Object::Number)
68
+ ::Code::Object::Decimal.new(raw + other.raw)
69
+ else
70
+ ::Code::Object::String.new(to_s + other.to_s)
71
+ end
72
+ end
52
73
  end
53
74
  end
54
75
  end
@@ -10,9 +10,13 @@ class Code
10
10
  def call(**args)
11
11
  operator = args.fetch(:operator, nil)
12
12
  arguments = args.fetch(:arguments, [])
13
+ context = args.fetch(:context)
14
+ io = args.fetch(:io)
13
15
 
14
16
  if operator == "values"
15
17
  values(arguments)
18
+ elsif operator == "each"
19
+ each(arguments, context: context, io: io)
16
20
  elsif key?(operator)
17
21
  fetch(operator)
18
22
  else
@@ -36,6 +40,10 @@ class Code
36
40
  raw.key?(key)
37
41
  end
38
42
 
43
+ def deep_dup
44
+ ::Code::Object::Dictionnary.new(raw.deep_dup)
45
+ end
46
+
39
47
  def to_s
40
48
  "{#{raw.map { |key, value| "#{key.inspect} => #{value.inspect}" }.join(", ")}}"
41
49
  end
@@ -50,6 +58,22 @@ class Code
50
58
  sig(arguments)
51
59
  ::Code::Object::List.new(raw.values)
52
60
  end
61
+
62
+ def each(arguments, context:, io:)
63
+ sig(arguments, ::Code::Object::Function)
64
+ argument = arguments.first.value
65
+ raw.each do |key, value|
66
+ argument.call(
67
+ arguments: [
68
+ ::Code::Object::Argument.new(key),
69
+ ::Code::Object::Argument.new(value)
70
+ ],
71
+ context: context,
72
+ io: io,
73
+ )
74
+ end
75
+ self
76
+ end
53
77
  end
54
78
  end
55
79
  end
@@ -32,7 +32,8 @@ class Code
32
32
  attr_reader :arguments, :body
33
33
 
34
34
  def call_function(args:, context:, io:)
35
- new_context = context.dup
35
+ new_context = context.deep_dup
36
+
36
37
  arguments.each.with_index do |argument, index|
37
38
  if argument.regular?
38
39
  if argument.splat?
@@ -21,13 +21,13 @@ class Code
21
21
 
22
22
  if operator == "even?"
23
23
  even?(arguments)
24
- elsif operator == "to_string"
25
- code_to_s(arguments)
26
24
  elsif operator == "*"
27
25
  multiplication(arguments)
28
26
  elsif operator == "/"
29
27
  division(arguments)
30
- elsif %w[% - + **].detect { |o| operator == o }
28
+ elsif operator == "+"
29
+ plus(arguments)
30
+ elsif %w[% - **].detect { |o| operator == o }
31
31
  integer_or_decimal_operation(operator.to_sym, arguments)
32
32
  elsif %w[> >= < <=].detect { |o| operator == o }
33
33
  comparaison(operator.to_sym, arguments)
@@ -38,16 +38,6 @@ class Code
38
38
  end
39
39
  end
40
40
 
41
- def +(other)
42
- if other.is_a?(::Code::Object::Integer)
43
- ::Code::Object::Integer.new(raw + other.raw)
44
- elsif other.is_a?(::Code::Object::Decimal)
45
- ::Code::Object::Decimal.new(raw + other.raw)
46
- else
47
- raise ::Code::Error::TypeError
48
- end
49
- end
50
-
51
41
  def succ
52
42
  ::Code::Object::Integer.new(raw + 1)
53
43
  end
@@ -67,11 +57,6 @@ class Code
67
57
  ::Code::Object::Boolean.new(raw.even?)
68
58
  end
69
59
 
70
- def code_to_s(arguments)
71
- sig(arguments)
72
- ::Code::Object::String.new(raw.to_s)
73
- end
74
-
75
60
  def multiplication(arguments)
76
61
  sig(arguments, [::Code::Object::Number, ::Code::Object::String])
77
62
  other = arguments.first.value
@@ -84,6 +69,19 @@ class Code
84
69
  end
85
70
  end
86
71
 
72
+ def plus(arguments)
73
+ sig(arguments, ::Code::Object)
74
+ other = arguments.first.value
75
+
76
+ if other.is_a?(::Code::Object::Integer)
77
+ ::Code::Object::Integer.new(raw + other.raw)
78
+ elsif other.is_a?(::Code::Object::Decimal)
79
+ ::Code::Object::Decimal.new(raw + other.raw)
80
+ else
81
+ ::Code::Object::String.new(to_s + other.to_s)
82
+ end
83
+ end
84
+
87
85
  def division(arguments)
88
86
  sig(arguments, ::Code::Object::Number)
89
87
  other = arguments.first.value
@@ -101,9 +99,9 @@ class Code
101
99
  end
102
100
 
103
101
  def integer_operation(operator, arguments)
104
- sig(arguments, ::Code::Object::Integer)
102
+ sig(arguments, ::Code::Object::Number)
105
103
  other = arguments.first.value
106
- ::Code::Object::Integer.new(raw.public_send(operator, other.raw))
104
+ ::Code::Object::Integer.new(raw.to_i.public_send(operator, other.raw.to_i))
107
105
  end
108
106
 
109
107
  def comparaison(operator, arguments)
@@ -63,6 +63,10 @@ class Code
63
63
  )
64
64
  end
65
65
 
66
+ def deep_dup
67
+ ::Code::Object::List.new(raw.deep_dup)
68
+ end
69
+
66
70
  def to_s
67
71
  "[#{raw.map(&:inspect).join(", ")}]"
68
72
  end
@@ -48,9 +48,9 @@ class Code
48
48
  end
49
49
 
50
50
  def plus(arguments)
51
- sig(arguments, ::Code::Object::String)
51
+ sig(arguments, ::Code::Object)
52
52
  other = arguments.first.value
53
- ::Code::Object::String.new(raw + other.raw)
53
+ ::Code::Object::String.new(raw + other.to_s)
54
54
  end
55
55
 
56
56
  def multiplication(arguments)
data/lib/code/object.rb CHANGED
@@ -13,6 +13,8 @@ class Code
13
13
  and_operator(arguments)
14
14
  elsif operator == "||"
15
15
  or_operator(arguments)
16
+ elsif operator == "to_string"
17
+ to_string(arguments)
16
18
  else
17
19
  raise ::Code::Error::Undefined.new(
18
20
  "#{operator} not defined on #{inspect}",
@@ -126,5 +128,10 @@ class Code
126
128
  other = arguments.first.value
127
129
  truthy? ? self : other
128
130
  end
131
+
132
+ def to_string(arguments)
133
+ sig(arguments)
134
+ ::Code::Object::String.new(to_s)
135
+ end
129
136
  end
130
137
  end
@@ -1,7 +1,7 @@
1
1
  class Code
2
2
  class Parser
3
3
  class AndOperator < Parslet::Parser
4
- rule(:equality) { ::Code::Parser::Equality.new }
4
+ rule(:greater_than) { ::Code::Parser::GreaterThan.new }
5
5
 
6
6
  rule(:ampersand) { str("&") }
7
7
 
@@ -14,12 +14,12 @@ class Code
14
14
 
15
15
  rule(:and_operator) do
16
16
  (
17
- equality.as(:first) >>
17
+ greater_than.as(:first) >>
18
18
  (
19
19
  whitespace? >> operator.as(:operator) >> whitespace? >>
20
- equality.as(:statement)
20
+ greater_than.as(:statement)
21
21
  ).repeat(1).as(:rest)
22
- ).as(:and_operator) | equality
22
+ ).as(:and_operator) | greater_than
23
23
  end
24
24
 
25
25
  root(:and_operator)
@@ -2,7 +2,7 @@ class Code
2
2
  class Parser
3
3
  class Call < Parslet::Parser
4
4
  rule(:dictionnary) { ::Code::Parser::Dictionnary.new }
5
- rule(:code) { ::Code::Parser::Code.new }
5
+ rule(:code) { ::Code::Parser::Code.new.present }
6
6
  rule(:name) { ::Code::Parser::Name.new }
7
7
  rule(:function_arguments) { ::Code::Parser::Function.new.arguments }
8
8
 
@@ -72,12 +72,12 @@ class Code
72
72
  rule(:block) do
73
73
  (
74
74
  whitespace >> do_keyword >> whitespace >>
75
- block_arguments.as(:arguments).maybe >> code.as(:body) >>
75
+ block_arguments.as(:arguments).maybe >> code.as(:body).maybe >>
76
76
  end_keyword
77
77
  ) |
78
78
  (
79
79
  whitespace? >> opening_curly_bracket >> whitespace >>
80
- block_arguments.as(:arguments).maybe >> code.as(:body) >>
80
+ block_arguments.as(:arguments).maybe >> code.as(:body).maybe >>
81
81
  closing_curly_bracket
82
82
  )
83
83
  end
@@ -8,11 +8,10 @@ class Code
8
8
  rule(:whitespace) { (space | newline).repeat(1) }
9
9
  rule(:whitespace?) { whitespace.maybe }
10
10
 
11
- rule(:code) do
12
- (whitespace?.ignore >> statement >> whitespace?.ignore).repeat(1) |
13
- whitespace?.ignore
11
+ rule(:present) do
12
+ (whitespace?.ignore >> statement >> whitespace?.ignore).repeat(1)
14
13
  end
15
-
14
+ rule(:code) { present | whitespace?.ignore }
16
15
  root(:code)
17
16
  end
18
17
  end
@@ -1,7 +1,7 @@
1
1
  class Code
2
2
  class Parser
3
3
  class Defined < Parslet::Parser
4
- rule(:equal) { ::Code::Parser::Equal.new }
4
+ rule(:range) { ::Code::Parser::Range.new }
5
5
  rule(:name) { ::Code::Parser::Name.new }
6
6
 
7
7
  rule(:defined_keyword) { str("defined?") }
@@ -11,7 +11,7 @@ class Code
11
11
  rule(:defined) do
12
12
  (
13
13
  defined_keyword >> opening_parenthesis >> name >> closing_parenthesis
14
- ).as(:defined) | equal
14
+ ).as(:defined) | range
15
15
  end
16
16
 
17
17
  root(:defined)
@@ -2,7 +2,7 @@ class Code
2
2
  class Parser
3
3
  class Dictionnary < Parslet::Parser
4
4
  rule(:list) { ::Code::Parser::List.new }
5
- rule(:code) { ::Code::Parser::Code.new }
5
+ rule(:code) { ::Code::Parser::Code.new.present }
6
6
  rule(:string) { ::Code::Parser::String.new }
7
7
 
8
8
  rule(:opening_curly_bracket) { str("{") }
@@ -1,7 +1,7 @@
1
1
  class Code
2
2
  class Parser
3
3
  class Equality < Parslet::Parser
4
- rule(:greater_than) { ::Code::Parser::GreaterThan.new }
4
+ rule(:while_parser) { ::Code::Parser::While.new }
5
5
 
6
6
  rule(:right_caret) { str(">") }
7
7
  rule(:left_caret) { str("<") }
@@ -22,12 +22,12 @@ class Code
22
22
 
23
23
  rule(:equality) do
24
24
  (
25
- greater_than.as(:first) >>
25
+ while_parser.as(:first) >>
26
26
  (
27
27
  whitespace? >> operator.as(:operator) >> whitespace? >>
28
- greater_than.as(:statement)
28
+ while_parser.as(:statement)
29
29
  ).repeat(1).as(:rest)
30
- ).as(:equality) | greater_than
30
+ ).as(:equality) | while_parser
31
31
  end
32
32
 
33
33
  root(:equality)
@@ -2,6 +2,7 @@ class Code
2
2
  class Parser
3
3
  class Function < Parslet::Parser
4
4
  rule(:call) { ::Code::Parser::Call.new }
5
+ rule(:code_present) { ::Code::Parser::Code.new.present }
5
6
  rule(:code) { ::Code::Parser::Code.new }
6
7
  rule(:name) { ::Code::Parser::Name.new }
7
8
 
@@ -22,14 +23,14 @@ class Code
22
23
  rule(:whitespace?) { whitespace.maybe }
23
24
 
24
25
  rule(:keyword_argument) do
25
- name >> whitespace? >> colon >> (whitespace? >> code.as(:default)).maybe
26
+ name >> whitespace? >> colon >> code_present.as(:default).maybe
26
27
  end
27
28
 
28
29
  rule(:regular_argument) do
29
30
  ampersand.as(:block).maybe >>
30
31
  (asterisk >> asterisk).as(:keyword_splat).maybe >>
31
32
  asterisk.as(:splat).maybe >> name >>
32
- (whitespace? >> equal >> whitespace? >> code.as(:default)).maybe
33
+ (whitespace? >> equal >> whitespace? >> code_present.as(:default)).maybe
33
34
  end
34
35
 
35
36
  rule(:argument) do
@@ -2,7 +2,7 @@ class Code
2
2
  class Parser
3
3
  class Group < Parslet::Parser
4
4
  rule(:name) { ::Code::Parser::Name.new }
5
- rule(:code) { ::Code::Parser::Code.new }
5
+ rule(:code) { ::Code::Parser::Code.new.present }
6
6
 
7
7
  rule(:opening_parenthesis) { str("(") }
8
8
  rule(:closing_parenthesis) { str(")") }
@@ -2,7 +2,7 @@ class Code
2
2
  class Parser
3
3
  class If < Parslet::Parser
4
4
  rule(:if_modifier) { ::Code::Parser::IfModifier.new }
5
- rule(:code) { ::Code::Parser::Code.new }
5
+ rule(:code) { ::Code::Parser::Code.new.present }
6
6
 
7
7
  rule(:if_keyword) { str("if") }
8
8
  rule(:else_keyword) { str("else") }
@@ -16,13 +16,13 @@ class Code
16
16
  rule(:if_rule) do
17
17
  (
18
18
  (if_keyword | unless_keyword).as(:if_operator) >> whitespace >>
19
- if_modifier.as(:if_statement) >> code.as(:if_body) >>
19
+ if_modifier.as(:if_statement) >> code.as(:if_body).maybe >>
20
20
  (
21
21
  else_keyword >>
22
22
  (
23
23
  whitespace >> (if_keyword | unless_keyword).as(:operator) >>
24
24
  whitespace >> if_modifier.as(:statement)
25
- ).maybe >> code.as(:body)
25
+ ).maybe >> code.as(:body).maybe
26
26
  ).repeat(1).as(:elses).maybe >> end_keyword
27
27
  ).as(:if) | if_modifier
28
28
  end
@@ -2,7 +2,7 @@ class Code
2
2
  class Parser
3
3
  class List < Parslet::Parser
4
4
  rule(:string) { ::Code::Parser::String.new }
5
- rule(:code) { ::Code::Parser::Code.new }
5
+ rule(:code) { ::Code::Parser::Code.new.present }
6
6
 
7
7
  rule(:opening_square_bracket) { str("[") }
8
8
  rule(:closing_square_bracket) { str("]") }
@@ -1,7 +1,7 @@
1
1
  class Code
2
2
  class Parser
3
3
  class NotKeyword < Parslet::Parser
4
- rule(:defined) { ::Code::Parser::Defined.new }
4
+ rule(:equal) { ::Code::Parser::Equal.new }
5
5
 
6
6
  rule(:not_keyword) { str("not") }
7
7
 
@@ -12,7 +12,7 @@ class Code
12
12
  rule(:whitespace) { (space | newline).repeat(1) }
13
13
 
14
14
  rule(:not_rule) do
15
- (not_keyword >> whitespace >> not_rule).as(:not_keyword) | defined
15
+ (not_keyword >> whitespace >> not_rule).as(:not_keyword) | equal
16
16
  end
17
17
 
18
18
  root(:not_rule)
@@ -1,7 +1,7 @@
1
1
  class Code
2
2
  class Parser
3
3
  class Statement < Parslet::Parser
4
- rule(:statement) { ::Code::Parser::While.new }
4
+ rule(:statement) { ::Code::Parser::Equality.new }
5
5
  root(:statement)
6
6
  end
7
7
  end
@@ -3,11 +3,14 @@ class Code
3
3
  class String < Parslet::Parser
4
4
  rule(:number) { ::Code::Parser::Number.new }
5
5
  rule(:name) { ::Code::Parser::Name.new.name }
6
+ rule(:code) { ::Code::Parser::Code.new }
6
7
 
7
8
  rule(:single_quote) { str("'") }
8
9
  rule(:double_quote) { str('"') }
9
10
  rule(:backslash) { str("\\") }
10
11
  rule(:colon) { str(":") }
12
+ rule(:opening_curly_bracket) { str("{") }
13
+ rule(:closing_curly_bracket) { str("}") }
11
14
 
12
15
  rule(:zero) { str("0") }
13
16
  rule(:one) { str("1") }
@@ -31,6 +34,10 @@ class Code
31
34
  rule(:t) { str("t") | str("T") }
32
35
  rule(:u) { str("u") | str("U") }
33
36
 
37
+ rule(:interpolation) do
38
+ opening_curly_bracket >> code >> closing_curly_bracket
39
+ end
40
+
34
41
  rule(:base_16_digit) do
35
42
  zero | one | two | three | four | five | six | seven | eight | nine |
36
43
  a | b | c | d | e | f
@@ -42,20 +49,28 @@ class Code
42
49
  end
43
50
 
44
51
  rule(:single_quoted_character) do
45
- escaped_character | (single_quote.absent? >> any)
52
+ escaped_character | (opening_curly_bracket.absent? >> single_quote.absent? >> any)
46
53
  end
47
54
 
48
55
  rule(:double_quoted_character) do
49
- escaped_character | (double_quote.absent? >> any)
56
+ escaped_character | (opening_curly_bracket.absent? >> double_quote.absent? >> any)
50
57
  end
51
58
 
52
59
  rule(:single_quoted_string) do
53
- single_quote.ignore >> single_quoted_character.repeat >>
60
+ single_quote.ignore >>
61
+ (
62
+ interpolation.as(:interpolation) |
63
+ single_quoted_character.repeat(1).as(:characters)
64
+ ).repeat >>
54
65
  single_quote.ignore
55
66
  end
56
67
 
57
68
  rule(:double_quoted_string) do
58
- double_quote.ignore >> double_quoted_character.repeat >>
69
+ double_quote.ignore >>
70
+ (
71
+ interpolation.as(:interpolation) |
72
+ double_quoted_character.repeat(1).as(:characters)
73
+ ).repeat >>
59
74
  double_quote.ignore
60
75
  end
61
76
 
@@ -1,7 +1,7 @@
1
1
  class Code
2
2
  class Parser
3
3
  class Ternary < Parslet::Parser
4
- rule(:range) { ::Code::Parser::Range.new }
4
+ rule(:defined) { ::Code::Parser::Defined.new }
5
5
 
6
6
  rule(:question_mark) { str("?") }
7
7
  rule(:colon) { str(":") }
@@ -13,10 +13,10 @@ class Code
13
13
 
14
14
  rule(:ternary) do
15
15
  (
16
- range.as(:left) >> whitespace >> question_mark >> whitespace? >>
16
+ defined.as(:left) >> whitespace >> question_mark >> whitespace? >>
17
17
  ternary.as(:middle) >>
18
18
  (whitespace? >> colon >> whitespace? >> ternary.as(:right)).maybe
19
- ).as(:ternary) | range
19
+ ).as(:ternary) | defined
20
20
  end
21
21
 
22
22
  root(:ternary)
data/lib/code-ruby.rb ADDED
@@ -0,0 +1,12 @@
1
+ require "parslet"
2
+ require "zeitwerk"
3
+ require "bigdecimal"
4
+ require "active_support"
5
+ require "active_support/core_ext/object/blank"
6
+ require "stringio"
7
+ require "timeout"
8
+
9
+ loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
10
+ loader.ignore(File.expand_path("lib/template-ruby.rb"))
11
+ loader.ignore(File.expand_path("lib/code-ruby.rb"))
12
+ loader.setup
data/lib/code.rb CHANGED
@@ -1,29 +1,30 @@
1
1
  class Code
2
- def initialize(input, io: $stdout, timeout: 10)
2
+ DEFAULT_TIMEOUT = Template::DEFAULT_TIMEOUT
3
+
4
+ def initialize(input, io: $stdout, timeout: DEFAULT_TIMEOUT)
3
5
  @input = input
4
- @parsed =
5
- Timeout.timeout(timeout) { ::Code::Parser::Code.new.parse(@input) }
6
+ @parsed = Timeout.timeout(timeout) { ::Code::Parser::Code.new.parse(@input) }
6
7
  @io = io
7
- @timeout = timeout
8
+ @timeout = timeout || DEFAULT_TIMEOUT
8
9
  end
9
10
 
10
- def self.evaluate(input, context = "", io: $stdout, timeout: 10)
11
+ def self.evaluate(input, context = "", io: $stdout, timeout: DEFAULT_TIMEOUT)
11
12
  new(input, io: io, timeout: timeout).evaluate(context)
12
13
  end
13
14
 
14
15
  def evaluate(context = "")
15
- Timeout.timeout(@timeout) do
16
+ Timeout.timeout(timeout) do
16
17
  if context.present?
17
- context = ::Code.evaluate(context, timeout: @timeout)
18
+ context = ::Code.evaluate(context, timeout: timeout)
18
19
  else
19
20
  context = ::Code::Object::Dictionnary.new
20
21
  end
21
22
 
22
- ::Code::Node::Code.new(parsed).evaluate(context: context, io: @io)
23
+ ::Code::Node::Code.new(parsed).evaluate(context: context, io: io)
23
24
  end
24
25
  end
25
26
 
26
27
  private
27
28
 
28
- attr_reader :input, :parsed
29
+ attr_reader :input, :parsed, :timeout, :io
29
30
  end
@@ -1,3 +1,3 @@
1
1
  require_relative "../template"
2
2
 
3
- Template::Version = Gem::Version.new("0.2.4")
3
+ Template::Version = Gem::Version.new("0.3.0")
data/lib/template-ruby.rb CHANGED
@@ -3,9 +3,11 @@ require "zeitwerk"
3
3
  require "bigdecimal"
4
4
  require "active_support"
5
5
  require "active_support/core_ext/object/blank"
6
+ require "active_support/core_ext/object/deep_dup"
6
7
  require "stringio"
7
8
  require "timeout"
8
9
 
9
10
  loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
10
- loader.ignore(__FILE__)
11
+ loader.ignore(File.expand_path("lib/template-ruby.rb"))
12
+ loader.ignore(File.expand_path("lib/code-ruby.rb"))
11
13
  loader.setup
data/lib/template.rb CHANGED
@@ -1,34 +1,40 @@
1
1
  class Template
2
- def initialize(input, io: StringIO.new, timeout: 10)
2
+ DEFAULT_TIMEOUT = 10
3
+
4
+ def initialize(input, io: StringIO.new, timeout: DEFAULT_TIMEOUT)
3
5
  @input = input
4
6
  @parsed =
5
7
  Timeout.timeout(timeout) do
6
8
  ::Template::Parser::Template.new.parse(@input)
7
9
  end
8
10
  @io = io
9
- @timeout = timeout
11
+ @timeout = timeout || DEFAULT_TIMEOUT
10
12
  end
11
13
 
12
- def self.render(input, context = "", io: StringIO.new, timeout: 10)
14
+ def self.render(input, context = "", io: StringIO.new, timeout: DEFAULT_TIMEOUT)
13
15
  new(input, io: io, timeout: timeout).render(context)
14
16
  end
15
17
 
16
18
  def render(context = "")
17
- Timeout.timeout(@timeout) do
19
+ Timeout.timeout(timeout) do
18
20
  if context.present?
19
- context = ::Code.evaluate(context, timeout: @timeout)
21
+ context = ::Code.evaluate(context, timeout: timeout)
20
22
  else
21
23
  context = ::Code::Object::Dictionnary.new
22
24
  end
23
25
 
24
- ::Template::Node::Template.new(@parsed).evaluate(
26
+ ::Template::Node::Template.new(parsed).evaluate(
25
27
  context: context,
26
- io: @io,
28
+ io: io,
27
29
  )
28
30
 
29
- @io.is_a?(StringIO) ? @io.string : nil
31
+ io.is_a?(StringIO) ? io.string : nil
30
32
  end
31
33
  end
34
+
35
+ private
36
+
37
+ attr_reader :parsed, :io, :timeout
32
38
  end
33
39
 
34
40
  require_relative "template/version"
@@ -14,7 +14,6 @@ RSpec.describe ::Code::Error::TypeError do
14
14
  '1 / ""',
15
15
  '1 ** ""',
16
16
  '1 % ""',
17
- '1 + ""',
18
17
  '1 - ""',
19
18
  '1 << ""',
20
19
  '1 >> ""',
@@ -48,7 +47,6 @@ RSpec.describe ::Code::Error::TypeError do
48
47
  '1.0 ** ""',
49
48
  '1.0 * ""',
50
49
  '1.0 % ""',
51
- '1.0 + ""',
52
50
  '1.0 - ""',
53
51
  '1.0 > ""',
54
52
  '1.0 >= ""',
@@ -4,42 +4,15 @@ RSpec.describe Code::Parser::Dictionnary do
4
4
  subject { described_class.new.parse(input) }
5
5
 
6
6
  [
7
- [
8
- '{name: "Dorian"}',
9
- {
10
- dictionnary: [{ key: { name: "name" }, value: [{ string: "Dorian" }] }],
11
- },
12
- ],
13
- [
14
- '{a: true, "b": false}',
15
- {
16
- dictionnary: [
17
- { key: { name: "a" }, value: [{ boolean: "true" }] },
18
- { key: { string: "b" }, value: [{ boolean: "false" }] },
19
- ],
20
- },
21
- ],
22
- [
23
- "{ true => 1, false => 2}",
24
- {
25
- dictionnary: [
26
- {
27
- key: [{ boolean: "true" }],
28
- value: [{ number: { base_10: { integer: { whole: "1" } } } }],
29
- },
30
- {
31
- key: [{ boolean: "false" }],
32
- value: [{ number: { base_10: { integer: { whole: "2" } } } }],
33
- },
34
- ],
35
- },
36
- ],
37
- ].each do |(input, expected)|
7
+ '{name: "Dorian"}',
8
+ '{a: true, "b": false}',
9
+ "{ true => 1, false => 2}",
10
+ ].each do |input|
38
11
  context input.inspect do
39
12
  let(:input) { input }
40
13
 
41
14
  it "succeeds" do
42
- expect(subject).to eq(expected)
15
+ expect { subject }.to_not raise_error
43
16
  end
44
17
  end
45
18
  end
@@ -4,26 +4,25 @@ RSpec.describe Code::Parser::String do
4
4
  subject { described_class.new.parse(input) }
5
5
 
6
6
  [
7
- ["'hello'", { string: "hello" }],
8
- ['"hello"', { string: "hello" }],
9
- ["''", { string: "" }],
10
- ['""', { string: "" }],
11
- ["'\\''", { string: "'" }],
12
- ['"\\t"', { string: "\\t" }],
13
- ["'\\r'", { string: "\\r" }],
14
- ['"\\b\\f\\n\\r\\t"', { string: "\\b\\f\\n\\r\\t" }],
15
- ['"\\uABCG"', { string: "uABCG" }],
16
- [
17
- "'\\u0123\\u4567\\u89aA\\ubBcC\\UdDeE\\ufFfF'",
18
- { string: "\\u0123\\u4567\\u89aA\\ubBcC\\UdDeE\\ufFfF" },
19
- ],
20
- [":asc", { string: "asc" }],
21
- ].each do |(input, expected)|
7
+ "'hello'",
8
+ '"hello"',
9
+ "''",
10
+ '""',
11
+ "'\\''",
12
+ '"\\t"',
13
+ "'\\r'",
14
+ '"\\b\\f\\n\\r\\t"',
15
+ '"\\uABCG"',
16
+ "'\\u0123\\u4567\\u89aA\\ubBcC\\UdDeE\\ufFfF'",
17
+ ":asc",
18
+ "'1 + 1 = {1 + 1}'",
19
+ "'a + b = {'{'a'}{'b'}'}'"
20
+ ].each do |input|
22
21
  context input.inspect do
23
22
  let(:input) { input }
24
23
 
25
24
  it "succeeds" do
26
- expect(subject).to eq(expected)
25
+ expect { subject }.to_not raise_error
27
26
  end
28
27
  end
29
28
  end
data/spec/code_spec.rb CHANGED
@@ -96,6 +96,18 @@ RSpec.describe Code do
96
96
  ["until true\nend", ""],
97
97
  %w[("Good".."Bad").first Good],
98
98
  ['"Dorian " * 2', "Dorian Dorian "],
99
+ ["while false end == nothing", "true"],
100
+ ['"1 + 1 = {1 + 1}"', "1 + 1 = 2"],
101
+ ["'1 + 1 = {1 + 1}'", "1 + 1 = 2"],
102
+ ["{}.to_string + [].to_string", "{}[]"],
103
+ ["'a' + 1", "a1"],
104
+ ["'a' + 1.0", "a1.0"],
105
+ ["1 + 'a'", "1a"],
106
+ ["1.0 + 'a'", "1.0a"],
107
+ ["1 << 1", "2"],
108
+ ["1.0 << 1", "2"],
109
+ ["1 << 1.0", "2"],
110
+ ["1.0 << 1.0", "2"],
99
111
  ].each do |(input, expected)|
100
112
  context input.inspect do
101
113
  let(:input) { input }
@@ -12,7 +12,13 @@ RSpec.describe Template do
12
12
  '{ user: { first_name: "Dorian" } }',
13
13
  "Hello Dorian",
14
14
  ],
15
+ [
16
+ "{add(1, 2)",
17
+ 'add = (a, b) => { a + b } { add: context(:add) }',
18
+ "3",
19
+ ],
15
20
  ["Hello {", "", "Hello "],
21
+ ["{{a: 1}.each { |k, v| print(k) } nothing", "", "a"],
16
22
  ["", "", ""],
17
23
  ].each do |(input, input_context, expected)|
18
24
  context "#{input.inspect} #{input_context.inspect}" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: template-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dorian Marié
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-09-02 00:00:00.000000000 Z
11
+ date: 2022-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -97,14 +97,17 @@ files:
97
97
  - Gemfile.lock
98
98
  - LICENSE
99
99
  - README.md
100
- - TODO.md
100
+ - bin/code
101
101
  - bin/template
102
+ - code-ruby.gemspec
102
103
  - docs/euler/1.template
103
104
  - docs/euler/2.template
104
105
  - docs/euler/3.template
105
106
  - docs/euler/4.template
106
107
  - docs/euler/5.template
108
+ - docs/fibonnaci.template
107
109
  - docs/precedence.template
110
+ - lib/code-ruby.rb
108
111
  - lib/code.rb
109
112
  - lib/code/error.rb
110
113
  - lib/code/node.rb
@@ -146,6 +149,9 @@ files:
146
149
  - lib/code/node/rescue.rb
147
150
  - lib/code/node/statement.rb
148
151
  - lib/code/node/string.rb
152
+ - lib/code/node/string_characters.rb
153
+ - lib/code/node/string_component.rb
154
+ - lib/code/node/string_interpolation.rb
149
155
  - lib/code/node/ternary.rb
150
156
  - lib/code/node/unary_minus.rb
151
157
  - lib/code/node/while.rb
@@ -242,7 +248,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
242
248
  - !ruby/object:Gem::Version
243
249
  version: '0'
244
250
  requirements: []
245
- rubygems_version: 3.3.18
251
+ rubygems_version: 3.3.7
246
252
  signing_key:
247
253
  specification_version: 4
248
254
  summary: A templating programming language
data/TODO.md DELETED
@@ -1 +0,0 @@
1
- - Code gem