mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-05-03 03:00:14 -04:00
Slightly altered syntax, similar to Underscore's Function#bind form, highly simplified lexing and parsing and no polluted scope. Passing tests included.
This commit is contained in:
@@ -272,14 +272,8 @@
|
|||||||
return $2.new_instance();
|
return $2.new_instance();
|
||||||
}), o("Super")
|
}), o("Super")
|
||||||
],
|
],
|
||||||
Curry: [o("Value CURRY Value CURRY_SEPARATOR Arguments", function() {
|
Curry: [o("Value CURRY Arguments", function() {
|
||||||
return new CurryNode($1, $3, $5);
|
|
||||||
}), o("Value CURRY Arguments", function() {
|
|
||||||
return new CurryNode($1, undefined, $3);
|
|
||||||
}), o("Value CURRY Value", function() {
|
|
||||||
return new CurryNode($1, $3);
|
return new CurryNode($1, $3);
|
||||||
}), o("Value CURRY", function() {
|
|
||||||
return new CurryNode($1);
|
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
// Extending an object by setting its prototype chain to reference a parent
|
// Extending an object by setting its prototype chain to reference a parent
|
||||||
|
|||||||
21
lib/lexer.js
21
lib/lexer.js
@@ -330,28 +330,9 @@
|
|||||||
// Here we detect the currying operator and change local state in order to
|
// Here we detect the currying operator and change local state in order to
|
||||||
// parse subsequent tokens accordingly.
|
// parse subsequent tokens accordingly.
|
||||||
Lexer.prototype.curry_token = function curry_token() {
|
Lexer.prototype.curry_token = function curry_token() {
|
||||||
var _a;
|
if (this.match(CURRY, 1)) {
|
||||||
if ((typeof (_a = this.currying) !== "undefined" && _a !== null)) {
|
|
||||||
if (this.match(/^\(/, 0) && [',', '<-'].indexOf(this.value()) !== -1) {
|
|
||||||
this.token('CALL_START', '(');
|
|
||||||
this.i += 1;
|
|
||||||
this.currying = undefined;
|
|
||||||
return true;
|
|
||||||
} else if (this.match(CURRY_SEPARATOR, 0)) {
|
|
||||||
this.token('CURRY_SEPARATOR', ',');
|
|
||||||
this.i += 1;
|
|
||||||
return true;
|
|
||||||
} else if (!this.match(/^\s*[\w@\$_\(]/, 0)) {
|
|
||||||
this.currying = undefined;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (this.match(CURRY, 1)) {
|
|
||||||
this.i += 2;
|
this.i += 2;
|
||||||
this.chunk = this.code.slice(this.i);
|
|
||||||
this.token('CURRY', '<-');
|
this.token('CURRY', '<-');
|
||||||
if (this.match(/^(\s*[\w@\$_\(])/, 1)) {
|
|
||||||
this.currying = true;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
21
lib/nodes.js
21
lib/nodes.js
@@ -506,32 +506,29 @@
|
|||||||
// Node to bind an context and/or some arguments to a function, returning a new function
|
// Node to bind an context and/or some arguments to a function, returning a new function
|
||||||
// After `Underscore.bind` from [Underscore](http://documentcloud.github.com/underscore/).
|
// After `Underscore.bind` from [Underscore](http://documentcloud.github.com/underscore/).
|
||||||
exports.CurryNode = (function() {
|
exports.CurryNode = (function() {
|
||||||
CurryNode = function CurryNode(meth, bind, args) {
|
CurryNode = function CurryNode(meth, args) {
|
||||||
this.children = flatten([(this.meth = meth), (this.bind = bind || literal('this')), (this.args = (args || []))]);
|
this.children = flatten([(this.meth = meth), (this.context = args[0]), (this.args = (args.slice(1) || []))]);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
__extends(CurryNode, CallNode);
|
__extends(CurryNode, CallNode);
|
||||||
CurryNode.prototype.type = 'Curry';
|
CurryNode.prototype.type = 'Curry';
|
||||||
CurryNode.prototype.code = "function(func, obj, args) {\n return function() {\n return func.apply(obj || {}, args.concat(__slice.call(arguments, 0)));\n };\n}";
|
|
||||||
CurryNode.prototype.slice = "Array.prototype.slice";
|
|
||||||
CurryNode.prototype.arguments = function arguments(o) {
|
CurryNode.prototype.arguments = function arguments(o) {
|
||||||
var _a, _b, _c, arg;
|
var _a, _b, _c, arg;
|
||||||
_a = this.args;
|
_a = this.args;
|
||||||
for (_b = 0, _c = _a.length; _b < _c; _b++) {
|
for (_b = 0, _c = _a.length; _b < _c; _b++) {
|
||||||
arg = _a[_b];
|
arg = _a[_b];
|
||||||
if (arg instanceof SplatNode) {
|
if (arg instanceof SplatNode) {
|
||||||
return literal(this.compile_splat_arguments(o));
|
return this.compile_splat_arguments(o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new ArrayNode(this.args);
|
return (new ArrayNode(this.args)).compile(o);
|
||||||
};
|
};
|
||||||
CurryNode.prototype.compile_node = function compile_node(o) {
|
CurryNode.prototype.compile_node = function compile_node(o) {
|
||||||
var call, ref;
|
var body, curried, curry;
|
||||||
o.scope.assign('__curry', this.code, true);
|
body = Expressions.wrap([literal('func.apply(obj || {}, args.concat(Array.prototype.slice.call(arguments, 0)));')]);
|
||||||
o.scope.assign('__slice', this.slice, true);
|
curried = new CodeNode([], body);
|
||||||
ref = new ValueNode(literal('__curry'));
|
curry = new CodeNode([literal('func'), literal('obj'), literal('args')], Expressions.wrap([curried]));
|
||||||
call = new CallNode(ref, [this.meth, this.bind, this.arguments(o)]);
|
return (new ParentheticalNode(new CallNode(curry, [this.meth, this.context, literal(this.arguments(o))]))).compile(o);
|
||||||
return call.compile(o);
|
|
||||||
};
|
};
|
||||||
return CurryNode;
|
return CurryNode;
|
||||||
}).call(this);
|
}).call(this);
|
||||||
|
|||||||
224
lib/parser.js
224
lib/parser.js
File diff suppressed because one or more lines are too long
@@ -384,7 +384,7 @@
|
|||||||
// Tokens that indicate the close of a clause of an expression.
|
// Tokens that indicate the close of a clause of an expression.
|
||||||
EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END);
|
EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END);
|
||||||
// Tokens that, if followed by an `IMPLICIT_CALL`, indicate a function invocation.
|
// Tokens that, if followed by an `IMPLICIT_CALL`, indicate a function invocation.
|
||||||
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END'];
|
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', 'CURRY'];
|
||||||
// If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
|
// If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
|
||||||
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'TRY', 'DELETE', 'TYPEOF', 'SWITCH', 'EXTENSION', 'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!', 'NOT', '@', '->', '=>', '[', '(', '{'];
|
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'TRY', 'DELETE', 'TYPEOF', 'SWITCH', 'EXTENSION', 'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!', 'NOT', '@', '->', '=>', '[', '(', '{'];
|
||||||
// Tokens indicating that the implicit call must enclose a block of expressions.
|
// Tokens indicating that the implicit call must enclose a block of expressions.
|
||||||
|
|||||||
@@ -265,10 +265,7 @@ grammar: {
|
|||||||
]
|
]
|
||||||
|
|
||||||
Curry: [
|
Curry: [
|
||||||
o "Value CURRY Value CURRY_SEPARATOR Arguments", -> new CurryNode $1, $3, $5
|
o "Value CURRY Arguments", -> new CurryNode $1, $3
|
||||||
o "Value CURRY Arguments", -> new CurryNode $1, undefined, $3
|
|
||||||
o "Value CURRY Value", -> new CurryNode $1, $3
|
|
||||||
o "Value CURRY", -> new CurryNode $1
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Extending an object by setting its prototype chain to reference a parent
|
# Extending an object by setting its prototype chain to reference a parent
|
||||||
|
|||||||
@@ -244,24 +244,9 @@ exports.Lexer: class Lexer
|
|||||||
# Here we detect the currying operator and change local state in order to
|
# Here we detect the currying operator and change local state in order to
|
||||||
# parse subsequent tokens accordingly.
|
# parse subsequent tokens accordingly.
|
||||||
curry_token: ->
|
curry_token: ->
|
||||||
if @currying?
|
if @match(CURRY, 1)
|
||||||
if @match(/^\(/, 0) and [',', '<-'].indexOf(@value()) isnt -1
|
|
||||||
@token 'CALL_START', '('
|
|
||||||
@i += 1
|
|
||||||
@currying: undefined
|
|
||||||
true
|
|
||||||
else if @match(CURRY_SEPARATOR, 0)
|
|
||||||
@token 'CURRY_SEPARATOR', ','
|
|
||||||
@i += 1
|
|
||||||
true
|
|
||||||
else if not @match(/^\s*[\w@\$_\(]/, 0)
|
|
||||||
@currying: undefined
|
|
||||||
false
|
|
||||||
else if @match(CURRY, 1)
|
|
||||||
@i += 2
|
@i += 2
|
||||||
@chunk = @code.slice(@i)
|
|
||||||
@token 'CURRY', '<-'
|
@token 'CURRY', '<-'
|
||||||
@currying: true if @match(/^(\s*[\w@\$_\(])/, 1)
|
|
||||||
true
|
true
|
||||||
|
|
||||||
# We treat all other single characters as a token. Eg.: `( ) , . !`
|
# We treat all other single characters as a token. Eg.: `( ) , . !`
|
||||||
|
|||||||
@@ -393,29 +393,20 @@ exports.CallNode: class CallNode extends BaseNode
|
|||||||
# After `Underscore.bind` from [Underscore](http://documentcloud.github.com/underscore/).
|
# After `Underscore.bind` from [Underscore](http://documentcloud.github.com/underscore/).
|
||||||
exports.CurryNode: class CurryNode extends CallNode
|
exports.CurryNode: class CurryNode extends CallNode
|
||||||
type: 'Curry'
|
type: 'Curry'
|
||||||
code: '''
|
|
||||||
function(func, obj, args) {
|
|
||||||
return function() {
|
|
||||||
return func.apply(obj || {}, args.concat(__slice.call(arguments, 0)));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
'''
|
|
||||||
slice: "Array.prototype.slice"
|
|
||||||
|
|
||||||
constructor: (meth, bind, args) ->
|
constructor: (meth, args) ->
|
||||||
@children: flatten [@meth: meth, @bind: bind or literal('this'), @args: (args or [])]
|
@children: flatten [@meth: meth, @context: args[0], @args: (args.slice(1) or [])]
|
||||||
|
|
||||||
arguments: (o) ->
|
arguments: (o) ->
|
||||||
for arg in @args
|
for arg in @args
|
||||||
return literal(@compile_splat_arguments(o)) if arg instanceof SplatNode
|
return @compile_splat_arguments(o) if arg instanceof SplatNode
|
||||||
new ArrayNode(@args)
|
(new ArrayNode(@args)).compile o
|
||||||
|
|
||||||
compile_node: (o) ->
|
compile_node: (o) ->
|
||||||
o.scope.assign('__curry', @code, true)
|
body: Expressions.wrap([literal('func.apply(obj || {}, args.concat(Array.prototype.slice.call(arguments, 0)));')])
|
||||||
o.scope.assign('__slice', @slice, true)
|
curried: new CodeNode([], body)
|
||||||
ref: new ValueNode literal('__curry')
|
curry: new CodeNode([literal('func'), literal('obj'), literal('args')], Expressions.wrap([curried]))
|
||||||
call: new CallNode ref, [@meth, @bind, @arguments(o)]
|
(new ParentheticalNode(new CallNode(curry, [@meth, @context, literal(@arguments(o))]))).compile o
|
||||||
call.compile(o)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ EXPRESSION_END: pair[1] for pair in BALANCED_PAIRS
|
|||||||
EXPRESSION_CLOSE: ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat EXPRESSION_END
|
EXPRESSION_CLOSE: ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat EXPRESSION_END
|
||||||
|
|
||||||
# Tokens that, if followed by an `IMPLICIT_CALL`, indicate a function invocation.
|
# Tokens that, if followed by an `IMPLICIT_CALL`, indicate a function invocation.
|
||||||
IMPLICIT_FUNC: ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END']
|
IMPLICIT_FUNC: ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', 'CURRY']
|
||||||
|
|
||||||
# If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
|
# If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
|
||||||
IMPLICIT_CALL: ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START',
|
IMPLICIT_CALL: ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START',
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ f: (x,y,z) ->
|
|||||||
x*y*z*((@num or 4) + 5)
|
x*y*z*((@num or 4) + 5)
|
||||||
|
|
||||||
obj: {num: 5}
|
obj: {num: 5}
|
||||||
g: f <- obj, (1,1)
|
g: f <- obj, 1,1
|
||||||
h: f <- (1,2)
|
h: f <- {}, 1,2
|
||||||
i: f <- obj
|
i: f <- obj
|
||||||
|
|
||||||
ok g(2) is 20
|
ok g(2) is 20
|
||||||
|
|||||||
Reference in New Issue
Block a user