You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
5.1 KiB
177 lines
5.1 KiB
'use strict'; |
|
|
|
var Tokenizer = require('../tokenizer'); |
|
var List = require('../utils/list'); |
|
var sequence = require('./sequence'); |
|
var noop = function() {}; |
|
|
|
function createParseContext(name) { |
|
return function() { |
|
return this[name](); |
|
}; |
|
} |
|
|
|
function processConfig(config) { |
|
var parserConfig = { |
|
context: {}, |
|
scope: {}, |
|
atrule: {}, |
|
pseudo: {} |
|
}; |
|
|
|
if (config.parseContext) { |
|
for (var name in config.parseContext) { |
|
switch (typeof config.parseContext[name]) { |
|
case 'function': |
|
parserConfig.context[name] = config.parseContext[name]; |
|
break; |
|
|
|
case 'string': |
|
parserConfig.context[name] = createParseContext(config.parseContext[name]); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if (config.scope) { |
|
for (var name in config.scope) { |
|
parserConfig.scope[name] = config.scope[name]; |
|
} |
|
} |
|
|
|
if (config.atrule) { |
|
for (var name in config.atrule) { |
|
var atrule = config.atrule[name]; |
|
|
|
if (atrule.parse) { |
|
parserConfig.atrule[name] = atrule.parse; |
|
} |
|
} |
|
} |
|
|
|
if (config.pseudo) { |
|
for (var name in config.pseudo) { |
|
var pseudo = config.pseudo[name]; |
|
|
|
if (pseudo.parse) { |
|
parserConfig.pseudo[name] = pseudo.parse; |
|
} |
|
} |
|
} |
|
|
|
if (config.node) { |
|
for (var name in config.node) { |
|
parserConfig[name] = config.node[name].parse; |
|
} |
|
} |
|
|
|
return parserConfig; |
|
} |
|
|
|
module.exports = function createParser(config) { |
|
var parser = { |
|
scanner: new Tokenizer(), |
|
filename: '<unknown>', |
|
needPositions: false, |
|
onParseError: noop, |
|
onParseErrorThrow: false, |
|
parseAtrulePrelude: true, |
|
parseRulePrelude: true, |
|
parseValue: true, |
|
parseCustomProperty: false, |
|
|
|
readSequence: sequence, |
|
|
|
createList: function() { |
|
return new List(); |
|
}, |
|
createSingleNodeList: function(node) { |
|
return new List().appendData(node); |
|
}, |
|
getFirstListNode: function(list) { |
|
return list && list.first(); |
|
}, |
|
getLastListNode: function(list) { |
|
return list.last(); |
|
}, |
|
|
|
parseWithFallback: function(consumer, fallback) { |
|
var startToken = this.scanner.currentToken; |
|
|
|
try { |
|
return consumer.call(this); |
|
} catch (e) { |
|
if (this.onParseErrorThrow) { |
|
throw e; |
|
} |
|
|
|
var fallbackNode = fallback.call(this, startToken); |
|
|
|
this.onParseErrorThrow = true; |
|
this.onParseError(e, fallbackNode); |
|
this.onParseErrorThrow = false; |
|
|
|
return fallbackNode; |
|
} |
|
}, |
|
|
|
getLocation: function(start, end) { |
|
if (this.needPositions) { |
|
return this.scanner.getLocationRange( |
|
start, |
|
end, |
|
this.filename |
|
); |
|
} |
|
|
|
return null; |
|
}, |
|
getLocationFromList: function(list) { |
|
if (this.needPositions) { |
|
var head = this.getFirstListNode(list); |
|
var tail = this.getLastListNode(list); |
|
return this.scanner.getLocationRange( |
|
head !== null ? head.loc.start.offset - this.scanner.startOffset : this.scanner.tokenStart, |
|
tail !== null ? tail.loc.end.offset - this.scanner.startOffset : this.scanner.tokenStart, |
|
this.filename |
|
); |
|
} |
|
|
|
return null; |
|
} |
|
}; |
|
|
|
config = processConfig(config || {}); |
|
for (var key in config) { |
|
parser[key] = config[key]; |
|
} |
|
|
|
return function(source, options) { |
|
options = options || {}; |
|
|
|
var context = options.context || 'default'; |
|
var ast; |
|
|
|
parser.scanner.setSource(source, options.offset, options.line, options.column); |
|
parser.filename = options.filename || '<unknown>'; |
|
parser.needPositions = Boolean(options.positions); |
|
parser.onParseError = typeof options.onParseError === 'function' ? options.onParseError : noop; |
|
parser.onParseErrorThrow = false; |
|
parser.parseAtrulePrelude = 'parseAtrulePrelude' in options ? Boolean(options.parseAtrulePrelude) : true; |
|
parser.parseRulePrelude = 'parseRulePrelude' in options ? Boolean(options.parseRulePrelude) : true; |
|
parser.parseValue = 'parseValue' in options ? Boolean(options.parseValue) : true; |
|
parser.parseCustomProperty = 'parseCustomProperty' in options ? Boolean(options.parseCustomProperty) : false; |
|
|
|
if (!parser.context.hasOwnProperty(context)) { |
|
throw new Error('Unknown context `' + context + '`'); |
|
} |
|
|
|
ast = parser.context[context].call(parser, options); |
|
|
|
if (!parser.scanner.eof) { |
|
parser.scanner.error(); |
|
} |
|
|
|
return ast; |
|
}; |
|
};
|
|
|