Changeset 69

Show
Ignore:
Timestamp:
05/15/05 16:30:32 (4 years ago)
Author:
pragma
Message:

--

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/xml/IXMLConsumer.d

    r46 r69  
    2828private import xml.XMLAttributes; 
    2929 
    30 interface IXMLConsumer{ 
    31     void xmlProlog(XMLAttributes attribs); 
    32     void xmlStartDoctype(char[] name); 
    33     void xmlEndDoctype(char[] name); 
    34      
    35     void xmlProcessingInstruction(char[] name,char[] data); 
    36      
    37     void xmlComment(char[] text); 
    38      
    39     void xmlStartElement(char[] name,XMLAttributes attribs,bit isEmpty); 
    40     void xmlEndElement(char[] name,XMLAttributes attribs,bit isEmpty); 
    41     void xmlText(char[] text); 
     30enum TextType: short{ 
     31    NormalText, 
     32    EntityText, 
     33    CDATAText, 
    4234} 
    4335 
     36interface IXMLConsumer(CharT){ 
     37    mixin MXMLAttribute!(CharT); 
     38     
     39    enum DTDOperation: CharT{ 
     40        StartSequence = '(', 
     41        EndSequence = ')', 
     42        Or = '|', 
     43        And = '&', 
     44        OneOrMore = '*', 
     45        ZeroOrMore = '+', 
     46        ZeroOrOne = '?', 
     47    } 
     48 
     49    //TODO: break these out? 
     50    CharT[] getEntityValue(CharT[] entity,bit parsed);   
     51    bit xmlIsParsedProcessingInstruction(CharT[] name); 
     52     
     53    void xmlStartDTD(CharT[] name,XMLAttributes externalID); 
     54    void xmlEndDTD(CharT[] name,XMLAttributes externalID); 
     55     
     56    void xmlStartDTDElement(CharT[] name); 
     57    void xmlSetDTDElementChild(CharT[] name,CharT[] childName); 
     58    void xmlSetDTDElementOperation(CharT[] name,DTDOperation type); 
     59    void xmlEndDTDElement(CharT[] name); 
     60 
     61    void xmlStartDTDAttlist(CharT[] name); 
     62    void xmlSetDTDAttlistDef(CharT[] name,CharT[] type,CharT[] defaultDecl,CharT[] defaultValue); 
     63    void xmlEndDTDAttlist(CharT[] name); 
     64     
     65    void xmlSetDTDEntity(CharT[] name,CharT[] value,CharT[] ndata,bit parsed); 
     66         
     67    void xmlSetDTDNotation(CharT[] name, CharT[] publicId, CharT[] systemLiteral); 
     68         
     69    void xmlParsedProcessingInstruction(CharT[] name,XMLAttributes attribs); 
     70    void xmlProcessingInstruction(CharT[] name,CharT[] data); 
     71     
     72    void xmlComment(CharT[] text); 
     73     
     74    void xmlStartElement(CharT[] name,XMLAttributes attribs,bit isEmpty); 
     75    void xmlEndElement(CharT[] name,XMLAttributes attribs,bit isEmpty); 
     76    void xmlText(CharT[] text,TextType type); 
     77} 
     78 
     79alias IXMLConsumer!(char) Utf8IXMLConsumer; 
     80alias IXMLConsumer!(wchar) Utf16IXMLConsumer; 
     81alias IXMLConsumer!(dchar) Utf32IXMLConsumer; 
  • trunk/xml/IXMLConsumerAdapter.d

    r46 r69  
    2929private import xml.XMLAttributes; 
    3030 
    31 class IXMLConsumerAdapter : IXMLConsumer{ 
    32     void xmlProlog(XMLAttributes attribs){} 
    33     void xmlStartDoctype(char[] name){} 
    34     void xmlEndDoctype(char[] name){} 
     31class IXMLConsumerAdapter(CharT) : IXMLConsumer!(CharT){ 
     32    CharT[] getEntityValue(CharT[] entity,bit parsed){ return "&" ~ entity ~ ";"; }  
     33    bit xmlIsParsedProcessingInstruction(CharT[] name){ return false; } 
    3534     
    36     void xmlProcessingInstruction(char[] name,char[] data){} 
     35    void xmlStartDTD(CharT[] name,XMLAttributes externalID){} 
     36    void xmlEndDTD(CharT[] name,XMLAttributes externalID){} 
    3737     
    38     void xmlComment(char[] text){} 
     38    void xmlStartDTDElement(CharT[] name){} 
     39    void xmlSetDTDElementChild(CharT[] name,CharT[] childName){} 
     40    void xmlSetDTDElementOperation(CharT[] name,DTDOperation type){} 
     41    void xmlEndDTDElement(CharT[] name){} 
     42 
     43    void xmlStartDTDAttlist(CharT[] name){} 
     44    void xmlSetDTDAttlistDef(CharT[] name,CharT[] type,CharT[] defaultDecl,CharT[] defaultValue){} 
     45    void xmlEndDTDAttlist(CharT[] name){} 
    3946     
    40     void xmlStartElement(char[] name,XMLAttributes attribs,bit isEmpty){} 
    41     void xmlEndElement(char[] name,XMLAttributes attribs,bit isEmpty){} 
    42     void xmlText(char[] text){} 
     47    void xmlSetDTDEntity(CharT[] name,CharT[] value,CharT[] ndata,bit parsed){} 
     48         
     49    void xmlSetDTDNotation(CharT[] name, CharT[] publicId, CharT[] systemLiteral){} 
     50         
     51    void xmlParsedProcessingInstruction(CharT[] name,XMLAttributes attribs){} 
     52    void xmlProcessingInstruction(CharT[] name,CharT[] data){} 
     53     
     54    void xmlComment(CharT[] text){} 
     55     
     56    void xmlStartElement(CharT[] name,XMLAttributes attribs,bit isEmpty){} 
     57    void xmlEndElement(CharT[] name,XMLAttributes attribs,bit isEmpty){} 
     58    void xmlText(CharT[] text,TextType type){} 
    4359} 
    4460 
     61alias IXMLConsumerAdapter!(char) Utf8IXMLConsumerAdapter; 
     62alias IXMLConsumerAdapter!(wchar) Utf16IXMLConsumerAdapter; 
     63alias IXMLConsumerAdapter!(dchar) Utf32IXMLConsumerAdapter; 
    4564 
    46  
  • trunk/xml/IXMLProvider.d

    r46 r69  
    2626module xml.IXMLProvider; 
    2727 
    28 private import xml.ILexerStream; 
     28interface IXMLProvider(CharT){ 
     29    CharT[] resolveExternalID(CharT[] pubid,CharT[] system); 
     30
    2931 
    30 interface IXMLProvider{ 
    31    ILexerStream resolveExternalID(char[] pubid,char[] system)
    32 
     32alias IXMLProvider!(char) Utf8IXMLProvider; 
     33alias IXMLProvider!(wchar) Utf16IXMLProvider
     34alias IXMLProvider!(dchar) Utf32IXMLProvider; 
  • trunk/xml/OfflineProvider.d

    r46 r69  
    2626module xml.OfflineProvider; 
    2727 
    28 private import xml.ILexerStream; 
    29 private import xml.SimpleStream; 
    3028private import xml.IXMLProvider; 
    3129private import xml.XMLException; 
     30private import xml.Unicode; 
    3231 
    3332private import std.file; 
    3433private import std.utf; 
    3534 
    36 class OfflineProvider : IXMLProvider{ 
     35class OfflineProvider(CharT) : IXMLProvider!(CharT){ 
     36    mixin MCharConversionFunctions!(CharT); 
     37     
    3738    char[] currentPath; 
    3839     
     
    4950    } 
    5051     
    51     ILexerStream resolveExternalID(char[] pubid,char[] system){ 
     52    CharT[] resolveExternalID(CharT[] pubid,CharT[] system){ 
    5253        //throw out the public identifier (not useful here) 
     54        char[] utf8System = toUTF8(system); 
    5355         
    54         if(system.length == 0) return new SimpleStream(""); 
    55          
    56          
    57          
     56        if(system.length == 0) return ""; 
     57 
    5858        if(system.length < 5 || system[0..5] == "http:"){ 
    5959            return null; // can't do this offline 
     
    6565         
    6666        if(system[0] != '/' || system[0] != '\\'){ 
    67             filename = currentPath ~ system; 
     67            filename = currentPath ~ utf8System; 
    6868        } 
    6969        else{ 
    70             filename = system; 
     70            filename = utf8System; 
    7171        } 
    7272                 
    7373        try{ 
    74             return new SimpleStream(toUTF8(cast(char[])std.file.read(filename))); 
     74            return charTConvert(cast(char[])std.file.read(filename)); 
    7575        } 
    7676        catch(Exception e){ 
    77             throw new XMLException("failure to resolve file: " ~ system); 
     77            throw new XMLException("failure to resolve file: " ~ utf8System); 
    7878        } 
    7979        return null; 
    8080    } 
    8181} 
     82 
     83alias OfflineProvider!(char) Utf8OfflineProviderBase; 
     84alias OfflineProvider!(wchar) Utf16OfflineProviderBase; 
     85alias OfflineProvider!(dchar) Utf32OfflineProviderBase; 
  • trunk/xml/Position.d

    r43 r69  
    2929 
    3030struct Position{ 
    31     uint row,col
     31    uint line,column,offset
    3232    char[] toString(){ 
    33         return format("(%d,%d)",row,col); 
     33        return format("(%d,%d)",line,column); 
    3434    } 
    3535} 
  • trunk/xml/XMLAttributes.d

    r42 r69  
    2626module xml.XMLAttributes; 
    2727 
    28 alias char[][char[]] XMLAttributes; 
     28private import xml.Unicode; 
     29 
     30template MXMLAttribute(CharT){ 
     31    alias CharT[][CharT[]] XMLAttributes; 
     32
     33 
     34template MXMLAttributeFunctions(CharT){ 
     35    CharT[] toCharTString(XMLAttributes attribs){ 
     36        CharT[] output = ""; 
     37        foreach(CharT[] name,CharT[] value; attribs){ 
     38            output ~= name ~ "=" ~ "\"" ~ value ~ "\"" ~ " "; 
     39        } 
     40        return output; 
     41    } 
     42 
     43    CharT[] toCharTString(CharT ch){ 
     44        CharT[] value; 
     45        value.length = 1; 
     46        value[0] = ch; 
     47        return value; 
     48    } 
     49
  • trunk/xml/XMLException.d

    r48 r69  
    2626module xml.XMLException; 
    2727 
    28 private import xml.Position; 
    29 private import xml.BaseParser; 
    30 private import xml.BaseLexer; 
    31 private import xml.ILexerStream; 
    32  
    3328private import std.string; 
    34  
    35 //TODO: add chaining, named streams and references to originating lexer 
    3629 
    3730class XMLException : Exception{ 
     
    5245    } 
    5346} 
    54  
    55 class XMLLexerException : XMLException{ 
    56     public BaseLexer lex; 
    57     public Position pos; 
    58     public char[] streamName; 
    59      
    60     public this(BaseLexer lex,ILexerStream stream,char[] reason){ 
    61         super(reason); 
    62         this.lex = lex; 
    63         pos = stream.getPosition(); 
    64         streamName = stream.getStreamName(); 
    65     } 
    66      
    67     public this(Exception cause,BaseLexer lex,ILexerStream stream,char[] reason){ 
    68         super(cause,reason); 
    69         this.lex = lex; 
    70         pos = stream.getPosition(); 
    71         streamName = stream.getStreamName(); 
    72     }    
    73      
    74     public char[] toString(){ 
    75         return format("%s (%d,%d): %s\nToken Dump:\n%s",streamName,pos.row,pos.col,super.toString(),lex.toString()); 
    76     } 
    77 } 
    78  
    79 class XMLParserException : XMLException{ 
    80     public BaseParser parser; 
    81     public Position pos; 
    82     public char[] streamName; 
    83      
    84     public this(BaseParser parser,Position pos,char[] reason){ 
    85         super(reason); 
    86         this.parser = parser; 
    87         this.pos = pos; 
    88         streamName = parser.getStreamName(); 
    89     } 
    90      
    91     public this(Exception cause,BaseParser parser,Position pos,char[] reason){ 
    92         super(cause,reason); 
    93         this.parser = parser; 
    94         this.pos = pos; 
    95         streamName = parser.getStreamName(); 
    96     }    
    97      
    98     public char[] toString(){ 
    99         return format("%s (%d,%d): %s\nToken Dump:\n%s",streamName,pos.row,pos.col,super.toString(),parser.toString()); 
    100     } 
    101 } 
  • trunk/xml/XMLParser.d

    r48 r69  
    2626module xml.XMLParser; 
    2727 
     28private import xml.IXMLConsumer; 
    2829private import xml.IXMLProvider; 
    29 private import xml.IXMLConsumer; 
    30 private import xml.ILexerStream; 
    31 private import xml.XMLLexer; 
    32 private import xml.XMLToken; 
    33 private import xml.BaseLexer; 
    34 private import xml.BaseParser; 
    35 private import xml.XMLException; 
    36 private import xml.DTDParser; 
    37 private import xml.SimpleStream; 
    3830private import xml.OfflineProvider; 
    3931private import xml.XMLAttributes; 
    40  
    41 private import std.string; 
    42  
    43 /* 
    44     - string-content is not validated. 
    45     -attribute ordering for certain situations is not validated. 
    46     TODO: note that all content is assumed UTF8 or converted to UTF8 prior to parsing 
    47      
    48 */ 
    49  
    50 class XMLParser : BaseParser{ 
    51     protected BaseLexer getLexer(){ 
    52         return new XMLLexer(); 
    53     } 
    54          
    55     protected void startParse(){ 
    56         getNext(TokStartElem); 
    57          
    58         if(peek().type == TokQuestion){ 
    59             parseProlog(); 
    60         } 
    61         parseMisc(); 
    62          
    63         if(peek().type == TokName){ 
    64             parseElement(); 
    65         } 
    66         parseMisc(); 
    67     } 
    68      
    69     protected void parseProlog(){ 
    70         /* 
    71             [22]    prolog     ::=      XMLDecl? Misc* (doctypedecl Misc*)? 
    72             [23]    XMLDecl    ::=      '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' 
    73             [24]    VersionInfo    ::=      S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"') 
    74             [25]    Eq     ::=      S? '=' S? 
    75             [26]    VersionNum     ::=      '1.0' 
    76         */ 
    77          
    78         //?xml 
    79         getNext(TokQuestion); 
    80         if(std.string.tolower(getNext().value) != "xml"){ 
    81             throw new XMLParserException(this,peek().pos,"document prolog must begin with an 'xml' processing instruction"); 
    82         } 
    83          
    84         getNext(TokSpace); 
    85          
    86         // attribs 
    87         consumer.xmlProlog(parseAttributes()); 
    88          
    89         // ?> 
    90         getNext(TokQuestion); 
    91         getNext(TokEndElem); 
    92          
    93         parseMisc(); 
    94          
    95         if(peek().type == TokBang){ 
    96             getNext(); // eat token 
    97             parseDoctype(); 
    98         } 
    99     } 
    100      
    101     protected void parseMisc(){ 
    102         /* 
    103         [27]    Misc       ::=      Comment | PI | S     
    104         */ 
     32private import xml.Position; 
     33 
     34private import std.utf; 
     35private import std.stdio; 
     36 
     37class XMLParser(CharT){ 
     38    mixin MXMLAttribute!(CharT); 
     39    mixin MXMLAttributeFunctions!(CharT); 
     40 
     41    IXMLConsumer!(CharT) consumer; 
     42    IXMLProvider!(CharT) provider; 
     43    CharT[] source; 
     44    uint cursor; 
     45    Position pos; 
     46    Position lastPosition; 
     47     
     48    /+++ public parser +++/ 
     49     
     50    public void parse(IXMLConsumer!(CharT) consumer,CharT[] source){ 
     51        parse(consumer,new OfflineProvider!(CharT),source); 
     52    }    
     53             
     54    public void parse(IXMLConsumer!(CharT) consumer,IXMLProvider!(CharT) provider,CharT[] source){ 
     55        this.consumer = consumer; 
     56        this.source = source; 
     57        this.provider = provider; 
     58        cursor = 0; 
     59        pos.offset = 0; 
     60        pos.line = 1; 
     61        pos.column = 1; 
     62        lastPosition = pos; 
     63         
     64        debug printf("xml: %.*s",source); 
     65         
     66        parseContent(true); 
     67    } 
     68     
     69    public Position getPosition(){ 
     70        return pos; 
     71    } 
     72     
     73    public Position getLastPosition(){ 
     74        return lastPosition; 
     75    } 
     76     
     77     
     78    /+++ exception handler +++/ 
     79     
     80    protected void exception(CharT[] value){ 
     81        throw new Exception(lastPosition.toString() ~ " - " ~ toUTF8(value)); 
     82    } 
     83     
     84    /+++ lexer rules +++/ 
     85     
     86    protected bit hasMore(){ 
     87        return cursor < source.length; 
     88    } 
     89         
     90    protected CharT getNext(CharT match=0){ 
     91        if(!hasMore()) return '\0'; // exception("unexpected EOF"); 
     92        CharT next = source[cursor]; 
     93        if(match != 0 && next != match){ 
     94            exception("unexpected token (expected '" ~ toCharTString(match) ~ "' not '" ~ toCharTString(next) ~ "')"); 
     95        } 
     96        cursor++; 
     97        if(cursor == '\n'){ 
     98            pos.line++; 
     99            pos.column = 1; 
     100        } 
     101        else{ 
     102            pos.column++; 
     103        } 
     104        pos.offset=cursor; 
     105        //writefln("getNext: '%c'",next); 
     106        return next; 
     107    } 
     108     
     109    protected CharT peek(){ 
     110        if(!hasMore()) return '\0'; 
     111        return source[cursor]; 
     112    } 
     113     
     114    protected CharT[] getUntil(CharT[] match){ 
     115        uint start = cursor; 
    105116        while(hasMore()){ 
    106             switch(peek().type){ 
    107             case TokSpace: 
    108                 getNext(); // eat whitespace 
    109                 break; 
    110                  
    111             case TokComment: 
    112                 consumer.xmlComment(getNext().value); 
    113                 break; 
    114                  
    115             case TokStartElem: 
    116                 getNext(); // eat '<' token 
    117                 if(peek().type == TokQuestion){ 
    118                     getNext(); // eat '?' token 
    119                     parseProcessingInstruction(); 
    120                     break; 
     117            if(source[cursor..cursor+match.length] == match){ 
     118                return source[start..cursor]; 
     119            } 
     120            getNext(); 
     121        } 
     122        return null; // dummy 
     123    }    
     124     
     125    protected CharT[] parseWhitespace(bit required){ 
     126        uint start = cursor; 
     127        if(required && peek() > 32) exception("expected whitespace"); 
     128        while(hasMore() && peek() <= 32){ 
     129            getNext(); 
     130        } 
     131        return source[start..cursor]; 
     132    } 
     133     
     134    protected CharT[] contentGetNext(){ 
     135        uint start = cursor; 
     136        while(hasMore()){ 
     137            CharT ch = peek(); 
     138            if(ch == '&' || ch == '<' || ch == ']'){ 
     139                if(start == cursor) getNext(); 
     140                return source[start..cursor]; 
     141            } 
     142            getNext(); 
     143        } 
     144        return null; // dummy 
     145    } 
     146     
     147    protected bit isDTDExpressionChar(CharT ch){ 
     148        return  
     149            ch == '&' || ch == '|' || ch == '*' || ch == '+' || 
     150            ch == '(' || ch == ')' || ch == ',' || ch == '&' || 
     151            ch == '%' 
     152        ;    
     153    } 
     154     
     155    protected bit isElementChar(CharT ch){ 
     156        return  
     157            ch == '?' || ch == '!' || ch == '=' || ch == '\'' || 
     158            ch == '\"' || ch == '>' || ch == '<' ||  
     159            ch == '[' || ch == ']' || ch == '/' || //ch == '-' || 
     160            isDTDExpressionChar(ch); 
     161        ; 
     162    } 
     163         
     164    protected CharT[] elementGetNext(){ 
     165        uint start = cursor; 
     166        while(hasMore()){ 
     167            CharT ch = peek(); 
     168            if(ch <= 32 || isElementChar(ch)){ 
     169                if(start == cursor) getNext(); 
     170                return source[start..cursor]; 
     171            } 
     172            getNext(); 
     173        } 
     174        return null; // dummy        
     175    } 
     176 
     177    /+++ Parser +++/ 
     178 
     179    protected void parseEntity(){ 
     180        consumer.xmlText(getUntil(";"),TextType.EntityText); 
     181        getNext(); 
     182    } 
     183     
     184    protected CharT[] parseString(){ 
     185        CharT[] value; 
     186        lastPosition = pos; 
     187        CharT delim = getNext(); 
     188         
     189        if(delim == '\'' || delim == '\"'){ 
     190            while(hasMore()){ 
     191                CharT tok = getNext(); 
     192                if(tok == delim) break; 
     193                else if(tok == '&'){ 
     194                    value ~= consumer.getEntityValue(getUntil(";"),true); 
     195                    getNext(); 
    121196                } 
    122197                else{ 
    123                     return; // something other than a PI, let parent worry about it 
     198                    value ~= tok; 
    124199                } 
    125                 break; 
    126                  
    127             default: 
    128                 return; // let parent worry about it 
    129             } 
    130         } 
    131     }    
    132      
    133     protected void parseDoctype(){ 
    134         /* 
    135         [28]    doctypedecl    ::=      '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubset ']' S?)? '>'   [VC: Root Element Type][WFC: External Subset] 
    136         [28a]       DeclSep    ::=      PEReference | S [WFC: PE Between Declarations] 
    137         [28b]       intSubset      ::=      (markupdecl | DeclSep)* 
    138         [29]    markupdecl     ::=      elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment    [VC: Proper Declaration/PE Nesting][WFC: PEs in Internal Subset] 
    139         [75]    ExternalID     ::=      'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral       
    140         */ 
    141         char[] name,pubidLiteral,systemLiteral; 
    142          
    143         getNext(TokName,"DOCTYPE"); 
    144         getNextOptional(TokSpace); 
    145          
    146         name = getNext(TokName).value; 
    147         getNextOptional(TokSpace);       
    148          
    149         if(peek().type == TokName){ 
    150             ILexerStream newStream = parseExternalID(); 
    151              
    152             if(newStream){ 
    153                 // check for non-empty stream 
    154                 if(newStream.hasMore()){ 
    155                     //parse against DTD 
    156                     DTDParser parser = new DTDParser(); 
    157                     parser.parse(consumer,provider,newStream); 
    158                      
    159                     //copy any useful bits 
    160                     copyParserData(parser); 
    161                 } 
    162             } 
    163             else{ 
    164                 throw new XMLParserException(this,peek().pos,"could not resolve external DTD"); 
    165             } 
    166              
    167             getNextOptional(TokSpace); 
    168         } 
    169          
    170         consumer.xmlStartDoctype(name); 
    171      
    172         // parse all the elements under the doctype 
    173         if(peek().type == TokDTD){ 
    174             SimpleStream newStream = new SimpleStream(getNext().value); 
    175             DTDParser parser; 
    176             try{ 
    177                 parser = new DTDParser(); 
    178                 parser.parse(consumer,provider,newStream); 
    179             } 
    180             catch(XMLException e){ 
    181                 throw new XMLParserException(e,this,peek().pos,"Error in parsing DTD"); 
    182             } 
    183  
    184             //copy any useful bits 
    185             copyParserData(parser); 
    186             getNextOptional(TokSpace); 
    187         } 
    188          
    189         getNext(TokEndElem); 
    190          
    191         consumer.xmlEndDoctype(name); 
    192     } 
    193      
    194     protected ILexerStream parseExternalID(){ 
    195         char[] pubidLiteral; 
    196         char[] systemLiteral; 
    197          
    198         if(peek().value == "SYSTEM"){ 
    199             pubidLiteral = ""; // empty value 
    200             getNext(); // eat token 
    201             getNext(TokSpace); 
    202             systemLiteral = parseString();   
    203         } 
    204         else if(peek().value == "PUBLIC"){ 
    205             getNext(); // eat token 
    206             getNext(TokSpace); 
    207             pubidLiteral = parseString(); 
    208             getNext(TokSpace); 
    209             systemLiteral = parseString();               
     200            } 
    210201        } 
    211202        else{ 
    212             throw new XMLParserException(this,peek().pos,"document external identity must begin with 'PUBLIC' or 'SYSTEM'"); 
    213         } 
    214          
    215         return provider.resolveExternalID(pubidLiteral,systemLiteral); 
    216     } 
    217      
    218     protected void parseElement(){ 
    219         char[] name;         
    220         XMLAttributes attribs; 
    221          
    222         name = getNext(TokName).value; 
    223          
    224         attribs = parseAttributes(); 
    225         getNextOptional(TokSpace); 
    226          
    227         if(peek().type == TokSlash){ 
    228             getNext(); // eat token 
    229             getNext(TokEndElem); 
    230             consumer.xmlStartElement(name,attribs,true); 
    231             consumer.xmlEndElement(name,attribs,true); 
    232         } 
    233         else{ 
    234             getNext(TokEndElem);  
    235             consumer.xmlStartElement(name,attribs,false); 
    236              
    237             parseElementContent(); 
    238          
    239             getNext(TokSlash); 
    240             getNext(TokName,name); 
    241             getNextOptional(TokSpace); 
    242             getNext(TokEndElem); 
    243              
    244             consumer.xmlEndElement(name,attribs,false); 
    245         } 
    246     } 
    247          
    248     protected void parseElementContent(){ 
     203            exception("Expected ' or \""); 
     204        } 
     205        return value; 
     206    } 
     207     
     208    protected void parseContent(bit isTopLevel=false,bit isDTD=false){ 
    249209        while(hasMore()){ 
    250             switch(peek().type){ 
    251             case TokStartElem: 
    252                 getNext(); // eat token 
    253                  
    254                 if(peek().type == TokSlash){ 
     210            lastPosition = pos; 
     211            CharT[] tok = contentGetNext(); 
     212            switch(tok){ 
     213            case "<": 
     214                if(peek() == '/'){ 
     215                    if(isTopLevel) exception("tag closed out of sequence"); 
    255216                    return; // end parsing the content, as the tag is ending 
    256217                } 
     
    260221                break; 
    261222                 
    262             case TokEntity: 
    263                 consumer.xmlText(parseEntity()); 
    264                 break; 
    265              
    266             case TokSpace: 
    267             case TokChars: 
    268                 consumer.xmlText(getNext().value); 
     223            case "&": 
     224                parseEntity(); 
     225                break; 
     226                 
     227            case "]": 
     228                if(isDTD) return;  
     229                // fallthrough   
     230 
     231            default: 
     232                consumer.xmlText(tok,TextType.NormalText); 
     233                break; 
     234            } 
     235        } 
     236    } 
     237     
     238    /+++ DTD Parsers +++/ 
     239    protected void parseElement(){ 
     240        switch(peek()){ 
     241        case '!': 
     242            getNext(); 
     243            if(peek() == '-'){ // comment 
     244                getNext(); 
     245                getNext('-'); 
     246                consumer.xmlComment(getUntil("--")); 
     247                getNext(); 
     248                getNext(); 
     249                getNext('>'); 
    269250                break;           
     251            } 
     252                     
     253            CharT[] tok = elementGetNext(); 
     254            switch(tok){             
     255            case "DOCTYPE": 
     256                parseWhitespace(true); 
     257                parseDTDDoctype(); 
     258                break; 
     259 
     260            case "ELEMENT": 
     261                parseWhitespace(true); 
     262                parseDTDElement(); 
     263                break; 
     264 
     265            case "ATTLIST": 
     266                parseWhitespace(true); 
     267                parseDTDAttlist(); 
     268                break; 
     269 
     270            case "ENTITY":       
     271                parseWhitespace(true); 
     272                parseDTDEntity(); 
     273                break; 
     274 
     275            case "NOTATION": 
     276                parseWhitespace(true); 
     277                parseDTDNotation(); 
     278                break;   
     279 
     280            default: 
     281                exception("Unknown DTD element: <!" ~ tok ~ ">"); 
     282 
     283                //recovery 
     284                getUntil(">"); 
     285                getNext(); 
     286            } 
     287            break; 
     288             
     289        case '?': 
     290            getNext(); 
     291            parseProcessingInstruction(); 
     292            break; 
     293             
     294        case '[': 
     295            getNext(); 
     296            parseCDATA(); 
     297            break; 
     298             
     299        default: 
     300            parseStandardElement(); 
     301        } 
     302    } 
     303     
     304     
     305    /+++ DTD parsers +++/ 
     306     
     307    protected void parseDTDDoctype(){ 
     308        CharT[] name = elementGetNext(); 
     309        parseWhitespace(true); 
     310         
     311        XMLAttributes externalID; 
     312        if(peek() != '[' && peek() != '>'){ 
     313            externalID = parseExternalId(); 
     314             
     315            CharT[] externalDTD = provider.resolveExternalID(externalID["PUBLIC"],externalID["SYSTEM"]); 
     316             
     317            // parse the external DTD through a new parser (using the same callbacks) 
     318            //XMLParserBase!(wchar) parser = new XMLParserBase!(CharT)(); 
     319            //parser.parse(consumer,provider,externalDTD); 
     320             
     321            parseWhitespace(false); 
     322        } 
     323     
     324        consumer.xmlStartDTD(name,externalID); 
     325         
     326        if(peek() == '['){ 
     327            getNext();           
     328            parseContent(false,true); 
     329            parseWhitespace(false); 
     330        } 
     331     
     332        getNext('>'); 
     333        consumer.xmlEndDTD(name,externalID); 
     334    } 
     335     
     336    protected void parseDTDElement(){ 
     337        CharT[] name = elementGetNext(); 
     338        parseWhitespace(true); 
     339         
     340        consumer.xmlStartDTDElement(name); 
     341         
     342        //TODO: assume more responsibility than this 
     343        while(hasMore() && peek() != '>'){ 
     344            CharT ch = peek(); 
     345            if(isDTDExpressionChar(ch)){ 
     346                consumer.xmlSetDTDElementOperation(name,cast(IXMLConsumer!(CharT).DTDOperation)getNext()); 
     347            } 
     348            else if(isElementChar(ch)){ 
     349                exception("unexpected token '" ~ toCharTString(ch) ~ "'"); 
     350            } 
     351            else{ 
     352                consumer.xmlSetDTDElementChild(name,elementGetNext());           
     353            } 
     354            parseWhitespace(false); 
     355        } 
     356         
     357        consumer.xmlEndDTDElement(name); 
     358         
     359        getNext('>');    
     360    } 
     361     
     362    protected void parseDTDAttlist(){ 
     363        CharT[] name = elementGetNext(); 
     364        parseWhitespace(true); 
     365         
     366        consumer.xmlStartDTDAttlist(name); 
     367     
     368        while(hasMore() && peek() != '>'){ 
     369            CharT[] attName = elementGetNext(); 
     370            parseWhitespace(true); 
     371             
     372            //TODO: this needs help for parsing notations and enumerations 
     373            CharT[] attType = elementGetNext(); 
     374            parseWhitespace(true); 
     375             
     376            CharT[] defaultValue = elementGetNext(); 
     377            parseWhitespace(false); 
     378             
     379            consumer.xmlSetDTDAttlistDef(name,attName,attType,defaultValue); 
     380        } 
     381     
     382        consumer.xmlEndDTDAttlist(name);         
     383        getNext('>');    
     384    } 
     385     
     386    protected void parseDTDEntity(){ 
     387        if(peek() == '%'){ 
     388            getNext(); 
     389            parseWhitespace(true); 
     390             
     391            CharT[] name = elementGetNext(); 
     392            parseWhitespace(true);   
     393             
     394            CharT ch = peek(); 
     395            if(ch == '\'' || ch == '"'){ 
     396                CharT[] value = parseString(); 
     397                consumer.xmlSetDTDEntity(name,value,null,true); 
     398            } 
     399            else{ 
     400                XMLAttributes externalId = parseExternalId(); 
    270401                 
    271             case TokComment: 
    272                 consumer.xmlComment(getNext().value)
    273                 break
     402               //TODO: chase down the externalId via provider and a custom consumer that will capture the needed entity def 
     403                CharT[] value
     404                consumer.xmlSetDTDEntity(name,value,null,true)
    274405                 
    275             default: 
    276                 getNext(TokNull); // force error 
    277                 break; 
    278             } 
    279         } 
    280     }    
    281      
    282     protected void parseProcesingInstruction(){ 
    283         char[] name,data; 
    284          
    285         name = getNext(TokName).value; 
    286         getNext(TokSpace); 
    287          
    288         if(tolower(name) == "xml"){ 
    289             throw new XMLParserException(this,peek().pos,"processing instruction 'xml' not allowed outside the prolog"); 
    290         } 
    291         data = getNext(TokChars).value; 
    292  
    293         getNext(TokQuestion); 
    294         getNext(TokEndElem); 
    295          
    296         consumer.xmlProcessingInstruction(name,data); 
    297     } 
    298  
     406                parseWhitespace(false); 
     407            } 
     408        } 
     409