diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..94eac6fb0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "bundles/CoffeeScriptBundle.tmbundle"] + path = bundles/CoffeeScriptBundle.tmbundle + url = git://github.com/jashkenas/coffee-script-tmbundle diff --git a/bundles/CoffeeScriptBundle.tmbundle b/bundles/CoffeeScriptBundle.tmbundle new file mode 160000 index 000000000..71ece5f64 --- /dev/null +++ b/bundles/CoffeeScriptBundle.tmbundle @@ -0,0 +1 @@ +Subproject commit 71ece5f64a06c5e0d30a23eb2e551da64978a46d diff --git a/spec/app/parser-spec.coffee b/spec/app/parser-spec.coffee new file mode 100644 index 000000000..5ee0b25ba --- /dev/null +++ b/spec/app/parser-spec.coffee @@ -0,0 +1,18 @@ +Parser = require 'parser' +plist = require 'plist' +fs = require 'fs' + +describe "Parser", -> + parser = null + + beforeEach -> + coffee_plist = fs.read(require.resolve 'CoffeeScriptBundle.tmbundle/Syntaxes/CoffeeScript.tmLanguage') + plist.parseString coffee_plist, (err, grammar) -> + parser = new Parser(grammar[0]) + + describe ".getLineTokens(line, state)", -> + describe "when the state is omitted (start state)", -> + describe "when line matches a single pattern with no capture groups", -> + fit "returns a single token with the correct scope", -> + {tokens, state} = parser.getLineTokens("return") + expect(token.scopes).toEqual ['source.coffee', 'keyword.control.coffee'] diff --git a/src/app/parser.coffee b/src/app/parser.coffee new file mode 100644 index 000000000..9be30f04b --- /dev/null +++ b/src/app/parser.coffee @@ -0,0 +1,20 @@ +module.exports = +class Parser + constructor: (@grammar) -> + + getLineTokens: (line, state=@getStartState()) -> + + getStartState: -> + console.log @grammar + [new ParserState(@grammar)] + +class ParserState + scopeName: null + patterns: null + + constructor: ({@scopeName, patterns}) -> + + @patterns = patterns.map (pattern) -> + console.log pattern.match + + #matchcount = new RegExp("(?:(" + state[i].regex + ")|(.))").exec("a").length - 2; \ No newline at end of file diff --git a/src/stdlib/require.coffee b/src/stdlib/require.coffee index 186c79545..875f94fc4 100644 --- a/src/stdlib/require.coffee +++ b/src/stdlib/require.coffee @@ -7,6 +7,7 @@ paths = [ "#{atom.loadPath}/src" "#{atom.loadPath}/vendor" "#{atom.loadPath}/static" + "#{atom.loadPath}/bundles" "#{atom.loadPath}" ] diff --git a/vendor/plist.js b/vendor/plist.js new file mode 100644 index 000000000..5be42d004 --- /dev/null +++ b/vendor/plist.js @@ -0,0 +1,214 @@ +;(function (exports, sax) { + //Checks if running in a non-browser environment + var inNode = typeof window === 'undefined' ? true : false; + + function Parser() { + sax.SAXParser.call(this, false, { lowercasetags: true, trim: false }); + } + + var inherits = null; + if (inNode) { + var fs = require('fs'); + inherits = require('util').inherits; //use node provided function + } else { //use in browser + if ("create" in Object) { + inherits = function(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; + } else { + var klass = function() {}; + inherits = function(ctor, superCtor) { + klass.prototype = superCtor.prototype; + ctor.prototype = new klass; + ctor.prototype['constructor'] = ctor; + } + } + } + inherits(Parser, sax.SAXParser); //inherit from sax (browser-style or node-style) + + Parser.prototype.getInteger = function (string) { + this.value = parseInt(string, 10); + } + Parser.prototype.getReal = function (string) { + this.value = parseFloat(string); + } + Parser.prototype.getString = function (string) { + this.value += string; + } + Parser.prototype.getData = function(string) { + // todo: parse base64 encoded data + this.value += string; + } + Parser.prototype.getDate = function (string) { + this.value = new Date(string); + } + + Parser.prototype.addToDict = function (value) { + this.dict[this.key] = value; + } + Parser.prototype.addToArray = function (value) { + this.array.push(value); + } + + Parser.prototype.onopentag = function (tag) { + switch (tag.name) { + case 'dict': + this.stack.push(this.context); + this.context = { + value: function() { + return this.dict; + }, + dict: {}, + setKey: function(key) { + this.key = key; + }, + setValue: function(value) { + this.dict[this.key] = value; + } + } + break; + case 'plist': + case 'array': + this.stack.push(this.context); + this.context = { + value: function() { + return this.array; + }, + array: [], + setKey: function(key) { + console.log('unexpected element in array'); + }, + setValue: function(value) { + this.array.push(value); + } + } + break; + case 'key': + this.ontext = function (text) { + this.context.setKey(text); + } + break; + case 'integer': + this.ontext = this.getInteger; + break; + case 'real': + this.ontext = this.getReal; + break; + case 'string': + this.value = ''; + this.ontext = this.getString; + this.oncdata = this.getString; + break; + case 'data': + this.value = ''; + this.ontext = this.getData; + this.oncdata = this.getData; + break; + case 'true': + this.value = true; + break; + case 'false': + this.value = false; + break; + case 'date': + this.ontext = this.getDate; + break; + default: + console.log('ignored tag', tag.name); + break; + } + } + Parser.prototype.onclosetag = function (tag) { + var value; + switch (tag) { + case 'dict': + case 'array': + case 'plist': + var value = this.context.value(); + this.context = this.stack.pop(); + this.context.setValue(value); + break; + case 'true': + case 'false': + case 'string': + case 'integer': + case 'real': + case 'date': + case 'data': + this.context.setValue(this.value); + break; + case 'key': + break; + default: + console.log('closing', tag, 'tag ignored'); + } + this.oncdata = this.ontext = this.checkWhitespace; + } + Parser.prototype.checkWhitespace = function (data) { + if (!data.match(/^[ \t\r\n]*$/)) { + console.log('unexpected non-whitespace data', data); + } + } + Parser.prototype.oncomment = function (comment) { + } + Parser.prototype.onerror = function (error) { + console.log('sax parser error:', error); + throw error; + } + + if (inNode) Parser.prototype.parseFile = function (xmlfile, callback) { //browsers aren't capable of opening files, instead use AJAX + var parser = this; + parser.stack = [ ]; + parser.context = { + callback: callback, + value: function() {}, + setKey: function(key) {}, + setValue: function(value) { + callback(null, value); + }, + } + var rs = fs.createReadStream(xmlfile, { + encoding: 'utf8' + }); + rs.on('data', function(data) { parser.write(data); }); + rs.on('end', function() { parser.close(); }); + } + + Parser.prototype.parseString = function (xml, callback) { + var parser = this; + parser.stack = [ ]; + parser.context = { + callback: callback, + value: function() {}, + setKey: function(key) {}, + setValue: function(value) { + this.callback(null, value); + }, + }; + parser.write(xml); + parser.close(); + } + + exports.Parser = Parser; + + exports.parseString = function (xml, callback) { + var parser = new Parser(); + parser.parseString(xml, callback); + } + + if (inNode) exports.parseFile = function (filename, callback) { //Do not expose no created method + var parser = new Parser(); + parser.parseFile(filename, callback); + } +})(typeof exports === 'undefined' ? plist = {} : exports, require('sax')) // Changed by sobo to always `require` sax +//the above line checks for exports (defined in node) and uses it, or creates a global variable and exports to that. +//also, if in node, require sax node-style, in browser the developer must use a