xmk 0.1.2
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.
- checksums.yaml +7 -0
- data/bin/xmk +3 -0
- data/lib/xmk/lexer.rb +224 -0
- data/lib/xmk/parser.rb +347 -0
- data/lib/xmk/token.rb +29 -0
- data/lib/xmk.rb +87 -0
- data/syntax/rouge.rb +35 -0
- data/syntax/vim.vim +35 -0
- metadata +52 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3821a25480789beeb46b176c9c0b3b51fa285e9245e45e9787f508c0b82804f5
|
|
4
|
+
data.tar.gz: 554630e394e1fbd0a4f905f938f1e4cd1311f1189394cd493e9d590d2d79ac71
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4db33b8cd0340b19c8526aad808f77fb645d48aa98d550867879cc775aa918ddeb6c138f05caf218871536d73664b549ceb161705842d742cba974cf52980063
|
|
7
|
+
data.tar.gz: e7834f5626629dd5cef037bbf67701c541e86a1a52ad4ae7b2a20349ea07b271cb227e7d7ea41c9947d2cbff1e6b7d82ec064df7a768a4f7df1b667a7b6b8558
|
data/bin/xmk
ADDED
data/lib/xmk/lexer.rb
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
class Lexer
|
|
2
|
+
|
|
3
|
+
def initialize
|
|
4
|
+
@tokens = Array.new
|
|
5
|
+
@start = 0
|
|
6
|
+
@current = -1
|
|
7
|
+
@line = 0
|
|
8
|
+
@string = ""
|
|
9
|
+
@file = nil
|
|
10
|
+
@in_label = false
|
|
11
|
+
@assignment_type = ""
|
|
12
|
+
|
|
13
|
+
if File.exists?("xmkfile")
|
|
14
|
+
@file = File.open("xmkfile", "r")
|
|
15
|
+
else
|
|
16
|
+
puts "No xmkfile found"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def scan
|
|
21
|
+
while (c = self.advance) != nil
|
|
22
|
+
@start = @current
|
|
23
|
+
case c
|
|
24
|
+
when "#" # comment
|
|
25
|
+
self.end_line
|
|
26
|
+
when "+" # append/declaration
|
|
27
|
+
if self.peek == "="
|
|
28
|
+
@assignment_type = "append"
|
|
29
|
+
@tokens.push(Token.new(Token::ADD, '+=', @line))
|
|
30
|
+
self.advance(2)
|
|
31
|
+
self.get_value(Token::VAL)
|
|
32
|
+
end
|
|
33
|
+
when "=" # declaration
|
|
34
|
+
@assignment_type = "assign"
|
|
35
|
+
@tokens.push(Token.new(Token::SET, '=', @line))
|
|
36
|
+
self.advance
|
|
37
|
+
self.get_value(Token::VAL)
|
|
38
|
+
when "@" # target
|
|
39
|
+
loop do
|
|
40
|
+
c = self.advance
|
|
41
|
+
c = self.handle_comment_backslash(c)
|
|
42
|
+
break if self.terminates?(c)
|
|
43
|
+
end
|
|
44
|
+
@tokens.push(Token.new(Token::TAR, @string[@start+1 .. @current].strip,
|
|
45
|
+
@line))
|
|
46
|
+
when "$" # cmd
|
|
47
|
+
self.get_value(Token::CMD)
|
|
48
|
+
when "_" # label
|
|
49
|
+
self.handle_label
|
|
50
|
+
else
|
|
51
|
+
if self.terminates?(c)
|
|
52
|
+
@in_label = false
|
|
53
|
+
@tokens.push(Token.new(Token::LBR, c, @line))
|
|
54
|
+
elsif c.match?(/[A-Z]/) # var
|
|
55
|
+
loop do
|
|
56
|
+
self.advance
|
|
57
|
+
break unless self.peek.match?(/[A-Z_]/)
|
|
58
|
+
end
|
|
59
|
+
@tokens.push(Token.new(
|
|
60
|
+
Token::VAR, @string[@start .. @current].strip, @line))
|
|
61
|
+
elsif c.match?(/[a-z]/) # cmd / label / platform
|
|
62
|
+
if @in_label # cmd
|
|
63
|
+
self.get_value(Token::CMD)
|
|
64
|
+
else # label || platform
|
|
65
|
+
if self.find("@") # platform
|
|
66
|
+
loop do
|
|
67
|
+
self.advance
|
|
68
|
+
break if self.peek == ":"
|
|
69
|
+
end
|
|
70
|
+
@tokens.push(Token.new(Token::PLAT, @string[@start .. @current],
|
|
71
|
+
@line))
|
|
72
|
+
parse_target = false
|
|
73
|
+
loop do
|
|
74
|
+
c = self.advance
|
|
75
|
+
if c == "@"
|
|
76
|
+
self.advance
|
|
77
|
+
parse_target = true
|
|
78
|
+
@start = @current
|
|
79
|
+
end
|
|
80
|
+
if (c == " " || self.terminates?(c)) && parse_target
|
|
81
|
+
parse_target = false
|
|
82
|
+
@tokens.push(Token.new(Token::TARLINK,
|
|
83
|
+
@string[@start .. @current-1], @line))
|
|
84
|
+
end
|
|
85
|
+
break if self.terminates?(c)
|
|
86
|
+
end
|
|
87
|
+
else
|
|
88
|
+
self.handle_label
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
@file.close
|
|
95
|
+
@tokens.push(Token.new(Token::LBR, "", @line))
|
|
96
|
+
@tokens.push(Token.new(Token::EOF, "end_of_file", @line))
|
|
97
|
+
@tokens
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def find(c)
|
|
101
|
+
# check if char occurs before comment or newline
|
|
102
|
+
res = false
|
|
103
|
+
i = 0
|
|
104
|
+
backup = Array.new
|
|
105
|
+
loop do
|
|
106
|
+
d = self.consume
|
|
107
|
+
backup.push(d)
|
|
108
|
+
i += 1
|
|
109
|
+
res = true if d == c
|
|
110
|
+
break if d == "#"
|
|
111
|
+
break if self.terminates?(d)
|
|
112
|
+
end
|
|
113
|
+
i.times do |j|
|
|
114
|
+
self.push_back(backup[i-j-1])
|
|
115
|
+
end
|
|
116
|
+
res
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def get_value(t)
|
|
120
|
+
if self.terminates?(@string[@current])
|
|
121
|
+
return @tokens.push(Token.new(Token::LBR, "", @line))
|
|
122
|
+
end
|
|
123
|
+
@start = @current
|
|
124
|
+
loop do
|
|
125
|
+
c = self.advance
|
|
126
|
+
c = self.handle_comment_backslash(c)
|
|
127
|
+
break if self.terminates?(c)
|
|
128
|
+
end
|
|
129
|
+
str = @string[@start .. @current-1].rstrip
|
|
130
|
+
if t == Token::VAL && @assignment_type == "assign"
|
|
131
|
+
str = str.lstrip
|
|
132
|
+
end
|
|
133
|
+
@tokens.push(Token.new(t, str, @line))
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def handle_label
|
|
137
|
+
@in_label = true
|
|
138
|
+
loop do
|
|
139
|
+
self.advance
|
|
140
|
+
break if self.peek == ":"
|
|
141
|
+
end
|
|
142
|
+
@tokens.push(Token.new(Token::LAB, @string[@start .. @current],
|
|
143
|
+
@line))
|
|
144
|
+
self.end_line
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def handle_comment_backslash(c)
|
|
148
|
+
if c == "\\"
|
|
149
|
+
loop do
|
|
150
|
+
if self.terminates?(self.peek)
|
|
151
|
+
@string[@current] = self.consume
|
|
152
|
+
break
|
|
153
|
+
else
|
|
154
|
+
@string[@current] = self.consume
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
while self.terminates?(@string[@current])
|
|
158
|
+
@string[@current] = self.consume
|
|
159
|
+
end
|
|
160
|
+
while @string[@current] == " "
|
|
161
|
+
@string[@current] = self.consume
|
|
162
|
+
end
|
|
163
|
+
elsif c == "#"
|
|
164
|
+
loop do
|
|
165
|
+
@string[@current] = self.consume
|
|
166
|
+
if self.terminates?(self.peek)
|
|
167
|
+
@string[@current] = self.consume
|
|
168
|
+
c = @string[@current]
|
|
169
|
+
break
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
c
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def advance(n = 1)
|
|
177
|
+
c = ""
|
|
178
|
+
i = n
|
|
179
|
+
@current += n
|
|
180
|
+
loop do
|
|
181
|
+
c = self.consume
|
|
182
|
+
@string << c if c
|
|
183
|
+
i -= 1
|
|
184
|
+
break if i == 0
|
|
185
|
+
end
|
|
186
|
+
c
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def peek
|
|
190
|
+
c = self.consume
|
|
191
|
+
self.push_back(c)
|
|
192
|
+
c
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def consume
|
|
196
|
+
if self.terminates?(@string[@current])
|
|
197
|
+
@line += 1
|
|
198
|
+
end
|
|
199
|
+
@file.getc
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def push_back(c)
|
|
203
|
+
if self.terminates?(c)
|
|
204
|
+
@line -= 1
|
|
205
|
+
end
|
|
206
|
+
@file.ungetc(c)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def terminates?(c)
|
|
210
|
+
return true if c == "\n"
|
|
211
|
+
return true if c.nil?
|
|
212
|
+
return true if c == "\r"
|
|
213
|
+
return false
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def end_line
|
|
217
|
+
# strips away characters until line break
|
|
218
|
+
loop do
|
|
219
|
+
c = self.advance
|
|
220
|
+
break if self.terminates?(c)
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
end
|
data/lib/xmk/parser.rb
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
|
|
3
|
+
class Parser
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def initialize(tokens)
|
|
7
|
+
@failsafe = false
|
|
8
|
+
@state = State::ROOT
|
|
9
|
+
@tokens = tokens
|
|
10
|
+
@env_stack = Array.new
|
|
11
|
+
@env_stack.push(Hash.new)
|
|
12
|
+
@i = 0
|
|
13
|
+
@i_memory = 0
|
|
14
|
+
@env_label = ""
|
|
15
|
+
@env_name = "root"
|
|
16
|
+
@plat_name = ""
|
|
17
|
+
@target_state = Hash.new
|
|
18
|
+
@target_name = ""
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def parse
|
|
22
|
+
loop do
|
|
23
|
+
case @tokens[@i].type
|
|
24
|
+
when Token::LBR
|
|
25
|
+
if @env_label != ""
|
|
26
|
+
if Xmk.options[:verbose]
|
|
27
|
+
Xmk.print("leave label: #{@env_label}")
|
|
28
|
+
end
|
|
29
|
+
@env_label = ""
|
|
30
|
+
end
|
|
31
|
+
if @tokens[@i+1].type == Token::PLAT && @state == State::PLAT
|
|
32
|
+
self.leave_platform
|
|
33
|
+
end
|
|
34
|
+
if @tokens[@i+1].type == Token::TAR && @state == State::TAR
|
|
35
|
+
self.leave_target
|
|
36
|
+
end
|
|
37
|
+
when Token::PLAT
|
|
38
|
+
@plat_name = @tokens[@i].lexeme
|
|
39
|
+
case @state
|
|
40
|
+
when State::ROOT
|
|
41
|
+
loop do
|
|
42
|
+
@i += 1
|
|
43
|
+
break if @tokens[@i+1].type == Token::EOF
|
|
44
|
+
end
|
|
45
|
+
when State::TAR
|
|
46
|
+
allow = false
|
|
47
|
+
if Xmk.platforms.empty? || Xmk.platforms.include?(@plat_name)
|
|
48
|
+
while @tokens[@i+1].type == Token::TARLINK
|
|
49
|
+
@i += 1
|
|
50
|
+
if @env_name == @tokens[@i].lexeme
|
|
51
|
+
allow = true
|
|
52
|
+
break
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
if @target_state[:"#{@env_name}"].include?(@plat_name)
|
|
57
|
+
# leave target
|
|
58
|
+
allow = false
|
|
59
|
+
self.leave_target
|
|
60
|
+
@state = State::QUIT
|
|
61
|
+
loop do
|
|
62
|
+
@i += 1
|
|
63
|
+
break if @tokens[@i+1].type == Token::EOF
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
if allow
|
|
67
|
+
@src_files = Array.new
|
|
68
|
+
@obj_files = Array.new
|
|
69
|
+
@compilation_flags = Array.new
|
|
70
|
+
if Xmk.options[:verbose]
|
|
71
|
+
Xmk.print("ENTER PLATFORM: #{@plat_name}")
|
|
72
|
+
end
|
|
73
|
+
@env_memory = @env_name
|
|
74
|
+
@env_name = @plat_name
|
|
75
|
+
@state = State::PLAT
|
|
76
|
+
self.env_stack_grow
|
|
77
|
+
else
|
|
78
|
+
unless @state == State::QUIT
|
|
79
|
+
unless @failsafe
|
|
80
|
+
@i = @i_memory
|
|
81
|
+
@failsafe = true
|
|
82
|
+
else
|
|
83
|
+
loop do
|
|
84
|
+
@i += 1
|
|
85
|
+
break if @tokens[@i+1].type == Token::PLAT
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
when State::PLAT
|
|
91
|
+
Xmk.error("Invalid state.", @tokens[@i].line)
|
|
92
|
+
end
|
|
93
|
+
when Token::TAR
|
|
94
|
+
case @state
|
|
95
|
+
when State::ROOT, State::TAR
|
|
96
|
+
if @state == State::TAR
|
|
97
|
+
self.leave_target
|
|
98
|
+
end
|
|
99
|
+
if Xmk.targets.empty? || Xmk.targets.include?(@tokens[@i].lexeme)
|
|
100
|
+
if Xmk.options[:verbose]
|
|
101
|
+
Xmk.print("@ENTER TARGET: #{@tokens[@i].lexeme}")
|
|
102
|
+
end
|
|
103
|
+
@target_name = @tokens[@i].lexeme
|
|
104
|
+
@target_state[:"#{@tokens[@i].lexeme}"] = Array.new
|
|
105
|
+
@env_name = @tokens[@i].lexeme
|
|
106
|
+
@state = State::TAR
|
|
107
|
+
self.env_stack_grow
|
|
108
|
+
else
|
|
109
|
+
loop do
|
|
110
|
+
@i += 1
|
|
111
|
+
if @tokens[@i+1].type == Token::EOF
|
|
112
|
+
break
|
|
113
|
+
end
|
|
114
|
+
break if @tokens[@i+1].type == Token::TAR
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
when State::TAR
|
|
118
|
+
when State::PLAT
|
|
119
|
+
Xmk.error("Hierarchy violation. Target scope required" <<
|
|
120
|
+
" to appear before platform scope.", $tokens[@i].line)
|
|
121
|
+
end
|
|
122
|
+
when Token::CMD
|
|
123
|
+
if @env_label == "_rule"
|
|
124
|
+
@src_files = Array.new
|
|
125
|
+
@obj_files = Array.new
|
|
126
|
+
@compilation_flags = Array.new
|
|
127
|
+
@env_stack.last[:SRC].split.each do |src|
|
|
128
|
+
Dir.glob(src) do |f|
|
|
129
|
+
@src_files.push(f)
|
|
130
|
+
i = File.dirname(f).index('/')
|
|
131
|
+
i = File.dirname(f).length if i.nil?
|
|
132
|
+
@obj_files.push(File.join(@env_stack.last[:OBJ],
|
|
133
|
+
File.dirname(f)[i .. -1],
|
|
134
|
+
"#{File.basename(f,'.*')}.o"))
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
@obj_files.each do |o|
|
|
138
|
+
Pathname(o).dirname.mkpath
|
|
139
|
+
@compilation_flags.push(!File.exist?(o))
|
|
140
|
+
end
|
|
141
|
+
@src_files.each_with_index do |src, i|
|
|
142
|
+
next if @compilation_flags[i]
|
|
143
|
+
recipe = Parser.run_print_verbose_only(
|
|
144
|
+
var_replace(@tokens[@i].lexeme).gsub('$~', src), true)
|
|
145
|
+
recipe.slice!(0..recipe.index(':'))
|
|
146
|
+
recipe.gsub!("\n", '')
|
|
147
|
+
recipe.gsub!("\\", '')
|
|
148
|
+
recipe.squeeze(" ")
|
|
149
|
+
recipe = recipe[1..-1]
|
|
150
|
+
recipe = recipe.split(" ")
|
|
151
|
+
objmtime = File.mtime(@obj_files[i])
|
|
152
|
+
recipe.each do |dep|
|
|
153
|
+
if File.mtime(dep) > objmtime
|
|
154
|
+
@compilation_flags[i] = (File.mtime(dep) > objmtime)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
elsif @env_label == "_compile"
|
|
159
|
+
@src_files.each_with_index do |src, i|
|
|
160
|
+
if @compilation_flags[i]
|
|
161
|
+
if Xmk.options[:verbose]
|
|
162
|
+
Xmk.print("Recompilation for #{src}.")
|
|
163
|
+
end
|
|
164
|
+
Parser.run(var_replace(@tokens[@i].lexeme.gsub('$~', src).gsub(
|
|
165
|
+
'$&', @obj_files[i])))
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
elsif @env_label == "_link"
|
|
169
|
+
Parser.run(var_replace(@tokens[@i].lexeme.gsub('$&&',
|
|
170
|
+
@obj_files.join(" "))))
|
|
171
|
+
else
|
|
172
|
+
Parser.run(var_replace(@tokens[@i].lexeme))
|
|
173
|
+
end
|
|
174
|
+
when Token::LAB
|
|
175
|
+
if Xmk.options[:verbose]
|
|
176
|
+
Xmk.print("attempt label: #{@tokens[@i].lexeme}")
|
|
177
|
+
end
|
|
178
|
+
unless @tokens[@i].lexeme[0] == "_"
|
|
179
|
+
unless Xmk.labels.include?(@tokens[@i].lexeme)
|
|
180
|
+
# if not included skip to end of label
|
|
181
|
+
if Xmk.options[:verbose]
|
|
182
|
+
Xmk.print("skip label: #{@tokens[@i].lexeme}")
|
|
183
|
+
end
|
|
184
|
+
loop do
|
|
185
|
+
@i += 1
|
|
186
|
+
break if @tokens[@i].type == Token::LBR
|
|
187
|
+
end
|
|
188
|
+
else
|
|
189
|
+
if Xmk.options[:verbose]
|
|
190
|
+
Xmk.print("enter label: #{@tokens[@i].lexeme}")
|
|
191
|
+
end
|
|
192
|
+
@env_label = @tokens[@i].lexeme
|
|
193
|
+
end
|
|
194
|
+
else
|
|
195
|
+
if @tokens[@i].lexeme == "_plat" && @state == State::TAR
|
|
196
|
+
@i_memory = @i
|
|
197
|
+
loop do
|
|
198
|
+
@i += 1
|
|
199
|
+
if @tokens[@i].type == Token::EOF
|
|
200
|
+
@i = @i_memory
|
|
201
|
+
break
|
|
202
|
+
end
|
|
203
|
+
break if @tokens[@i+1].type == Token::PLAT
|
|
204
|
+
end
|
|
205
|
+
else
|
|
206
|
+
if Xmk.options[:verbose]
|
|
207
|
+
Xmk.print("enter label: #{@tokens[@i].lexeme}")
|
|
208
|
+
end
|
|
209
|
+
@env_label = @tokens[@i].lexeme
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
when Token::VAR
|
|
213
|
+
unless @tokens[@i+1].type == Token::SET ||
|
|
214
|
+
@tokens[@i+1].type == Token::ADD
|
|
215
|
+
Xmk.error("Expect '=' or '+=' after variable",
|
|
216
|
+
@tokens[@i].line)
|
|
217
|
+
end
|
|
218
|
+
when Token::SET, Token::ADD
|
|
219
|
+
unless @tokens[@i-1].type == Token::VAR &&
|
|
220
|
+
@tokens[@i+1].type == Token::VAL
|
|
221
|
+
Xmk.error("Expect <VAR>=|+=<VALUE>.",
|
|
222
|
+
@tokens[@i].line)
|
|
223
|
+
else
|
|
224
|
+
case @tokens[@i].type
|
|
225
|
+
when Token::SET
|
|
226
|
+
self.set_var
|
|
227
|
+
when Token::ADD
|
|
228
|
+
if @env_stack.last.has_key?(:"#{@tokens[@i-1].lexeme}")
|
|
229
|
+
self.add_var
|
|
230
|
+
else
|
|
231
|
+
self.set_var
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
if Xmk.options[:verbose]
|
|
236
|
+
Xmk.print("$(#{@tokens[@i-1].lexeme}): " <<
|
|
237
|
+
" #{@env_stack.last[:"#{@tokens[@i-1].lexeme}"]}")
|
|
238
|
+
end
|
|
239
|
+
when Token::EOF
|
|
240
|
+
if @state == State::PLAT
|
|
241
|
+
self.leave_platform
|
|
242
|
+
else
|
|
243
|
+
break
|
|
244
|
+
end
|
|
245
|
+
else
|
|
246
|
+
end
|
|
247
|
+
@i += 1
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def leave_platform
|
|
252
|
+
i = @i
|
|
253
|
+
loop do
|
|
254
|
+
i -= 1
|
|
255
|
+
break if @tokens[i].type == Token::PLAT
|
|
256
|
+
end
|
|
257
|
+
if Xmk.options[:verbose]
|
|
258
|
+
Xmk.print("LEAVE PLATFORM: #{@tokens[i].lexeme}")
|
|
259
|
+
end
|
|
260
|
+
@state = State::TAR
|
|
261
|
+
@env_name = @env_memory
|
|
262
|
+
@env_stack.pop
|
|
263
|
+
@target_state[:"#{@env_name}"].push(@tokens[i].lexeme)
|
|
264
|
+
loop do
|
|
265
|
+
if @tokens[@i].type == Token::EOF
|
|
266
|
+
@i = @i_memory
|
|
267
|
+
break
|
|
268
|
+
end
|
|
269
|
+
break if @tokens[@i+1].type == Token::PLAT
|
|
270
|
+
@i += 1
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def leave_target
|
|
275
|
+
if Xmk.options[:verbose]
|
|
276
|
+
i = @i
|
|
277
|
+
loop do
|
|
278
|
+
i -= 1
|
|
279
|
+
break if @tokens[i].type == Token::TAR
|
|
280
|
+
end
|
|
281
|
+
Xmk.print("@LEAVE TARGET: #{@tokens[i].lexeme}")
|
|
282
|
+
end
|
|
283
|
+
@env_name = "root"
|
|
284
|
+
@state = State::ROOT
|
|
285
|
+
@env_stack.pop
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def env_stack_grow
|
|
289
|
+
h = Hash.new
|
|
290
|
+
@env_stack.last.each do |k,v|
|
|
291
|
+
h[k] = v.dup
|
|
292
|
+
end
|
|
293
|
+
@env_stack.push(h)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def set_var
|
|
297
|
+
@env_stack.last[:"#{@tokens[@i-1].lexeme}"] =
|
|
298
|
+
self.var_replace(@tokens[@i+1].lexeme)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def add_var
|
|
302
|
+
@env_stack.last[:"#{@tokens[@i-1].lexeme}"] <<
|
|
303
|
+
self.var_replace(@tokens[@i+1].lexeme)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def var_replace(str)
|
|
307
|
+
str2 = str.dup
|
|
308
|
+
str2.scan(/\${1}\({1}([A-Z_]+){1}\)/) do |m|
|
|
309
|
+
if @env_stack.last.has_key?(:"#{m[0]}")
|
|
310
|
+
str.gsub!("$(#{m[0]})", @env_stack.last[:"#{m[0]}"])
|
|
311
|
+
else
|
|
312
|
+
Xmk.error("Variable #{m[0]} not defined",
|
|
313
|
+
@tokens[@i+1].line)
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
str.gsub('$@', @target_name).gsub('$!',@plat_name)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def self.run(cmd, noprint = false)
|
|
320
|
+
unless Xmk.options[:disable_cmd]
|
|
321
|
+
if Xmk.options[:verbose]
|
|
322
|
+
Xmk.print("execute #{cmd}")
|
|
323
|
+
end
|
|
324
|
+
v = %x{#{cmd}}
|
|
325
|
+
return v if v.strip.empty?
|
|
326
|
+
puts v unless noprint
|
|
327
|
+
return v
|
|
328
|
+
end
|
|
329
|
+
nil
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def self.run_print_verbose_only(cmd, noprint = false)
|
|
333
|
+
unless Xmk.options[:disable_cmd]
|
|
334
|
+
if Xmk.options[:verbose]
|
|
335
|
+
Xmk.print("execute #{cmd}")
|
|
336
|
+
end
|
|
337
|
+
v = %x{#{cmd}}
|
|
338
|
+
return v if v.strip.empty?
|
|
339
|
+
if Xmk.options[:verbose]
|
|
340
|
+
puts v unless noprint
|
|
341
|
+
end
|
|
342
|
+
return v
|
|
343
|
+
end
|
|
344
|
+
nil
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
end
|
data/lib/xmk/token.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
class Token
|
|
2
|
+
attr_reader :type, :lexeme, :literal, :line
|
|
3
|
+
|
|
4
|
+
VAR = 0 # variable
|
|
5
|
+
SET = 1 # set var =
|
|
6
|
+
ADD = 2 # append var +=
|
|
7
|
+
TAR = 3 # target
|
|
8
|
+
WS = 4 # whitespace
|
|
9
|
+
CMD = 5 # commang
|
|
10
|
+
LAB = 6 # label
|
|
11
|
+
VAL = 7 # value
|
|
12
|
+
PLAT = 8 # platform
|
|
13
|
+
TARLINK = 9 # platform-target link
|
|
14
|
+
LBR = 10 # linebreak
|
|
15
|
+
|
|
16
|
+
EOF =9999
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def initialize(type, lexeme, line)
|
|
20
|
+
@type = type
|
|
21
|
+
@lexeme = lexeme
|
|
22
|
+
@line = line
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_s
|
|
26
|
+
"#{@type} #{@lexeme} #{@line}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
data/lib/xmk.rb
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require 'optparse'
|
|
2
|
+
require 'xmk/token.rb'
|
|
3
|
+
require 'xmk/lexer.rb'
|
|
4
|
+
require 'xmk/parser.rb'
|
|
5
|
+
|
|
6
|
+
module State
|
|
7
|
+
|
|
8
|
+
ROOT = 0
|
|
9
|
+
TAR = 1
|
|
10
|
+
PLAT = 2
|
|
11
|
+
QUIT = 3
|
|
12
|
+
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module Xmk
|
|
16
|
+
|
|
17
|
+
def self.main()
|
|
18
|
+
@@targets = Array.new
|
|
19
|
+
@@platforms = Array.new
|
|
20
|
+
@@labels = Array.new
|
|
21
|
+
options = {}
|
|
22
|
+
options[:verbose] = false
|
|
23
|
+
options[:skipcomp] = false
|
|
24
|
+
options[:disable_cmd] = false
|
|
25
|
+
optparse = OptionParser.new do|opts|
|
|
26
|
+
opts.banner = "Usage: xmk [options]"
|
|
27
|
+
opts.on('-h', '--help', 'Display this screen') do
|
|
28
|
+
puts opts
|
|
29
|
+
exit
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
opts.on('-t', '--target TARGET', 'Target') do |arg|
|
|
33
|
+
@@targets.push(arg)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
opts.on('-p', '--platform PLATFORM', 'Platform') do |arg|
|
|
37
|
+
@@platforms.push(arg)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
opts.on('-l', '--label LABEL', 'Label') do |arg|
|
|
41
|
+
@@labels.push(arg)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
opts.on('-v', '--verbose', 'Verbose mode') do
|
|
45
|
+
options[:verbose] = true
|
|
46
|
+
end
|
|
47
|
+
opts.on('-s', '--skip-compilation', 'Skip compilation') do
|
|
48
|
+
options[:skipcomp] = true
|
|
49
|
+
end
|
|
50
|
+
opts.on('-d', '--disable-commands', 'Disable commands') do
|
|
51
|
+
options[:disable_cmd] = true
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
optparse.parse!
|
|
55
|
+
|
|
56
|
+
@@options = options
|
|
57
|
+
|
|
58
|
+
lexer = Lexer.new
|
|
59
|
+
Parser.new(lexer.scan).parse
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.error(msg, l)
|
|
63
|
+
puts "#{msg} on line #{l}"
|
|
64
|
+
exit(65)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.options
|
|
68
|
+
@@options
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.labels
|
|
72
|
+
@@labels
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def self.platforms
|
|
76
|
+
@@platforms
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.targets
|
|
80
|
+
@@targets
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def self.print(msg)
|
|
84
|
+
puts "xmk: #{msg}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
data/syntax/rouge.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*- #
|
|
2
|
+
# # frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Rouge
|
|
5
|
+
module Lexers
|
|
6
|
+
class Xmk < RegexLexer
|
|
7
|
+
title 'xmk'
|
|
8
|
+
desc 'XMake Prototype'
|
|
9
|
+
tag 'xmk'
|
|
10
|
+
filenames 'xmkfile', '*.xmk'
|
|
11
|
+
|
|
12
|
+
state :whitespace do
|
|
13
|
+
rule %r/\s+/m, Text::Whitespace
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
#rule %r/(?<==).*(?=($|\\|#))/, Name
|
|
17
|
+
|
|
18
|
+
state :root do
|
|
19
|
+
mixin :whitespace
|
|
20
|
+
|
|
21
|
+
rule /#.*/, Comment::Single
|
|
22
|
+
rule %r/[A-Z]+(?=\+|\=){1}/, Text
|
|
23
|
+
rule %r/[+=]/, Operator
|
|
24
|
+
#rule %r/(?<=\=).*$/, String
|
|
25
|
+
rule %r/[_a-z0-9]+:/, Str
|
|
26
|
+
rule %r/@[a-z0-9]+/, Name::Function
|
|
27
|
+
rule %r/\$(~|!|@|&{1,2})/, Name::Variable
|
|
28
|
+
rule %r/\$\([A-Z]+\)/, Name
|
|
29
|
+
rule %r/\\/, Str::Escape
|
|
30
|
+
rule %r/.*/, Name
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/syntax/vim.vim
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
" detect file
|
|
2
|
+
autocmd BufRead,BufNewFile xmkfile setlocal filetype=xmk
|
|
3
|
+
|
|
4
|
+
" syntax highlighting
|
|
5
|
+
syn keyword xmkKeyword _cmd _plat _pre _post _link _compile _rule
|
|
6
|
+
|
|
7
|
+
syntax match xmkComment "\v#.*$"
|
|
8
|
+
"syntax match xmkVar "\v[A-Z]"
|
|
9
|
+
"syntax match xmkVar "\v[A-Z]+((\+|\=){1})\@="
|
|
10
|
+
syntax match xmkVar "\v\zs[A-Z]+\ze(\+|\=){1}"
|
|
11
|
+
syntax match xmkTarget "\v\@[a-z]+"
|
|
12
|
+
syntax match xmkKeyword "\v[a-z0-9]+:{1}"
|
|
13
|
+
syntax match xmkReplace "\v\$\("
|
|
14
|
+
syntax match xmkReplace "\v\("
|
|
15
|
+
syntax match xmkReplace "\v\)"
|
|
16
|
+
syntax match xmkReplace "\v\$\@"
|
|
17
|
+
syntax match xmkReplace "\v\$\!"
|
|
18
|
+
syntax match xmkReplace "\v\$\~"
|
|
19
|
+
syntax match xmkReplace "\v\$\&"
|
|
20
|
+
syntax match xmkReplace "\v\$\&\&"
|
|
21
|
+
syntax match xmkNewlineTerm "\v\\"
|
|
22
|
+
syntax match xmkOperator "\v\+"
|
|
23
|
+
syntax match xmkOperator "\v\="
|
|
24
|
+
|
|
25
|
+
highlight link xmkComment Comment
|
|
26
|
+
highlight link xmkVar Type
|
|
27
|
+
highlight link xmkTarget Structure
|
|
28
|
+
highlight link xmkKeyword Keyword
|
|
29
|
+
highlight link xmkReplace Type
|
|
30
|
+
highlight link xmkNewlineTerm Delimiter
|
|
31
|
+
highlight link xmkOperator Operator
|
|
32
|
+
|
|
33
|
+
hi Structure ctermfg=darkcyan
|
|
34
|
+
hi Type ctermfg=darkmagenta
|
|
35
|
+
hi Operator ctermfg=darkred
|
metadata
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: xmk
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.2
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- ryu
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2022-03-28 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: 'A working protorype for yet another build automation tool inspired by
|
|
14
|
+
Make. '
|
|
15
|
+
email: ryu@tohya.net
|
|
16
|
+
executables:
|
|
17
|
+
- xmk
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- bin/xmk
|
|
22
|
+
- lib/xmk.rb
|
|
23
|
+
- lib/xmk/lexer.rb
|
|
24
|
+
- lib/xmk/parser.rb
|
|
25
|
+
- lib/xmk/token.rb
|
|
26
|
+
- syntax/rouge.rb
|
|
27
|
+
- syntax/vim.vim
|
|
28
|
+
homepage: https://www.tohya.net
|
|
29
|
+
licenses:
|
|
30
|
+
- MIT
|
|
31
|
+
metadata: {}
|
|
32
|
+
post_install_message:
|
|
33
|
+
rdoc_options: []
|
|
34
|
+
require_paths:
|
|
35
|
+
- lib
|
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
requirements: []
|
|
47
|
+
rubyforge_project:
|
|
48
|
+
rubygems_version: 2.7.6.2
|
|
49
|
+
signing_key:
|
|
50
|
+
specification_version: 4
|
|
51
|
+
summary: Build automation tool for C and C++ projects
|
|
52
|
+
test_files: []
|