-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcombiner.cr
executable file
·169 lines (137 loc) · 5.59 KB
/
combiner.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/bin/env crystal
module Combiner
extend self
def self.combine(filename : String | Path)
filename = Path[filename] if filename.is_a? String
cat_file filename
end
class CompileException < Exception
getter error, stack
def initialize(error : String)
@error = error
@stack = [] of NamedTuple(file: String, line: Int32)
end
def add_stack(file : Path, line : Int32) : CompileException
@stack << { file: file.to_s, line: line }
self
end
def stacked_error
e = error
stack.each do |s|
e += "\nin #{s[:file]}:#{s[:line]}"
end
e
end
end
def cat_file(file : Path)
result = ""
l = 0
File.each_line file do |line|
begin
result += expand_line(file, line) + "\n"
rescue ex : CompileException
raise ex.add_stack file, l
end
l += 1
end
return result
end
enum SearchMode
Equality
SubString
Regex
end
def match?(search_mode : SearchMode, a, b)
case search_mode
when SearchMode::Equality
a == b
when SearchMode::SubString
a.includes? b
when SearchMode::Regex
a =~ b
else
raise Exception.new "Internal Error: Unknown SearchMode"
end
end
def cat_file_from_to(from, to, file : Path, search_mode : SearchMode, include_edges = true)
result = ""
in_block = false
found = false
l = 0
File.each_line file do |line|
begin
stripline = line.rstrip()
starting = match? search_mode, stripline, from
ending = match? search_mode, stripline, to
in_block = true if starting && include_edges && !found
in_block = false if in_block && ending && !include_edges
result += expand_line(file, line) + '\n' if in_block
in_block = true if starting && !found
in_block = false if ending && found
found = true if in_block
rescue ex : CompileException
raise ex.add_stack file, l
end
l += 1
end
if found == false
raise CompileException
.new("Matchgroup searching for »#{from}« to »#{to}« didn't find anything")
.add_stack file, l
end
return result
end
def expand_line(file, line)
md = /€\[([^]]+)\](<([^>]+)>)?/.match(line)
if md.nil?
return line
else
filename = Path.new md[1]
filename = (file.sibling filename).normalize unless filename.absolute?
selector = md[3]? || "cat"
if selector == "cat"
return cat_file filename
elsif selector =~ /^from-to/
split = selector.split("|")
if split.size != 3
raise CompileException.new "Wrong number of blocks for start-end selector: #{selector} into #{split}"
end
case split[0]
when "from-to", "from-to-full"
return cat_file_from_to split[1], split[2], filename, search_mode = SearchMode::Equality
when "from-to-substring"
return cat_file_from_to split[1], split[2], filename, search_mode = SearchMode::SubString
when "from-to-regex"
return cat_file_from_to split[1], split[2], filename, search_mode = SearchMode::Regex
else
raise CompileException.new "#{line} Unknown from-to selector: #{split[0]}"
end
elsif selector =~ /^between/
split = selector.split("|")
if split.size != 3
raise CompileException.new "#{line}: Wrong number of blocks for from-to selector: #{selector} into #{split}"
end
case split[0]
when "between", "between-full"
return cat_file_from_to split[1], split[2], filename, search_mode = SearchMode::Equality, include_edges = false
when "between-substring"
return cat_file_from_to split[1], split[2], filename, search_mode = SearchMode::SubString, include_edges = false
when "between-regex"
return cat_file_from_to split[1], split[2], filename, search_mode = SearchMode::Regex, include_edges = false
else
raise CompileException.new "#{line} Unknown between selector: #{split[0]}"
end
elsif selector =~ /^kmodule/
split = selector.split("|")
if split.size != 2
raise CompileException.new "#{line}: No module name given for kmodule selector: #{selector}"
else
modulename = split[1]
return cat_file_from_to "module #{modulename}", "endmodule", filename, search_mode = SearchMode::Equality
end
else
raise CompileException.new "Non-valid »#{selector}« selector given"
end
end
end
end # module