Control Flow 9
This commit is contained in:
parent
e749367886
commit
f0ff14e5b5
6 changed files with 133 additions and 14 deletions
|
|
@ -16,6 +16,7 @@ abstract class Expr{
|
|||
R visit(Binary expr);
|
||||
R visit(Grouping expr);
|
||||
R visit(Literal expr);
|
||||
R visit(Logical expr);
|
||||
R visit(Unary expr);
|
||||
R visit(Variable expr);
|
||||
}
|
||||
|
|
@ -47,6 +48,12 @@ abstract class Expr{
|
|||
TValue value;
|
||||
mixin defCtorAndAccept;
|
||||
}
|
||||
static class Logical : typeof(this){
|
||||
Expr left;
|
||||
Token operator;
|
||||
Expr right;
|
||||
mixin defCtorAndAccept;
|
||||
}
|
||||
static class Unary : typeof(this){
|
||||
Token operator;
|
||||
Expr right;
|
||||
|
|
|
|||
|
|
@ -75,6 +75,12 @@ class Interpreter : Stmt.Visitor!void, Expr.Visitor!TValue {
|
|||
void visit(Stmt.Expression stmt){
|
||||
evaluate(stmt.expression);
|
||||
}
|
||||
void visit(Stmt.If stmt){
|
||||
if(isTruthy(evaluate(stmt.condition)))
|
||||
execute(stmt.thenBranch);
|
||||
else if(stmt.elseBranch)
|
||||
execute(stmt.elseBranch);
|
||||
}
|
||||
void visit(Stmt.Print stmt){
|
||||
TValue value = evaluate(stmt.expression);
|
||||
writeln(tvalueToString(value));
|
||||
|
|
@ -82,6 +88,10 @@ class Interpreter : Stmt.Visitor!void, Expr.Visitor!TValue {
|
|||
void visit(Stmt.Var stmt){
|
||||
environment.define(stmt.name.lexeme, stmt.initialiser is null ? TValue.nil(0) : evaluate(stmt.initialiser));
|
||||
}
|
||||
void visit(Stmt.While stmt){
|
||||
while(isTruthy(evaluate(stmt.condition)))
|
||||
execute(stmt.body);
|
||||
}
|
||||
|
||||
TValue visit(Expr.Literal expr){
|
||||
return expr.value;
|
||||
|
|
@ -101,6 +111,17 @@ class Interpreter : Stmt.Visitor!void, Expr.Visitor!TValue {
|
|||
assert(0);
|
||||
}
|
||||
}
|
||||
TValue visit(Expr.Logical expr){
|
||||
TValue left = evaluate(expr.left);
|
||||
if(expr.operator.type == TokenType.OR){
|
||||
if(isTruthy(left))
|
||||
return left;
|
||||
} else {
|
||||
if(!isTruthy(left))
|
||||
return left;
|
||||
}
|
||||
return evaluate(expr.right);
|
||||
}
|
||||
TValue visit(Expr.Binary expr){
|
||||
TValue left = evaluate(expr.left);
|
||||
TValue right = evaluate(expr.right);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@ import jlox.interpreter;
|
|||
import jlox.expr;
|
||||
import jlox.stmt;
|
||||
|
||||
private class MainException : Exception{
|
||||
this(){
|
||||
super(null);
|
||||
}
|
||||
}
|
||||
|
||||
static class Lox{
|
||||
private static Interpreter interpreter;
|
||||
static this(){
|
||||
|
|
@ -56,7 +62,7 @@ static class Lox{
|
|||
static void runFile(string path){
|
||||
string bytes = readText(path);
|
||||
run(bytes);
|
||||
enforce(!hadError && !hadRuntimeError);
|
||||
enforce(!hadError && !hadRuntimeError, new MainException);
|
||||
}
|
||||
static void runPrompt(){
|
||||
while(true){
|
||||
|
|
@ -75,10 +81,14 @@ int main(string[] argv){
|
|||
auto args = new Program("lox")
|
||||
.add(new Argument("path").optional.acceptsFiles)
|
||||
.parse(argv);
|
||||
if(args.arg("path"))
|
||||
Lox.runFile(args.arg("path"));
|
||||
else
|
||||
Lox.runPrompt();
|
||||
try{
|
||||
if(args.arg("path"))
|
||||
Lox.runFile(args.arg("path"));
|
||||
else
|
||||
Lox.runPrompt();
|
||||
} catch(MainException rte){
|
||||
return Lox.hadError ? 1 : 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ class Parser{
|
|||
Lox.error(token, message);
|
||||
return new ParseError("hello");
|
||||
}
|
||||
private void synchronize(){
|
||||
private void synchronise(){
|
||||
advance();
|
||||
with(TokenType) while(!isAtEnd){
|
||||
if(previous().type == SEMICOLON)
|
||||
|
|
@ -89,11 +89,61 @@ class Parser{
|
|||
consume(TokenType.SEMICOLON, "Expect ';' after expression.");
|
||||
return new Stmt.Expression(expr);
|
||||
}
|
||||
private Stmt ifStatement(){
|
||||
consume(TokenType.LEFT_PAREN, "Expect '(' after 'if'.");
|
||||
Expr condition = expression();
|
||||
consume(TokenType.RIGHT_PAREN, "Expect ')' after if condition.");
|
||||
Stmt thenBranch = statement();
|
||||
Stmt elseBranch = null;
|
||||
if(match(TokenType.ELSE))
|
||||
elseBranch = statement();
|
||||
return new Stmt.If(condition, thenBranch, elseBranch);
|
||||
}
|
||||
private Stmt forStatement(){
|
||||
consume(TokenType.LEFT_PAREN, "Expect '(' after 'for'.");
|
||||
|
||||
Stmt initialiser;
|
||||
if(match(TokenType.SEMICOLON))
|
||||
initialiser = null;
|
||||
else if(match(TokenType.VAR))
|
||||
initialiser = varDeclaration();
|
||||
else
|
||||
initialiser = expressionStatement();
|
||||
|
||||
Expr condition = check(TokenType.SEMICOLON) ? new Expr.Literal(TValue.bln(true)) : expression();
|
||||
consume(TokenType.SEMICOLON, "Expect ';' after loop condition.");
|
||||
|
||||
Expr increment;
|
||||
if(!check(TokenType.RIGHT_PAREN))
|
||||
increment = expression();
|
||||
consume(TokenType.RIGHT_PAREN, "Expect ')' after for clauses.");
|
||||
|
||||
Stmt body = statement();
|
||||
if(increment !is null)
|
||||
body = new Stmt.Block([ body, new Stmt.Expression(increment) ]);
|
||||
body = new Stmt.While(condition, body);
|
||||
if(initialiser !is null)
|
||||
body = new Stmt.Block([ initialiser, body ]);
|
||||
return body;
|
||||
}
|
||||
private Stmt whileStatement(){
|
||||
consume(TokenType.LEFT_PAREN, "Expect '(' after 'while'.");
|
||||
Expr condition = expression();
|
||||
consume(TokenType.RIGHT_PAREN, "Expect ')' after condition.");
|
||||
Stmt body = statement();
|
||||
return new Stmt.While(condition, body);
|
||||
}
|
||||
private Stmt statement(){
|
||||
if(match(TokenType.LEFT_BRACE))
|
||||
return new Stmt.Block(block());
|
||||
if(match(TokenType.IF))
|
||||
return ifStatement();
|
||||
if(match(TokenType.FOR))
|
||||
return forStatement();
|
||||
if(match(TokenType.WHILE))
|
||||
return whileStatement();
|
||||
if(match(TokenType.PRINT))
|
||||
return printStatement();
|
||||
if(match(TokenType.LEFT_BRACE))
|
||||
return new Stmt.Block(block());
|
||||
return expressionStatement();
|
||||
}
|
||||
private Stmt[] block(){
|
||||
|
|
@ -107,7 +157,7 @@ class Parser{
|
|||
return assignment();
|
||||
}
|
||||
private Expr assignment(){
|
||||
Expr expr = equality();
|
||||
Expr expr = or();
|
||||
if(match(TokenType.EQUAL)){
|
||||
Token equals = previous();
|
||||
Expr value = assignment();
|
||||
|
|
@ -117,6 +167,24 @@ class Parser{
|
|||
}
|
||||
return expr;
|
||||
}
|
||||
private Expr or(){
|
||||
Expr expr = and();
|
||||
while(match(TokenType.OR)){
|
||||
Token operator = previous();
|
||||
Expr right = and();
|
||||
expr = new Expr.Logical(expr, operator, right);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
private Expr and(){
|
||||
Expr expr = equality();
|
||||
while(match(TokenType.AND)){
|
||||
Token operator = previous();
|
||||
Expr right = equality();
|
||||
expr = new Expr.Logical(expr, operator, right);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
private Expr equality(){
|
||||
Expr expr = comparison();
|
||||
while(match(TokenType.BANG_EQUAL, TokenType.EQUAL_EQUAL)){
|
||||
|
|
@ -181,11 +249,11 @@ class Parser{
|
|||
}
|
||||
private Stmt varDeclaration(){
|
||||
Token name = consume(TokenType.IDENTIFIER, "Expect variable name.");
|
||||
Expr initializer = null;
|
||||
Expr initialiser = null;
|
||||
if(match(TokenType.EQUAL))
|
||||
initializer = expression();
|
||||
initialiser = expression();
|
||||
consume(TokenType.SEMICOLON, "Expect ';' after variable declaration.");
|
||||
return new Stmt.Var(name, initializer);
|
||||
return new Stmt.Var(name, initialiser);
|
||||
}
|
||||
private Stmt declaration(){
|
||||
try {
|
||||
|
|
@ -193,7 +261,7 @@ class Parser{
|
|||
return varDeclaration();
|
||||
return statement();
|
||||
} catch(ParseError error){
|
||||
synchronize();
|
||||
synchronise();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ class Scanner {
|
|||
case "class": return CLASS;
|
||||
case "else": return ELSE;
|
||||
case "false": return FALSE;
|
||||
case ":or": return FOR;
|
||||
case "for": return FOR;
|
||||
case "fun": return FUN;
|
||||
case "if": return IF;
|
||||
case "nil": return NIL;
|
||||
|
|
|
|||
|
|
@ -13,8 +13,10 @@ abstract class Stmt{
|
|||
interface Visitor(R){
|
||||
R visit(Block expr);
|
||||
R visit(Expression expr);
|
||||
R visit(If expr);
|
||||
R visit(Print expr);
|
||||
R visit(Var expr);
|
||||
R visit(While expr);
|
||||
}
|
||||
private alias rTypes = AliasSeq!(void);
|
||||
static foreach(T; rTypes)
|
||||
|
|
@ -33,6 +35,12 @@ abstract class Stmt{
|
|||
Expr expression;
|
||||
mixin defCtorAndAccept;
|
||||
}
|
||||
static class If : typeof(this){
|
||||
Expr condition;
|
||||
Stmt thenBranch;
|
||||
Stmt elseBranch;
|
||||
mixin defCtorAndAccept;
|
||||
}
|
||||
static class Print : typeof(this){
|
||||
Expr expression;
|
||||
mixin defCtorAndAccept;
|
||||
|
|
@ -42,5 +50,10 @@ abstract class Stmt{
|
|||
Expr initialiser;
|
||||
mixin defCtorAndAccept;
|
||||
}
|
||||
static class While : typeof(this){
|
||||
Expr condition;
|
||||
Stmt body;
|
||||
mixin defCtorAndAccept;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue