Skip to content

Commit d3b4bfc

Browse files
committed
Conditionals work properly now. Yay!
1 parent 23fc41a commit d3b4bfc

9 files changed

Lines changed: 76 additions & 19 deletions

File tree

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
# LambdaScript
2+
3+
Dear reader,
4+
5+
I challenged myself to make a programming language during the summer before I started college. This project implemented the semantic rules of the lambda calculus, but with a few extra features. I have written a javascript code compiler for it, and an interpreter. The interpreter works well, but does not throw proper errors (haven't implemented exceptions), unable to evaluate conditionals that exist inside of abstractions, and resolve types properly. It is fully functional if you wish to only use pure lambda encodings, rather than the native arithmetical evaluation engine.
6+
27
Examples:
38
```haskell
49
-- interactive mode
5-
id = (λx.x)
6-
λ -> [ λ function ] (λx.x)
10+
id = (λx.x) (λx.x)
11+
λ -> (λx.x)
712

813
id 103
914
λ -> 103

source/ast/ast.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,24 @@ node_reference Operation::accept(backend::NodeVisitor *visitor) {
233233
return visitor->visitArithmeticalOperation(self);
234234
}
235235

236+
Condition::Condition(node_reference _condition, node_reference _consequent, node_reference _alternative) {
237+
condition = _condition;
238+
consequent = _consequent;
239+
alternative = _alternative;
240+
}
241+
242+
std::string Condition::to_string() {
243+
return "if " + condition->to_string() + " then " + consequent->to_string() + " else " + alternative->to_string();
244+
}
245+
std::string Condition::pretty_print() {
246+
return "(" + condition->pretty_print() + blue + " -> " + reset + consequent->pretty_print() + blue + " | " + reset + alternative->pretty_print() + ")";
247+
return blue + "if " + reset + condition->pretty_print() + blue + " then " + reset + consequent->pretty_print() + blue + " else " + reset + alternative->pretty_print();
248+
}
249+
node_reference Condition::accept(backend::NodeVisitor *visitor) {
250+
shared_ptr<Condition> self = static_pointer_cast<Condition>(shared_from_this());
251+
return visitor->visitCondition(self);
252+
}
253+
236254
PrintInstruction::PrintInstruction(node_reference valueToPrint) {
237255
type = PRINT;
238256
value = valueToPrint;

source/ast/ast.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class ImportInstruction;
2424
class Operation;
2525
class Assignment;
2626
class NativeAbstraction;
27-
// class Conditional;
27+
class Condition;
2828

2929
namespace typesystem {
3030
class TypeNode;
@@ -172,6 +172,17 @@ class Operation : public ASTNode {
172172
node_reference accept(backend::NodeVisitor *visitor) override;
173173
};
174174

175+
class Condition : public ASTNode {
176+
public:
177+
node_reference condition;
178+
node_reference consequent;
179+
node_reference alternative;
180+
Condition(node_reference _condition, node_reference _consequent, node_reference _alternative);
181+
std::string to_string() override;
182+
std::string pretty_print() override;
183+
node_reference accept(backend::NodeVisitor *visitor) override;
184+
};
185+
175186
class PrintInstruction : public ASTNode {
176187
public:
177188
node_reference value;

source/backend/NodeVisitor.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ ast::node_reference backend::NodeVisitor::visitArithmeticalOperation(std::shared
2727
op_node->operation_character = operation->operation_character;
2828
return op;
2929
}
30+
ast::node_reference backend::NodeVisitor::visitCondition(std::shared_ptr<ast::Condition> condition) {
31+
std::shared_ptr<ast::Condition> con = std::make_shared<ast::Condition>(condition->condition->accept(this), condition->consequent->accept(this), condition->alternative->accept(this));
32+
return con;
33+
}
3034
ast::node_reference backend::NodeVisitor::visitNativeAbstraction(std::shared_ptr<ast::NativeAbstraction> native_abstraction) {
3135
// Will call the native_abstractions .apply() method, where it will construct an AST tree
3236
// Sometimes the use of a new Curryable object will be used to keep track of previously provided arguments, until all are met, and the expression can be executed internally.

source/backend/NodeVisitor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ namespace backend {
1515
virtual ast::node_reference visitAbstraction(std::shared_ptr<ast::Abstraction>);
1616
virtual ast::node_reference visitAssignment(std::shared_ptr<ast::Assignment>);
1717
virtual ast::node_reference visitArithmeticalOperation(std::shared_ptr<ast::Operation>);
18+
virtual ast::node_reference visitCondition(std::shared_ptr<ast::Condition>);
1819
virtual ast::node_reference visitGrouping(std::shared_ptr<ast::Grouping>);
1920
virtual ast::node_reference visitNativeAbstraction(std::shared_ptr<ast::NativeAbstraction>);
2021
virtual ast::node_reference visitImportInstruction(std::shared_ptr<ast::ImportInstruction>);

source/backend/interpreter/Curried.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ ast::node_reference backend::interpreter::Curried::accept(backend::NodeVisitor*
3030
return interpreter_visitor->visitCurried(self);
3131
} else {
3232
// return visitor->visitGenericASTNode(generic_self);
33-
// return generic_self;
34-
return target_native_abstraction->accept(visitor);
33+
return std::static_pointer_cast<backend::interpreter::Curried>(generic_self);
34+
// return target_native_abstraction->accept(visitor);
3535
}
3636
}
3737
}

source/backend/interpreter/InterpreterVisitor.cpp

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,15 @@ ast::node_reference backend::interpreter::InterpreterVisitor::visitApplication(s
3838
// std::cout << "[app] " << application->pretty_print() << std::endl;
3939
ast::node_reference reduced_lhs = application->lhs->accept(this);
4040
// std::cout << "Is this code even being called? " << typeid(*reduced_lhs).name() << std::endl;
41-
if (!(typeid(*reduced_lhs) == typeid(ast::Abstraction))) {
42-
if (typeid(*reduced_lhs) == typeid(backend::interpreter::Curried)) {
43-
std::shared_ptr<backend::interpreter::Curried> curried = std::static_pointer_cast<backend::interpreter::Curried>(reduced_lhs);
44-
return curried->apply_argument(application->rhs->accept(this))->accept(this)->accept(this);
41+
if (typeid(*reduced_lhs) == typeid(backend::interpreter::Curried)) {
42+
std::shared_ptr<backend::interpreter::Curried> curried = std::static_pointer_cast<backend::interpreter::Curried>(reduced_lhs);
43+
ast::node_reference reduced_rhs = application->rhs->accept(this);
44+
if (typeid(*reduced_rhs) == typeid(ast::Variable)) {
45+
return application;
4546
}
47+
return curried->apply_argument(reduced_rhs)->accept(this);
48+
}
49+
if (!(typeid(*reduced_lhs) == typeid(ast::Abstraction))) {
4650
if (typeid(*reduced_lhs) == typeid(ast::Literal) || typeid(*reduced_lhs) == typeid(ast::Grouping)) {
4751
std::shared_ptr<ast::Grouping> grouping = std::make_shared<ast::Grouping>();
4852
grouping->nodes.push_back(reduced_lhs);
@@ -81,6 +85,25 @@ ast::node_reference backend::interpreter::InterpreterVisitor::visitArithmeticalO
8185
ast::node_reference operation_result = backend::evaluate_arithmetical_operation(op);
8286
return operation_result;
8387
}
88+
ast::node_reference backend::interpreter::InterpreterVisitor::visitCondition(std::shared_ptr<ast::Condition> condition) {
89+
ast::node_reference con = condition->condition->accept(this);
90+
bool truthy;
91+
if (typeid(*con) == typeid(ast::Literal)) {
92+
std::shared_ptr<ast::Literal> literal = std::static_pointer_cast<ast::Literal>(con);
93+
if (literal->valueType == ast::LiteralType::Int) {
94+
truthy = literal->getInt() > 0;
95+
} else if (literal->valueType == ast::LiteralType::Bool) {
96+
truthy = literal->getBool();
97+
} else if (literal->valueType == ast::LiteralType::Nil) {
98+
truthy = false;
99+
} else {
100+
truthy = false;
101+
}
102+
} else {
103+
truthy = false;
104+
}
105+
return truthy ? condition->consequent->accept(this) : condition->alternative->accept(this);
106+
}
84107
ast::node_reference backend::interpreter::InterpreterVisitor::visitNativeAbstraction(std::shared_ptr<ast::NativeAbstraction> native_abstraction) {
85108
// Will call the native_abstractions .apply() method, where it will construct an AST tree
86109
// Sometimes the use of a new Curryable object will be used to keep track of previously provided arguments, until all are met, and the expression can be executed internally.

source/backend/interpreter/InterpreterVisitor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class InterpreterVisitor : public NodeVisitor {
3030
ast::node_reference visitArithmeticalOperation(std::shared_ptr<ast::Operation>) override;
3131
ast::node_reference visitGrouping(std::shared_ptr<ast::Grouping>) override;
3232
ast::node_reference visitNativeAbstraction(std::shared_ptr<ast::NativeAbstraction>) override;
33+
ast::node_reference visitCondition(std::shared_ptr<ast::Condition>) override;
3334
ast::node_reference visitImportInstruction(std::shared_ptr<ast::ImportInstruction>) override;
3435
ast::node_reference visitPrintInstruction(std::shared_ptr<ast::PrintInstruction>) override;
3536
ast::node_reference visitGenericASTNode(std::shared_ptr<ast::ASTNode>) override;

source/language/ConstructorVisitor.cpp

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -156,18 +156,12 @@ antlrcpp::Any ConstructorVisitor::visitBody(LanguageParser::BodyContext *ctx) {
156156
antlrcpp::Any ConstructorVisitor::visitCondition(LanguageParser::ConditionContext *ctx) {
157157

158158
ast::node_reference condition = ctx->expression()->accept(this);
159-
ast::node_reference tru_expression = ctx->body(0)->accept(this);
160-
ast::node_reference fls_expression = ctx->body(1)->accept(this);
159+
ast::node_reference consequent = ctx->body(0)->accept(this);
160+
ast::node_reference alternitive = ctx->body(1)->accept(this);
161161

162-
// [flag TODO]
163-
// This is not the way to go. Conditionals should exist as an AST node, instead of being encoded as truthy applications.
164-
165-
ast::node_reference native_truthy = std::make_shared<backend::interpreter::Curried>(std::make_shared<backend::interpreter::native_library::Truthy>(), 1);
162+
ast::node_reference condition_node = std::make_shared<ast::Condition>(condition, consequent, alternitive);
166163

167-
ast::node_reference application_0 = make_shared<ast::Application>(native_truthy, condition);
168-
ast::node_reference application_1 = make_shared<ast::Application>(application_0, tru_expression);
169-
ast::node_reference application_2 = make_shared<ast::Application>(application_1, fls_expression);
170-
return application_2;
164+
return condition_node;
171165
}
172166

173167
antlrcpp::Any ConstructorVisitor::visitTypeBinding(LanguageParser::TypeBindingContext *ctx) {

0 commit comments

Comments
 (0)