Skip to content

Commit

Permalink
💪 add GeneratorExpressionParser
Browse files Browse the repository at this point in the history
  • Loading branch information
mohayonao committed Jul 11, 2014
1 parent 8cba963 commit 8608bad
Show file tree
Hide file tree
Showing 2 changed files with 894 additions and 16 deletions.
192 changes: 188 additions & 4 deletions src/sc/lang/compiler/parser/generator-expr.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
require("./parser");

var Token = sc.lang.compiler.Token;
var Message = sc.lang.compiler.Message;
var Node = sc.lang.compiler.Node;
var Parser = sc.lang.compiler.Parser;

Expand All @@ -17,9 +16,194 @@
}
sc.libs.extend(GeneratorExpressionParser, Parser);

/* istanbul ignore next */
GeneratorExpressionParser.prototype.parse = function() {
this.lexer.throwError({}, Message.NotImplemented, "generator literal");
return Node.createLiteral({ value: "nil", valueType: Token.NilLiteral });
this.expect("{");
this.expect(":"); // TODO: ;

var expr = this.parseExpressions();
var clauses = this.parseClauses().concat(expr);

this.expect("}");

return compile(clauses);
};

GeneratorExpressionParser.prototype.parseClauses = function() {
var elements = [];

while (this.match(",")) {
this.lex();
elements.push(this.parseClause());
}

return elements;
};

GeneratorExpressionParser.prototype.parseClause = function() {
if (this.match("var")) {
return this.parseVarClause();
}
if (this.match(":")) {
this.lex();
if (this.match(":")) {
this.lex();
return this.parseSideEffectClause();
}
return this.parseTerminationClause();
}

return this.parseClauseStartsWithIdentifier();
};

GeneratorExpressionParser.prototype.parseClauseStartsWithIdentifier = function() {
var lookahead = this.lookahead;
var first = this.parseIdentifierIf();
var second = this.parseIdentifierIf();

if (this.match("<-")) {
this.lex();
return this.parseGeneratorClause([ first, second ]);
}

this.unlex(lookahead);
return this.parseGuardClause();
};

GeneratorExpressionParser.prototype.parseIdentifierIf = function() {
if (this.lookahead.type === Token.Identifier) {
return this.parseIdentifier();
}
return null;
};

GeneratorExpressionParser.prototype.parseGeneratorClause = function(args) {
return {
type: "generator",
args: args,
expr: this.parseExpressions()
};
};

GeneratorExpressionParser.prototype.parseGuardClause = function() {
return {
type: "guard",
expr: this.parseExpressions()
};
};

GeneratorExpressionParser.prototype.parseVarClause = function() {
this.lex();
return {
type: "value",
expr: this.parseDeclarator({
type: "var",
delegate: function() {
return this.parseBinaryExpression();
}
})
};
};

GeneratorExpressionParser.prototype.parseTerminationClause = function() {
var token = this.lex();
if (token.type !== Token.Identifier || token.value !== "while") {
this.throwUnexpected(token);
}

return {
type: "termination",
expr: this.parseExpressions()
};
};

GeneratorExpressionParser.prototype.parseSideEffectClause = function() {
return {
type: "drop",
expr: this.parseExpressions()
};
};

function compile(clauses) {
return Node.createCallExpression(
Node.createFunctionExpression(null, [ next(clauses) ], {}),
Node.createIdentifier("r"), { list: [] }, "("
);
}

function next(clauses) {
var clause = clauses.shift();

if (clauses.length === 0) {
return Node.createCallExpression(
clause,
Node.createIdentifier("yield"),
{ list: [] }, "("
);
}

return creator[clause.type](clause, clauses);
}

function createCallExpression(callee, method, args) {
return Node.createCallExpression(
callee, Node.createIdentifier(method), { list: args }, "("
);
}

function createFunctionExpression(args, body) {
return Node.createFunctionExpression(args, [ body ], {});
}

var creator = {
generator: function(clause, clauses) {
// expr.do { |args| <next> }
var args = {
list: clause.args.filter(function(node) {
return node !== null;
}).map(function(node) {
return Node.createVariableDeclarator(node);
})
};
return createCallExpression(clause.expr, "do", [
createFunctionExpression(args, next(clauses))
]);
},
value: function(clause, clauses) {
// { <next> }.value(expr)
var args = {
list: [ Node.createVariableDeclarator(clause.expr.id) ]
};
return createCallExpression(
createFunctionExpression(args, next(clauses)),
"value", [ clause.expr.init ]
);
},
guard: function(clause, clauses) {
// expr.if { <next> }
return createCallExpression(
clause.expr,
"if",
[ createFunctionExpression(null, next(clauses)) ]
);
},
drop: function(clause, clauses) {
// expr; <next>
return [ clause.expr, next(clauses) ];
},
termination: function(clause, clauses) {
// expr.if { <next } { nil.alwaysYield }
var nil$alwaysYield = createCallExpression(
Node.createLiteral({ type: Token.NilLiteral, value: "nil" }),
"alwaysYield", []
);
return createCallExpression(
clause.expr,
"if",
[
createFunctionExpression(null, next(clauses)),
createFunctionExpression(null, nil$alwaysYield)
]
);
}
};
})(sc);
Loading

0 comments on commit 8608bad

Please sign in to comment.