source: webkit/trunk/Source/JavaScriptCore/offlineasm/parser.rb

Last change on this file was 295134, checked in by Elliott Williams, 3 years ago

Fix build failures introduced in "[Xcode] Prevent STP and other self-contained builds from overwriting content in the macOS SDK"
https://bugs.webkit.org/show_bug.cgi?id=240408

Reviewed by Alexey Proskuryakov.

Revert "Revert "[Xcode] Prevent STP and other self-contained builds from overwriting content in the macOS SDK""
This reverts commit 680fe6580f261df69a607a33b6252f3e19704169.

Apply small fixups across xcconfig files to address regressions:

  • Fix SYSTEM_HEADER_SEARCH_PATHS not referring to Catlyst's include directory.
  • Replace /usr/local/include literal in PROFILE_DATA_PATH with WK_ALTERNATE_WEBKIT_SDK_PATH and WK_LIBRARY_HEADERS_FOLDER_PATH to select the correct profdata for Catalyst or self-contained builds.
  • Source/JavaScriptCore/Configurations/Base.xcconfig: Clean up an old WK_STATICLIB_INSTALL_PREFIX, replacing it with WK_LIBRARY_HEADERS_FOLDER_PATH.
  • Source/JavaScriptCore/Configurations/JavaScriptCore.xcconfig:
  • Source/WebCore/Configurations/WebCore.xcconfig:
  • Source/WebCore/PAL/Configurations/PAL.xcconfig:
  • Source/WebCore/PAL/PAL.xcodeproj/project.pbxproj: PAL's SDKVariant.xcconfig was not included in the project for some reason. This is fine for building (as it's still includable) but prevents Xcode from indexing it. Add it.
  • Source/WebGPU/Configurations/WGSL.xcconfig:
  • Source/WebGPU/Configurations/WebGPU.xcconfig:
  • Source/WebKit/Configurations/BaseTarget.xcconfig:
  • Source/WebKit/Configurations/SandboxProfiles.xcconfig: Clean up an old WK_STATICLIB_INSTALL_PREFIX setting. We don't need to redirect sandbox profiles to a separate directory, like we do headers and static libraries, so leave these paths hard coded to /usr/local/include.
  • Source/WebKitLegacy/mac/Configurations/WebKitLegacy.xcconfig:

Canonical link: https://commits.webkit.org/251225@main

File size: 31.8 KB
Line 
1# Copyright (C) 2011-2021 Apple Inc. All rights reserved.
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions
5# are met:
6# 1. Redistributions of source code must retain the above copyright
7# notice, this list of conditions and the following disclaimer.
8# 2. Redistributions in binary form must reproduce the above copyright
9# notice, this list of conditions and the following disclaimer in the
10# documentation and/or other materials provided with the distribution.
11#
12# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
13# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
14# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
15# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
16# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
22# THE POSSIBILITY OF SUCH DAMAGE.
23
24require "config"
25require "ast"
26require "instructions"
27require "pathname"
28require "registers"
29require "self_hash"
30
31class SourceFile
32 @@fileNames = []
33
34 attr_reader :name, :basename, :fileNumber
35
36 def SourceFile.outputDotFileList(outp)
37 @@fileNames.each_index {
38 | index |
39 $asm.putStr "\".file #{index+1} \\\"#{@@fileNames[index]}\\\"\\n\""
40 }
41 end
42
43 def initialize(fileName)
44 @name = Pathname.new(fileName)
45 @basename = File.basename(fileName)
46 pathName = "#{@name.realpath}"
47 fileNumber = @@fileNames.index(pathName)
48 if not fileNumber
49 @@fileNames << pathName
50 fileNumber = @@fileNames.length
51 else
52 fileNumber += 1 # File numbers are 1 based
53 end
54 @fileNumber = fileNumber
55 end
56end
57
58class CodeOrigin
59 attr_reader :lineNumber
60
61 def initialize(sourceFile, lineNumber)
62 @sourceFile = sourceFile
63 @lineNumber = lineNumber
64 end
65
66 def fileName
67 @sourceFile.name
68 end
69
70 def debugDirective
71 $emitWinAsm ? nil : "\".loc #{@sourceFile.fileNumber} #{lineNumber}\\n\""
72 end
73
74 def to_s
75 "#{@sourceFile.basename}:#{lineNumber}"
76 end
77end
78
79class IncludeFile
80 @@includeDirs = []
81
82 attr_reader :fileName
83
84 def initialize(moduleName, defaultDir)
85 directory = nil
86 @@includeDirs.each {
87 | includePath |
88 fileName = File.join(includePath, moduleName + ".asm")
89 directory = includePath unless not File.file?(fileName)
90 }
91 if not directory
92 directory = defaultDir
93 end
94
95 @fileName = File.join(directory, moduleName + ".asm")
96 end
97
98 def self.processIncludeOptions()
99 while ARGV[0][/^-I/]
100 path = ARGV.shift[2..-1]
101 if not path
102 path = ARGV.shift
103 end
104 @@includeDirs << (path + "/")
105 end
106 end
107end
108
109class Token
110 attr_reader :codeOrigin, :string
111
112 def initialize(codeOrigin, string)
113 @codeOrigin = codeOrigin
114 @string = string
115 end
116
117 def ==(other)
118 if other.is_a? Token
119 @string == other.string
120 else
121 @string == other
122 end
123 end
124
125 def =~(other)
126 @string =~ other
127 end
128
129 def to_s
130 "#{@string.inspect} at #{codeOrigin}"
131 end
132
133 def parseError(*comment)
134 if comment.empty?
135 raise "Parse error: #{to_s}"
136 else
137 raise "Parse error: #{to_s}: #{comment[0]}"
138 end
139 end
140end
141
142class Annotation
143 attr_reader :codeOrigin, :type, :string
144 def initialize(codeOrigin, type, string)
145 @codeOrigin = codeOrigin
146 @type = type
147 @string = string
148 end
149end
150
151#
152# The lexer. Takes a string and returns an array of tokens.
153#
154
155def lex(str, file)
156 result = []
157 lineNumber = 1
158 annotation = nil
159 whitespaceFound = false
160 while not str.empty?
161 case str
162 when /\A\#([^\n]*)/
163 # comment, ignore
164 when /\A\/\/\ ?([^\n]*)/
165 # annotation
166 annotation = $1
167 annotationType = whitespaceFound ? :local : :global
168 when /\A\n/
169 # We've found a '\n'. Emit the last comment recorded if appropriate:
170 # We need to parse annotations regardless of whether the backend does
171 # anything with them or not. This is because the C++ backend may make
172 # use of this for its cloopDo debugging utility even if
173 # enableInstrAnnotations is not enabled.
174 if annotation
175 result << Annotation.new(CodeOrigin.new(file, lineNumber),
176 annotationType, annotation)
177 annotation = nil
178 end
179 result << Token.new(CodeOrigin.new(file, lineNumber), $&)
180 lineNumber += 1
181 when /\A[a-zA-Z%]([a-zA-Z0-9_.%]*)/
182 result << Token.new(CodeOrigin.new(file, lineNumber), $&)
183 when /\A\.([a-zA-Z0-9_]*)/
184 result << Token.new(CodeOrigin.new(file, lineNumber), $&)
185 when /\A_([a-zA-Z0-9_%]*)/
186 result << Token.new(CodeOrigin.new(file, lineNumber), $&)
187 when /\A([ \t]+)/
188 # whitespace, ignore
189 whitespaceFound = true
190 str = $~.post_match
191 next
192 when /\A0x([0-9a-fA-F]+)/
193 result << Token.new(CodeOrigin.new(file, lineNumber), $&.hex.to_s)
194 when /\A0([0-7]+)/
195 result << Token.new(CodeOrigin.new(file, lineNumber), $&.oct.to_s)
196 when /\A([0-9]+)/
197 result << Token.new(CodeOrigin.new(file, lineNumber), $&)
198 when /\A::/
199 result << Token.new(CodeOrigin.new(file, lineNumber), $&)
200 when /\A[:,\(\)\[\]=\+\-~\|&^*]/
201 result << Token.new(CodeOrigin.new(file, lineNumber), $&)
202 when /\A".*"/
203 result << Token.new(CodeOrigin.new(file, lineNumber), $&)
204 when /\?/
205 result << Token.new(CodeOrigin.new(file, lineNumber), $&)
206 else
207 raise "Lexer error at #{CodeOrigin.new(file, lineNumber).to_s}, unexpected sequence #{str[0..20].inspect}"
208 end
209 whitespaceFound = false
210 str = $~.post_match
211 end
212 result
213end
214
215#
216# Token identification.
217#
218
219def isRegister(token)
220 token =~ REGISTER_PATTERN
221end
222
223def isInstruction(token)
224 INSTRUCTION_SET.member? token.string
225end
226
227def isKeyword(token)
228 token =~ /\A((true)|(false)|(if)|(then)|(else)|(elsif)|(end)|(and)|(or)|(not)|(global)|(macro)|(const)|(constexpr)|(sizeof)|(error)|(include))\Z/ or
229 token =~ REGISTER_PATTERN or
230 isInstruction(token)
231end
232
233def isIdentifier(token)
234 token =~ /\A[a-zA-Z%]([a-zA-Z0-9_.%]*)\Z/ and not isKeyword(token)
235end
236
237def isLabel(token)
238 token =~ /\A_([a-zA-Z0-9_%]*)\Z/
239end
240
241def isLocalLabel(token)
242 token =~ /\A\.([a-zA-Z0-9_]*)\Z/
243end
244
245def isVariable(token)
246 isIdentifier(token) or isRegister(token)
247end
248
249def isInteger(token)
250 token =~ /\A[0-9]/
251end
252
253def isString(token)
254 token =~ /\A".*"/
255end
256
257#
258# The parser. Takes an array of tokens and returns an AST. Methods
259# other than parse(tokens) are not for public consumption.
260#
261
262class Parser
263 def initialize(data, fileName, options, sources=nil)
264 @tokens = lex(data, fileName)
265 @idx = 0
266 @annotation = nil
267 # FIXME: CMake does not currently set BUILT_PRODUCTS_DIR.
268 # https://bugs.webkit.org/show_bug.cgi?id=229340
269 @buildProductsDirectory = ENV['BUILT_PRODUCTS_DIR'];
270 @headersFolderPath = ENV['WK_LIBRARY_HEADERS_FOLDER_PATH'];
271 @options = options
272 @sources = sources
273 end
274
275 def parseError(*comment)
276 if @tokens[@idx]
277 @tokens[@idx].parseError(*comment)
278 else
279 if comment.empty?
280 raise "Parse error at end of file"
281 else
282 raise "Parse error at end of file: #{comment[0]}"
283 end
284 end
285 end
286
287 def consume(regexp)
288 if regexp
289 parseError unless @tokens[@idx] =~ regexp
290 else
291 parseError unless @idx == @tokens.length
292 end
293 @idx += 1
294 end
295
296 def skipNewLine
297 while @tokens[@idx] == "\n"
298 @idx += 1
299 end
300 end
301
302 def parsePredicateAtom
303 if @tokens[@idx] == "not"
304 codeOrigin = @tokens[@idx].codeOrigin
305 @idx += 1
306 Not.new(codeOrigin, parsePredicateAtom)
307 elsif @tokens[@idx] == "("
308 @idx += 1
309 skipNewLine
310 result = parsePredicate
311 parseError unless @tokens[@idx] == ")"
312 @idx += 1
313 result
314 elsif @tokens[@idx] == "true"
315 result = True.instance
316 @idx += 1
317 result
318 elsif @tokens[@idx] == "false"
319 result = False.instance
320 @idx += 1
321 result
322 elsif isIdentifier @tokens[@idx]
323 result = Setting.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string)
324 @idx += 1
325 result
326 else
327 parseError
328 end
329 end
330
331 def parsePredicateAnd
332 result = parsePredicateAtom
333 while @tokens[@idx] == "and"
334 codeOrigin = @tokens[@idx].codeOrigin
335 @idx += 1
336 skipNewLine
337 right = parsePredicateAtom
338 result = And.new(codeOrigin, result, right)
339 end
340 result
341 end
342
343 def parsePredicate
344 # some examples of precedence:
345 # not a and b -> (not a) and b
346 # a and b or c -> (a and b) or c
347 # a or b and c -> a or (b and c)
348
349 result = parsePredicateAnd
350 while @tokens[@idx] == "or"
351 codeOrigin = @tokens[@idx].codeOrigin
352 @idx += 1
353 skipNewLine
354 right = parsePredicateAnd
355 result = Or.new(codeOrigin, result, right)
356 end
357 result
358 end
359
360 def parseVariable
361 if isRegister(@tokens[@idx])
362 if @tokens[@idx] =~ FPR_PATTERN || @tokens[@idx] =~ WASM_FPR_PATTERN
363 result = FPRegisterID.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string)
364 else
365 result = RegisterID.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string)
366 end
367 elsif isIdentifier(@tokens[@idx])
368 result = Variable.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string)
369 else
370 parseError
371 end
372 @idx += 1
373 result
374 end
375
376 def parseConstExpr
377 if @tokens[@idx] == "constexpr"
378 @idx += 1
379 skipNewLine
380 if @tokens[@idx] == "("
381 codeOrigin, text = parseTextInParens
382 text = text.join
383 else
384 codeOrigin, text = parseColonColon
385 text = text.join("::")
386 end
387 ConstExpr.forName(codeOrigin, text)
388 else
389 parseError
390 end
391 end
392
393 def parseAddress(offset)
394 parseError unless @tokens[@idx] == "["
395 codeOrigin = @tokens[@idx].codeOrigin
396
397 # Three possibilities:
398 # [] -> AbsoluteAddress
399 # [a] -> Address
400 # [a,b] -> BaseIndex with scale = 1
401 # [a,b,c] -> BaseIndex
402
403 @idx += 1
404 if @tokens[@idx] == "]"
405 @idx += 1
406 return AbsoluteAddress.new(codeOrigin, offset)
407 end
408 a = parseVariable
409 if @tokens[@idx] == "]"
410 result = Address.new(codeOrigin, a, offset)
411 else
412 parseError unless @tokens[@idx] == ","
413 @idx += 1
414 b = parseVariable
415 if @tokens[@idx] == "]"
416 result = BaseIndex.new(codeOrigin, a, b, Immediate.new(codeOrigin, 1), offset)
417 else
418 parseError unless @tokens[@idx] == ","
419 @idx += 1
420 if ["1", "2", "4", "8"].member? @tokens[@idx].string
421 c = Immediate.new(codeOrigin, @tokens[@idx].string.to_i)
422 @idx += 1
423 elsif @tokens[@idx] == "constexpr"
424 c = parseConstExpr
425 else
426 c = parseVariable
427 end
428 parseError unless @tokens[@idx] == "]"
429 result = BaseIndex.new(codeOrigin, a, b, c, offset)
430 end
431 end
432 @idx += 1
433 result
434 end
435
436 def parseColonColon
437 skipNewLine
438 codeOrigin = @tokens[@idx].codeOrigin
439 parseError unless isIdentifier @tokens[@idx]
440 names = [@tokens[@idx].string]
441 @idx += 1
442 while @tokens[@idx] == "::"
443 @idx += 1
444 parseError unless isIdentifier @tokens[@idx]
445 names << @tokens[@idx].string
446 @idx += 1
447 end
448 raise if names.empty?
449 [codeOrigin, names]
450 end
451
452 def parseTextInParens
453 skipNewLine
454 codeOrigin = @tokens[@idx].codeOrigin
455 raise unless @tokens[@idx] == "("
456 @idx += 1
457 # need at least one item
458 raise if @tokens[@idx] == ")"
459 numEnclosedParens = 0
460 text = []
461 while @tokens[@idx] != ")" || numEnclosedParens > 0
462 if @tokens[@idx] == "("
463 numEnclosedParens += 1
464 elsif @tokens[@idx] == ")"
465 numEnclosedParens -= 1
466 end
467
468 text << @tokens[@idx].string
469 @idx += 1
470 end
471 @idx += 1
472 return [codeOrigin, text]
473 end
474
475
476 def parseExpressionAtom
477 skipNewLine
478 if @tokens[@idx] == "-"
479 @idx += 1
480 NegImmediate.new(@tokens[@idx - 1].codeOrigin, parseExpressionAtom)
481 elsif @tokens[@idx] == "~"
482 @idx += 1
483 BitnotImmediate.new(@tokens[@idx - 1].codeOrigin, parseExpressionAtom)
484 elsif @tokens[@idx] == "("
485 @idx += 1
486 result = parseExpression
487 parseError unless @tokens[@idx] == ")"
488 @idx += 1
489 result
490 elsif isInteger @tokens[@idx]
491 result = Immediate.new(@tokens[@idx].codeOrigin, @tokens[@idx].string.to_i)
492 @idx += 1
493 result
494 elsif isString @tokens[@idx]
495 result = StringLiteral.new(@tokens[@idx].codeOrigin, @tokens[@idx].string)
496 @idx += 1
497 result
498 elsif isIdentifier @tokens[@idx]
499 codeOrigin, names = parseColonColon
500 if names.size > 1
501 StructOffset.forField(codeOrigin, names[0..-2].join('::'), names[-1])
502 else
503 Variable.forName(codeOrigin, names[0])
504 end
505 elsif isRegister @tokens[@idx]
506 parseVariable
507 elsif @tokens[@idx] == "sizeof"
508 @idx += 1
509 codeOrigin, names = parseColonColon
510 Sizeof.forName(codeOrigin, names.join('::'))
511 elsif @tokens[@idx] == "constexpr"
512 parseConstExpr
513 elsif isLabel @tokens[@idx]
514 result = LabelReference.new(@tokens[@idx].codeOrigin, Label.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string))
515 @idx += 1
516 result
517 elsif isLocalLabel @tokens[@idx]
518 result = LocalLabelReference.new(@tokens[@idx].codeOrigin, LocalLabel.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string))
519 @idx += 1
520 result
521 else
522 parseError
523 end
524 end
525
526 def parseExpressionMul
527 skipNewLine
528 result = parseExpressionAtom
529 while @tokens[@idx] == "*"
530 if @tokens[@idx] == "*"
531 @idx += 1
532 result = MulImmediates.new(@tokens[@idx - 1].codeOrigin, result, parseExpressionAtom)
533 else
534 raise
535 end
536 end
537 result
538 end
539
540 def couldBeExpression
541 @tokens[@idx] == "-" or @tokens[@idx] == "~" or @tokens[@idx] == "sizeof" or @tokens[@idx] == "constexpr" or isInteger(@tokens[@idx]) or isString(@tokens[@idx]) or isVariable(@tokens[@idx]) or isLabel(@tokens[@idx]) or @tokens[@idx] == "("
542 end
543
544 def parseExpressionAdd
545 skipNewLine
546 result = parseExpressionMul
547 while @tokens[@idx] == "+" or @tokens[@idx] == "-"
548 if @tokens[@idx] == "+"
549 @idx += 1
550 result = AddImmediates.new(@tokens[@idx - 1].codeOrigin, result, parseExpressionMul)
551 elsif @tokens[@idx] == "-"
552 @idx += 1
553 result = SubImmediates.new(@tokens[@idx - 1].codeOrigin, result, parseExpressionMul)
554 else
555 raise
556 end
557 end
558 result
559 end
560
561 def parseExpressionAnd
562 skipNewLine
563 result = parseExpressionAdd
564 while @tokens[@idx] == "&"
565 @idx += 1
566 result = AndImmediates.new(@tokens[@idx - 1].codeOrigin, result, parseExpressionAdd)
567 end
568 result
569 end
570
571 def parseExpression
572 skipNewLine
573 result = parseExpressionAnd
574 while @tokens[@idx] == "|" or @tokens[@idx] == "^"
575 if @tokens[@idx] == "|"
576 @idx += 1
577 result = OrImmediates.new(@tokens[@idx - 1].codeOrigin, result, parseExpressionAnd)
578 elsif @tokens[@idx] == "^"
579 @idx += 1
580 result = XorImmediates.new(@tokens[@idx - 1].codeOrigin, result, parseExpressionAnd)
581 else
582 raise
583 end
584 end
585 result
586 end
587
588 def parseOperand(comment)
589 skipNewLine
590 if couldBeExpression
591 expr = parseExpression
592 if @tokens[@idx] == "["
593 parseAddress(expr)
594 else
595 expr
596 end
597 elsif @tokens[@idx] == "["
598 parseAddress(Immediate.new(@tokens[@idx].codeOrigin, 0))
599 elsif isLocalLabel @tokens[@idx]
600 result = LocalLabelReference.new(@tokens[@idx].codeOrigin, LocalLabel.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string))
601 @idx += 1
602 result
603 else
604 parseError(comment)
605 end
606 end
607
608 def parseMacroVariables
609 skipNewLine
610 consume(/\A\(\Z/)
611 variables = []
612 loop {
613 skipNewLine
614 if @tokens[@idx] == ")"
615 @idx += 1
616 break
617 elsif isIdentifier(@tokens[@idx])
618 variables << Variable.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string)
619 @idx += 1
620 skipNewLine
621 if @tokens[@idx] == ")"
622 @idx += 1
623 break
624 elsif @tokens[@idx] == ","
625 @idx += 1
626 else
627 parseError
628 end
629 else
630 parseError
631 end
632 }
633 variables
634 end
635
636 def parseSequence(final, comment)
637 firstCodeOrigin = @tokens[@idx].codeOrigin
638 list = []
639 loop {
640 if @tokens[@idx].is_a? Annotation
641 # This is the only place where we can encounter a global
642 # annotation, and hence need to be able to distinguish between
643 # them.
644 # globalAnnotations are the ones that start from column 0. All
645 # others are considered localAnnotations. The only reason to
646 # distinguish between them is so that we can format the output
647 # nicely as one would expect.
648
649 codeOrigin = @tokens[@idx].codeOrigin
650 annotationOpcode = (@tokens[@idx].type == :global) ? "globalAnnotation" : "localAnnotation"
651 list << Instruction.new(codeOrigin, annotationOpcode, [], @tokens[@idx].string)
652 @annotation = nil
653 @idx += 2 # Consume the newline as well.
654 elsif (@idx == @tokens.length and not final) or (final and @tokens[@idx] =~ final)
655 break
656 elsif @tokens[@idx] == "\n"
657 # ignore
658 @idx += 1
659 elsif @tokens[@idx] == "const"
660 @idx += 1
661 parseError unless isVariable @tokens[@idx]
662 variable = Variable.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string)
663 @idx += 1
664 parseError unless @tokens[@idx] == "="
665 @idx += 1
666 value = parseOperand("while inside of const #{variable.name}")
667 list << ConstDecl.new(@tokens[@idx].codeOrigin, variable, value)
668 elsif @tokens[@idx] == "error"
669 list << Error.new(@tokens[@idx].codeOrigin)
670 @idx += 1
671 elsif @tokens[@idx] == "if"
672 codeOrigin = @tokens[@idx].codeOrigin
673 @idx += 1
674 skipNewLine
675 predicate = parsePredicate
676 consume(/\A((then)|(\n))\Z/)
677 skipNewLine
678 ifThenElse = IfThenElse.new(codeOrigin, predicate, parseSequence(/\A((else)|(end)|(elsif))\Z/, "while inside of \"if #{predicate.dump}\""))
679 list << ifThenElse
680 while @tokens[@idx] == "elsif"
681 codeOrigin = @tokens[@idx].codeOrigin
682 @idx += 1
683 skipNewLine
684 predicate = parsePredicate
685 consume(/\A((then)|(\n))\Z/)
686 skipNewLine
687 elseCase = IfThenElse.new(codeOrigin, predicate, parseSequence(/\A((else)|(end)|(elsif))\Z/, "while inside of \"if #{predicate.dump}\""))
688 ifThenElse.elseCase = elseCase
689 ifThenElse = elseCase
690 end
691 if @tokens[@idx] == "else"
692 @idx += 1
693 ifThenElse.elseCase = parseSequence(/\Aend\Z/, "while inside of else case for \"if #{predicate.dump}\"")
694 @idx += 1
695 else
696 parseError unless @tokens[@idx] == "end"
697 @idx += 1
698 end
699 elsif @tokens[@idx] == "macro"
700 codeOrigin = @tokens[@idx].codeOrigin
701 @idx += 1
702 skipNewLine
703 parseError unless isIdentifier(@tokens[@idx])
704 name = @tokens[@idx].string
705 @idx += 1
706 variables = parseMacroVariables
707 body = parseSequence(/\Aend\Z/, "while inside of macro #{name}")
708 @idx += 1
709 list << Macro.new(codeOrigin, name, variables, body)
710 elsif @tokens[@idx] == "global"
711 codeOrigin = @tokens[@idx].codeOrigin
712 @idx += 1
713 skipNewLine
714 parseError unless isLabel(@tokens[@idx])
715 name = @tokens[@idx].string
716 @idx += 1
717 Label.setAsGlobal(codeOrigin, name)
718 elsif isInstruction @tokens[@idx]
719 codeOrigin = @tokens[@idx].codeOrigin
720 name = @tokens[@idx].string
721 @idx += 1
722 if (not final and @idx == @tokens.size) or (final and @tokens[@idx] =~ final)
723 # Zero operand instruction, and it's the last one.
724 list << Instruction.new(codeOrigin, name, [], @annotation)
725 @annotation = nil
726 break
727 elsif @tokens[@idx].is_a? Annotation
728 list << Instruction.new(codeOrigin, name, [], @tokens[@idx].string)
729 @annotation = nil
730 @idx += 2 # Consume the newline as well.
731 elsif @tokens[@idx] == "\n"
732 # Zero operand instruction.
733 list << Instruction.new(codeOrigin, name, [], @annotation)
734 @annotation = nil
735 @idx += 1
736 else
737 # It's definitely an instruction, and it has at least one operand.
738 operands = []
739 endOfSequence = false
740 loop {
741 operands << parseOperand("while inside of instruction #{name}")
742 if (not final and @idx == @tokens.size) or (final and @tokens[@idx] =~ final)
743 # The end of the instruction and of the sequence.
744 endOfSequence = true
745 break
746 elsif @tokens[@idx] == ","
747 # Has another operand.
748 @idx += 1
749 elsif @tokens[@idx].is_a? Annotation
750 @annotation = @tokens[@idx].string
751 @idx += 2 # Consume the newline as well.
752 break
753 elsif @tokens[@idx] == "\n"
754 # The end of the instruction.
755 @idx += 1
756 break
757 else
758 parseError("Expected a comma, newline, or #{final} after #{operands.last.dump}")
759 end
760 }
761 list << Instruction.new(codeOrigin, name, operands, @annotation)
762 @annotation = nil
763 if endOfSequence
764 break
765 end
766 end
767
768 # Check for potential macro invocation:
769 elsif isIdentifier @tokens[@idx]
770 codeOrigin = @tokens[@idx].codeOrigin
771 name = @tokens[@idx].string
772 @idx += 1
773 if @tokens[@idx] == "("
774 # Macro invocation.
775 @idx += 1
776 operands = []
777 skipNewLine
778 if @tokens[@idx] == ")"
779 @idx += 1
780 else
781 loop {
782 skipNewLine
783 if @tokens[@idx] == "macro"
784 # It's a macro lambda!
785 codeOriginInner = @tokens[@idx].codeOrigin
786 @idx += 1
787 variables = parseMacroVariables
788 body = parseSequence(/\Aend\Z/, "while inside of anonymous macro passed as argument to #{name}")
789 @idx += 1
790 operands << Macro.new(codeOriginInner, nil, variables, body)
791 else
792 operands << parseOperand("while inside of macro call to #{name}")
793 end
794 skipNewLine
795 if @tokens[@idx] == ")"
796 @idx += 1
797 break
798 elsif @tokens[@idx] == ","
799 @idx += 1
800 else
801 parseError "Unexpected #{@tokens[@idx].string.inspect} while parsing invocation of macro #{name}"
802 end
803 }
804 end
805 # Check if there's a trailing annotation after the macro invoke:
806 if @tokens[@idx].is_a? Annotation
807 @annotation = @tokens[@idx].string
808 @idx += 2 # Consume the newline as well.
809 end
810 list << MacroCall.new(codeOrigin, name, operands, @annotation)
811 @annotation = nil
812 else
813 parseError "Expected \"(\" after #{name}"
814 end
815 elsif isLabel @tokens[@idx] or isLocalLabel @tokens[@idx]
816 codeOrigin = @tokens[@idx].codeOrigin
817 name = @tokens[@idx].string
818 @idx += 1
819 parseError unless @tokens[@idx] == ":"
820 # It's a label.
821 if isLabel name
822 list << Label.forName(codeOrigin, name, true)
823 else
824 list << LocalLabel.forName(codeOrigin, name)
825 end
826 @idx += 1
827 elsif @tokens[@idx] == "include"
828 @idx += 1
829 isOptional = false
830 if @tokens[@idx] == "?"
831 isOptional = true
832 @idx += 1
833 end
834 parseError unless isIdentifier(@tokens[@idx])
835 moduleName = @tokens[@idx].string
836 @idx += 1
837 if @options[:webkit_additions_path]
838 additionsDirectoryName = @options[:webkit_additions_path]
839 else
840 additionsDirectoryName = "#{@buildProductsDirectory}#{@headersFolderPath}/WebKitAdditions/"
841 end
842 fileName = IncludeFile.new(moduleName, additionsDirectoryName).fileName
843 if not File.exists?(fileName)
844 fileName = IncludeFile.new(moduleName, @tokens[@idx].codeOrigin.fileName.dirname).fileName
845 end
846 fileExists = File.exists?(fileName)
847 raise "File not found: #{fileName}" if not fileExists and not isOptional
848 list << parse(fileName, @options, @sources) if fileExists
849 else
850 parseError "Expecting terminal #{final} #{comment}"
851 end
852 }
853 Sequence.new(firstCodeOrigin, list)
854 end
855
856 def parseIncludes(final, comment, options)
857 firstCodeOrigin = @tokens[@idx].codeOrigin
858 fileList = []
859 fileList << @tokens[@idx].codeOrigin.fileName
860 loop {
861 if (@idx == @tokens.length and not final) or (final and @tokens[@idx] =~ final)
862 break
863 elsif @tokens[@idx] == "include"
864 @idx += 1
865 isOptional = false
866 if @tokens[@idx] == "?"
867 isOptional = true
868 @idx += 1
869 end
870 parseError unless isIdentifier(@tokens[@idx])
871 moduleName = @tokens[@idx].string
872 @idx += 1
873 if @options[:webkit_additions_path]
874 additionsDirectoryName = @options[:webkit_additions_path]
875 else
876 additionsDirectoryName = "#{@buildProductsDirectory}#{@headersFolderPath}/WebKitAdditions/"
877 end
878 fileName = IncludeFile.new(moduleName, additionsDirectoryName).fileName
879 if not File.exists?(fileName)
880 fileName = IncludeFile.new(moduleName, @tokens[@idx].codeOrigin.fileName.dirname).fileName
881 end
882 fileExists = File.exists?(fileName)
883 raise "File not found: #{fileName}" if not fileExists and not isOptional
884 if fileExists
885 parser = Parser.new(readTextFile(fileName), SourceFile.new(fileName), options)
886 fileList << parser.parseIncludes(nil, "", options)
887 end
888 else
889 @idx += 1
890 end
891 }
892
893 return fileList
894 end
895end
896
897def readTextFile(fileName)
898 data = IO::read(fileName)
899
900 # On Windows, files may contain CRLF line endings (for example, git client might
901 # automatically replace \n with \r\n on Windows) which will fail our parsing.
902 # Thus, we'll just remove all \r from the data (keeping just the \n characters)
903 data.delete!("\r")
904
905 return data
906end
907
908def parseData(data, fileName, options, sources)
909 parser = Parser.new(data, SourceFile.new(fileName), options, sources)
910 parser.parseSequence(nil, "")
911end
912
913def parse(fileName, options, sources=nil)
914 sources << fileName if sources
915 parseData(readTextFile(fileName), fileName, options, sources)
916end
917
918def parseHash(fileName, options)
919 parser = Parser.new(readTextFile(fileName), SourceFile.new(fileName), options)
920 fileList = parser.parseIncludes(nil, "", options)
921 fileList.flatten!
922 fileListHash(fileList)
923end
924
Note: See TracBrowser for help on using the repository browser.