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
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), $&)
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)
@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
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
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
@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)
}
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)
end
def parseHash(fileName)
- dirHash(Pathname.new(fileName).dirname, /\.asm$/)
+ parser = Parser.new(IO::read(fileName), fileName)
+ fileList = parser.parseIncludes(nil, "")
+ fileListHash(fileList)
end