Check-in [2459e9a821]
Not logged in
Overview
SHA1 Hash:2459e9a8211a9a1182de150f2186c647cabf7453
Date: 2010-11-09 23:24:09
User: kinaba
Comment:refactored eof-driven REPL
Timelines: family | ancestors | descendants | both | trunk
Downloads: Tarball | ZIP archive
Other Links: files | file ages | manifest
Tags And Properties
Changes

Modified main.d from [81cf3e2a44d63239] to [b29be17d733aa309].

20 20 Value lastVal; 21 21 int lineno = 1; 22 22 int nextlineno = 1; 23 23 this() { ctx = createGlobalContext(); } 24 24 25 25 bool tryRun( string s ) 26 26 { 27 - nextlineno ++; 27 + scope(failure) 28 + { buf = ""; lineno = nextlineno; } 29 + 28 30 buf ~= s; 29 - try { 30 - AST a = parseString(buf, "<REPL>", lineno); 31 - buf = ""; 32 - lineno = nextlineno; 33 - lastVal = eval(a, ctx); 34 - } catch( LexException ) { 35 - // always EOF exception, so wait next 36 - return false; 37 - } catch( ParseException e ) { 38 - if( find(e.msg, "EOF")!="" ) // ultra ad-hoc 39 - return false; 40 - buf = ""; 41 - lineno = nextlineno; 42 - throw e; 43 - } 31 + nextlineno ++; 32 + try 33 + { lastVal = eval(parseString(buf, "<REPL>", lineno), ctx); } 34 + catch( UnexpectedEOF ) 35 + { return false; } // wait 36 + buf = ""; 37 + lineno = nextlineno; 44 38 return true; 45 39 } 46 40 47 41 bool singleInteraction() 48 42 { 49 43 writef(">> ", lineno); 50 44 string line = readln();

Modified polemy/eval.d from [cdb82702a34f8def] to [62b3543f769fc9c9].

175 175 assert_eq( r.val, new IntValue(BigInt(21+21*21)) ); 176 176 assert_eq( r.ctx.get("x","@val"), new IntValue(BigInt(21+21*21)) ); 177 177 assert_nothrow( r.ctx.get("x","@val") ); 178 178 assert_throw!RuntimeException( r.ctx.get("y","@val") ); 179 179 } 180 180 unittest 181 181 { 182 - assert_nothrow( evalString(`print("Hello, world!");`) ); 183 - assert_nothrow( evalString(`print(fun(){});`) ); 184 -} 185 -unittest 186 -{ 187 182 assert_eq( evalString(`let x=1; let y=(let x=2); x`).val, new IntValue(BigInt(1)) ); 188 183 assert_eq( evalString(`let x=1; let y=(let x=2;fun(){x}); y()`).val, new IntValue(BigInt(2)) ); 189 184 } 190 185 unittest 191 186 { 192 187 assert_eq( evalString(`@a x=1; @b x=2; @a(x)`).val, new IntValue(BigInt(1)) ); 193 188 assert_eq( evalString(`@a x=1; @b x=2; @b(x)`).val, new IntValue(BigInt(2)) ); 194 189 assert_eq( evalString(`let x=1; let _ = (@a x=2;2); x`).val, new IntValue(BigInt(1)) ); 195 190 assert_throw!Error( evalString(`let x=1; let _ = (@a x=2;2); @a(x)`) ); 196 191 } 197 192 198 193 unittest 199 194 { 200 - assert_nothrow( evalString(`var fac = fun(x){ 201 - 1; 202 - }; 203 - print(fac(3));`)); 204 - assert_nothrow( evalString(`var fac = fun(x){ 195 + assert_eq( evalString(`var fac = fun(x){ 205 196 if(x) 206 197 { x*fac(x-1); } 207 198 else 208 199 { 1; }; 209 200 }; 210 - print(fac(10));`)); 211 - assert_nothrow( evalString(`var fib = fun(x){ 201 + fac(10);`).val, new IntValue(BigInt(10*9*8*5040))); 202 + assert_eq( evalString(`var fib = fun(x){ 212 203 if(x<2) 213 204 { 1; } 214 205 else 215 206 { fib(x-1) + fib(x-2); }; 216 207 }; 217 - print(fib(10));`)); 208 + fib(10);`).val, new IntValue(BigInt(89))); 218 209 }

Modified polemy/lex.d from [a96449f7398ef311] to [1725bdb3bf054565].

7 7 module polemy.lex; 8 8 import polemy._common; 9 9 import std.file : readText; 10 10 import std.ctype : isspace, isalnum; 11 11 12 12 /// Exception from this module 13 13 14 -class LexException : Exception 14 +/*mixin*/ 15 +template ExceptionWithPosition() 15 16 { 16 17 const LexPosition pos; 17 - 18 18 this( const LexPosition pos, string msg, string file=null, size_t line=0, Throwable next=null ) 19 19 { super(sprintf!"[%s] %s"(pos, msg), file, line, next); this.pos = pos; } 20 +} 21 + 22 +class UnexpectedEOF : Exception 23 +{ 24 + mixin ExceptionWithPosition; 25 +} 26 + 27 +class LexException : Exception 28 +{ 29 + mixin ExceptionWithPosition; 20 30 }; 21 31 22 32 /// Represents a position in a source code 23 33 24 34 class LexPosition 25 35 { 26 36 immutable string filename; /// name of the source file ................................................................................ 158 168 bool isLetter (dchar c) { return !isSpace(c) && !isSymbol(c); } 159 169 } 160 170 161 171 string readQuoted(const LexPosition pos){char[] buf; return readQuoted(pos,buf);} 162 172 string readQuoted(const LexPosition pos, ref char[] buf) 163 173 { 164 174 if( reader.empty ) 165 - throw genex!LexException(pos, "EOF found while lexing a quoted-string"); 175 + throw genex!UnexpectedEOF(pos, "Quoted string not terminated"); 166 176 dchar c = reader.front; 167 177 reader.popFront; 168 178 if( c == '"' ) 169 179 return assumeUnique(buf); 170 180 if( c == '\\' && !reader.empty ) { 171 181 if( reader.front=='"' ) { 172 182 reader.popFront; ................................................................................ 289 299 assert_eq( lexf.front.str, "import" ); 290 300 assert_eq( lexf.front.pos.lineno, 8 ); 291 301 assert_eq( lexf.front.pos.column, 1 ); 292 302 } 293 303 294 304 unittest 295 305 { 296 - assert_throw!LexException( lexerFromString(`"`) ); 306 + assert_throw!UnexpectedEOF( lexerFromString(`"`) ); 297 307 } 298 308 299 309 unittest 300 310 { 301 311 auto lex = lexerFromString(`my # comment should`~"\r\n"~`# hey!! 302 312 be ignored. 303 313 hahaha"hihihi""hu\\\"huhu"#123 aa

Modified polemy/parse.d from [fafb5e120f10db35] to [a8c1fd55863166fb].

9 9 import polemy.lex; 10 10 import polemy.ast; 11 11 12 12 /// Exception from this module 13 13 14 14 class ParseException : Exception 15 15 { 16 - const LexPosition pos; 17 - 18 - this( const LexPosition pos, string msg, string file=null, size_t line=0, Throwable next=null ) 19 - { super(sprintf!"[%s] %s"(pos, msg), file, line, next); this.pos = pos; } 16 + mixin ExceptionWithPosition; 20 17 } 21 18 22 -private auto createException(Lexer)(Lexer lex, string msg) 23 - { return new ParseException(lex.empty?null:lex.front.pos, msg); } 24 - 25 19 /// Entry points of this module 26 20 27 21 auto parseString(S, T...)(S str, T fn_ln_cn) 28 22 { return parserFromString(str, fn_ln_cn).parse(); } 29 23 30 24 auto parseFile(S, T...)(S filename, T ln_cn) 31 25 { return parserFromFile(filename, ln_cn).parse(); } ................................................................................ 46 40 private class Parser(Lexer) 47 41 if( isForwardRange!(Lexer) && is(ElementType!(Lexer) == Token) ) 48 42 { 49 43 AST parse() 50 44 { 51 45 auto e = Body(); 52 46 if( !lex.empty ) 53 - throw createException(lex, "input is not ended but parser came to the end"); 47 + throw genex!ParseException(currentPosition(), "parsing ended but some tokens left"); 54 48 return e; 55 49 } 56 50 57 51 AST Body() 58 52 { 59 - if( lex.empty || !lex.front.quoted && lex.front.str=="}" ) 53 + if( lex.empty || !lex.front.quoted && ["}",")","]"].canFind(lex.front.str) ) 60 54 return doNothingExpression(); 61 55 62 56 auto saved = lex.save; 63 57 auto pos = lex.front.pos; 64 58 string kwd = lex.front.str; 65 59 if( tryEat("let") || tryEat("var") || tryEat("def") || tryEat("@") ) 66 60 { 67 61 if( kwd == "@" ) { 68 - kwd ~= eatId("after @"); 62 + kwd ~= eatId("after @",true); 69 63 if( tryEat("(") ) { 70 64 lex = saved; 71 65 goto asExpression; 72 66 } 73 67 } 74 68 immutable LexPosition varpos = (lex.empty ? null : lex.front.pos); 75 - string var = eatId("after "~kwd); 69 + string var = eatId("after "~kwd,true); 76 70 eat("=", "after "~kwd); 77 71 kwd = (kwd[0]=='@' ? kwd : ""); // "let, var, def ==> neutral layer" 78 72 auto e = E(0); 79 - if( tryEat(";") && !lex.empty && (lex.front.quoted || (lex.front.str!="}" && lex.front.str!=")")) ) 73 + if( tryEat(";") && !lex.empty && (lex.front.quoted || !["}",")","]"].canFind(lex.front.str)) ) 80 74 return new LetExpression(pos, var, kwd, e, Body()); 81 75 else 82 76 return new LetExpression(pos, var, kwd, e, new VarExpression(varpos, var)); 83 77 } 84 78 else 85 79 { 86 80 asExpression: 87 81 auto e = E(0); 88 82 if( tryEat(";") && !lex.empty && (lex.front.quoted || (lex.front.str!="}" && lex.front.str!=")")) ) 89 - return new LetExpression(pos, "_", "@val", e, Body()); 83 + return new LetExpression(pos, "_", "", e, Body()); 90 84 else 91 85 return e; 92 86 } 93 87 } 94 88 95 89 // [TODO] make customizable from program 96 90 static immutable string[][] operator_perferences = [ ................................................................................ 134 128 } 135 129 136 130 AST Funcall() 137 131 { 138 132 auto e = BaseExpression(); 139 133 while( tryEat("(") ) 140 134 { 135 + auto pos = currentPosition(); 141 136 AST[] args; 142 137 while( !tryEat(")") ) { 143 138 if( lex.empty ) 144 - throw createException(lex,"Unexpected EOF"); 139 + throw genex!UnexpectedEOF(pos,"Closing ')' for arguments not found"); 145 140 args ~= E(0); 146 141 if( !tryEat(",") ) { 147 142 eat(")", "after function parameters"); 148 143 break; 149 144 } 150 145 } 151 146 e = new FuncallExpression(e.pos, e, args); ................................................................................ 152 147 } 153 148 return e; 154 149 } 155 150 156 151 AST BaseExpression() 157 152 { 158 153 if( lex.empty ) 159 - throw createException(lex, "Reached EOF when tried to parse an expression"); 154 + throw genex!UnexpectedEOF(currentPosition(), "Reached EOF when tried to parse an expression"); 160 155 161 156 auto pos = lex.front.pos; 162 157 if( lex.front.quoted ) 163 158 { 164 159 scope(exit) lex.popFront; 165 160 return new StrLiteral(pos, lex.front.str); 166 161 } ................................................................................ 230 225 private: 231 226 Lexer lex; 232 227 this(Lexer lex) { this.lex = lex; } 233 228 234 229 void eat(string kwd, lazy string msg) 235 230 { 236 231 if( !tryEat(kwd) ) 237 - throw createException(lex, "'"~kwd~"' is expected "~msg~" but '" 238 - ~(lex.empty ? "EOF" : lex.front.str)~"' occured"); 232 + if( lex.empty ) 233 + throw genex!UnexpectedEOF( 234 + currentPosition(), sprintf!"%s is expected for %s but not found"(kwd,msg)); 235 + else 236 + throw genex!ParseException( 237 + currentPosition(), sprintf!"%s is expected for %s but not found"(kwd,msg)); 239 238 } 240 239 241 240 bool tryEat(string kwd) 242 241 { 243 242 if( lex.empty || lex.front.quoted || lex.front.str!=kwd ) 244 243 return false; 245 244 lex.popFront; 246 245 return true; 247 246 } 248 247 249 - string eatId(lazy string msg) 248 + string eatId(lazy string msg, bool allowQuoted=false) 250 249 { 251 - if( lex.empty || lex.front.quoted ) 252 - throw createException(lex, "identifier is expected but not found "~msg); 253 - string id = lex.front.str; 254 - lex.popFront; 255 - return id; 250 + if( lex.empty ) 251 + throw genex!UnexpectedEOF(currentPosition(), "identifier is expected but not found "~msg); 252 + if( !allowQuoted && lex.front.quoted ) 253 + throw genex!ParseException(currentPosition(), "identifier is expected but not found "~msg); 254 + scope(exit) lex.popFront; 255 + return lex.front.str; 256 256 } 257 257 258 258 bool isNumber(string s) 259 259 { 260 260 return find!(`a<'0'||'9'<a`)(s).empty; 261 261 } 262 262 263 263 AST doNothingExpression() 264 264 { 265 - return new IntLiteral(lex.empty?null:lex.front.pos, BigInt(178)); 265 + return new IntLiteral(currentPosition(), BigInt(178)); 266 + } 267 + 268 + immutable(LexPosition) currentPosition() 269 + { 270 + return lex.empty ? null : lex.front.pos; 266 271 } 267 272 } 268 273 269 274 unittest 270 275 { 271 276 mixin EasyAST; 272 277 273 278 assert_eq(parseString(`123`), intl(123)); 274 279 assert_eq(parseString(`"foo"`), strl("foo")); 275 280 assert_eq(parseString(`fun(){1}`), fun([],intl(1))); 276 281 assert_eq(parseString(`fun(x){1}`), fun(["x"],intl(1))); 277 282 assert_eq(parseString("\u03BB(){1}"), fun([],intl(1))); 278 283 assert_eq(parseString("\u03BB(x){1}"), fun(["x"],intl(1))); 279 - assert_eq(parseString(`1;2`), let("_","@val",intl(1),intl(2))); 280 - assert_eq(parseString(`1;2;`), let("_","@val",intl(1),intl(2))); 284 + assert_eq(parseString(`1;2`), let("_","",intl(1),intl(2))); 285 + assert_eq(parseString(`1;2;`), let("_","",intl(1),intl(2))); 281 286 assert_eq(parseString(`let x=1;2`), let("x","",intl(1),intl(2))); 282 287 assert_eq(parseString(`var x=1;2;`), let("x","",intl(1),intl(2))); 283 288 assert_eq(parseString(`def x=1`), let("x","",intl(1),var("x"))); 284 289 assert_eq(parseString(`@val x=1;`), let("x","@val",intl(1),var("x"))); 285 290 assert_eq(parseString(`@typ x="#int";`), let("x","@typ",strl("#int"),var("x"))); 286 291 assert_eq(parseString(`f(1,2)`), call(var("f"),intl(1),intl(2))); 287 292 assert_eq(parseString(`if(1){2}`), call(var("if"),intl(1),fun([],intl(2)),fun([],intl(178)))); ................................................................................ 315 320 call(var("fac"),intl(10)) 316 321 ) 317 322 ); 318 323 } 319 324 320 325 unittest 321 326 { 322 - assert_throw!ParseException(parseString(`1+`)); 327 + assert_throw!UnexpectedEOF(parseString(`1+`)); 323 328 assert_throw!ParseException(parseString(`1+2}`)); 324 - assert_throw!ParseException(parseString(`let "x"`)); 325 - assert_throw!ParseException(parseString(`var`)); 329 + assert_throw!UnexpectedEOF(parseString(`let "x"`)); 330 + assert_throw!UnexpectedEOF(parseString(`var`)); 326 331 assert_throw!ParseException(parseString(`@val x ==`)); 327 332 assert_throw!ParseException(parseString(`if(){1}`)); 328 - assert_throw!ParseException(parseString(`f(`)); 333 + assert_throw!UnexpectedEOF(parseString(`f(`)); 329 334 }

Modified tricks/test.d from [314aad0104b85557] to [09cc57fcd7c969ca].

13 13 void assert_throw(ExceptionType, T, string fn=__FILE__, size_t ln=__LINE__)(lazy T t, string msg="") 14 14 { 15 15 try 16 16 { t(); } 17 17 catch(ExceptionType) 18 18 { return; } 19 19 catch(Throwable e) 20 - { onAssertErrorMsg(fn, ln, msg.length ? msg : "exception ["~e.toString()~"]"); } 21 - onAssertErrorMsg(fn, ln, msg.length ? msg : "no execption"); 20 + { onAssertErrorMsg(fn, ln, msg.length ? msg : "bad exception\n >> "~e.toString()); } 21 + onAssertErrorMsg(fn, ln, msg.length ? msg : "not thrown"); 22 22 } 23 23 24 24 /// Unittest helper that asserts an expression must not throw anything 25 25 26 26 auto assert_nothrow(T, string fn=__FILE__, size_t ln=__LINE__)(lazy T t, string msg="") 27 27 { 28 28 try 29 29 { return t(); } 30 30 catch(Throwable e) 31 - { onAssertErrorMsg(fn, ln, msg.length ? msg : "exception ["~e.toString()~"]"); } 31 + { onAssertErrorMsg(fn, ln, msg.length ? msg : "bad exception\n >> "~e.toString()); } 32 32 assert(false); 33 33 } 34 34 35 35 unittest 36 36 { 37 37 auto error = {throw new Error("hello");}; 38 38 auto nothing = (){};