Skip to content

Instantly share code, notes, and snippets.

@mbildner
Last active August 29, 2015 14:25
Show Gist options
  • Save mbildner/9a069acfb59cf99f3076 to your computer and use it in GitHub Desktop.
Save mbildner/9a069acfb59cf99f3076 to your computer and use it in GitHub Desktop.
a super simple parser
'use strict';
var ACCESS_REGEX = /[\[\]\.]/;
var BOOLEAN_REGEX = /^(true|false)$/;
var NUMBER_REGEX = /^[+-]?(\d*\.)?\d+$/;
var DOUBLE_QUOTE_REGEX = /^".*"$/;
var SINGLE_QUOTE_REGEX = /^'.*'$/;
var NULL_REGEX = /^null$/;
function getType (str) {
if (BOOLEAN_REGEX.test(str)) {
return 'boolean';
}
if (NUMBER_REGEX.test(str)) {
return 'number';
}
if (DOUBLE_QUOTE_REGEX.test(str) || SINGLE_QUOTE_REGEX.test(str)) {
return 'string';
}
if (NULL_REGEX.test(str)) {
return 'null';
}
return 'variable';
}
function extractVal (token) {
switch (token.type) {
case 'boolean':
return {
'true': true,
'false': false
}[token.val];
case 'number':
return +token.val;
case 'null':
return null;
case 'string':
return token.val.slice(1, -1);
}
}
function Tokenizer (){
this.accessors = [];
var collector = [];
this.consumeToken = function(){
var token = {};
var str = collector.splice(0, collector.length).join('');
token.val = str;
token.repr = str;
token.type = getType(token.val);
token.finished = !ACCESS_REGEX.test(token.val);
if (token.val.length) {
this.accessors.push(token);
}
};
this.finish = function(){
if (collector.length) {
this.consumeToken();
}
};
this.addChar = function(c){
collector.push(c);
};
}
function Parser (scope){
this.parse = function (str){
return normalizeAccess(str);
};
this.eval = function(str){
var accessors = normalizeAccess(str);
return evaluate(accessors, scope);
};
this.log = function(str){
console.log(this.eval(str));
};
function normalizeAccess (str) {
var tokenizer = new Tokenizer();
function insideBrackets (){
return openBracketCount > closeBracketCount;
}
var i,
c,
accessors = [],
openBracketCount = 0,
closeBracketCount = 0;
for (i=0; i<str.length; i++) {
c = str.charAt(i);
switch(c) {
case '.':
insideBrackets() ? tokenizer.addChar(c) : tokenizer.finish();
break;
case '[':
insideBrackets() ? tokenizer.addChar(c) : tokenizer.finish();
openBracketCount++;
break;
case ']':
closeBracketCount++;
insideBrackets() ? tokenizer.addChar(c) : tokenizer.finish();
break;
default:
tokenizer.addChar(c);
break;
}
if (openBracketCount === closeBracketCount) {
openBracketCount = closeBracketCount = 0;
}
}
tokenizer.finish();
return tokenizer.accessors.map(function(token){
if (!token.finished) {
token.val = normalizeAccess(token.val);
}
return token;
});
}
function isPrimitive (token) {
return token.type !== 'variable';
}
function evaluate (tree, scope) {
if (isPrimitive(tree[0])) {
return extractVal(tree[0]);
}
return tree.reduce(function(accum, current){
return Array.isArray(current.val)
? accum[evaluate(current.val, scope)]
: accum[current.val];
}, scope);
}
}
var companies = {
41: {
name: 'pivotal labs'
}
};
var users = [{
first: 'moshe',
last: 'bildner',
company_id: 41
}];
var scope = {
users: users,
companies: companies,
};
var demo = companies[users[0].company_id].name;
var demoString = 'companies[users[0].company_id].name';
var parser = new Parser(scope);
console.log(parser.eval(demoString) === 'pivotal labs');
console.log(parser.eval('"moshe bildner"') === 'moshe bildner');
console.log(parser.eval("'moshe bildner'") === 'moshe bildner');
console.log(parser.eval('true') === true);
console.log(parser.eval('null') === null);
console.log(parser.parse(demoString));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment