interscript 2.1.0 → 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +6 -0
  3. data/Rakefile +9 -1
  4. data/bin/console +4 -8
  5. data/interscript.gemspec +2 -1
  6. data/lib/interscript.rb +78 -0
  7. data/lib/interscript/compiler/javascript.rb +6 -1
  8. data/lib/interscript/compiler/ruby.rb +5 -0
  9. data/lib/interscript/detector.rb +62 -0
  10. data/lib/interscript/dsl.rb +35 -2
  11. data/lib/interscript/dsl/document.rb +2 -1
  12. data/lib/interscript/dsl/group.rb +7 -6
  13. data/lib/interscript/dsl/group/parallel.rb +2 -2
  14. data/lib/interscript/dsl/tests.rb +2 -2
  15. data/lib/interscript/interpreter.rb +5 -1
  16. data/lib/interscript/node.rb +4 -0
  17. data/lib/interscript/node/alias_def.rb +6 -0
  18. data/lib/interscript/node/dependency.rb +16 -0
  19. data/lib/interscript/node/document.rb +34 -0
  20. data/lib/interscript/node/group.rb +13 -2
  21. data/lib/interscript/node/item.rb +4 -0
  22. data/lib/interscript/node/item/alias.rb +12 -0
  23. data/lib/interscript/node/item/any.rb +7 -0
  24. data/lib/interscript/node/item/capture.rb +11 -0
  25. data/lib/interscript/node/item/group.rb +29 -1
  26. data/lib/interscript/node/item/repeat.rb +4 -0
  27. data/lib/interscript/node/item/stage.rb +4 -0
  28. data/lib/interscript/node/item/string.rb +7 -0
  29. data/lib/interscript/node/metadata.rb +10 -0
  30. data/lib/interscript/node/rule.rb +3 -0
  31. data/lib/interscript/node/rule/funcall.rb +12 -2
  32. data/lib/interscript/node/rule/run.rb +16 -3
  33. data/lib/interscript/node/rule/sub.rb +165 -4
  34. data/lib/interscript/node/stage.rb +30 -4
  35. data/lib/interscript/node/tests.rb +10 -0
  36. data/lib/interscript/stdlib.rb +45 -3
  37. data/lib/interscript/utils/helpers.rb +39 -0
  38. data/lib/interscript/version.rb +1 -1
  39. data/lib/interscript/visualize/json.rb +12 -4
  40. metadata +17 -1
@@ -4,6 +4,10 @@ class Interscript::Node
4
4
  raise NotImplementedError, "You can't construct a Node directly"
5
5
  end
6
6
 
7
+ def ==(other)
8
+ self.class == other.class
9
+ end
10
+
7
11
  def to_hash
8
12
  { :class => self.class.to_s,
9
13
  :question => "is something missing?"
@@ -7,6 +7,12 @@ class Interscript::Node::AliasDef < Interscript::Node
7
7
  @data = data
8
8
  end
9
9
 
10
+ def ==(other)
11
+ super &&
12
+ self.name == other.name &&
13
+ self.data == other.data
14
+ end
15
+
10
16
  def to_hash
11
17
  { :class => self.class.to_s,
12
18
  :name => @name,
@@ -4,6 +4,22 @@ class Interscript::Node::Dependency < Interscript::Node
4
4
  def initialize
5
5
  end
6
6
 
7
+ def reverse
8
+ rdep = self.class.new
9
+ rdep.name = name
10
+ rdep.full_name = Interscript::Node::Document.reverse_name(full_name)
11
+ rdep.import = import
12
+ rdep.document = document&.reverse
13
+ rdep
14
+ end
15
+
16
+ def ==(other)
17
+ super &&
18
+ self.full_name == other.full_name &&
19
+ self.import == other.import &&
20
+ self.name == other.name
21
+ end
22
+
7
23
  def to_hash
8
24
  { :class => self.class.to_s,
9
25
  :name => @name,
@@ -1,6 +1,7 @@
1
1
  class Interscript::Node::Document
2
2
  attr_accessor :metadata, :tests, :name
3
3
  attr_accessor :dependencies, :aliases, :stages, :dep_aliases
4
+ attr_accessor :reversed_from
4
5
 
5
6
  def initialize
6
7
  puts "Interscript::Node::Document.new " if $DEBUG
@@ -34,6 +35,39 @@ class Interscript::Node::Document
34
35
  end
35
36
  end
36
37
 
38
+ def reverse
39
+ @reverse ||= self.class.new.tap do |rdoc|
40
+ rdoc.name = self.class.reverse_name(name)
41
+ rdoc.metadata = metadata&.reverse
42
+ rdoc.tests = tests&.reverse
43
+ rdoc.dependencies = dependencies.map(&:reverse)
44
+ rdoc.stages = stages.transform_values(&:reverse)
45
+ rdoc.dep_aliases = dep_aliases.transform_values(&:reverse)
46
+ rdoc.aliases = aliases
47
+ end
48
+ end
49
+
50
+ def self.reverse_name(name)
51
+ newname = (name || "noname").split("-")
52
+ newname[2], newname[3] = newname[3], newname[2] if newname.length >= 4
53
+ newname = newname.join("-")
54
+ if newname == name
55
+ newname.gsub!("-reverse", "")
56
+ end
57
+ if newname == name
58
+ newname += "-reverse"
59
+ end
60
+ newname
61
+ end
62
+
63
+ def ==(other)
64
+ self.class == other.class &&
65
+ self.metadata == other.metadata &&
66
+ self.tests == other.tests &&
67
+ self.stages == other.stages &&
68
+ self.aliases == other.aliases
69
+ end
70
+
37
71
  def to_hash
38
72
  { :class => self.class.to_s, :metadata => @metadata&.to_hash,
39
73
  :tests => @tests&.to_hash,
@@ -1,7 +1,8 @@
1
1
  class Interscript::Node::Group < Interscript::Node
2
- attr_accessor :children
2
+ attr_accessor :children, :reverse_run
3
3
 
4
- def initialize
4
+ def initialize(reverse_run: nil)
5
+ @reverse_run = reverse_run
5
6
  @children = []
6
7
  end
7
8
 
@@ -20,11 +21,21 @@ class Interscript::Node::Group < Interscript::Node
20
21
  self
21
22
  end
22
23
 
24
+ def reverse
25
+ self.class.new(reverse_run: reverse_run.nil? ? nil : !reverse_run).tap do |r|
26
+ r.children = self.children.reverse.map(&:reverse)
27
+ end
28
+ end
29
+
23
30
  def to_hash
24
31
  { :class => self.class.to_s,
25
32
  :children => @children.map{|x| x.to_hash} }
26
33
  end
27
34
 
35
+ def ==(other)
36
+ super && self.children == other.children && self.reverse_run == other.reverse_run
37
+ end
38
+
28
39
  def inspect
29
40
  @children.map(&:inspect).join("\n").gsub(/^/, " ")
30
41
  end
@@ -36,6 +36,10 @@ class Interscript::Node::Item < Interscript::Node
36
36
  :item => self.item }
37
37
  end
38
38
 
39
+ def ==(other)
40
+ super
41
+ end
42
+
39
43
  def self.try_convert(i)
40
44
  i = Interscript::Node::Item::String.new(i) if i.class == ::String
41
45
  raise TypeError, "Wrong type #{i.class}, expected I::Node::Item" unless Interscript::Node::Item === i
@@ -10,6 +10,10 @@ class Interscript::Node::Item::Alias < Interscript::Node::Item
10
10
  !map && Interscript::Stdlib::ALIASES.has_key?(name)
11
11
  end
12
12
 
13
+ def boundary_like?
14
+ Interscript::Stdlib.boundary_like_alias?(name)
15
+ end
16
+
13
17
  def max_length
14
18
  if stdlib?
15
19
  ([:none].include? name) ? 0 : 1
@@ -19,6 +23,10 @@ class Interscript::Node::Item::Alias < Interscript::Node::Item
19
23
  end
20
24
  end
21
25
 
26
+ # Not implemented properly
27
+ def downcase; self; end
28
+ def upcase; self; end
29
+
22
30
  def first_string
23
31
  self
24
32
  end
@@ -32,6 +40,10 @@ class Interscript::Node::Item::Alias < Interscript::Node::Item
32
40
  }
33
41
  end
34
42
 
43
+ def ==(other)
44
+ super && self.name == other.name && self.map == other.map
45
+ end
46
+
35
47
  def inspect
36
48
  if map
37
49
  "map.#{map}.#{name}"
@@ -25,6 +25,9 @@ class Interscript::Node::Item::Any < Interscript::Node::Item
25
25
  end
26
26
  end
27
27
 
28
+ def downcase; self.class.new(self.data.map(&:downcase)); end
29
+ def upcase; self.class.new(self.data.map(&:upcase)); end
30
+
28
31
  def first_string
29
32
  case @value
30
33
  when Array
@@ -70,6 +73,10 @@ class Interscript::Node::Item::Any < Interscript::Node::Item
70
73
  hash
71
74
  end
72
75
 
76
+ def ==(other)
77
+ super && self.data == other.data
78
+ end
79
+
73
80
  def inspect
74
81
  "any(#{value.inspect})"
75
82
  end
@@ -15,11 +15,18 @@ class Interscript::Node::Item::CaptureGroup < Interscript::Node::Item
15
15
  data.nth_string
16
16
  end
17
17
 
18
+ def downcase; self.dup.tap { |i| i.data = i.data.downcase }; end
19
+ def upcase; self.dup.tap { |i| i.data = i.data.upcase }; end
20
+
18
21
  def to_hash
19
22
  { :class => self.class.to_s,
20
23
  :data => self.data.to_hash }
21
24
  end
22
25
 
26
+ def ==(other)
27
+ super && self.data == other.data
28
+ end
29
+
23
30
  def inspect
24
31
  "capture(#{@data.inspect})"
25
32
  end
@@ -44,6 +51,10 @@ class Interscript::Node::Item::CaptureRef < Interscript::Node::Item
44
51
  :id => self.id }
45
52
  end
46
53
 
54
+ def ==(other)
55
+ super && self.id == other.id
56
+ end
57
+
47
58
  def inspect
48
59
  "ref(#{@id.inspect})"
49
60
  end
@@ -10,11 +10,35 @@ class Interscript::Node::Item::Group < Interscript::Node::Item
10
10
  def +(item)
11
11
  item = Interscript::Node::Item.try_convert(item)
12
12
  out = self.dup
13
- out.children << item
13
+ if Interscript::Node::Item::Group === item
14
+ out.children += item.children
15
+ else
16
+ out.children << item
17
+ end
14
18
  out.verify!
15
19
  out
16
20
  end
17
21
 
22
+ def compact
23
+ out = self.dup do |n|
24
+ n.children = n.children.reject do |i|
25
+ (Interscript::Node::Alias === i && i.name == :none) ||
26
+ (Interscript::Node::String === i && i.data == "")
27
+ end
28
+ end
29
+
30
+ if out.children.count == 0
31
+ Interscript::Node::Alias.new(:none)
32
+ elsif out.children.count == 1
33
+ out.children.first
34
+ else
35
+ out
36
+ end
37
+ end
38
+
39
+ def downcase; self.dup.tap { |i| i.children = i.children.map(&:downcase) }; end
40
+ def upcase; self.dup.tap { |i| i.children = i.children.map(&:upcase) }; end
41
+
18
42
  # Verify if a group is valid
19
43
  def verify!
20
44
  wrong = @children.find do |i|
@@ -45,6 +69,10 @@ class Interscript::Node::Item::Group < Interscript::Node::Item
45
69
  :children => self.children.map{|x| x.to_hash} }
46
70
  end
47
71
 
72
+ def ==(other)
73
+ super && self.children == other.children
74
+ end
75
+
48
76
  def inspect
49
77
  @children.map(&:inspect).join("+")
50
78
  end
@@ -22,6 +22,10 @@ class Interscript::Node::Item::Repeat < Interscript::Node::Item
22
22
  :data => self.data.to_hash }
23
23
  end
24
24
 
25
+ def ==(other)
26
+ super && self.data == other.data
27
+ end
28
+
25
29
  def inspect
26
30
  str = case self
27
31
  when Interscript::Node::Item::Maybe
@@ -13,6 +13,10 @@ class Interscript::Node::Item::Stage < Interscript::Node::Item
13
13
  }
14
14
  end
15
15
 
16
+ def ==(other)
17
+ super && self.name == other.name && self.map == other.map
18
+ end
19
+
16
20
  def inspect
17
21
  if map
18
22
  "map.#{@map}.stage.#{@name}"
@@ -17,6 +17,9 @@ class Interscript::Node::Item::String < Interscript::Node::Item
17
17
  self.data
18
18
  end
19
19
 
20
+ def downcase; self.dup.tap { |i| i.data = i.data.downcase }; end
21
+ def upcase; self.dup.tap { |i| i.data = i.data.upcase }; end
22
+
20
23
  alias nth_string first_string
21
24
 
22
25
  def + other
@@ -33,6 +36,10 @@ class Interscript::Node::Item::String < Interscript::Node::Item
33
36
  end
34
37
  end
35
38
 
39
+ def ==(other)
40
+ super && self.data == other.data
41
+ end
42
+
36
43
  def inspect
37
44
  @data.inspect
38
45
  end
@@ -11,6 +11,16 @@ class Interscript::Node::MetaData < Interscript::Node
11
11
  @data[k]
12
12
  end
13
13
 
14
+ def reverse
15
+ self.class.new(data.dup, **{}).tap do |rmd|
16
+ rmd[:source_script], rmd[:destination_script] = rmd[:destination_script], rmd[:source_script]
17
+ end
18
+ end
19
+
20
+ def ==(other)
21
+ super && self.data == other.data
22
+ end
23
+
14
24
  def to_hash
15
25
  {:class => self.class.to_s,
16
26
  :data => @data}
@@ -1,4 +1,7 @@
1
1
  class Interscript::Node::Rule < Interscript::Node
2
+ def ==(other)
3
+ super && self.reverse_run == other.reverse_run
4
+ end
2
5
  end
3
6
 
4
7
  require "interscript/node/rule/sub"
@@ -1,7 +1,8 @@
1
1
  class Interscript::Node::Rule::Funcall < Interscript::Node::Rule
2
- attr_accessor :name, :kwargs
3
- def initialize name, **kwargs
2
+ attr_accessor :name, :kwargs, :reverse_run
3
+ def initialize name, reverse_run: nil, **kwargs
4
4
  @name = name
5
+ @reverse_run = reverse_run
5
6
  @kwargs = kwargs
6
7
  end
7
8
 
@@ -12,6 +13,15 @@ class Interscript::Node::Rule::Funcall < Interscript::Node::Rule
12
13
  }
13
14
  end
14
15
 
16
+ def reverse
17
+ self.class.new(Interscript::Stdlib.reverse_function[@name.to_sym],
18
+ reverse_run: reverse_run.nil? ? nil : !reverse_run, **kwargs)
19
+ end
20
+
21
+ def ==
22
+ super && self.name == other.name && self.kwargs == other.kwargs
23
+ end
24
+
15
25
  def inspect
16
26
  "#{@name} #{kwargs.inspect[1..-2]}"
17
27
  end
@@ -1,7 +1,8 @@
1
1
  class Interscript::Node::Rule::Run < Interscript::Node::Rule
2
- attr_accessor :stage
3
- def initialize stage
2
+ attr_accessor :stage, :reverse_run
3
+ def initialize stage, reverse_run: nil
4
4
  @stage = stage
5
+ @reverse_run = reverse_run
5
6
  end
6
7
 
7
8
  def to_hash
@@ -9,7 +10,19 @@ class Interscript::Node::Rule::Run < Interscript::Node::Rule
9
10
  :stage => self.stage.to_hash }
10
11
  end
11
12
 
13
+ def reverse
14
+ Interscript::Node::Rule::Run.new(stage,
15
+ reverse_run: reverse_run.nil? ? nil : !reverse_run
16
+ )
17
+ end
18
+
19
+ def ==(other)
20
+ super && self.stage == other.stage
21
+ end
22
+
12
23
  def inspect
13
- "run #{@stage.inspect}"
24
+ out = "run #{@stage.inspect}"
25
+ out += ", reverse_run: #{@reverse_run.inspect}" unless reverse_run.nil?
26
+ out
14
27
  end
15
28
  end
@@ -1,12 +1,19 @@
1
1
  class Interscript::Node::Rule::Sub < Interscript::Node::Rule
2
2
  attr_accessor :from, :to
3
3
  attr_accessor :before, :not_before, :after, :not_after
4
+ attr_accessor :reverse_before, :reverse_not_before, :reverse_after, :reverse_not_after
5
+ attr_accessor :reverse_run
4
6
  attr_accessor :priority
5
7
 
6
- def initialize from, to, before: nil, not_before: nil, after: nil, not_after: nil, priority: nil
8
+ def initialize (from, to,
9
+ before: nil, not_before: nil,
10
+ after: nil, not_after: nil,
11
+ priority: nil, reverse_run: nil)
7
12
  self.from = Interscript::Node::Item.try_convert from
8
13
  if to == :upcase
9
14
  self.to = :upcase
15
+ elsif to == :downcase
16
+ self.to = :downcase
10
17
  else
11
18
  self.to = Interscript::Node::Item.try_convert to
12
19
  end
@@ -16,6 +23,8 @@ class Interscript::Node::Rule::Sub < Interscript::Node::Rule
16
23
  #raise TypeError, "Can't supply both before and not_before" if before && not_before
17
24
  #raise TypeError, "Can't supply both after and not_after" if after && not_after
18
25
 
26
+ self.reverse_run = reverse_run
27
+
19
28
  self.before = Interscript::Node::Item.try_convert(before) if before
20
29
  self.after = Interscript::Node::Item.try_convert(after) if after
21
30
  self.not_before = Interscript::Node::Item.try_convert(not_before) if not_before
@@ -37,7 +46,13 @@ class Interscript::Node::Rule::Sub < Interscript::Node::Rule
37
46
  puts params.inspect if $DEBUG
38
47
  hash = { :class => self.class.to_s,
39
48
  :from => self.from.to_hash,
40
- :to => Symbol === self.to ? self.to : self.to.to_hash
49
+ :to => Symbol === self.to ? self.to : self.to.to_hash,
50
+ :reverse_run => self.reverse_run,
51
+ :before => self.before&.to_hash,
52
+ :not_before => self.not_before&.to_hash,
53
+ :after => self.after&.to_hash,
54
+ :not_after => self.not_after&.to_hash,
55
+ :priority => self.priority
41
56
  }
42
57
 
43
58
  hash[:before] = self.before&.to_hash if self.before
@@ -49,19 +64,165 @@ class Interscript::Node::Rule::Sub < Interscript::Node::Rule
49
64
  hash
50
65
  end
51
66
 
67
+ def reverse
68
+ if to == :upcase
69
+ xfrom = from.downcase
70
+ xto = :downcase
71
+ elsif to == :downcase
72
+ xfrom = from.upcase
73
+ xto = :upcase
74
+ else
75
+ xto, xfrom = reverse_transfer(from, to)
76
+ end
77
+
78
+ # A special case: sub "a", "" shouldn't be present in a reverse map
79
+ rrun = self.reverse_run.nil? ? nil : !self.reverse_run
80
+ if rrun.nil? && !has_assertions? &&
81
+ (xfrom == "" ||
82
+ (Interscript::Node::Item::String === xfrom && xfrom.data == '') ||
83
+ (Interscript::Node::Item::Alias === xfrom && xfrom.name == :none)
84
+ )
85
+
86
+ rrun = true
87
+ end
88
+
89
+ Interscript::Node::Rule::Sub.new(xfrom, xto,
90
+ before: before, after: after,
91
+ not_before: not_before, not_after: not_after,
92
+
93
+ reverse_run: rrun,
94
+
95
+ priority: priority ? -priority : nil
96
+ )
97
+ end
98
+
99
+ def has_assertions?
100
+ !!(before || not_before || not_after || after)
101
+ end
102
+
103
+ # Attempt to transfer some references to boundary/line_begin around.
104
+ # Those in general should go into before/after clauses, but for now
105
+ # let's try to get the best compatibility possible. Also, CaptureGroup,
106
+ # CaptureRef need to be shifted around
107
+ def reverse_transfer from, to
108
+ # This part is about moving initial and final boundary like aliases
109
+ case from
110
+ when Interscript::Node::Item::Group
111
+ first = from.children.first
112
+ last = from.children.last
113
+
114
+ if Interscript::Node::Item::Alias === first && first.boundary_like?
115
+ out = Interscript::Node::Item::Group.new + first + to
116
+ to = out.compact
117
+
118
+ from = from.dup.tap do |i|
119
+ i.children = i.children[1..-1]
120
+ end.compact
121
+ end
122
+
123
+ if Interscript::Node::Item::Alias === last && last.boundary_like?
124
+ out = Interscript::Node::Item::Group.new + to + last
125
+ to = out.compact
126
+
127
+ from = from.dup.tap do |i|
128
+ i.children = i.children[0..-2]
129
+ end.compact
130
+ end
131
+ when Interscript::Node::Item::Alias
132
+ if from.boundary_like?
133
+ to = if from.name.to_s.end_with? "_end"
134
+ Interscript::Node::Item::Group.new + to + from
135
+ else
136
+ Interscript::Node::Item::Group.new + from + to
137
+ end
138
+ from = Interscript::Node::Item::Alias.new(:none)
139
+ end
140
+ end
141
+
142
+ # This part is about moving backreferences
143
+ state = {left:[], right:[]}
144
+
145
+ from = reverse_transfer_visit(from, :from, state)
146
+ to = reverse_transfer_visit(to, :to, state)
147
+
148
+ [from, to]
149
+ end
150
+
151
+ private def reverse_transfer_visit(node, type, state)
152
+ node = Interscript::Node::Item.try_convert(node)
153
+
154
+ case node
155
+ when Interscript::Node::Item::Alias
156
+ if node.name == :kor_maybedash
157
+ state[:left] << node
158
+ Interscript::Node::Item::CaptureRef.new(state[:left].length)
159
+ else
160
+ node
161
+ end
162
+ when Interscript::Node::Item::String
163
+ node
164
+ when Interscript::Node::Item::Any
165
+ if Array === node.value
166
+ node.dup.tap do |i|
167
+ i.value = i.value.map { |c| reverse_transfer_visit(c, type, state) }
168
+ end
169
+ else
170
+ node
171
+ end
172
+ when Interscript::Node::Item::Group
173
+ node.dup.tap do |i|
174
+ i.children = i.children.map { |c| reverse_transfer_visit(c, type, state) }
175
+ end
176
+ when Interscript::Node::Item::Repeat
177
+ node.dup.tap do |i|
178
+ i.data = reverse_transfer_visit(i.data, type, state)
179
+ end
180
+ when Interscript::Node::Item::CaptureRef
181
+ if type == :from
182
+ node
183
+ elsif state[:right][node.id]
184
+ node
185
+ else
186
+ state[:right][node.id] = true
187
+ state[:left][node.id - 1] or raise "Capture count doesn't match"
188
+ end
189
+ when Interscript::Node::Item::CaptureGroup
190
+ state[:left] << node
191
+ out = Interscript::Node::Item::CaptureRef.new(state[:left].length)
192
+ reverse_transfer_visit(node.data, type, state) # Visit but don't care
193
+ out
194
+ else
195
+ raise "Type #{node.class} unhandled!"
196
+ end
197
+ end
198
+
199
+ def ==(other)
200
+ super &&
201
+ self.from == other.from &&
202
+ self.to == other.to &&
203
+ self.before == other.before &&
204
+ self.after == other.after &&
205
+ self.not_before == other.not_before &&
206
+ self.not_after == other.not_after &&
207
+ self.priority == other.priority
208
+ end
209
+
52
210
  def inspect
53
211
  out = "sub "
54
212
  params = []
55
213
  params << @from.inspect
56
- if @to == :upcase
57
- params << "upcase"
214
+ if Symbol === @to
215
+ params << @to.to_s
58
216
  else
59
217
  params << @to.inspect
60
218
  end
219
+ params << "reverse_run: #{@reverse_run.inspect}" unless @reverse_run.nil?
220
+
61
221
  params << "before: #{@before.inspect}" if @before
62
222
  params << "after: #{@after.inspect}" if @after
63
223
  params << "not_before: #{@not_before.inspect}" if @not_before
64
224
  params << "not_after: #{@not_after.inspect}" if @not_after
225
+
65
226
  params << "priority: #{@priority.inspect}" if @priority
66
227
  out << params.join(", ")
67
228
  end