Skip to content

Commit

Permalink
Make modules just an expression at the top level.
Browse files Browse the repository at this point in the history
No more hacky ModuleAst stuff. Just a single expression.
Also fix a bug with lexing a // comment at the end of a file.
  • Loading branch information
munificent committed May 4, 2012
1 parent 583696d commit 31d2f17
Show file tree
Hide file tree
Showing 23 changed files with 220 additions and 265 deletions.
32 changes: 11 additions & 21 deletions src/Compiler/Compiler.cpp
Original file line number Diff line number Diff line change
@@ -1,40 +1,30 @@
#include "Compiler.h"
#include "ErrorReporter.h"
#include "Method.h"
#include "Module.h"
#include "Node.h"
#include "Object.h"
#include "VM.h"

namespace magpie
{
void Compiler::compileModule(VM& vm, gc<ModuleAst> module,
ErrorReporter& reporter)
Module* Compiler::compileModule(VM& vm, gc<Node> module,
ErrorReporter& reporter)
{
// Declare methods first so we can resolve mutually recursive calls.
for (int i = 0; i < module->methods().count(); i++)
{
const DefMethodNode& methodAst = *module->methods()[i]->asDefMethodNode();
vm.methods().declare(methodAst.name());
}
// TODO(bob): Temp hackish. Wrap the module body in a fake method.
DefMethodNode* method = new DefMethodNode(module->pos(),
String::create("<module>"),
new VariablePattern(String::create("<unused>")), module);

// Try to compile all of the methods.
for (int i = 0; i < module->methods().count(); i++)
{
const DefMethodNode& methodAst = *module->methods()[i]->asDefMethodNode();
gc<Method> method = compileMethod(vm, methodAst, reporter);

// Bail if there was a compile error.
if (method.isNull()) return;

vm.methods().define(methodAst.name(), method);
}
gc<Method> body = compileMethod(vm, *method, reporter);
return new Module(body);
}

gc<Method> Compiler::compileMethod(VM& vm, const DefMethodNode& methodAst,
gc<Method> Compiler::compileMethod(VM& vm, const DefMethodNode& method,
ErrorReporter& reporter)
{
Compiler compiler(vm, reporter);
return compiler.compile(methodAst);
return compiler.compile(method);
}

Compiler::Compiler(VM& vm, ErrorReporter& reporter)
Expand Down
10 changes: 5 additions & 5 deletions src/Compiler/Compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ namespace magpie
{
class ErrorReporter;
class Method;
class MethodAst;
class ModuleAst;
class Module;
class Node;
class Object;
class VM;

class Compiler : private NodeVisitor, private PatternVisitor
{
public:
static void compileModule(VM& vm, gc<ModuleAst> module,
ErrorReporter& reporter);
static gc<Method> compileMethod(VM& vm, const DefMethodNode& methodAst,
static Module* compileModule(VM& vm, gc<Node> module,
ErrorReporter& reporter);
static gc<Method> compileMethod(VM& vm, const DefMethodNode& method,
ErrorReporter& reporter);

virtual ~Compiler() {}
Expand Down
3 changes: 1 addition & 2 deletions src/Syntax/Lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,7 @@ namespace magpie

void Lexer::skipLineComment()
{
// TODO(bob): Handle EOF.
while (peek() != '\n') advance();
while (!isDone() && peek() != '\n') advance();
}

void Lexer::skipBlockComment()
Expand Down
9 changes: 0 additions & 9 deletions src/Syntax/Node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,6 @@

namespace magpie
{
ModuleAst::ModuleAst(Array<gc<Node> >& methods)
: methods_(methods)
{}

void ModuleAst::reach()
{
Memory::reach(methods_);
}

void AndNode::trace(std::ostream& out) const
{
out << "(and " << left_ << " " << right_ << ")";
Expand Down
12 changes: 0 additions & 12 deletions src/Syntax/Node.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,6 @@ namespace magpie
NO_COPY(PatternVisitor);
};

class ModuleAst : public Managed
{
public:
ModuleAst(Array<gc<Node> >& methods);

const Array<gc<Node> > methods() const { return methods_; }

virtual void reach();
private:
Array<gc<Node> > methods_;
};

#include "Node.generated.h"

// Base class for all AST pattern node classes.
Expand Down
48 changes: 17 additions & 31 deletions src/Syntax/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,41 +55,21 @@ namespace magpie
{ NULL, NULL, -1 } // TOKEN_EOF
};

gc<ModuleAst> Parser::parseModule()
gc<Node> Parser::parseModule()
{
Array<gc<Node> > methods;
Array<gc<Node> > exprs;

do
{
if (lookAhead(TOKEN_EOF)) break;

// Method definition.
SourcePos start = current().pos();
consume(TOKEN_DEF, "The top level of a module contains only method definitions.");
gc<Token> name = consume(TOKEN_NAME,
"Expect a method name after 'def'.");

// TODO(bob): Parse real pattern(s).
gc<Pattern> pattern = NULL;
consume(TOKEN_LEFT_PAREN, "Temp.");
if (lookAhead(TOKEN_NAME))
{
pattern = parsePattern();
}
consume(TOKEN_RIGHT_PAREN, "Temp.");

gc<Node> body = parseBlock();

SourcePos span = start.spanTo(current().pos());
methods.add(new DefMethodNode(span, name->text(), pattern, body));
exprs.add(statementLike());
}
while (match(TOKEN_LINE));

// TODO(bob): Should validate that we are at EOF here.

return new ModuleAst(methods);
return createSequence(exprs);
}

gc<Node> Parser::parseBlock(TokenType endToken)
{
TokenType dummy;
Expand Down Expand Up @@ -122,11 +102,7 @@ namespace magpie
// single-expression block case.
if (current().is(TOKEN_END)) consume();

// If there is just one expression in the sequence, don't wrap it.
if (exprs.count() == 1) return exprs[0];

SourcePos span = exprs[0]->pos().spanTo(current().pos());
return new SequenceNode(span, exprs);
return createSequence(exprs);
}
else
{
Expand Down Expand Up @@ -226,7 +202,6 @@ namespace magpie
while (precedence < expressions_[current().type()].precedence)
{
token = consume();

InfixParseFn infix = expressions_[token->type()].infix;
left = (this->*infix)(left, token);
}
Expand Down Expand Up @@ -318,6 +293,17 @@ namespace magpie
}
}

gc<Node> Parser::createSequence(const Array<gc<Node> >& exprs)
{
// If there is just one expression in the sequence, don't wrap it.
if (exprs.count() == 1) return exprs[0];

// TODO(bob): Using current() here and elsewhere is wrong. That's one
// token past the span.
SourcePos span = exprs[0]->pos().spanTo(exprs[-1]->pos());
return new SequenceNode(span, exprs);
}

const Token& Parser::current()
{
fillLookAhead(1);
Expand Down
4 changes: 3 additions & 1 deletion src/Syntax/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace magpie
reporter_(reporter)
{}

gc<ModuleAst> parseModule();
gc<Node> parseModule();

private:
typedef gc<Node> (Parser::*PrefixParseFn)(gc<Token> token);
Expand Down Expand Up @@ -58,6 +58,8 @@ namespace magpie
gc<Pattern> parsePattern();
gc<Pattern> variablePattern();

gc<Node> createSequence(const Array<gc<Node> >& exprs);

// Gets the Token the parser is currently looking at.
const Token& current();

Expand Down
2 changes: 1 addition & 1 deletion src/VM/Fiber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace magpie
ASSERT(callFrames_.count() == 0, "Cannot re-initialize Fiber.");

// TODO(bob): What should the arg object be here?
call(method, 0, gc<Object>());
call(method, 0, vm_.nothing());
}

gc<Object> Fiber::run()
Expand Down
12 changes: 11 additions & 1 deletion src/VM/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,19 @@ namespace magpie
class Module
{
public:
Module(gc<Method> body)
: body_(body)
{}

void reach();

private:
gc<Method> body() const { return body_; }

private:
// The code comprosing a module is compiled to a fake method, so that
// loading a module is basically just executing a function call.
gc<Method> body_;

NO_COPY(Module);
};
}
9 changes: 5 additions & 4 deletions src/VM/VM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ namespace magpie
nothing_ = new NothingObject();
}

gc<Object> VM::run()
void VM::loadModule(Module* module)
{
fiber_->init(methods_.findMain());
modules_.add(module);
fiber_->init(module->body());

while (true)
{
Expand All @@ -34,10 +35,10 @@ namespace magpie
// If the fiber returns null, it's still running but it did a GC run.
// Since that moves the fiber, we return back to here so we can invoke
// run() again at its new location in memory.
if (!result.isNull()) return result;
if (!result.isNull()) return;
}
}

void VM::reachRoots()
{
methods_.reach();
Expand Down
4 changes: 3 additions & 1 deletion src/VM/VM.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace magpie
public:
VM();

gc<Object> run();
void loadModule(Module* module);

virtual void reachRoots();

Expand All @@ -26,6 +26,8 @@ namespace magpie
// The globally available top-level methods.
MethodScope& methods() { return methods_; }

inline gc<Object> nothing() const { return nothing_; }

inline gc<Object> getBool(bool value) const
{
return value ? true_ : false_;
Expand Down
7 changes: 3 additions & 4 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,16 @@ int main(int argc, char * const argv[])
ErrorReporter reporter;
gc<String> source = readFile(fileName);
Parser parser(fileName, source, reporter);
gc<ModuleAst> module = parser.parseModule();
gc<Node> moduleAst = parser.parseModule();

if (reporter.numErrors() > 0) return 1;

// Compile it.
Compiler::compileModule(vm, module, reporter);
Module* module = Compiler::compileModule(vm, moduleAst, reporter);

if (reporter.numErrors() > 0) return 1;

// Invoke main().
vm.run();
vm.loadModule(module);

return 0;
}
56 changes: 27 additions & 29 deletions test/and.mag
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
def main()
// Note: These tests implicitly depend on non-zero ints being truthy.
// Also rely on print() returning its argument.
// Note: These tests implicitly depend on non-zero ints being truthy.
// Also rely on print() returning its argument.

// Return the first non-true argument.
print(0 and false) // expect: 0
print(1 and 0) // expect: 0
print(1 and 2 and 0) // expect: 0
// Return the first non-true argument.
print(0 and false) // expect: 0
print(1 and 0) // expect: 0
print(1 and 2 and 0) // expect: 0

// Return the last argument if all are true.
print(1 and true) // expect: true
print(1 and 2 and 3) // expect: 3
// Return the last argument if all are true.
print(1 and true) // expect: true
print(1 and 2 and 3) // expect: 3

// Short-circuit at the first false argument.
print(true) and // expect: true
print(false) and // expect: false
print(false) // should not print
// Short-circuit at the first false argument.
print(true) and // expect: true
print(false) and // expect: false
print(false) // should not print

// Call 'true?' on the arguments to determine truthiness.
// TODO(bob): Support this.
/*
it should("call 'true?' on the arguments to determine truth") with
val left = TruthTest new(false)
val right = TruthTest new(false)
left and right
left called shouldEqual(true)
right called shouldEqual(false)
end
*/
// Call 'true?' on the arguments to determine truthiness.
// TODO(bob): Support this.
/*
it should("call 'true?' on the arguments to determine truth") with
val left = TruthTest new(false)
val right = TruthTest new(false)
left and right
left called shouldEqual(true)
right called shouldEqual(false)
end
*/

// Swallow a trailing newline.
print(true and
true) // expect: true
end
// Swallow a trailing newline.
print(true and
true) // expect: true
2 changes: 2 additions & 0 deletions test/comments/block_at_end_of_file.mag
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
print("ok") // expect: ok
/* comment */
2 changes: 2 additions & 0 deletions test/comments/line_at_end_of_file.mag
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
print("ok") // expect: ok
// comment
Loading

0 comments on commit 31d2f17

Please sign in to comment.