removing extensions from CoffeeScript
This commit is contained in:
parent
b7faa4a7f2
commit
f7345ffaac
|
@ -57,13 +57,6 @@
|
||||||
__dirname = path.dirname(__filename);
|
__dirname = path.dirname(__filename);
|
||||||
return eval(exports.compile(code, options));
|
return eval(exports.compile(code, options));
|
||||||
});
|
});
|
||||||
// Extend CoffeeScript with a custom language extension. It should hook in to
|
|
||||||
// the **Lexer** (as a peer of any of the lexer's tokenizing methods), and
|
|
||||||
// push a token on to the stack that contains a **Node** as the value (as a
|
|
||||||
// peer of the nodes in [nodes.coffee](nodes.html)).
|
|
||||||
exports.extend = function(func) {
|
|
||||||
return Lexer.extensions.push(func);
|
|
||||||
};
|
|
||||||
// The real Lexer produces a generic stream of tokens. This object provides a
|
// The real Lexer produces a generic stream of tokens. This object provides a
|
||||||
// thin wrapper around it, compatible with the Jison API. We can then pass it
|
// thin wrapper around it, compatible with the Jison API. We can then pass it
|
||||||
// directly as a "Jison lexer".
|
// directly as a "Jison lexer".
|
||||||
|
|
|
@ -78,7 +78,7 @@
|
||||||
// CoffeeScript is the **Expression** -- everything that can be an expression
|
// CoffeeScript is the **Expression** -- everything that can be an expression
|
||||||
// is one. Expressions serve as the building blocks of many other rules, making
|
// is one. Expressions serve as the building blocks of many other rules, making
|
||||||
// them somewhat circular.
|
// them somewhat circular.
|
||||||
Expression: [o("Value"), o("Call"), o("Code"), o("Operation"), o("Assign"), o("If"), o("Try"), o("While"), o("For"), o("Switch"), o("Extends"), o("Class"), o("Splat"), o("Existence"), o("Comment"), o("Extension")],
|
Expression: [o("Value"), o("Call"), o("Code"), o("Operation"), o("Assign"), o("If"), o("Try"), o("While"), o("For"), o("Switch"), o("Extends"), o("Class"), o("Splat"), o("Existence"), o("Comment")],
|
||||||
// A an indented block of expressions. Note that the [Rewriter](rewriter.html)
|
// A an indented block of expressions. Note that the [Rewriter](rewriter.html)
|
||||||
// will convert some postfix forms into blocks for us, by adjusting the
|
// will convert some postfix forms into blocks for us, by adjusting the
|
||||||
// token stream.
|
// token stream.
|
||||||
|
@ -453,9 +453,6 @@
|
||||||
return new ParentheticalNode($2);
|
return new ParentheticalNode($2);
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
// A language extension to CoffeeScript from the outside. We simply pass
|
|
||||||
// it through unaltered.
|
|
||||||
Extension: [o("EXTENSION")],
|
|
||||||
// The condition portion of a while loop.
|
// The condition portion of a while loop.
|
||||||
WhileSource: [
|
WhileSource: [
|
||||||
o("WHILE Expression", function() {
|
o("WHILE Expression", function() {
|
||||||
|
|
22
lib/lexer.js
22
lib/lexer.js
|
@ -74,9 +74,6 @@
|
||||||
// short-circuiting if any of them succeed. Their order determines precedence:
|
// short-circuiting if any of them succeed. Their order determines precedence:
|
||||||
// `@literalToken` is the fallback catch-all.
|
// `@literalToken` is the fallback catch-all.
|
||||||
Lexer.prototype.extractNextToken = function() {
|
Lexer.prototype.extractNextToken = function() {
|
||||||
if (this.extensionToken()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (this.identifierToken()) {
|
if (this.identifierToken()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -108,19 +105,6 @@
|
||||||
};
|
};
|
||||||
// Tokenizers
|
// Tokenizers
|
||||||
// ----------
|
// ----------
|
||||||
// Language extensions get the highest priority, first chance to tag tokens
|
|
||||||
// as something else.
|
|
||||||
Lexer.prototype.extensionToken = function() {
|
|
||||||
var _d, _e, _f, extension;
|
|
||||||
_e = Lexer.extensions;
|
|
||||||
for (_d = 0, _f = _e.length; _d < _f; _d++) {
|
|
||||||
extension = _e[_d];
|
|
||||||
if (extension.call(this)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
// Matches identifying literals: variables, keywords, method names, etc.
|
// Matches identifying literals: variables, keywords, method names, etc.
|
||||||
// Check to ensure that JavaScript reserved words aren't being used as
|
// Check to ensure that JavaScript reserved words aren't being used as
|
||||||
// identifiers. Because CoffeeScript reserves a handful of keywords that are
|
// identifiers. Because CoffeeScript reserves a handful of keywords that are
|
||||||
|
@ -667,12 +651,8 @@
|
||||||
prev = this.prev(2);
|
prev = this.prev(2);
|
||||||
return this.value() && this.value().match && this.value().match(NO_NEWLINE) && prev && (prev[0] !== '.') && !this.value().match(CODE);
|
return this.value() && this.value().match && this.value().match(NO_NEWLINE) && prev && (prev[0] !== '.') && !this.value().match(CODE);
|
||||||
};
|
};
|
||||||
// Lexer Properties
|
|
||||||
// ----------------
|
|
||||||
// There are no exensions to the core lexer by default.
|
|
||||||
Lexer.extensions = [];
|
|
||||||
return Lexer;
|
return Lexer;
|
||||||
}).call(this);
|
})();
|
||||||
// Constants
|
// Constants
|
||||||
// ---------
|
// ---------
|
||||||
// Keywords that CoffeeScript shares in common with JavaScript.
|
// Keywords that CoffeeScript shares in common with JavaScript.
|
||||||
|
|
386
lib/parser.js
386
lib/parser.js
File diff suppressed because one or more lines are too long
|
@ -419,7 +419,7 @@
|
||||||
// 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', '@'];
|
||||||
// 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', '!', '!!', 'THIS', 'NULL', '@', '->', '=>', '[', '(', '{'];
|
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'TRY', 'DELETE', 'TYPEOF', 'SWITCH', 'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!', 'THIS', 'NULL', '@', '->', '=>', '[', '(', '{'];
|
||||||
// Tokens indicating that the implicit call must enclose a block of expressions.
|
// Tokens indicating that the implicit call must enclose a block of expressions.
|
||||||
IMPLICIT_BLOCK = ['->', '=>', '{', '[', ','];
|
IMPLICIT_BLOCK = ['->', '=>', '{', '[', ','];
|
||||||
// Tokens that always mark the end of an implicit call for single-liners.
|
// Tokens that always mark the end of an implicit call for single-liners.
|
||||||
|
|
|
@ -55,13 +55,6 @@ exports.run: ((code, options) ->
|
||||||
eval exports.compile code, options
|
eval exports.compile code, options
|
||||||
)
|
)
|
||||||
|
|
||||||
# Extend CoffeeScript with a custom language extension. It should hook in to
|
|
||||||
# the **Lexer** (as a peer of any of the lexer's tokenizing methods), and
|
|
||||||
# push a token on to the stack that contains a **Node** as the value (as a
|
|
||||||
# peer of the nodes in [nodes.coffee](nodes.html)).
|
|
||||||
exports.extend: (func) ->
|
|
||||||
Lexer.extensions.push func
|
|
||||||
|
|
||||||
# The real Lexer produces a generic stream of tokens. This object provides a
|
# The real Lexer produces a generic stream of tokens. This object provides a
|
||||||
# thin wrapper around it, compatible with the Jison API. We can then pass it
|
# thin wrapper around it, compatible with the Jison API. We can then pass it
|
||||||
# directly as a "Jison lexer".
|
# directly as a "Jison lexer".
|
||||||
|
|
|
@ -100,7 +100,6 @@ grammar: {
|
||||||
o "Splat"
|
o "Splat"
|
||||||
o "Existence"
|
o "Existence"
|
||||||
o "Comment"
|
o "Comment"
|
||||||
o "Extension"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# A an indented block of expressions. Note that the [Rewriter](rewriter.html)
|
# A an indented block of expressions. Note that the [Rewriter](rewriter.html)
|
||||||
|
@ -396,12 +395,6 @@ grammar: {
|
||||||
o "( Line )", -> new ParentheticalNode $2
|
o "( Line )", -> new ParentheticalNode $2
|
||||||
]
|
]
|
||||||
|
|
||||||
# A language extension to CoffeeScript from the outside. We simply pass
|
|
||||||
# it through unaltered.
|
|
||||||
Extension: [
|
|
||||||
o "EXTENSION"
|
|
||||||
]
|
|
||||||
|
|
||||||
# The condition portion of a while loop.
|
# The condition portion of a while loop.
|
||||||
WhileSource: [
|
WhileSource: [
|
||||||
o "WHILE Expression", -> new WhileNode $2
|
o "WHILE Expression", -> new WhileNode $2
|
||||||
|
|
|
@ -60,7 +60,6 @@ exports.Lexer: class Lexer
|
||||||
# short-circuiting if any of them succeed. Their order determines precedence:
|
# short-circuiting if any of them succeed. Their order determines precedence:
|
||||||
# `@literalToken` is the fallback catch-all.
|
# `@literalToken` is the fallback catch-all.
|
||||||
extractNextToken: ->
|
extractNextToken: ->
|
||||||
return if @extensionToken()
|
|
||||||
return if @identifierToken()
|
return if @identifierToken()
|
||||||
return if @numberToken()
|
return if @numberToken()
|
||||||
return if @heredocToken()
|
return if @heredocToken()
|
||||||
|
@ -75,13 +74,6 @@ exports.Lexer: class Lexer
|
||||||
# Tokenizers
|
# Tokenizers
|
||||||
# ----------
|
# ----------
|
||||||
|
|
||||||
# Language extensions get the highest priority, first chance to tag tokens
|
|
||||||
# as something else.
|
|
||||||
extensionToken: ->
|
|
||||||
for extension in Lexer.extensions
|
|
||||||
return true if extension.call this
|
|
||||||
false
|
|
||||||
|
|
||||||
# Matches identifying literals: variables, keywords, method names, etc.
|
# Matches identifying literals: variables, keywords, method names, etc.
|
||||||
# Check to ensure that JavaScript reserved words aren't being used as
|
# Check to ensure that JavaScript reserved words aren't being used as
|
||||||
# identifiers. Because CoffeeScript reserves a handful of keywords that are
|
# identifiers. Because CoffeeScript reserves a handful of keywords that are
|
||||||
|
@ -451,12 +443,6 @@ exports.Lexer: class Lexer
|
||||||
@value() and @value().match and @value().match(NO_NEWLINE) and
|
@value() and @value().match and @value().match(NO_NEWLINE) and
|
||||||
prev and (prev[0] isnt '.') and not @value().match(CODE)
|
prev and (prev[0] isnt '.') and not @value().match(CODE)
|
||||||
|
|
||||||
# Lexer Properties
|
|
||||||
# ----------------
|
|
||||||
|
|
||||||
# There are no exensions to the core lexer by default.
|
|
||||||
@extensions: []
|
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
# ---------
|
# ---------
|
||||||
|
|
||||||
|
|
|
@ -282,7 +282,7 @@ IMPLICIT_FUNC: ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@']
|
||||||
|
|
||||||
# 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',
|
||||||
'TRY', 'DELETE', 'TYPEOF', 'SWITCH', 'EXTENSION',
|
'TRY', 'DELETE', 'TYPEOF', 'SWITCH',
|
||||||
'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!',
|
'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!',
|
||||||
'THIS', 'NULL',
|
'THIS', 'NULL',
|
||||||
'@', '->', '=>', '[', '(', '{']
|
'@', '->', '=>', '[', '(', '{']
|
||||||
|
|
|
@ -6,68 +6,3 @@ js: CoffeeScript.compile("one\r\ntwo", {noWrap: on})
|
||||||
|
|
||||||
ok js is "one;\ntwo;"
|
ok js is "one;\ntwo;"
|
||||||
|
|
||||||
|
|
||||||
# Try out language extensions to CoffeeScript.
|
|
||||||
|
|
||||||
# Create the Node were going to add -- a literal syntax for splitting
|
|
||||||
# strings into letters.
|
|
||||||
class SplitNode extends BaseNode
|
|
||||||
type: 'Split'
|
|
||||||
|
|
||||||
constructor: (variable) ->
|
|
||||||
@variable: variable
|
|
||||||
|
|
||||||
compileNode: (o) ->
|
|
||||||
"'${@variable}'.split('')"
|
|
||||||
|
|
||||||
# Extend CoffeeScript with our lexing function that matches --wordgoeshere--
|
|
||||||
# and creates a SplitNode.
|
|
||||||
CoffeeScript.extend ->
|
|
||||||
return false unless variable: @match(/^--(\w+)--/, 1)
|
|
||||||
@i: + variable.length + 4
|
|
||||||
@token 'EXTENSION', new SplitNode(variable)
|
|
||||||
true
|
|
||||||
|
|
||||||
# Compile with the extension.
|
|
||||||
js: CoffeeScript.compile 'return --tobesplit--', {noWrap: on}
|
|
||||||
|
|
||||||
ok js is "return 'tobesplit'.split('');"
|
|
||||||
|
|
||||||
|
|
||||||
# Let's try a different extension, for Ruby-style array literals.
|
|
||||||
|
|
||||||
class WordArrayNode extends BaseNode
|
|
||||||
type: 'WordArray'
|
|
||||||
|
|
||||||
constructor: (words) ->
|
|
||||||
@words: words
|
|
||||||
|
|
||||||
compileNode: (o) ->
|
|
||||||
strings = ("\"$word\"" for word in @words).join ', '
|
|
||||||
"[$strings]"
|
|
||||||
|
|
||||||
CoffeeScript.extend ->
|
|
||||||
return false unless words: @chunk.match(/^%w\{(.*?)\}/)
|
|
||||||
@i: + words[0].length
|
|
||||||
@token 'EXTENSION', new WordArrayNode(words[1].split(/\s+/))
|
|
||||||
true
|
|
||||||
|
|
||||||
js: CoffeeScript.compile 'puts %w{one two three}', {noWrap: on}
|
|
||||||
|
|
||||||
ok js is 'puts(["one", "two", "three"]);'
|
|
||||||
|
|
||||||
|
|
||||||
# Finally, let's try an extension that converts `a << b` into `a.push(b)`.
|
|
||||||
|
|
||||||
CoffeeScript.extend ->
|
|
||||||
return false unless @chunk.match(/^<</)
|
|
||||||
@i: + 2
|
|
||||||
@token 'PROPERTY_ACCESS', '.'
|
|
||||||
@token 'IDENTIFIER', 'push'
|
|
||||||
|
|
||||||
js: CoffeeScript.compile 'a << b', {noWrap: on}
|
|
||||||
|
|
||||||
ok js is 'a.push(b);'
|
|
||||||
|
|
||||||
Lexer.extensions: []
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue