Skip to content

Instantly share code, notes, and snippets.

@alucky0707
Last active December 18, 2015 19:49
Show Gist options
  • Save alucky0707/5835680 to your computer and use it in GitHub Desktop.
Save alucky0707/5835680 to your computer and use it in GitHub Desktop.
Parsecっぽい何か
// TODO: コメント書け!
(function(exports) {
var
debug = false;
function extend(trait, target) {
var
p;
for(p in trait) {
target[p] = trait[p];
}
return target;
}
function bind(func, self) {
return function() {
return func.apply(self, arguments);
};
}
function ret(val) {
return Object(val);
}
function toArray(n, args) {
return Array.prototype.slice.call(args, n);
}
function Parser(gen) {
return function(src) {
var
rule, res, error,
rules = extend(Combinators, {}),
ctx = new Context(src);
for(rule in rules) {
rules[rule] = bind(rules[rule], ctx);
}
gen(rules);
res = rules.start();
if(res === null) {
error = new Error('[offset:' + ctx.offset + ']' + ctx.error);
error.offset = ctx.offset;
error.message = ctx.error;
throw error;
}else {
return res;
}
};
}
function Rule(func, self) {
return function rule() {
var
result,
copy = (self || this).copy();
(self || this).error = null;
result = func.apply(self || this, arguments);
(self || this).lastcall = [func, arguments, copy];
if(debug) console.log(func.name, result, (self || this).src);
return result;
}
}
var Combinators = {};
Combinators.$ = Rule(function $(rule) {
return Rule(rule, this);
});
Combinators.many = Rule(function many(result) {
var
rule = this.lastcall[0],
args = this.lastcall[1],
results = [];
if(typeof result === 'function') {
rule = result;
args = [];
result = rule.apply(this, args);
}
while(result !== null) {
results.push(result);
result = rule.apply(this, args);
}
return results;
});
Combinators.some = Rule(function some(result) {
var
rule = this.lastcall[0],
args = this.lastcall[1],
results = [];
if(typeof result === 'function') {
rule = result;
args = [];
result = rule.apply(this, args);
}
if(result === null) return null;
while(result !== null) {
results.push(result);
result = rule.apply(this, args);
}
return results;
});
Combinators.when = Rule(function when(result) {
var
c = this.lastcall[2];
if(typeof result === 'function') {
copy(c, this);
result = result.apply(this, []);
}
copy(this, c);
return !!result;
});
Combinators.s = Combinators.string = Rule(function string(str) {
var
sliced = this.src.slice(0, str.length);
if(sliced !== str) {
this.error = 'Expected string "' + str + '"';
return null;
}
this.src = this.src.slice(str.length);
this.offset += str.length;
return ret(sliced);
});
Combinators.anyChar = Rule(function anyChar() {
var
c = this.src.charAt(0);
if(this.src === '') {
this.error = 'Unexpected EOF';
return null;
}
this.src = this.src.slice(1);
this.offset += 1;
return ret(c);
});
Combinators.eof = Rule(function eof() {
if(this.src === '') {
return true;
}else {
this.error = 'Expected EOF, but found "' + this.src.charAt(0) + '"';
return null;
}
});
Combinators.r = Combinators.regexp = Rule(function regexp(regex) {
var
matched = null;
regex = new RegExp('^(?:' + regex.source + ')', (regex.ignoreCase?'i':''));
this.src = this.src.replace(regex, function() {
var
args = toArray(0, arguments);
args = args.slice(0, args.length - 2);
matched = args.length === 1 ? args[0] : args;
this.offset += args[0].length;
return '';
});
if(matched === null) {
this.error = 'Expected pattern ' + regex;
return null;
}else {
return ret(matched);
}
});
Combinators.oneOf = Rule(function oneOf(chrs) {
var
i;
for(i = 0; i < chrs.length; i++) {
if(this.src.charAt(0) === chrs.charAt(i)) {
this.src = this.src.slice(1);
this.offset += 1;
return ret(chrs.charAt(i));
}
}
this.error = 'Expected one of "' + chrs + '"';
return null;
});
Combinators.noneOf = Rule(function noneOf(chrs) {
var
i, c = this.src.charAt(0);
if(chrs.indexOf(c) !== -1) {
this.error = 'Unexpected one of "' + chrs + '"';
return null;
}
this.src = this.src.slice(1);
this.offset += 1;
return ret(c);
});
Combinators.attempt = Rule(function attempt(result) {
var
c = this.lastcall[2];
if(typeof result === 'function') {
copy(c, this);
result = result.apply(this, []);
}
if(result !== null) return result;
copy(this, c);
return null;
});
function Context(src) {
this.src = src;
this.offset = 0;
this.error = null;
}
Context.prototype.copy = function() {
return copy(new Context, this);
}
function copy(obj, a) {
obj.src = a.src;
obj.lastcall = a.lastcall;
obj.offset = a.offset;
obj.error = a.error;
return obj;
}
exports.Parser = Parser;
})('module' in this && 'exports' in module ? module.exports : this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment