Skip to content

Commit

Permalink
update: add resolver (static analysis)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rohith-Raju committed Jun 8, 2024
1 parent b60cbda commit 7fd4f67
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 22 deletions.
6 changes: 5 additions & 1 deletion include/Interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "Token.h"
#include "env/Env.h"
#include "utls/Object.h"
#include <unordered_map>
#include <vector>

class Interpreter {
Expand All @@ -33,6 +34,7 @@ class Interpreter {

private:
Environment *environment = globals;
std::unordered_map<Expr *, int> locals;

public:
Interpreter();
Expand All @@ -47,7 +49,9 @@ class Interpreter {

void interpret(std::vector<Statement *> &statements);

void resolve();
void resolve(Expr *expr, int depth);

Object lookUpVariable(Token *name, Expr *expr);

void visitPrintStmnt(Print *stmnt);

Expand Down
9 changes: 6 additions & 3 deletions include/Resolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
#define CRUX_RESOLVER_H

#include "Interpreter.h"
#include <stack>
#include <unordered_map>
#include <vector>

enum FunctionType { FunctionType_None, FunctionType_Function };

struct varFlags {
bool isInitilised;
bool isInitilized;
bool isReferenced;
};

Expand All @@ -17,9 +18,10 @@ class Resolver {
private:
std::vector<std::unordered_map<std::string, varFlags> *> scopes;
Interpreter *interpreter;
Resolver(Interpreter *interpreter);
FunctionType currentFunction = FunctionType_None;

public:
Resolver(Interpreter *interpreter);
void resolve(std::vector<Statement *> stmnts);
void resolve(Expr *expr);
void resolve(Statement *stmnt);
Expand All @@ -31,6 +33,7 @@ class Resolver {
void define(Token *name);

void resolveLocal(Expr *expr, Token *name);
void resolveFunction(Function *stmnt, FunctionType type);

Object excecuteBlock(std::vector<Statement *> stmnts, Environment *env);

Expand Down
4 changes: 4 additions & 0 deletions include/env/Env.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "Token.h"
#include "utls/Object.h"
#include <string>
#include <unordered_map>

class Environment {
Expand All @@ -21,8 +22,11 @@ class Environment {
void define(Token *tkn, Object value);
void define(std::string name, Object value);
void assign(Token *name, Object value);
void assignAt(int distance, Token *name, Object value);
Object get(Token *name);
void deepClean(Environment *enclosing);
Object getAt(int distance, std::string name);
Environment *ancestor(int distance);
};

#endif
10 changes: 7 additions & 3 deletions main.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#include "Error.h"
#include "Interpreter.h"
#include "Resolver.h"
#include "Scanner.h"
#include <Parser.h>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
Expand Down Expand Up @@ -61,8 +61,12 @@ void runCode(std::string source) {
Scanner scanner(source);
std::vector<Token> tokens = scanner.scanTokens();
Parser parser(tokens);
std::vector<Statement *> expression = parser.parse();
interpreter.interpret(expression);
std::vector<Statement *> statements = parser.parse();
Resolver *resolver = new Resolver(&interpreter);
resolver->resolve(statements);
if (crux::hadRuntimeError)
return;
interpreter.interpret(statements);
}

int main(int argc, char *argv[]) {
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ set(Sources Error.cpp
Interpreter.cpp
Statement.cpp
Function.cpp
Resolver.cpp
env/Env.cpp
)

Expand Down
26 changes: 21 additions & 5 deletions src/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Interpreter::Interpreter() {
globals = new Environment();
environment = globals;
Object clock(new ClockFunction());
environment->define("clock", clock);
globals->define("clock", clock);
}
}

Expand Down Expand Up @@ -57,6 +57,8 @@ void Interpreter::excecute(Statement *stmnt) {
}
}

void Interpreter::resolve(Expr *expr, int depth) { locals[expr] = depth; }

Object Interpreter::evaluate(Expr *expr) {
switch (expr->type) {
case ExprType_Binary:
Expand Down Expand Up @@ -177,8 +179,8 @@ void Interpreter::visitReturnStmnt(Return *stmnt) {
}

void Interpreter::visitBlockStmnt(Block *stmnt) {
Environment *locals = new Environment(environment);
excecuteBlock(stmnt->stmnt, locals);
Environment vars(environment);
excecuteBlock(stmnt->stmnt, &vars);
}

Object Interpreter::excecuteBlock(std::vector<Statement *> stmnts,
Expand All @@ -201,7 +203,15 @@ Object Interpreter::excecuteBlock(std::vector<Statement *> stmnts,
}

Object Interpreter::visitVariableExp(Variable *expr) {
return environment->get(expr->name);
return lookUpVariable(expr->name, expr);
}

Object Interpreter::lookUpVariable(Token *name, Expr *expr) {
auto it = locals.find(expr);
if (it != locals.end())
return environment->getAt(it->second, name->lexeme);
else
return globals->get(name);
}

Object Interpreter::visitLogicalExp(Logical *expr) {
Expand All @@ -218,7 +228,13 @@ Object Interpreter::visitLogicalExp(Logical *expr) {
Object Interpreter::visitAssignment(Assignment *expr) {
Object value = evaluate(expr->value);

environment->assign(expr->name, value);
auto it = locals.find(expr);

if (it != locals.end())
environment->assignAt(it->second, expr->name, value);
else
globals->assign(expr->name, value);

return value;
}

Expand Down
94 changes: 86 additions & 8 deletions src/Resolver.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//
// Resolver.cpp
//
//

#include "Resolver.h"
#include "Error.h"
#include "Expr.h"
#include "Statement.h"
#include "Token.h"
#include <string>
#include <unordered_map>
#include <vector>
Expand Down Expand Up @@ -85,22 +87,27 @@ void Resolver::declare(Token *name) {
if (scopes.empty())
return;
std::unordered_map<std::string, varFlags> *scope = scopes.back();

if (scope->find(name->lexeme) != scope->end())
crux::error(*name, "Variable already declared with the same name");

(*scope)[name->lexeme].isReferenced = false;
(*scope)[name->lexeme].isInitilised = false;
(*scope)[name->lexeme].isInitilized = false;
}

void Resolver::define(Token *name) {
if (scopes.empty())
return;
std::unordered_map<std::string, varFlags> *scope = scopes.back();
(*scope)[name->lexeme].isInitilised = true;
(*scope)[name->lexeme].isInitilized = true;
}

void Resolver::resolveLocal(Expr *expr, Token *name) {
for (int i = scopes.size(); i >= 0; i--) {
if (scopes[i]->find(name->lexeme) != scopes[i]->end())
interpreter.resolve(expr, scopes.size() - i - 1);
return;
for (int i = scopes.size() - 1; i >= 0; i--) {
if (scopes[i]->find(name->lexeme) != scopes[i]->end()) {
interpreter->resolve(expr, scopes.size() - i - 1);
return;
}
}
}

Expand All @@ -122,7 +129,7 @@ void Resolver::visitVariableExp(Variable *expr) {
if (!scopes.empty()) {
auto scope = scopes.back();
auto it = scope->find(expr->name->lexeme);
if (it != scope->end() && !it->second.isInitilised == true)
if (it != scope->end() && !it->second.isInitilized == true)
crux::error(*expr->name,
"Can't read local variable in its own initializer.");
}
Expand All @@ -133,3 +140,74 @@ void Resolver::visitAssignment(Assignment *expr) {
resolve(expr->value);
resolveLocal(expr, expr->name);
}

void Resolver::visitFuncStmnt(Function *stmnt) {
declare(stmnt->name);
define(stmnt->name);
resolveFunction(stmnt, FunctionType_Function);
}

void Resolver::resolveFunction(Function *stmnt, FunctionType type) {
FunctionType enclosingFunction = currentFunction;
currentFunction = type;
beginScope();
for (Token *params : stmnt->params) {
declare(params);
define(params);
}
resolve(stmnt->body);
endScope();
currentFunction = enclosingFunction;
}

void Resolver::visitExprStmnt(Expression *stmnt) { resolve(stmnt->expression); }

void Resolver::visitIfStmnt(If *stmnt) {
resolve(stmnt->condition);
resolve(stmnt->thenBranch);
if (stmnt->elseBranch)
resolve(stmnt->elseBranch);
}

void Resolver::visitPrintStmnt(Print *expr) { resolve(expr->expression); }

void Resolver::visitReturnStmnt(Return *expr) {
if (currentFunction == FunctionType_None) {
crux::error(*expr->keyword, "Can't return from top level");
}
if (expr->value)
resolve(expr->value);
}

void Resolver::visitWhileStmnt(While *stmnt) {
resolve(stmnt->condition);
resolve(stmnt->body);
}

void Resolver::visitBinaryExp(Binary *expr) {
resolve(expr->left);
resolve(expr->right);
}

void Resolver::visitCall(Call *expr) {
resolve(expr->callee);
for (Expr *arg : expr->arguments)
resolve(arg);
}

void Resolver::visitGroupExp(Grouping *expr) { resolve(expr->expression); }

void Resolver::visitLiteral(Literal *expr) { return; }

void Resolver::visitLogicalExp(Logical *expr) {
resolve(expr->left);
resolve(expr->right);
}

void Resolver::visitUnaryExp(Unary *expr) { resolve(expr->right); }

void Resolver::visitTernaryExp(Ternary *expr) {
resolve(expr->condition);
resolve(expr->expression1);
resolve(expr->expression2);
}
16 changes: 16 additions & 0 deletions src/env/Env.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ void Environment::assign(Token *name, Object value) {
throw RuntimeError(*name, "Undefined variable " + name->lexeme);
}

void Environment::assignAt(int distance, Token *name, Object value) {
ancestor(distance)->values[name->lexeme] = value;
}

Object Environment::get(Token *name) {
if (values.find(name->lexeme) != values.end()) {
Object obj = values[name->lexeme];
Expand All @@ -63,3 +67,15 @@ Object Environment::get(Token *name) {
}
throw RuntimeError(*name, "Unexpected variable " + name->lexeme);
}

Object Environment::getAt(int distance, std::string name) {
return ancestor(distance)->values[name];
}

Environment *Environment::ancestor(int distance) {
Environment *env = this;
for (int i = 0; i < distance; i++) {
env = env->enclosing;
}
return env;
}
10 changes: 10 additions & 0 deletions test.crux
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
var a = "global";
{
fun showA() {
print a;
}

showA();
var a = "block";
showA();
}
8 changes: 7 additions & 1 deletion test/TestInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "Expr.h"
#include "Interpreter.h"
#include "Parser.h"
#include "Resolver.h"
#include "Scanner.h"
#include "gtest/gtest.h"
#include <Statement.h>
Expand Down Expand Up @@ -95,6 +96,8 @@ TEST(InterpreterTest, TestWhileLoop) {
std::vector<Statement *> statements = p.parse();
testing::internal::CaptureStdout();
Interpreter *interpreter = new Interpreter();
Resolver *resolver = new Resolver(interpreter);
resolver->resolve(statements);
interpreter->interpret(statements);
std::string result = testing::internal::GetCapturedStdout();
ASSERT_EQ(result, "5.000000\n4.000000\n3.000000\n2.000000\n1.000000\n");
Expand All @@ -107,7 +110,10 @@ TEST(InterpreterTest, TestForLoop) {
Parser p(tokens);
std::vector<Statement *> statements = p.parse();
testing::internal::CaptureStdout();
Interpreter{}.interpret(statements);
Interpreter *interpreter = new Interpreter();
Resolver *resolver = new Resolver(interpreter);
resolver->resolve(statements);
interpreter->interpret(statements);
std::string result = testing::internal::GetCapturedStdout();
ASSERT_EQ(result, "0.000000\n1.000000\n2.000000\n3.000000\n4.000000\n");
}

0 comments on commit 7fd4f67

Please sign in to comment.