1a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar//===-- llvm/MC/MCAsmParser.h - Abstract Asm Parser Interface ---*- C++ -*-===// 2a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar// 3a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar// The LLVM Compiler Infrastructure 4a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar// 5a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar// This file is distributed under the University of Illinois Open Source 6a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar// License. See LICENSE.TXT for details. 7a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar// 8a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar//===----------------------------------------------------------------------===// 9a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar 10674be02d525d4e24bc6943ed9274958c580bcfbcJakub Staszak#ifndef LLVM_MC_MCPARSER_MCASMPARSER_H 11674be02d525d4e24bc6943ed9274958c580bcfbcJakub Staszak#define LLVM_MC_MCPARSER_MCASMPARSER_H 12a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar 13d8b7aa26134d2abee777f745c32005e63dea2455Chris Lattner#include "llvm/ADT/ArrayRef.h" 14c0c67b03b03d73d3614a084d467a388c35d264d1Eli Bendersky#include "llvm/ADT/StringRef.h" 159bac6b29b832419f8b76bb2c27af74bb57a8d99aEli Bendersky#include "llvm/MC/MCParser/AsmLexer.h" 16255f89faee13dc491cb64fbeae3c763e7e2ea4e6Chandler Carruth#include "llvm/Support/DataTypes.h" 17e240beb0652f47adb5f58040ce17e43dcf25653fDaniel Dunbar 18a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbarnamespace llvm { 19d1e3b44d6c0094eda2e2a854d5fdb6a0d7ba327eDaniel Dunbarclass MCAsmInfo; 20dbd692a66e6a5f60ec3ff120ed27ae3a918c375fDaniel Dunbarclass MCAsmLexer; 2181ea00f45d59953d34a1db4973dd72d14080ab15Daniel Dunbarclass MCAsmParserExtension; 226ce004dc76a8761a7e1a8830206ccaaf96736615Daniel Dunbarclass MCContext; 23c18274ba9cabd5699452870daae3c2d9accecc37Daniel Dunbarclass MCExpr; 24b1f8c139c5c1b1a50bf65b8141dd57434c793e54Chad Rosierclass MCInstPrinter; 25b1f8c139c5c1b1a50bf65b8141dd57434c793e54Chad Rosierclass MCInstrInfo; 26b8b70521def36d0da103b8a103872f37b3b49aa2Daniel Dunbarclass MCStreamer; 2794b9550a32d189704a8eae55505edf62662c0534Evan Chengclass MCTargetAsmParser; 28e240beb0652f47adb5f58040ce17e43dcf25653fDaniel Dunbarclass SMLoc; 29d8b7aa26134d2abee777f745c32005e63dea2455Chris Lattnerclass SMRange; 304e82bbb745160f19d76b9b5de5b93ebe224e1acfDaniel Dunbarclass SourceMgr; 31e240beb0652f47adb5f58040ce17e43dcf25653fDaniel Dunbarclass Twine; 32a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar 33cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hinesclass InlineAsmIdentifierInfo { 34b1f8c139c5c1b1a50bf65b8141dd57434c793e54Chad Rosierpublic: 35cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hines void *OpDecl; 36cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hines bool IsVarDecl; 37cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hines unsigned Length, Size, Type; 3844021515d76ec9b529f2adbc252552869b1357d5Chad Rosier 39cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hines void clear() { 40cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hines OpDecl = nullptr; 41cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hines IsVarDecl = false; 42cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hines Length = 1; 43cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hines Size = 0; 44cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hines Type = 0; 45cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hines } 46cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hines}; 4744021515d76ec9b529f2adbc252552869b1357d5Chad Rosier 48cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hines/// MCAsmParserSemaCallback - Generic Sema callback for assembly parser. 49cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hinesclass MCAsmParserSemaCallback { 50cd81d94322a39503e4a3e87b6ee03d4fcb3465fbStephen Hinespublic: 512ee6c7ff80a5b2ff84d882c24db4bad35b948f91Jim Grosbach virtual ~MCAsmParserSemaCallback(); 5244021515d76ec9b529f2adbc252552869b1357d5Chad Rosier virtual void *LookupInlineAsmIdentifier(StringRef &LineBuf, 5326f3bb997f0c5d7951d61d28a26ca6ac1481090cJohn McCall InlineAsmIdentifierInfo &Info, 5426f3bb997f0c5d7951d61d28a26ca6ac1481090cJohn McCall bool IsUnevaluatedContext) = 0; 55505bca3617fe310a5ff07914e3cf3ea6ae4d27edChad Rosier 56ec13022c392747ef166e6be738fc6f00bd7c52d3Chad Rosier virtual bool LookupInlineAsmField(StringRef Base, StringRef Member, 57ec13022c392747ef166e6be738fc6f00bd7c52d3Chad Rosier unsigned &Offset) = 0; 58b1f8c139c5c1b1a50bf65b8141dd57434c793e54Chad Rosier}; 59b1f8c139c5c1b1a50bf65b8141dd57434c793e54Chad Rosier 60a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar/// MCAsmParser - Generic assembler parser interface, for use by target specific 61a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar/// assembly parsers. 62a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbarclass MCAsmParser { 6381ea00f45d59953d34a1db4973dd72d14080ab15Daniel Dunbarpublic: 641edf6ca2cbb4b01db44683d5e9479a240cfcf497Daniel Dunbar typedef bool (*DirectiveHandler)(MCAsmParserExtension*, StringRef, SMLoc); 65171192f149dce679cd520f85ffced4789448b017Eli Bendersky typedef std::pair<MCAsmParserExtension*, DirectiveHandler> 66171192f149dce679cd520f85ffced4789448b017Eli Bendersky ExtensionDirectiveHandler; 6781ea00f45d59953d34a1db4973dd72d14080ab15Daniel Dunbar 6881ea00f45d59953d34a1db4973dd72d14080ab15Daniel Dunbarprivate: 691f7210e808373fa92be3a2d4fa653a6f79d5088bCraig Topper MCAsmParser(const MCAsmParser &) LLVM_DELETED_FUNCTION; 701f7210e808373fa92be3a2d4fa653a6f79d5088bCraig Topper void operator=(const MCAsmParser &) LLVM_DELETED_FUNCTION; 71d1e3b44d6c0094eda2e2a854d5fdb6a0d7ba327eDaniel Dunbar 7294b9550a32d189704a8eae55505edf62662c0534Evan Cheng MCTargetAsmParser *TargetParser; 73d1e3b44d6c0094eda2e2a854d5fdb6a0d7ba327eDaniel Dunbar 743c14ca47fc057c5999cd41ce2782e0ebaa2bcf7dDaniel Dunbar unsigned ShowParsedOperands : 1; 753c14ca47fc057c5999cd41ce2782e0ebaa2bcf7dDaniel Dunbar 76a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbarprotected: // Can only create subclasses. 77a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar MCAsmParser(); 78dc4c7da5d4c73bef2ce73ce0f96019457337c402Daniel Dunbar 79a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbarpublic: 80a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar virtual ~MCAsmParser(); 81dbd692a66e6a5f60ec3ff120ed27ae3a918c375fDaniel Dunbar 82cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach virtual void addDirectiveHandler(StringRef Directive, 83171192f149dce679cd520f85ffced4789448b017Eli Bendersky ExtensionDirectiveHandler Handler) = 0; 8481ea00f45d59953d34a1db4973dd72d14080ab15Daniel Dunbar 854e82bbb745160f19d76b9b5de5b93ebe224e1acfDaniel Dunbar virtual SourceMgr &getSourceManager() = 0; 864e82bbb745160f19d76b9b5de5b93ebe224e1acfDaniel Dunbar 87dbd692a66e6a5f60ec3ff120ed27ae3a918c375fDaniel Dunbar virtual MCAsmLexer &getLexer() = 0; 88e240beb0652f47adb5f58040ce17e43dcf25653fDaniel Dunbar 896ce004dc76a8761a7e1a8830206ccaaf96736615Daniel Dunbar virtual MCContext &getContext() = 0; 906ce004dc76a8761a7e1a8830206ccaaf96736615Daniel Dunbar 9181ea00f45d59953d34a1db4973dd72d14080ab15Daniel Dunbar /// getStreamer - Return the output streamer for the assembler. 92b8b70521def36d0da103b8a103872f37b3b49aa2Daniel Dunbar virtual MCStreamer &getStreamer() = 0; 93b8b70521def36d0da103b8a103872f37b3b49aa2Daniel Dunbar 9494b9550a32d189704a8eae55505edf62662c0534Evan Cheng MCTargetAsmParser &getTargetParser() const { return *TargetParser; } 9594b9550a32d189704a8eae55505edf62662c0534Evan Cheng void setTargetParser(MCTargetAsmParser &P); 96d1e3b44d6c0094eda2e2a854d5fdb6a0d7ba327eDaniel Dunbar 97a005c3140d8c16fd68b7e3a3dffb3153609f7527Devang Patel virtual unsigned getAssemblerDialect() { return 0;} 980db58bfecea020ffcdfa1fc6458995371e1c3c50Devang Patel virtual void setAssemblerDialect(unsigned i) { } 99a005c3140d8c16fd68b7e3a3dffb3153609f7527Devang Patel 1003c14ca47fc057c5999cd41ce2782e0ebaa2bcf7dDaniel Dunbar bool getShowParsedOperands() const { return ShowParsedOperands; } 1013c14ca47fc057c5999cd41ce2782e0ebaa2bcf7dDaniel Dunbar void setShowParsedOperands(bool Value) { ShowParsedOperands = Value; } 1023c14ca47fc057c5999cd41ce2782e0ebaa2bcf7dDaniel Dunbar 103d1e3b44d6c0094eda2e2a854d5fdb6a0d7ba327eDaniel Dunbar /// Run - Run the parser on the input source buffer. 104d1e3b44d6c0094eda2e2a854d5fdb6a0d7ba327eDaniel Dunbar virtual bool Run(bool NoInitialTextSection, bool NoFinalize = false) = 0; 105d1e3b44d6c0094eda2e2a854d5fdb6a0d7ba327eDaniel Dunbar 10684125ca43c758fd21fdab2b05196e0df57c55c96Chad Rosier virtual void setParsingInlineAsm(bool V) = 0; 107c5ac87d067861309fb461b9c53f9e429fbe0d067Chad Rosier virtual bool isParsingInlineAsm() = 0; 10884125ca43c758fd21fdab2b05196e0df57c55c96Chad Rosier 109cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach /// parseMSInlineAsm - Parse ms-style inline assembly. 110cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach virtual bool parseMSInlineAsm(void *AsmLoc, std::string &AsmString, 111b1f8c139c5c1b1a50bf65b8141dd57434c793e54Chad Rosier unsigned &NumOutputs, unsigned &NumInputs, 1125a719fcb5ea91ec4e7af6fc2e48ec31774a859ddChad Rosier SmallVectorImpl<std::pair<void *, bool> > &OpDecls, 113b1f8c139c5c1b1a50bf65b8141dd57434c793e54Chad Rosier SmallVectorImpl<std::string> &Constraints, 114b1f8c139c5c1b1a50bf65b8141dd57434c793e54Chad Rosier SmallVectorImpl<std::string> &Clobbers, 115b1f8c139c5c1b1a50bf65b8141dd57434c793e54Chad Rosier const MCInstrInfo *MII, 116b1f8c139c5c1b1a50bf65b8141dd57434c793e54Chad Rosier const MCInstPrinter *IP, 117b1f8c139c5c1b1a50bf65b8141dd57434c793e54Chad Rosier MCAsmParserSemaCallback &SI) = 0; 118b1f8c139c5c1b1a50bf65b8141dd57434c793e54Chad Rosier 11936b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines /// Note - Emit a note at the location \p L, with the message \p Msg. 12036b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines virtual void Note(SMLoc L, const Twine &Msg, 12136b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines ArrayRef<SMRange> Ranges = None) = 0; 12236b56886974eae4f9c5ebc96befd3e7bfe5de338Stephen Hines 123c5252da873d547a19069eaf9030fec203f128f66Dmitri Gribenko /// Warning - Emit a warning at the location \p L, with the message \p Msg. 124f8cd708f14a7a172d51899b68809510ae0c4c4c8Joerg Sonnenberger /// 125f8cd708f14a7a172d51899b68809510ae0c4c4c8Joerg Sonnenberger /// \return The return value is true, if warnings are fatal. 126d8b7aa26134d2abee777f745c32005e63dea2455Chris Lattner virtual bool Warning(SMLoc L, const Twine &Msg, 1275c332dbd30d9398ed25b30c3080506f7b8e92290Dmitri Gribenko ArrayRef<SMRange> Ranges = None) = 0; 128e240beb0652f47adb5f58040ce17e43dcf25653fDaniel Dunbar 129c5252da873d547a19069eaf9030fec203f128f66Dmitri Gribenko /// Error - Emit an error at the location \p L, with the message \p Msg. 130e240beb0652f47adb5f58040ce17e43dcf25653fDaniel Dunbar /// 131e240beb0652f47adb5f58040ce17e43dcf25653fDaniel Dunbar /// \return The return value is always true, as an idiomatic convenience to 132e240beb0652f47adb5f58040ce17e43dcf25653fDaniel Dunbar /// clients. 133d8b7aa26134d2abee777f745c32005e63dea2455Chris Lattner virtual bool Error(SMLoc L, const Twine &Msg, 1345c332dbd30d9398ed25b30c3080506f7b8e92290Dmitri Gribenko ArrayRef<SMRange> Ranges = None) = 0; 135e240beb0652f47adb5f58040ce17e43dcf25653fDaniel Dunbar 136b9a25b7744ed12b80031426978decce3d4cebbd7Sean Callanan /// Lex - Get the next AsmToken in the stream, possibly handling file 137b9a25b7744ed12b80031426978decce3d4cebbd7Sean Callanan /// inclusion first. 138b9a25b7744ed12b80031426978decce3d4cebbd7Sean Callanan virtual const AsmToken &Lex() = 0; 139dc4c7da5d4c73bef2ce73ce0f96019457337c402Daniel Dunbar 14018b8323de70e3461b5d035e3f9e4f6dfaf5e674bSean Callanan /// getTok - Get the current AsmToken from the stream. 14118b8323de70e3461b5d035e3f9e4f6dfaf5e674bSean Callanan const AsmToken &getTok(); 142dc4c7da5d4c73bef2ce73ce0f96019457337c402Daniel Dunbar 143dc4c7da5d4c73bef2ce73ce0f96019457337c402Daniel Dunbar /// \brief Report an error at the current lexer location. 1445c332dbd30d9398ed25b30c3080506f7b8e92290Dmitri Gribenko bool TokError(const Twine &Msg, ArrayRef<SMRange> Ranges = None); 145dc4c7da5d4c73bef2ce73ce0f96019457337c402Daniel Dunbar 146cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach /// parseIdentifier - Parse an identifier or string (as a quoted identifier) 147c5252da873d547a19069eaf9030fec203f128f66Dmitri Gribenko /// and set \p Res to the identifier contents. 148cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach virtual bool parseIdentifier(StringRef &Res) = 0; 14938a4e2acb7d01fbba3544882084bc9955812b7d3Daniel Dunbar 1506a46d571b461246e36f82c146e17bf614d2114eaDaniel Dunbar /// \brief Parse up to the end of statement and return the contents from the 1516a46d571b461246e36f82c146e17bf614d2114eaDaniel Dunbar /// current token until the end of the statement; the current token on exit 1526a46d571b461246e36f82c146e17bf614d2114eaDaniel Dunbar /// will be either the EndOfStatement or EOF. 153cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach virtual StringRef parseStringToEndOfStatement() = 0; 1546a46d571b461246e36f82c146e17bf614d2114eaDaniel Dunbar 155cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach /// parseEscapedString - Parse the current token as a string which may include 156bfdcc70d34f9c2bf3d4815c6d29fd43f01db8b76Daniel Dunbar /// escaped characters and return the string contents. 157cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach virtual bool parseEscapedString(std::string &Data) = 0; 158bfdcc70d34f9c2bf3d4815c6d29fd43f01db8b76Daniel Dunbar 159cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach /// eatToEndOfStatement - Skip to the end of the current statement, for error 160cbf8a98c7c652e96967623c80cb945fef001b090Chris Lattner /// recovery. 161cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach virtual void eatToEndOfStatement() = 0; 16298311ecb4ae9c82baba9e3a48acf756a81c8e9a4Jim Grosbach 163cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach /// parseExpression - Parse an arbitrary expression. 164c18274ba9cabd5699452870daae3c2d9accecc37Daniel Dunbar /// 165c18274ba9cabd5699452870daae3c2d9accecc37Daniel Dunbar /// @param Res - The value of the expression. The result is undefined 166c18274ba9cabd5699452870daae3c2d9accecc37Daniel Dunbar /// on error. 167c18274ba9cabd5699452870daae3c2d9accecc37Daniel Dunbar /// @result - False on success. 168cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach virtual bool parseExpression(const MCExpr *&Res, SMLoc &EndLoc) = 0; 169cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach bool parseExpression(const MCExpr *&Res); 170dc4c7da5d4c73bef2ce73ce0f96019457337c402Daniel Dunbar 171ba69b366929a39d393e7eed0bbf2edc31d8de599Chad Rosier /// parsePrimaryExpr - Parse a primary expression. 172ba69b366929a39d393e7eed0bbf2edc31d8de599Chad Rosier /// 173ba69b366929a39d393e7eed0bbf2edc31d8de599Chad Rosier /// @param Res - The value of the expression. The result is undefined 174ba69b366929a39d393e7eed0bbf2edc31d8de599Chad Rosier /// on error. 175ba69b366929a39d393e7eed0bbf2edc31d8de599Chad Rosier /// @result - False on success. 176ba69b366929a39d393e7eed0bbf2edc31d8de599Chad Rosier virtual bool parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc) = 0; 177ba69b366929a39d393e7eed0bbf2edc31d8de599Chad Rosier 178cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach /// parseParenExpression - Parse an arbitrary expression, assuming that an 179c18274ba9cabd5699452870daae3c2d9accecc37Daniel Dunbar /// initial '(' has already been consumed. 180c18274ba9cabd5699452870daae3c2d9accecc37Daniel Dunbar /// 181c18274ba9cabd5699452870daae3c2d9accecc37Daniel Dunbar /// @param Res - The value of the expression. The result is undefined 182c18274ba9cabd5699452870daae3c2d9accecc37Daniel Dunbar /// on error. 183c18274ba9cabd5699452870daae3c2d9accecc37Daniel Dunbar /// @result - False on success. 184cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach virtual bool parseParenExpression(const MCExpr *&Res, SMLoc &EndLoc) = 0; 185c18274ba9cabd5699452870daae3c2d9accecc37Daniel Dunbar 186cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach /// parseAbsoluteExpression - Parse an expression which must evaluate to an 187e240beb0652f47adb5f58040ce17e43dcf25653fDaniel Dunbar /// absolute value. 188e240beb0652f47adb5f58040ce17e43dcf25653fDaniel Dunbar /// 189e240beb0652f47adb5f58040ce17e43dcf25653fDaniel Dunbar /// @param Res - The value of the absolute expression. The result is undefined 190e240beb0652f47adb5f58040ce17e43dcf25653fDaniel Dunbar /// on error. 191e240beb0652f47adb5f58040ce17e43dcf25653fDaniel Dunbar /// @result - False on success. 192cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach virtual bool parseAbsoluteExpression(int64_t &Res) = 0; 193318cad33231f765f9b6b2af4bb43f8c281b99d19Eli Bendersky 194cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach /// checkForValidSection - Ensure that we have a valid section set in the 1954913d7b3b22a0302bea69c98673b887ae2d89e40Eric Christopher /// streamer. Otherwise, report an error and switch to .text. 196cb2ae3d98e3bb36e5813f8f69b00d39efd026dcdJim Grosbach virtual void checkForValidSection() = 0; 197a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar}; 198a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar 199d1e3b44d6c0094eda2e2a854d5fdb6a0d7ba327eDaniel Dunbar/// \brief Create an MCAsmParser instance. 2001b84cce77f8bccc905b4800927ce9016f76c1c40Jim GrosbachMCAsmParser *createMCAsmParser(SourceMgr &, MCContext &, 201d1e3b44d6c0094eda2e2a854d5fdb6a0d7ba327eDaniel Dunbar MCStreamer &, const MCAsmInfo &); 202d1e3b44d6c0094eda2e2a854d5fdb6a0d7ba327eDaniel Dunbar 203a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar} // End llvm namespace 204a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar 205a3af370dc12f6d5100da5d614ab0a62da135569aDaniel Dunbar#endif 206