Skip to content

Commit

Permalink
Unfinished.
Browse files Browse the repository at this point in the history
  • Loading branch information
uhop committed Aug 19, 2013
1 parent e08eb6f commit d33231b
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 4 deletions.
4 changes: 2 additions & 2 deletions bottomUp/Parser.js
Expand Up @@ -41,11 +41,11 @@
var left = this.token.left,
right = this.stack.length ? this.stack[this.stack.length - 1].right : 0;

if(left > right){
if(right < left){
this.expected = this.token.next;
this.stack.push(this.token);
this.state = "supply";
}else if(left < right){
}else if(right > left){
this.state = this.stack.length ? "consume" : "eos";
}else{
if(this.stack.length){
Expand Down
82 changes: 82 additions & 0 deletions bottomUp/Parser2.js
@@ -0,0 +1,82 @@
/* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))})
([], function(){
"use strict";

var consumeReadyState = {consume: 1, eos: 1},
eoi = {id: "End of Input", left: -1, right: -1};

function Parser2(grammar, name, priority){
this.reset(grammar, name, priority);
}

Parser2.prototype = {
reset: function(grammar, name, priority){
this.stack = [];
this.state = "supply";
this.grammar = grammar;
this.expected = name || "main";
this.priority = priority || 0;
},
_consume: function(){
if(this.state == "eos"){
this.state = "done";
return this.token;
}
//assert((this.state in consumeReadyState) && this.stack.length);
var token = this.stack.pop();
this._decide();
return token;
},
_decide: function(token){
if(token){
//assert(this.state == "supply");
this.token = token;
}

var left = this.token.left,
right = this.stack.length ? this.stack[this.stack.length - 1].right : this.priority;

if(right < left){
this.expected = this.token.next;
this.stack.push(this.token);
this.state = "supply";
}else{
this.state = this.stack.length ? "consume" : "eos";
}
},
getExpectedState: function(){
return this.state == "done" ? null : this.grammar[this.expected];
},
putToken: function(token, scanner){
if(token){
if(!token.ignore){
//assert(this.state == "supply");
this._decide(token);
while(this.state in consumeReadyState){
token = this._consume();
if(token !== eoi){
this.onToken(token);
}
}
}
}else{
this.putToken(eoi, scanner);
if(this.state != "done"){
throw Error("Can't find a legal token" +
(scanner ? " at (" + scanner.line + ", " + scanner.pos + ") in: " +
(scanner.buffer.length > 16 ? scanner.buffer.substring(0, 16) + "..." :
scanner.buffer) + "\n" : ".\n") +
"Tried '" + this.expected + "': " +
this.grammar[this.expected].tokens.map(function(token){
return "'" + token.id + "'";
}).join(", ") + ".");
}
}
},
onToken: function(token){
console.log(token.id + " (" + token.line + ", " + token.pos + "): " + token.value);
}
};

return Parser2;
});
109 changes: 109 additions & 0 deletions tests/expr2.js
@@ -0,0 +1,109 @@
/* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))})
(["../bottomUp/Grammar"], function(Grammar){
"use strict";

function operand(pattern){
return {
pattern: pattern,
left: 0,
right: 0,
next: "operator"
};
}

function prefix(priority, pattern){
return {
pattern: pattern,
left: priority || 70,
right: 0,
next: "operator"
};
}

function infix(priority, pattern){
return {
pattern: pattern,
left: priority,
right: priority,
next: "operand"
};
}

function infixr(priority, pattern){
return {
pattern: pattern,
left: priority,
right: priority - 1,
next: "operand"
};
}

function pattern(priority, rule, next){
return {
pattern: rule[0],
left: priority,
right: 0,
rule: rule,
next: next || "operator"
};
}

function maybe(){
var rule = Array.prototype.slice.call(arguments, 0);
rule.optional = true;
return rule;
}

function repeat(){
var rule = Array.prototype.slice.call(arguments, 0);
rule.optional = true;
rule.repeatable = true;
return rule;
}

var expr = new Grammar({
operand: {
// values
id: operand(/[A-Za-z_\$]\w*/),
num: operand(/(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+-]?\d+)?|\.\d+(?:[eE][+-]?\d+)?/),
// unary prefix operators
"pre+": operand("+"),
"pre-": operand("-"),
// parentheses
"(": operand(),
"[": operand(),
// technical
ws: {
pattern: /[\u0009\u000B\u000C\u0020\u00A0\uFEFF]+/,
ignore: true
},
crlf: {
pattern: /[\u000A\u2028\u2029]|\u000D\u000A|\u000D/,
ignore: true
}
},
operator: {
// the ternary operator
"?": pattern(20, ["?", 0, ":", 0]),
// binary operators
"*": infix(60),
"/": infix(60),
"+": infix(50),
"-": infix(50),
// parentheses
sub: pattern(80, ["[", 0, "]"]),
call: pattern(80, ["(", maybe(0, repeat(",", 0)), ")"]),
// technical
ws: {
pattern: /[\u0009\u000B\u000C\u0020\u00A0\uFEFF]+/,
ignore: true
},
crlf: {
pattern: /[\u000A\u2028\u2029]|\u000D\u000A|\u000D/,
ignore: true
}
}
});

return expr;
});
33 changes: 33 additions & 0 deletions tests/test_expr2.js
@@ -0,0 +1,33 @@
var Scanner = require("../Scanner");
var Parser = require("../bottomUp/Parser2");

var expr = require("./expr2");


var scanner = new Scanner();

//scanner.addBuffer("(1 + 2) * 3[4]", true);
scanner.addBuffer("[a[b]]", true);


console.log("Buffer: " + scanner.buffer);

var parser = new Parser(expr, "operand");

for(;;){
var expected = parser.getExpectedState();
if(!expected){
// we are done
break;
}
var token = scanner.getToken(expected);
if(token === true){
throw Error("Scanner requests more data, which is impossible.");
}
parser.putToken(token, scanner);
}

if(!scanner.isFinished()){
throw Error("Unprocessed symbols: " +
(scanner.buffer.length > 16 ? scanner.buffer.substring(0, 16) + "..." : scanner.buffer));
}
4 changes: 2 additions & 2 deletions topDown/Grammar.js
Expand Up @@ -29,7 +29,7 @@
}
});
}, this);
};
}

// helpers

Expand Down Expand Up @@ -133,7 +133,7 @@
makeState(item);
item = item.state;
}
if(!naked){
if(!naked && typeof item == "object"){
var newIndex = index + 1;
if(newIndex === rule.length && rule.repeatable){
newIndex = 0;
Expand Down

0 comments on commit d33231b

Please sign in to comment.