Last active
December 18, 2015 19:49
-
-
Save alucky0707/5835680 to your computer and use it in GitHub Desktop.
Parsecっぽい何か
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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