]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - offlineasm/parser.rb
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / offlineasm / parser.rb
index 11863c72468dc944860922598ea60ceebb377b6a..a122a68c4c3ad8e6ae20e8819d53ceb2120f45a8 100644 (file)
@@ -21,6 +21,7 @@
 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 # THE POSSIBILITY OF SUCH DAMAGE.
 
+require "config"
 require "ast"
 require "instructions"
 require "pathname"
@@ -40,6 +41,36 @@ class CodeOrigin
     end
 end
 
+class IncludeFile
+    @@includeDirs = []
+
+    attr_reader :fileName
+
+    def initialize(moduleName, defaultDir)
+        directory = nil
+        @@includeDirs.each {
+            | includePath |
+            fileName = includePath + (moduleName + ".asm")
+            directory = includePath unless not File.file?(fileName)
+        }
+        if not directory
+            directory = defaultDir
+        end
+
+        @fileName = directory + (moduleName + ".asm")
+    end
+
+    def self.processIncludeOptions()
+        while ARGV[0][/-I/]
+            path = ARGV.shift[2..-1]
+            if not path
+                path = ARGV.shift
+            end
+            @@includeDirs << (path + "/")
+        end
+    end
+end
+
 class Token
     attr_reader :codeOrigin, :string
     
@@ -73,6 +104,15 @@ class Token
     end
 end
 
+class Annotation
+    attr_reader :codeOrigin, :type, :string
+    def initialize(codeOrigin, type, string)
+        @codeOrigin = codeOrigin
+        @type = type
+        @string = string
+    end
+end
+
 #
 # The lexer. Takes a string and returns an array of tokens.
 #
@@ -81,14 +121,30 @@ def lex(str, fileName)
     fileName = Pathname.new(fileName)
     result = []
     lineNumber = 1
+    annotation = nil
+    whitespaceFound = false
     while not str.empty?
         case str
         when /\A\#([^\n]*)/
             # comment, ignore
+        when /\A\/\/\ ?([^\n]*)/
+            # annotation
+            annotation = $1
+            annotationType = whitespaceFound ? :local : :global
         when /\A\n/
+            # We've found a '\n'.  Emit the last comment recorded if appropriate:
+            # We need to parse annotations regardless of whether the backend does
+            # anything with them or not. This is because the C++ backend may make
+            # use of this for its cloopDo debugging utility even if
+            # enableInstrAnnotations is not enabled.
+            if annotation
+                result << Annotation.new(CodeOrigin.new(fileName, lineNumber),
+                                         annotationType, annotation)
+                annotation = nil
+            end
             result << Token.new(CodeOrigin.new(fileName, lineNumber), $&)
             lineNumber += 1
-        when /\A[a-zA-Z]([a-zA-Z0-9_]*)/
+        when /\A[a-zA-Z]([a-zA-Z0-9_.]*)/
             result << Token.new(CodeOrigin.new(fileName, lineNumber), $&)
         when /\A\.([a-zA-Z0-9_]*)/
             result << Token.new(CodeOrigin.new(fileName, lineNumber), $&)
@@ -96,6 +152,9 @@ def lex(str, fileName)
             result << Token.new(CodeOrigin.new(fileName, lineNumber), $&)
         when /\A([ \t]+)/
             # whitespace, ignore
+            whitespaceFound = true
+            str = $~.post_match
+            next
         when /\A0x([0-9a-fA-F]+)/
             result << Token.new(CodeOrigin.new(fileName, lineNumber), $&.hex.to_s)
         when /\A0([0-7]+)/
@@ -106,9 +165,12 @@ def lex(str, fileName)
             result << Token.new(CodeOrigin.new(fileName, lineNumber), $&)
         when /\A[:,\(\)\[\]=\+\-~\|&^*]/
             result << Token.new(CodeOrigin.new(fileName, lineNumber), $&)
+        when /\A".*"/
+            result << Token.new(CodeOrigin.new(fileName, lineNumber), $&)
         else
             raise "Lexer error at #{CodeOrigin.new(fileName, lineNumber).to_s}, unexpected sequence #{str[0..20].inspect}"
         end
+        whitespaceFound = false
         str = $~.post_match
     end
     result
@@ -123,17 +185,17 @@ def isRegister(token)
 end
 
 def isInstruction(token)
-    token =~ INSTRUCTION_PATTERN
+    INSTRUCTION_SET.member? token.string
 end
 
 def isKeyword(token)
-    token =~ /\A((true)|(false)|(if)|(then)|(else)|(elsif)|(end)|(and)|(or)|(not)|(macro)|(const)|(sizeof)|(error)|(include))\Z/ or
+    token =~ /\A((true)|(false)|(if)|(then)|(else)|(elsif)|(end)|(and)|(or)|(not)|(global)|(macro)|(const)|(sizeof)|(error)|(include))\Z/ or
         token =~ REGISTER_PATTERN or
-        token =~ INSTRUCTION_PATTERN
+        isInstruction(token)
 end
 
 def isIdentifier(token)
-    token =~ /\A[a-zA-Z]([a-zA-Z0-9_]*)\Z/ and not isKeyword(token)
+    token =~ /\A[a-zA-Z]([a-zA-Z0-9_.]*)\Z/ and not isKeyword(token)
 end
 
 def isLabel(token)
@@ -152,6 +214,10 @@ def isInteger(token)
     token =~ /\A[0-9]/
 end
 
+def isString(token)
+    token =~ /\A".*"/
+end
+
 #
 # The parser. Takes an array of tokens and returns an AST. Methods
 # other than parse(tokens) are not for public consumption.
@@ -161,6 +227,7 @@ class Parser
     def initialize(data, fileName)
         @tokens = lex(data, fileName)
         @idx = 0
+        @annotation = nil
     end
     
     def parseError(*comment)
@@ -192,8 +259,9 @@ class Parser
     
     def parsePredicateAtom
         if @tokens[@idx] == "not"
+            codeOrigin = @tokens[@idx].codeOrigin
             @idx += 1
-            parsePredicateAtom
+            Not.new(codeOrigin, parsePredicateAtom)
         elsif @tokens[@idx] == "("
             @idx += 1
             skipNewLine
@@ -335,6 +403,10 @@ class Parser
             result = Immediate.new(@tokens[@idx].codeOrigin, @tokens[@idx].string.to_i)
             @idx += 1
             result
+        elsif isString @tokens[@idx]
+            result = StringLiteral.new(@tokens[@idx].codeOrigin, @tokens[@idx].string)
+            @idx += 1
+            result
         elsif isIdentifier @tokens[@idx]
             codeOrigin, names = parseColonColon
             if names.size > 1
@@ -348,6 +420,14 @@ class Parser
             @idx += 1
             codeOrigin, names = parseColonColon
             Sizeof.forName(codeOrigin, names.join('::'))
+        elsif isLabel @tokens[@idx]
+            result = LabelReference.new(@tokens[@idx].codeOrigin, Label.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string))
+            @idx += 1
+            result
+        elsif isLocalLabel @tokens[@idx]
+            result = LocalLabelReference.new(@tokens[@idx].codeOrigin, LocalLabel.forName(@tokens[@idx].codeOrigin, @tokens[@idx].string))
+            @idx += 1
+            result
         else
             parseError
         end
@@ -368,7 +448,7 @@ class Parser
     end
     
     def couldBeExpression
-        @tokens[@idx] == "-" or @tokens[@idx] == "~" or @tokens[@idx] == "sizeof" or isInteger(@tokens[@idx]) or isVariable(@tokens[@idx]) or @tokens[@idx] == "("
+        @tokens[@idx] == "-" or @tokens[@idx] == "~" or @tokens[@idx] == "sizeof" or isInteger(@tokens[@idx]) or isString(@tokens[@idx]) or isVariable(@tokens[@idx]) or @tokens[@idx] == "("
     end
     
     def parseExpressionAdd
@@ -473,6 +553,20 @@ class Parser
         loop {
             if (@idx == @tokens.length and not final) or (final and @tokens[@idx] =~ final)
                 break
+            elsif @tokens[@idx].is_a? Annotation
+                # This is the only place where we can encounter a global
+                # annotation, and hence need to be able to distinguish between
+                # them.
+                # globalAnnotations are the ones that start from column 0. All
+                # others are considered localAnnotations.  The only reason to
+                # distinguish between them is so that we can format the output
+                # nicely as one would expect.
+
+                codeOrigin = @tokens[@idx].codeOrigin
+                annotationOpcode = (@tokens[@idx].type == :global) ? "globalAnnotation" : "localAnnotation"
+                list << Instruction.new(codeOrigin, annotationOpcode, [], @tokens[@idx].string)
+                @annotation = nil
+                @idx += 2 # Consume the newline as well.
             elsif @tokens[@idx] == "\n"
                 # ignore
                 @idx += 1
@@ -527,17 +621,31 @@ class Parser
                 body = parseSequence(/\Aend\Z/, "while inside of macro #{name}")
                 @idx += 1
                 list << Macro.new(codeOrigin, name, variables, body)
+            elsif @tokens[@idx] == "global"
+                codeOrigin = @tokens[@idx].codeOrigin
+                @idx += 1
+                skipNewLine
+                parseError unless isLabel(@tokens[@idx])
+                name = @tokens[@idx].string
+                @idx += 1
+                Label.setAsGlobal(codeOrigin, name)
             elsif isInstruction @tokens[@idx]
                 codeOrigin = @tokens[@idx].codeOrigin
                 name = @tokens[@idx].string
                 @idx += 1
                 if (not final and @idx == @tokens.size) or (final and @tokens[@idx] =~ final)
                     # Zero operand instruction, and it's the last one.
-                    list << Instruction.new(codeOrigin, name, [])
+                    list << Instruction.new(codeOrigin, name, [], @annotation)
+                    @annotation = nil
                     break
+                elsif @tokens[@idx].is_a? Annotation
+                    list << Instruction.new(codeOrigin, name, [], @tokens[@idx].string)
+                    @annotation = nil
+                    @idx += 2 # Consume the newline as well.
                 elsif @tokens[@idx] == "\n"
                     # Zero operand instruction.
-                    list << Instruction.new(codeOrigin, name, [])
+                    list << Instruction.new(codeOrigin, name, [], @annotation)
+                    @annotation = nil
                     @idx += 1
                 else
                     # It's definitely an instruction, and it has at least one operand.
@@ -552,6 +660,10 @@ class Parser
                         elsif @tokens[@idx] == ","
                             # Has another operand.
                             @idx += 1
+                        elsif @tokens[@idx].is_a? Annotation
+                            @annotation = @tokens[@idx].string
+                            @idx += 2 # Consume the newline as well.
+                            break
                         elsif @tokens[@idx] == "\n"
                             # The end of the instruction.
                             @idx += 1
@@ -560,11 +672,14 @@ class Parser
                             parseError("Expected a comma, newline, or #{final} after #{operands.last.dump}")
                         end
                     }
-                    list << Instruction.new(codeOrigin, name, operands)
+                    list << Instruction.new(codeOrigin, name, operands, @annotation)
+                    @annotation = nil
                     if endOfSequence
                         break
                     end
                 end
+
+            # Check for potential macro invocation:
             elsif isIdentifier @tokens[@idx]
                 codeOrigin = @tokens[@idx].codeOrigin
                 name = @tokens[@idx].string
@@ -601,7 +716,13 @@ class Parser
                             end
                         }
                     end
-                    list << MacroCall.new(codeOrigin, name, operands)
+                    # Check if there's a trailing annotation after the macro invoke:
+                    if @tokens[@idx].is_a? Annotation
+                        @annotation = @tokens[@idx].string
+                        @idx += 2 # Consume the newline as well.
+                    end
+                    list << MacroCall.new(codeOrigin, name, operands, @annotation)
+                    @annotation = nil
                 else
                     parseError "Expected \"(\" after #{name}"
                 end
@@ -612,7 +733,7 @@ class Parser
                 parseError unless @tokens[@idx] == ":"
                 # It's a label.
                 if isLabel name
-                    list << Label.forName(codeOrigin, name)
+                    list << Label.forName(codeOrigin, name, true)
                 else
                     list << LocalLabel.forName(codeOrigin, name)
                 end
@@ -621,7 +742,7 @@ class Parser
                 @idx += 1
                 parseError unless isIdentifier(@tokens[@idx])
                 moduleName = @tokens[@idx].string
-                fileName = @tokens[@idx].codeOrigin.fileName.dirname + (moduleName + ".asm")
+                fileName = IncludeFile.new(moduleName, @tokens[@idx].codeOrigin.fileName.dirname).fileName
                 @idx += 1
                 $stderr.puts "offlineasm: Including file #{fileName}"
                 list << parse(fileName)
@@ -631,6 +752,29 @@ class Parser
         }
         Sequence.new(firstCodeOrigin, list)
     end
+
+    def parseIncludes(final, comment)
+        firstCodeOrigin = @tokens[@idx].codeOrigin
+        fileList = []
+        fileList << @tokens[@idx].codeOrigin.fileName
+        loop {
+            if (@idx == @tokens.length and not final) or (final and @tokens[@idx] =~ final)
+                break
+            elsif @tokens[@idx] == "include"
+                @idx += 1
+                parseError unless isIdentifier(@tokens[@idx])
+                moduleName = @tokens[@idx].string
+                fileName = IncludeFile.new(moduleName, @tokens[@idx].codeOrigin.fileName.dirname).fileName
+                @idx += 1
+                
+                fileList << fileName
+            else
+                @idx += 1
+            end
+        }
+
+        return fileList
+    end
 end
 
 def parseData(data, fileName)
@@ -643,6 +787,8 @@ def parse(fileName)
 end
 
 def parseHash(fileName)
-    dirHash(Pathname.new(fileName).dirname, /\.asm$/)
+    parser = Parser.new(IO::read(fileName), fileName)
+    fileList = parser.parseIncludes(nil, "")
+    fileListHash(fileList)
 end