4198578702 2010-11-07 kinaba: /** 4198578702 2010-11-07 kinaba: * Authors: k.inaba 4198578702 2010-11-07 kinaba: * License: NYSL 0.9982 http://www.kmonos.net/nysl/ 4198578702 2010-11-07 kinaba: * 4198578702 2010-11-07 kinaba: * Lexer for Polemy programming language. 4198578702 2010-11-07 kinaba: */ 423f308350 2010-11-07 kinaba: module polemy.lex; 423f308350 2010-11-07 kinaba: import polemy._common; 3464a035ec 2010-11-20 kinaba: import polemy.failure; 5e407d7cf8 2010-11-08 kinaba: import std.file : readText; 5e407d7cf8 2010-11-08 kinaba: import std.ctype : isspace, isalnum; 423f308350 2010-11-07 kinaba: 8d297342aa 2010-11-08 kinaba: /// Represents a lexer token 423f308350 2010-11-07 kinaba: 8d297342aa 2010-11-08 kinaba: class Token 8d297342aa 2010-11-08 kinaba: { 8d297342aa 2010-11-08 kinaba: immutable LexPosition pos; /// Position where the token occurred in the source 8d297342aa 2010-11-08 kinaba: immutable string str; /// The token string itself 8d297342aa 2010-11-08 kinaba: immutable bool quoted; /// Was it a "quoted" token or unquoted? 423f308350 2010-11-07 kinaba: 8de5b49cdf 2010-11-09 kinaba: mixin SimpleClass; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: unittest 423f308350 2010-11-07 kinaba: { 2bdfb8a182 2010-11-21 kinaba: auto p = new LexPosition("hello.cpp", 123, 45); 8d297342aa 2010-11-08 kinaba: auto t = new Token(p, "class", false); 8d297342aa 2010-11-08 kinaba: auto u = new Token(p, "class", true); 61998c472a 2010-11-08 kinaba: 61998c472a 2010-11-08 kinaba: assert_eq( t.pos, p ); 61998c472a 2010-11-08 kinaba: assert_eq( t.str, "class" ); 8d297342aa 2010-11-08 kinaba: assert( !t.quoted ); 8d297342aa 2010-11-08 kinaba: assert_eq( t, new Token(p, "class", false) ); 8d297342aa 2010-11-08 kinaba: assert_lt( t, new Token(p, "struct", false) ); 8d297342aa 2010-11-08 kinaba: assert_ne( t, u ); 8d297342aa 2010-11-08 kinaba: assert( u.quoted ); 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: assert( !__traits(compiles, new Token) ); 423f308350 2010-11-07 kinaba: assert( !__traits(compiles, t.pos=p) ); 423f308350 2010-11-07 kinaba: assert( !__traits(compiles, t.str=789) ); 8d297342aa 2010-11-08 kinaba: assert( !__traits(compiles, t.quoted=true) ); 8d297342aa 2010-11-08 kinaba: } 8d297342aa 2010-11-08 kinaba: b985f3bf91 2010-11-08 kinaba: /// Named Construtors for Lexer 8d297342aa 2010-11-08 kinaba: 1c01f44f52 2010-11-13 kinaba: Lexer lexerFromFile(T...)( string filename, T ln_cn ) 8d297342aa 2010-11-08 kinaba: { 1c01f44f52 2010-11-13 kinaba: return lexerFromString( std.file.readText(filename), filename, ln_cn ); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 1c01f44f52 2010-11-13 kinaba: /// Named Construtor for Lexer 38fcc662be 2010-11-10 kinaba: 38fcc662be 2010-11-10 kinaba: LexerT!(PositionedReader!CharSeq) /* ddoc doesn't recognize auto return... bugzilla:2581 */ 38fcc662be 2010-11-10 kinaba: lexerFromString(CharSeq)( CharSeq str, string filename="<unnamed>", int lineno=1, int column=1 ) 423f308350 2010-11-07 kinaba: { 5e407d7cf8 2010-11-08 kinaba: return new LexerT!(PositionedReader!CharSeq)( 5e407d7cf8 2010-11-08 kinaba: PositionedReader!CharSeq(str, filename, lineno, column) 5e407d7cf8 2010-11-08 kinaba: ); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 1c01f44f52 2010-11-13 kinaba: /// Standard Lexer Type (all you have to know is that this is a forward range of Tokens!) 423f308350 2010-11-07 kinaba: 5e407d7cf8 2010-11-08 kinaba: alias LexerT!(PositionedReader!string) Lexer; 5e407d7cf8 2010-11-08 kinaba: 5e407d7cf8 2010-11-08 kinaba: /// Lexer Implementation 5e407d7cf8 2010-11-08 kinaba: 5e407d7cf8 2010-11-08 kinaba: class LexerT(Reader) 1c01f44f52 2010-11-13 kinaba: if( isForwardRange!(Reader) && is(ElementType!(Reader)==dchar) ) 423f308350 2010-11-07 kinaba: { 4198578702 2010-11-07 kinaba: /// Range primitive 423f308350 2010-11-07 kinaba: bool empty() /*@property*/ 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: return current is null; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 4198578702 2010-11-07 kinaba: /// Range primitive 423f308350 2010-11-07 kinaba: Token front() /*@property*/ 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: return std.exception.enforce(current, "Lexer has already reached the end"); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 4198578702 2010-11-07 kinaba: /// Range primitive 423f308350 2010-11-07 kinaba: void popFront() /*@property*/ 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: std.exception.enforce(current, "Lexer has already reached the end"); 423f308350 2010-11-07 kinaba: current = readNext(); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 4198578702 2010-11-07 kinaba: /// Range primitive 5e407d7cf8 2010-11-08 kinaba: typeof(this) save() /*@property*/ 423f308350 2010-11-07 kinaba: { 5e407d7cf8 2010-11-08 kinaba: return new typeof(this)(reader.save, current); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: private: // implementation 423f308350 2010-11-07 kinaba: 5e407d7cf8 2010-11-08 kinaba: Reader reader; 423f308350 2010-11-07 kinaba: Token current; 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: invariant() 423f308350 2010-11-07 kinaba: { 1c01f44f52 2010-11-13 kinaba: assert( reader.empty || !isSpace(reader.front) ); 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: 5e407d7cf8 2010-11-08 kinaba: this( Reader reader, Token current = null ) 5e407d7cf8 2010-11-08 kinaba: { 5e407d7cf8 2010-11-08 kinaba: this.reader = reader; 5e407d7cf8 2010-11-08 kinaba: readWhile!isSpace(); 5e407d7cf8 2010-11-08 kinaba: this.current = (current is null ? readNext() : current); 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: 1c01f44f52 2010-11-13 kinaba: public static 1c01f44f52 2010-11-13 kinaba: { 5e407d7cf8 2010-11-08 kinaba: bool isSpace (dchar c) { return std.ctype.isspace(c)!=0; } 5e407d7cf8 2010-11-08 kinaba: bool isSymbol (dchar c) { return 0x21<=c && c<=0x7f && !std.ctype.isalnum(c) && c!='_' && c!='\''; } 3995a5eb6a 2010-11-21 kinaba: bool isSSymbol (dchar c) { return "()[]{};,@".canFind(c); } aa770610d3 2010-11-08 kinaba: bool isMSymbol (dchar c) { return isSymbol(c) && !isSSymbol(c) && c!='"' && c!='#'; } 5e407d7cf8 2010-11-08 kinaba: bool isLetter (dchar c) { return !isSpace(c) && !isSymbol(c); } 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 5e407d7cf8 2010-11-08 kinaba: string readQuoted(const LexPosition pos){char[] buf; return readQuoted(pos,buf);} 5e407d7cf8 2010-11-08 kinaba: string readQuoted(const LexPosition pos, ref char[] buf) 423f308350 2010-11-07 kinaba: { 5e407d7cf8 2010-11-08 kinaba: if( reader.empty ) 2459e9a821 2010-11-09 kinaba: throw genex!UnexpectedEOF(pos, "Quoted string not terminated"); 5e407d7cf8 2010-11-08 kinaba: dchar c = reader.front; 5e407d7cf8 2010-11-08 kinaba: reader.popFront; 5e407d7cf8 2010-11-08 kinaba: if( c == '"' ) 5e407d7cf8 2010-11-08 kinaba: return assumeUnique(buf); 5e407d7cf8 2010-11-08 kinaba: if( c == '\\' && !reader.empty ) { 5e407d7cf8 2010-11-08 kinaba: if( reader.front=='"' ) { 5e407d7cf8 2010-11-08 kinaba: reader.popFront; 5e407d7cf8 2010-11-08 kinaba: return readQuoted(pos,buf ~= '\"'); 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: if( reader.front=='\\' ) { 5e407d7cf8 2010-11-08 kinaba: reader.popFront; 5e407d7cf8 2010-11-08 kinaba: return readQuoted(pos,buf ~= '\\'); 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: return readQuoted(pos,buf ~= c); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 5e407d7cf8 2010-11-08 kinaba: string readWhile(alias fn)() 423f308350 2010-11-07 kinaba: { 5e407d7cf8 2010-11-08 kinaba: char[] buf; 5e407d7cf8 2010-11-08 kinaba: for(; !reader.empty && fn(reader.front); reader.popFront) 5e407d7cf8 2010-11-08 kinaba: buf ~= reader.front; 5e407d7cf8 2010-11-08 kinaba: return assumeUnique(buf); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: Token readNext() 423f308350 2010-11-07 kinaba: { 5e407d7cf8 2010-11-08 kinaba: if( reader.empty ) 423f308350 2010-11-07 kinaba: return null; 5e407d7cf8 2010-11-08 kinaba: scope(success) 5e407d7cf8 2010-11-08 kinaba: readWhile!isSpace(); 5e407d7cf8 2010-11-08 kinaba: if( reader.front == '#' ) // comment 5e407d7cf8 2010-11-08 kinaba: { 5e407d7cf8 2010-11-08 kinaba: reader = find(reader, '\n'); 5e407d7cf8 2010-11-08 kinaba: readWhile!isSpace(); 5e407d7cf8 2010-11-08 kinaba: return readNext(); 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: else if( reader.front == '"' ) // quoted 5e407d7cf8 2010-11-08 kinaba: { 5e407d7cf8 2010-11-08 kinaba: auto pos = reader.currentPosition(); 5e407d7cf8 2010-11-08 kinaba: reader.popFront; 5e407d7cf8 2010-11-08 kinaba: return new Token(pos, readQuoted(pos), true); 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: else if( isSSymbol(reader.front) ) // paren 5e407d7cf8 2010-11-08 kinaba: { 5e407d7cf8 2010-11-08 kinaba: auto pos = reader.currentPosition(); 5e407d7cf8 2010-11-08 kinaba: string s; s~=reader.front; reader.popFront; 5e407d7cf8 2010-11-08 kinaba: return new Token(pos, s, false); 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: else if( isMSymbol(reader.front) ) // symbol 423f308350 2010-11-07 kinaba: { 5e407d7cf8 2010-11-08 kinaba: auto pos = reader.currentPosition(); 5e407d7cf8 2010-11-08 kinaba: return new Token(pos, readWhile!isMSymbol(), false); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: else 423f308350 2010-11-07 kinaba: { 5e407d7cf8 2010-11-08 kinaba: auto pos = reader.currentPosition(); 5e407d7cf8 2010-11-08 kinaba: return new Token(pos, readWhile!isLetter(), false); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: unittest 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: assert( std.range.isForwardRange!(Lexer) ); 7de80acfb8 2010-11-09 kinaba: assert( is(ElementType!(Lexer) == Token) ); 8d297342aa 2010-11-08 kinaba: } 8d297342aa 2010-11-08 kinaba: 8d297342aa 2010-11-08 kinaba: unittest 8d297342aa 2010-11-08 kinaba: { 5e407d7cf8 2010-11-08 kinaba: auto lex = lexerFromString("this is a \t\r\n pen :-( @@; "); 8d297342aa 2010-11-08 kinaba: Token[] ts = std.array.array(lex); 8d297342aa 2010-11-08 kinaba: 8d297342aa 2010-11-08 kinaba: assert_eq( ts[0].pos.lineno, 1 ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[0].pos.column, 1 ); 8d297342aa 2010-11-08 kinaba: assert( !ts[0].quoted ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[0].str, "this" ); 8d297342aa 2010-11-08 kinaba: 8d297342aa 2010-11-08 kinaba: assert_eq( ts[1].pos.lineno, 1 ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[1].pos.column, 6 ); 8d297342aa 2010-11-08 kinaba: assert( !ts[1].quoted ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[1].str, "is" ); 8d297342aa 2010-11-08 kinaba: 8d297342aa 2010-11-08 kinaba: assert_eq( ts[2].pos.lineno, 1 ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[2].pos.column, 9 ); 8d297342aa 2010-11-08 kinaba: assert( !ts[2].quoted ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[2].str, "a" ); 8d297342aa 2010-11-08 kinaba: 8d297342aa 2010-11-08 kinaba: assert_eq( ts[3].pos.lineno, 2 ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[3].pos.column, 2 ); 8d297342aa 2010-11-08 kinaba: assert( !ts[3].quoted ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[3].str, "pen" ); 8d297342aa 2010-11-08 kinaba: 8d297342aa 2010-11-08 kinaba: assert_eq( ts[4].pos.lineno, 2 ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[4].pos.column, 6 ); 5e407d7cf8 2010-11-08 kinaba: assert_eq( ts[4].str, ":-" ); 8d297342aa 2010-11-08 kinaba: 8d297342aa 2010-11-08 kinaba: assert_eq( ts[5].pos.lineno, 2 ); 5e407d7cf8 2010-11-08 kinaba: assert_eq( ts[5].pos.column, 8 ); 5e407d7cf8 2010-11-08 kinaba: assert_eq( ts[5].str, "(" ); 7465fcdd7f 2010-11-09 kinaba: assert_eq( ts[6].str, "@" ); 7465fcdd7f 2010-11-09 kinaba: assert_eq( ts[7].str, "@" ); 7465fcdd7f 2010-11-09 kinaba: assert_eq( ts[8].str, ";" ); // paren and simicolons, atmarks are split 8d297342aa 2010-11-08 kinaba: 7465fcdd7f 2010-11-09 kinaba: assert_eq( ts.length, 9 ); 8d297342aa 2010-11-08 kinaba: } 8d297342aa 2010-11-08 kinaba: 8d297342aa 2010-11-08 kinaba: unittest 8d297342aa 2010-11-08 kinaba: { 5e407d7cf8 2010-11-08 kinaba: // !! be sure to run the unittest on the root of the source directory 8d297342aa 2010-11-08 kinaba: auto lexf = lexerFromFile("polemy/lex.d"); 8d297342aa 2010-11-08 kinaba: lexf = find!`a.str == "module"`(lexf); 8d297342aa 2010-11-08 kinaba: assert_eq( lexf.front.str, "module" ); 8d297342aa 2010-11-08 kinaba: assert_eq( lexf.front.pos.filename, "polemy/lex.d" ); 8d297342aa 2010-11-08 kinaba: assert_eq( lexf.front.pos.lineno, 7 ); 8d297342aa 2010-11-08 kinaba: assert_eq( lexf.front.pos.column, 1 ); 8d297342aa 2010-11-08 kinaba: lexf.popFront; 8d297342aa 2010-11-08 kinaba: assert_eq( lexf.front.str, "polemy" ); 8d297342aa 2010-11-08 kinaba: assert_eq( lexf.front.pos.lineno, 7 ); 8d297342aa 2010-11-08 kinaba: assert_eq( lexf.front.pos.column, 8 ); 8d297342aa 2010-11-08 kinaba: lexf.popFront; 8d297342aa 2010-11-08 kinaba: lexf.popFront; 8d297342aa 2010-11-08 kinaba: lexf.popFront; 8d297342aa 2010-11-08 kinaba: lexf.popFront; 8d297342aa 2010-11-08 kinaba: assert_eq( lexf.front.str, "import" ); 8d297342aa 2010-11-08 kinaba: assert_eq( lexf.front.pos.lineno, 8 ); 8d297342aa 2010-11-08 kinaba: assert_eq( lexf.front.pos.column, 1 ); 8d297342aa 2010-11-08 kinaba: } 8d297342aa 2010-11-08 kinaba: 8d297342aa 2010-11-08 kinaba: unittest 8d297342aa 2010-11-08 kinaba: { 2459e9a821 2010-11-09 kinaba: assert_throw!UnexpectedEOF( lexerFromString(`"`) ); 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: 5e407d7cf8 2010-11-08 kinaba: unittest 5e407d7cf8 2010-11-08 kinaba: { 8d297342aa 2010-11-08 kinaba: auto lex = lexerFromString(`my # comment should`~"\r\n"~`# hey!! 8d297342aa 2010-11-08 kinaba: be ignored. 8d297342aa 2010-11-08 kinaba: hahaha"hihihi""hu\\\"huhu"#123 aa 5e407d7cf8 2010-11-08 kinaba: 123 aa "aaa`~"\n"~`bbb # 123`~"\r\n"~`eee" 8d297342aa 2010-11-08 kinaba: zzz 8d297342aa 2010-11-08 kinaba: `); 423f308350 2010-11-07 kinaba: Token[] ts = std.array.array(lex); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[0].str, "my" ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[0].pos.lineno, 1 ); 8d297342aa 2010-11-08 kinaba: assert( !ts[0].quoted ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[1].str, "be" ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[1].pos.lineno, 3 ); 8d297342aa 2010-11-08 kinaba: assert( !ts[1].quoted ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[2].str, "ignored" ); 8d297342aa 2010-11-08 kinaba: assert( !ts[2].quoted ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[3].str, "." ); 8d297342aa 2010-11-08 kinaba: assert( !ts[3].quoted ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[4].str, "hahaha" ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[4].pos.lineno, 4 ); 8d297342aa 2010-11-08 kinaba: assert( !ts[4].quoted ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[5].str, "hihihi" ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[5].pos.lineno, 4 ); 8d297342aa 2010-11-08 kinaba: assert( ts[5].quoted ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[6].str, `hu\"huhu` ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[6].pos.lineno, 4 ); 8d297342aa 2010-11-08 kinaba: assert( ts[6].quoted ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[7].str, "123" ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[7].pos.lineno, 5 ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[8].str, "aa" ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[9].pos.lineno, 5 ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[9].str, "aaa\nbbb # 123\neee" ); 8d297342aa 2010-11-08 kinaba: assert( ts[9].quoted ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts[10].pos.lineno, 8 ); 8d297342aa 2010-11-08 kinaba: assert( !ts[10].quoted ); 8d297342aa 2010-11-08 kinaba: assert_eq( ts.length, 11 ); 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: 5e407d7cf8 2010-11-08 kinaba: unittest 5e407d7cf8 2010-11-08 kinaba: { 5e407d7cf8 2010-11-08 kinaba: auto lex2 = lexerFromString(" a12\n3a 5 "); 5e407d7cf8 2010-11-08 kinaba: assert_eq( lex2.front.str, "a12" ); 5e407d7cf8 2010-11-08 kinaba: lex2.popFront; 5e407d7cf8 2010-11-08 kinaba: auto lex3 = lex2.save; 5e407d7cf8 2010-11-08 kinaba: assert_eq( lex2.front.str, "3a" ); 5e407d7cf8 2010-11-08 kinaba: lex2.popFront; 5e407d7cf8 2010-11-08 kinaba: assert_eq( lex3.front.str, "3a" ); 5e407d7cf8 2010-11-08 kinaba: assert_eq( lex2.front.str, "5" ); 5e407d7cf8 2010-11-08 kinaba: lex2.popFront; 5e407d7cf8 2010-11-08 kinaba: lex3.popFront; 5e407d7cf8 2010-11-08 kinaba: assert( lex2.empty ); 5e407d7cf8 2010-11-08 kinaba: assert( !lex3.empty ); 5e407d7cf8 2010-11-08 kinaba: assert_eq( lex3.front.str, "5" ); 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: aa770610d3 2010-11-08 kinaba: unittest aa770610d3 2010-11-08 kinaba: { aa770610d3 2010-11-08 kinaba: auto lex = lexerFromString(`=""`); aa770610d3 2010-11-08 kinaba: assert_eq(lex.front.str, "="); lex.popFront; aa770610d3 2010-11-08 kinaba: assert_eq(lex.front.str, ""); lex.popFront; aa770610d3 2010-11-08 kinaba: assert( lex.empty ); 7465fcdd7f 2010-11-09 kinaba: assert_eq( lexerFromString(`-@`).front.str, "-" ); aa770610d3 2010-11-08 kinaba: } aa770610d3 2010-11-08 kinaba: 5e407d7cf8 2010-11-08 kinaba: /// Forward range for reader character by character, 5e407d7cf8 2010-11-08 kinaba: /// keeping track of position information and caring \r\n -> \n conversion. 5e407d7cf8 2010-11-08 kinaba: 5e407d7cf8 2010-11-08 kinaba: struct PositionedReader(CharSeq) 1c01f44f52 2010-11-13 kinaba: if( isForwardRange!(CharSeq) && is(ElementType!(CharSeq)==dchar) ) 5e407d7cf8 2010-11-08 kinaba: { 5e407d7cf8 2010-11-08 kinaba: CharSeq buffer; 5e407d7cf8 2010-11-08 kinaba: string filename; 5e407d7cf8 2010-11-08 kinaba: int lineno; 5e407d7cf8 2010-11-08 kinaba: int column; 5e407d7cf8 2010-11-08 kinaba: 5e407d7cf8 2010-11-08 kinaba: /// Range primitive 5e407d7cf8 2010-11-08 kinaba: bool empty() /*@property*/ 5e407d7cf8 2010-11-08 kinaba: { 5e407d7cf8 2010-11-08 kinaba: return buffer.empty; 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: 5e407d7cf8 2010-11-08 kinaba: /// Range primitive 5e407d7cf8 2010-11-08 kinaba: dchar front() /*@property*/ 5e407d7cf8 2010-11-08 kinaba: { 5e407d7cf8 2010-11-08 kinaba: dchar c = buffer.front; 5e407d7cf8 2010-11-08 kinaba: return (c=='\r' ? '\n' : c); 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: 5e407d7cf8 2010-11-08 kinaba: /// Range primitive 5e407d7cf8 2010-11-08 kinaba: void popFront() /*@property*/ 5e407d7cf8 2010-11-08 kinaba: { 5e407d7cf8 2010-11-08 kinaba: dchar c = buffer.front; 5e407d7cf8 2010-11-08 kinaba: buffer.popFront; 5e407d7cf8 2010-11-08 kinaba: if( c=='\r' ) 5e407d7cf8 2010-11-08 kinaba: { 5e407d7cf8 2010-11-08 kinaba: if( !buffer.empty && buffer.front=='\n' ) 5e407d7cf8 2010-11-08 kinaba: buffer.popFront; 5e407d7cf8 2010-11-08 kinaba: c = '\n'; 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: if( c=='\n' ) 5e407d7cf8 2010-11-08 kinaba: { 5e407d7cf8 2010-11-08 kinaba: lineno ++; 5e407d7cf8 2010-11-08 kinaba: column = 1; 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: else 5e407d7cf8 2010-11-08 kinaba: column ++; 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: 5e407d7cf8 2010-11-08 kinaba: /// Range primitive 5e407d7cf8 2010-11-08 kinaba: typeof(this) save() /*@property*/ 5e407d7cf8 2010-11-08 kinaba: { 5e407d7cf8 2010-11-08 kinaba: return this; 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: 5e407d7cf8 2010-11-08 kinaba: /// Get the current position 2bdfb8a182 2010-11-21 kinaba: LexPosition currentPosition() const 5e407d7cf8 2010-11-08 kinaba: { 2bdfb8a182 2010-11-21 kinaba: return new LexPosition(filename, lineno, column); 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: } 5e407d7cf8 2010-11-08 kinaba: 5e407d7cf8 2010-11-08 kinaba: unittest 5e407d7cf8 2010-11-08 kinaba: { 5e407d7cf8 2010-11-08 kinaba: assert( isForwardRange!(PositionedReader!string) ); 5e407d7cf8 2010-11-08 kinaba: assert( is(ElementType!(PositionedReader!string) == dchar) ); 423f308350 2010-11-07 kinaba: }