Check-in [b985f3bf91]
Not logged in
Overview
SHA1 Hash:b985f3bf912b784e838de05d6f794ae2b22f68c9
Date: 2010-11-08 23:59:30
User: kinaba
Comment:refactored parser to change AST to ML-like one.
Timelines: family | ancestors | descendants | both | trunk
Downloads: Tarball | ZIP archive
Other Links: files | file ages | manifest
Tags And Properties
Changes

Modified .poseidon from [0e48df10e4abff23] to [1c3854b81b30b626].

29 29 <projectFiles> 30 30 <source> 31 31 <name>d2stacktrace\dbghelp.d</name> 32 32 <name>d2stacktrace\stacktrace.d</name> 33 33 <name>main.d</name> 34 34 <name>polemy\_common.d</name> 35 35 <name>polemy\ast.d</name> 36 - <name>polemy\eval.d</name> 37 36 <name>polemy\lex.d</name> 38 37 <name>polemy\parse.d</name> 39 38 <name>polemy\tricks.d</name> 40 - <name>polemy\value.d</name> 41 39 </source> 42 40 <interface /> 43 41 <resource /> 44 42 <othersDMD /> 45 43 <others> 46 44 <name>build.bat</name> 47 45 <name>build.sh</name>

Modified main.d from [e01d02a22ae40e90] to [e7185e89f4df4ae3].

2 2 * Authors: k.inaba 3 3 * License: NYSL 0.9982 (http://www.kmonos.net/nysl/ 4 4 * 5 5 * Entry point for Polemy interpreter. 6 6 */ 7 7 8 8 import std.stdio; 9 -import polemy.eval; 10 9 11 10 version(unittest) static ~this() 12 11 { 13 12 writeln( "***********************" ); 14 13 writeln( "* All Tests Passed <3 *" ); 15 14 writeln( "*********************** (Press Enter to finish)" ); 16 15 readln(); 17 16 } 18 17 19 18 void main( string[] args ) 20 19 { 21 20 }

Modified polemy/ast.d from [f03af1208bc8c434] to [4e37845b6ce5021a].

4 4 * 5 5 * Syntax tree for Polemy programming language. 6 6 */ 7 7 module polemy.ast; 8 8 import polemy._common; 9 9 import polemy.lex; 10 10 11 -alias Statement[] Program; 12 - 13 -abstract class Statement 11 +abstract class AST 14 12 { 15 13 immutable LexPosition pos; 16 14 mixin SimpleConstructor; 17 15 } 18 16 19 -class DeclStatement : Statement 20 -{ 21 - string var; 22 - Expression expr; 23 - mixin SimpleClass; 24 -} 25 - 26 -class ExprStatement : Statement 27 -{ 28 - Expression expr; 29 - mixin SimpleClass; 30 -} 31 - 32 -abstract class Expression 33 -{ 34 - immutable LexPosition pos; 35 - mixin SimpleConstructor; 36 -} 37 - 38 -class StrLiteralExpression : Expression 17 +class StrLiteral : AST 39 18 { 40 19 string data; 41 20 mixin SimpleClass; 42 21 } 43 22 44 -class IntLiteralExpression : Expression 23 +class IntLiteral : AST 45 24 { 46 25 BigInt data; 47 26 mixin SimpleClass; 27 + this(immutable LexPosition pos, long n) {super(pos); data = n;} 28 + this(immutable LexPosition pos, BigInt n) {super(pos); data = n;} 29 + this(immutable LexPosition pos, string n) {super(pos); data = BigInt(n);} 48 30 } 49 31 50 -class VarExpression : Expression 32 +class VarExpression : AST 51 33 { 52 34 string var; 53 35 mixin SimpleClass; 54 36 } 55 37 56 -class AssignExpression : Expression 38 +class LetExpression : AST 57 39 { 58 - Expression lhs; 59 - Expression rhs; 40 + string var; 41 + AST init; 42 + AST expr; 60 43 mixin SimpleClass; 61 44 } 62 45 63 -class FuncallExpression : Expression 46 +class FuncallExpression : AST 64 47 { 65 - Expression fun; 66 - Expression[] args; 67 - this(immutable LexPosition pos, Expression fun, Expression[] args...) 48 + AST fun; 49 + AST[] args; 50 + this(immutable LexPosition pos, AST fun, AST[] args...) 68 51 { super(pos); this.fun=fun; this.args=args.dup; } 69 52 mixin SimpleClass; 70 53 } 71 54 72 -class FunLiteralExpression : Expression 55 +class FunLiteral : AST 73 56 { 74 57 string[] params; 75 - Program funbody; 58 + AST funbody; 76 59 mixin SimpleClass; 77 60 } 78 61 79 62 /// Handy Generator for AST nodes. To use this, mixin EasyAst; 80 63 81 64 /*mixin*/ 82 -template EasyAst() 65 +template EasyAST() 83 66 { 84 67 template genEast(T) 85 68 { T genEast(P...)(P ps) { return new T(LexPosition.dummy, ps); } } 86 69 87 - alias genEast!DeclStatement decl; 88 - alias genEast!ExprStatement expr; 70 + alias genEast!StrLiteral strl; 71 + alias genEast!IntLiteral intl; 72 + auto fun(string[] xs, AST ps) { return genEast!FunLiteral(xs,ps); } 89 73 alias genEast!VarExpression var; 90 - alias genEast!FuncallExpression funcall; 91 - alias genEast!IntLiteralExpression intl; 92 - alias genEast!StrLiteralExpression strl; 93 - alias genEast!FunLiteralExpression fun; 74 + alias genEast!LetExpression let; 75 + alias genEast!FuncallExpression call; 94 76 }

Modified polemy/lex.d from [bee9af8d0f8f7348] to [50586221a28fd499].

9 9 import std.file : readText; 10 10 import std.ctype : isspace, isalnum; 11 11 12 12 /// Exception from this module 13 13 14 14 class LexException : Exception 15 15 { 16 - this( const LexPosition pos, string msg ) 17 - { super(sprintf!"%s [%s]"(msg, pos)); this.pos = pos; } 18 16 const LexPosition pos; 17 + 18 + private this( const LexPosition pos, string msg ) 19 + { super(sprintf!"%s [%s]"(msg, pos)); this.pos = pos; } 19 20 }; 20 21 21 22 /// Represents a position in a source code 22 23 23 24 class LexPosition 24 25 { 25 26 immutable string filename; /// name of the source file 26 - immutable int lineno; /// line number, 1, 2, ... 27 - immutable int column; /// column, 1, 2, ... 27 + immutable int lineno; /// 1-origin 28 + immutable int column; /// 1-origin 28 29 29 30 override string toString() const 30 31 { return sprintf!"%s:%d:%d"(filename, lineno, column); } 31 32 32 33 mixin SimpleConstructor; 33 34 mixin SimpleCompare; 34 35 ................................................................................ 83 84 84 85 assert( !__traits(compiles, new Token) ); 85 86 assert( !__traits(compiles, t.pos=p) ); 86 87 assert( !__traits(compiles, t.str=789) ); 87 88 assert( !__traits(compiles, t.quoted=true) ); 88 89 } 89 90 90 -/// Named Construtor for Lexer 91 +/// Named Construtors for Lexer 91 92 92 93 auto lexerFromFile(T...)( string filename, T rest ) 93 94 { 94 95 return lexerFromString( std.file.readText(filename), filename, rest ); 95 96 } 96 97 97 -/// Named Construtor for Lexer 98 - 99 98 auto lexerFromString(CharSeq)( CharSeq str, string filename="<unnamed>", int lineno=1, int column=1 ) 100 99 { 101 100 return new LexerT!(PositionedReader!CharSeq)( 102 101 PositionedReader!CharSeq(str, filename, lineno, column) 103 102 ); 104 103 } 105 104 106 -/// Standard Lexer Type (all users have to know is that this is a forward range of Tokens) 105 +/// Standard Lexer Type (all you have to know is that this is a forward range of Tokens) 107 106 108 107 alias LexerT!(PositionedReader!string) Lexer; 109 108 110 109 /// Lexer Implementation 111 110 112 111 class LexerT(Reader) 113 112 if( isForwardRange!(Reader) && is(ElementType!(Reader) == dchar) )

Modified polemy/parse.d from [51dd3bd106fd9ecb] to [10baa46251b8a0b6].

5 5 * Parser for Polemy programming language 6 6 */ 7 7 module polemy.parse; 8 8 import polemy._common; 9 9 import polemy.lex; 10 10 import polemy.ast; 11 11 12 -/// Parsing Failure 12 +/// Exception from this module 13 13 14 -class ParserException : Exception 14 +class ParseException : Exception 15 15 { 16 -private: 17 - this(string msg) { super(msg); } 18 - static ParserException create(Lexer)(Lexer lex, string msg) 19 - { 20 - return new ParserException(sprintf!"[%s] %s"( 21 - lex.empty ? "EOF" : to!string(lex.front.pos), msg)); 22 - } 16 + const LexPosition pos; 17 + 18 + private this( const LexPosition pos, string msg ) 19 + { super(sprintf!"%s [%s]"(msg, pos)); this.pos = pos; } 23 20 } 21 + 22 +private auto createException(Lexer)(Lexer lex, string msg) 23 + { return new ParseException(lex.empty?null:lex.front.pos, msg); } 24 + 25 +/// Entry point of this module 26 + 27 +auto parseString(S, T...)(S str, T fn_ln_cn) 28 + { return parserFromString(str, fn_ln_cn).parse(); } 29 + 30 +auto parseFile(S, T...)(S filename,T ln_cn) 31 + { return parserFromString(filename, ln_cn).parse(); } 24 32 25 33 /// Named Constructor of Parser 26 34 27 -auto parserFromLexer(Lexer)(Lexer lex) 28 -{ 29 - return new Parser!Lexer(lex); 30 -} 35 +private auto parserFromLexer(Lexer)(Lexer lex) 36 + { return new Parser!Lexer(lex); } 31 37 32 -/// Named Constructor of Parser (just fwd to lexerFromString) 38 +private auto parserFromString(T...)(T params) 39 + { return parserFromLexer(polemy.lex.lexerFromString(params)); } 33 40 34 -auto parserFromString(T...)(T params) 35 -{ 36 - return parserFromLexer(polemy.lex.lexerFromString(params)); 37 -} 38 - 39 -/// Named Constructor of Parser (just fwd to lexerFromFile) 40 - 41 -auto parserFromFile(T...)(T params) 42 -{ 43 - return parserFromLexer(polemy.lex.lexerFromFile(params)); 44 -} 41 +private auto parserFromFile(T...)(T params) 42 + { return parserFromLexer(polemy.lex.lexerFromFile(params)); } 45 43 46 44 /// Parser 47 45 48 -class Parser(Lexer) 46 +private class Parser(Lexer) 47 + if( isForwardRange!(Lexer) && is(ElementType!(Lexer) == Token) ) 49 48 { 50 - this(Lexer lex) 49 + AST parse() 51 50 { 52 - this.lex = lex; 53 - } 54 - 55 - Program parseProgram() 56 - { 57 - Program p = parseStatements(); 58 - if( !lex.empty ) { 59 - auto e = ParserException.create(lex, "cannot reach eof"); 60 - throw e; 61 - } 62 - return p; 51 + auto e = Body(); 52 + if( !lex.empty ) 53 + throw createException(lex, "input is not ended but parser came to the end"); 54 + return e; 63 55 } 64 56 65 - Program parseStatements() 57 + AST Body() 66 58 { 67 - Program p; 68 - while( !lex.empty && (lex.front.quoted || lex.front.str!="}") ) 69 - p ~= parseStatement(); 70 - return p; 71 - } 59 + if( lex.empty || !lex.front.quoted && lex.front.str=="}" ) 60 + return doNothingExpression(); 72 61 73 - Statement parseStatement() 74 - { 75 - auto saved = lex.save; 76 - scope(failure) lex = saved; 77 - 78 - if( lex.empty ) 79 - throw new ParserException("EOF during parsing a statement"); 80 62 auto pos = lex.front.pos; 81 - 82 - if( !lex.front.quoted && lex.front.str=="var" ) 63 + if( tryEat("var") ) 83 64 { 84 - // "var" Var "=" Expression ";" 85 - lex.popFront; 86 - string var = lex.front.str; 87 - lex.popFront; 88 - eat("=", "for variable declaration"); 89 - auto parsed = new DeclStatement(pos, var, parseExpression()); 90 - eat(";", "after variable declaration"); 91 - return parsed; 65 + immutable LexPosition varpos = (lex.empty ? null : lex.front.pos); 66 + string var = eatId("after var"); 67 + eat("=", "after var"); 68 + auto e = E(0); 69 + if( tryEat(";") && !lex.empty && (lex.front.quoted || (lex.front.str!="}" && lex.front.str!=")")) ) 70 + return new LetExpression(pos, var, e, Body()); 71 + else 72 + return new LetExpression(pos, var, e, new VarExpression(varpos, var)); 92 73 } 93 74 else 94 75 { 95 - // Expression ";" 96 - auto parsed = new ExprStatement(pos, parseExpression()); 97 - eat(";", "after statement"); 98 - return parsed; 76 + auto e = E(0); 77 + if( tryEat(";") && !lex.empty && (lex.front.quoted || (lex.front.str!="}" && lex.front.str!=")")) ) 78 + return new LetExpression(pos, "_", e, Body()); 79 + else 80 + return e; 99 81 } 100 82 } 101 83 102 - Expression parseExpression() 103 - { 104 - auto saved = lex.save; 105 - scope(failure) lex = saved; 106 - return parseE(0); 107 - } 108 - 109 - // [TODO] multi-char operators are not supported by the lexer... 84 + // [TODO] make customizable from program 110 85 static immutable string[][] operator_perferences = [ 111 - ["="], 112 - ["or"], 113 - ["and"], 86 + ["||"], 87 + ["&&"], 114 88 ["!="], 115 89 ["=="], 116 90 ["<","<=",">",">="], 117 91 ["|"], 118 92 ["^"], 119 93 ["&"], 120 94 ["<<", ">>"], 121 95 ["+","-"], 122 - ["*","/","%"] 96 + ["~"], 97 + ["*","/","%"], 98 + ["^^"] 123 99 ]; 124 100 125 - Expression parseE(int level = 0) 101 + AST E(int level) 126 102 { 127 103 if( operator_perferences.length <= level ) 128 - return parseBaseExpression(); 104 + return Funcall(); 129 105 else 130 106 { 131 107 auto ops = operator_perferences[level]; 132 - auto e = parseE(level+1); 108 + auto e = E(level+1); 133 109 seq: 134 110 while( !lex.empty ) 135 111 { 136 112 auto pos = lex.front.pos; 137 113 foreach(op; ops) 138 114 if( tryEat(op) ) 139 115 { 140 - if( op == "=" ) // right assoc 141 - return new AssignExpression(e.pos, e, parseE(level)); 142 - else 143 - e = new FuncallExpression(e.pos, new VarExpression(pos, op), e, parseE(level+1)); 116 + e = new FuncallExpression(e.pos, new VarExpression(pos, op), e, E(level+1)); 144 117 continue seq; 145 118 } 146 119 break; 147 120 } 148 121 return e; 149 122 } 150 123 } 151 124 152 - Expression parseBaseExpression() 125 + AST Funcall() 153 126 { 154 - if( lex.empty ) 155 - throw new ParserException("EOF during parsing an expression"); 156 - auto pos = lex.front.pos; 157 - Expression e = parseBaseBaseExpression(); 158 - while( tryEat("(") ) // funcall 127 + auto e = BaseExpression(); 128 + while( tryEat("(") ) 159 129 { 160 - Expression[] args; 130 + AST[] args; 161 131 while( !tryEat(")") ) { 162 - if( lex.empty ) { 163 - auto ex = ParserException.create(lex,"Unexpected EOF"); 164 - throw ex; 165 - } 166 - args ~= parseE(); 132 + if( lex.empty ) 133 + throw createException(lex,"Unexpected EOF"); 134 + args ~= E(0); 167 135 if( !tryEat(",") ) { 168 136 eat(")", "after function parameters"); 169 137 break; 170 138 } 171 139 } 172 - e = new FuncallExpression(pos, e, args); 140 + e = new FuncallExpression(e.pos, e, args); 173 141 } 174 142 return e; 175 143 } 176 144 177 - Expression parseBaseBaseExpression() 145 + AST BaseExpression() 178 146 { 179 147 if( lex.empty ) 180 - throw new ParserException("EOF during parsing an expression"); 148 + throw createException(lex, "Reached EOF when tried to parse an expression"); 149 + 181 150 auto pos = lex.front.pos; 182 - 183 151 if( lex.front.quoted ) 184 152 { 185 153 scope(exit) lex.popFront; 186 - return new StrLiteralExpression(pos, lex.front.str); 154 + return new StrLiteral(pos, lex.front.str); 187 155 } 188 - if( isNumber(lex.front.str) ) // is_number 156 + if( isNumber(lex.front.str) ) 189 157 { 190 158 scope(exit) lex.popFront; 191 - return new IntLiteralExpression(pos, BigInt(cast(string)lex.front.str)); 159 + return new IntLiteral(pos, BigInt(cast(string)lex.front.str)); 192 160 } 193 161 if( tryEat("(") ) 194 162 { 195 - auto e = parseE(); 163 + auto e = Body(); 196 164 eat(")", "after parenthesized expression"); 197 165 return e; 198 166 } 199 167 if( tryEat("if") ) 200 168 { 201 169 eat("(", "after if"); 202 - auto cond = parseE(); 170 + auto cond = E(0); 203 171 eat(")", "after if condition"); 204 172 auto thenPos = lex.front.pos; 205 173 eat("{", "after if condition"); 206 - Statement[] th = parseStatements(); 174 + auto th = Body(); 207 175 eat("}", "after if-then body"); 208 - Statement[] el; 209 - auto elsePos = lex.front.pos; 176 + auto el = doNothingExpression(); 177 + auto elsePos = (lex.empty ? LexPosition.dummy : lex.front.pos); 210 178 if( tryEat("else") ) { 211 179 eat("{", "after else"); 212 - el = parseStatements(); 180 + el = Body(); 213 181 eat("}", "after else body"); 214 182 } 215 183 return new FuncallExpression(pos, 216 184 new VarExpression(pos, "if"), 217 185 cond, 218 - new FunLiteralExpression(thenPos, [], th), 219 - new FunLiteralExpression(elsePos, [], el) 186 + new FunLiteral(thenPos, [], th), 187 + new FunLiteral(elsePos, [], el) 220 188 ); 221 189 } 222 - 223 190 if( tryEat("fun") ) 224 191 { 225 192 eat("(", "after fun"); 226 193 string[] params; 227 - while(!tryEat(")")) 194 + while( !tryEat(")") ) 228 195 { 229 - if( lex.empty ) { 230 - auto e = ParserException.create(lex,"Unexpected EOF"); 231 - throw e; 232 - } 233 - if( lex.front.quoted ) { 234 - auto e = ParserException.create(lex,"Identifier Expected for parameters"); 235 - throw e; 236 - } 237 - params ~= lex.front.str; 238 - lex.popFront; 196 + params ~= eatId("for function parameter"); 239 197 if( !tryEat(",") ) { 240 198 eat(")", "after function parameters"); 241 199 break; 242 200 } 243 201 } 244 202 eat("{", "after function parameters"); 245 - Statement[] funbody; 246 - while(!tryEat("}")) { 247 - if( lex.empty ) { 248 - auto e = ParserException.create(lex,"Unexpected EOF"); 249 - throw e; 250 - } 251 - funbody ~= parseStatement(); 252 - } 253 - return new FunLiteralExpression(pos, params, funbody); 203 + auto funbody = Body(); 204 + eat("}", "after function body"); 205 + return new FunLiteral(pos, params, funbody); 254 206 } 255 207 scope(exit) lex.popFront; 256 208 return new VarExpression(pos, lex.front.str); 257 209 } 258 210 259 211 private: 260 212 Lexer lex; 213 + this(Lexer lex) { this.lex = lex; } 261 214 262 215 void eat(string kwd, lazy string msg) 263 216 { 264 217 if( !tryEat(kwd) ) 265 - { 266 - auto e = ParserException.create(lex, "'"~kwd~"' is expected "~msg~" but '" 218 + throw createException(lex, "'"~kwd~"' is expected "~msg~" but '" 267 219 ~(lex.empty ? "EOF" : lex.front.str)~"' occured"); 268 - throw e; 269 - } 270 220 } 271 221 272 222 bool tryEat(string kwd) 273 223 { 274 224 if( lex.empty || lex.front.quoted || lex.front.str!=kwd ) 275 225 return false; 276 226 lex.popFront; 277 227 return true; 278 228 } 229 + 230 + string eatId(lazy string msg) 231 + { 232 + if( lex.empty || lex.front.quoted ) 233 + throw createException(lex, "identifier is expected but not found "~msg); 234 + string id = lex.front.str; 235 + lex.popFront; 236 + return id; 237 + } 279 238 280 239 bool isNumber(string s) 281 240 { 282 241 return find!(`a<'0'||'9'<a`)(s).empty; 283 242 } 284 -} 285 243 286 -unittest 287 -{ 288 - auto p = parserFromString(` 289 - var x = 100; 290 - var y = 200; 291 - `); 292 - Program prog = p.parseProgram(); 293 - assert( prog.length == 2 ); 294 - auto p0 = cast(DeclStatement)prog[0]; 295 - auto p1 = cast(DeclStatement)prog[1]; 296 - assert( p0.var == "x" ); 297 - assert( p1.var == "y" ); 298 - assert( (cast(IntLiteralExpression)p0.expr).data == 100 ); 299 - assert( (cast(IntLiteralExpression)p1.expr).data == 200 ); 244 + AST doNothingExpression() 245 + { 246 + return new IntLiteral(lex.empty?null:lex.front.pos, BigInt(178)); 247 + } 300 248 } 301 249 302 250 unittest 303 251 { 304 - auto p = parserFromString(` 305 - var zzz = 100; # comment 306 - zzz = zzz + zzz * "fo\no"; # comment 307 - 42; 308 - `); 252 + mixin EasyAST; 253 + 254 + assert_eq(parseString(`123`), intl(123)); 255 + assert_eq(parseString(`"foo"`), strl("foo")); 256 + assert_eq(parseString(`fun(){1}`), fun([],intl(1))); 257 + assert_eq(parseString(`fun(x){1}`), fun(["x"],intl(1))); 258 + assert_eq(parseString(`1;2`), let("_",intl(1),intl(2))); 259 + assert_eq(parseString(`1;2;`), let("_",intl(1),intl(2))); 260 + assert_eq(parseString(`var x=1;2`), let("x",intl(1),intl(2))); 261 + assert_eq(parseString(`var x=1;2;`), let("x",intl(1),intl(2))); 262 + assert_eq(parseString(`var x=1`), let("x",intl(1),var("x"))); 263 + assert_eq(parseString(`var x=1;`), let("x",intl(1),var("x"))); 264 + assert_eq(parseString(`f(1,2)`), call(var("f"),intl(1),intl(2))); 265 + assert_eq(parseString(`if(1){2}`), call(var("if"),intl(1),fun([],intl(2)),fun([],intl(178)))); 266 + assert_eq(parseString(`if(1){2}else{3}`), call(var("if"),intl(1),fun([],intl(2)),fun([],intl(3)))); 267 + assert_eq(parseString(`if(1){}else{3}()()`), 268 + call(call(call(var("if"),intl(1),fun([],intl(178)),fun([],intl(3)))))); 269 + assert_eq(parseString(`1+2*3`), call(var("+"),intl(1),call(var("*"),intl(2),intl(3)))); 270 + assert_eq(parseString(`(1+2)*3`), call(var("*"),call(var("+"),intl(1),intl(2)),intl(3))); 271 + assert_eq(parseString(`1*(2+3)`), call(var("*"),intl(1),call(var("+"),intl(2),intl(3)))); 272 + assert_eq(parseString(`1*2+3`), call(var("+"),call(var("*"),intl(1),intl(2)),intl(3))); 309 273 310 - mixin EasyAst; 311 - auto s0 = decl("zzz", intl(BigInt(100))); 312 - auto s1 = expr( new AssignExpression(null, 313 - new VarExpression(null, "zzz"), 314 - new FuncallExpression(null, new VarExpression(null,"+"), 315 - new VarExpression(null, "zzz"), 316 - new FuncallExpression(null, new VarExpression(null,"*"), 317 - new VarExpression(null, "zzz"), 318 - new StrLiteralExpression(null, "fo\\no") 319 - )))); 320 - auto s2 = new ExprStatement(null, new IntLiteralExpression(null, BigInt(42))); 274 + assert_eq(parseString(` 275 + var x = 100; #comment 276 + var y = 200; #comment!!!!! 277 + x+y 278 + `), 279 + let("x", intl(100), let("y", intl(200), call(var("+"), var("x"), var("y")))) 280 + ); 321 281 322 - Program prog = p.parseProgram(); 323 - assert( prog.length == 3 ); 324 - assert( prog[0] == s0 ); 325 - assert( prog[1] == s1 ); 326 - assert( prog[2] == s2 ); 282 + assert_eq(parseString(` 283 + var fac = fun(x){ if(x <= 1) {1} else {x*fac(x-1)} }; 284 + fac(10) 285 + `), 286 + let("fac", fun(["x"], 287 + call(var("if"), 288 + call(var("<="), var("x"), intl(1)), 289 + fun([], intl(1)), 290 + fun([], call(var("*"), var("x"), call(var("fac"),call(var("-"),var("x"),intl(1))))) 291 + )), 292 + call(var("fac"),intl(10)) 293 + ) 294 + ); 327 295 } 328 296 329 297 unittest 330 298 { 331 - auto p = parserFromString(` 332 - var f = fun(x,y){x+y;}; 333 - f(1,fun(abc){}(4)); 334 - `); 335 - mixin EasyAst; 336 - Program prog = p.parseProgram(); 337 - assert( prog.length == 2 ); 338 - assert( prog[0] == decl("f", fun( 339 - ["x","y"], [expr(funcall(var("+"), var("x"), var("y")))] 340 - ))); 341 - assert( prog[1] == expr(funcall(var("f"), 342 - intl(BigInt(1)), 343 - funcall( 344 - fun(["abc"], cast(Statement[])[]), 345 - intl(BigInt(4)) 346 - )))); 347 -} 348 - 349 -unittest 350 -{ 351 - auto p = parserFromString(`var x = 1; var f = fun(){x=x+1;}; f(); f(); x;`); 352 - Program prog = p.parseProgram(); 353 -} 354 - 355 -unittest 356 -{ 357 - auto p = parserFromString(`if(x<2){1;}else{x;};`); 358 - Program prog = p.parseProgram(); 359 - assert( prog[0] == new ExprStatement(null, new FuncallExpression(null, 360 - new VarExpression(null, "if"), 361 - new FuncallExpression(null, new VarExpression(null,"<"), new VarExpression(null,"x"), 362 - new IntLiteralExpression(null, BigInt(2))), 363 - new FunLiteralExpression(null, [], [new ExprStatement(null, new IntLiteralExpression(null, BigInt(1)))]), 364 - new FunLiteralExpression(null, [], [new ExprStatement(null, new VarExpression(null, "x"))]) 365 - ))); 299 + assert_throw!ParseException(parseString(`1+`)); 300 + assert_throw!ParseException(parseString(`1+2}`)); 301 + assert_throw!ParseException(parseString(`var "x"`)); 302 + assert_throw!ParseException(parseString(`var`)); 303 + assert_throw!ParseException(parseString(`var x ==`)); 304 + assert_throw!ParseException(parseString(`if(){1}`)); 305 + assert_throw!ParseException(parseString(`f(`)); 366 306 }