New: #if macro statement works for simple literals but not for macro identifiers and defined keyword

This commit is contained in:
Martin Carlberg
2013-03-28 22:43:26 +01:00
parent 2751f4fea9
commit 84df301f3e
2 changed files with 176 additions and 13 deletions
+174 -11
View File
@@ -266,7 +266,7 @@ if (typeof exports != "undefined" && !exports.acorn) {
// Same as input but for the current token. If options.preprocess is used
// this can differ due to macros.
var tokInput;
var tokInput, preTokInput;
// These store the position of the previous token, which is useful
// when finishing a node and assigning its `end` position.
@@ -295,6 +295,7 @@ if (typeof exports != "undefined" && !exports.acorn) {
// These are used by the preprocess tokenizer.
var preTokPos, preTokType, preTokVal, preTokStart, preTokEnd;
var preLastStart, preLastEnd;
var preprocessStack = [];
var preprocessMacroParamterListMode = false;
@@ -389,6 +390,7 @@ if (typeof exports != "undefined" && !exports.acorn) {
var _preEndif = {keyword: "endif"};
var _preElseIf = {keyword: "elif"};
var _prePragma = {keyword: "pragma"};
var _preDefined = {keyword: "defined"};
var _preprocessParamItem = {type: "preprocessParamItem"}
@@ -418,7 +420,7 @@ if (typeof exports != "undefined" && !exports.acorn) {
// Map Preprocessor keyword names to token types.
var keywordTypesPreprocess = {"define": _preDefine, "pragma": _prePragma, "ifdef": _preIfdef, "ifndef": _preIfndef, "undef": _preUndef, "endif": _preEndif, "else": _preElse, "elif": _preElseIf};
var keywordTypesPreprocess = {"define": _preDefine, "pragma": _prePragma, "ifdef": _preIfdef, "ifndef": _preIfndef, "undef": _preUndef, "if": _preIf, "endif": _preEndif, "else": _preElse, "elif": _preElseIf};
// Punctuation token types. Again, the `type` property is purely for debugging.
@@ -447,14 +449,14 @@ if (typeof exports != "undefined" && !exports.acorn) {
// binary operators with a very low precedence, that should result
// in AssignmentExpression nodes.
var _slash = {binop: 10, beforeExpr: true}, _eq = {isAssign: true, beforeExpr: true};
var _assign = {isAssign: true, beforeExpr: true}, _plusmin = {binop: 9, prefix: true, beforeExpr: true};
var _slash = {binop: 10, beforeExpr: true, preprocess: true}, _eq = {isAssign: true, beforeExpr: true, preprocess: true};
var _assign = {isAssign: true, beforeExpr: true}, _plusmin = {binop: 9, prefix: true, beforeExpr: true, preprocess: true};
var _incdec = {postfix: true, prefix: true, isUpdate: true}, _prefix = {prefix: true, beforeExpr: true};
var _bin1 = {binop: 1, beforeExpr: true}, _bin2 = {binop: 2, beforeExpr: true};
var _bin3 = {binop: 3, beforeExpr: true}, _bin4 = {binop: 4, beforeExpr: true};
var _bin5 = {binop: 5, beforeExpr: true}, _bin6 = {binop: 6, beforeExpr: true};
var _bin7 = {binop: 7, beforeExpr: true}, _bin8 = {binop: 8, beforeExpr: true};
var _bin10 = {binop: 10, beforeExpr: true};
var _bin1 = {binop: 1, beforeExpr: true, preprocess: true}, _bin2 = {binop: 2, beforeExpr: true, preprocess: true};
var _bin3 = {binop: 3, beforeExpr: true, preprocess: true}, _bin4 = {binop: 4, beforeExpr: true, preprocess: true};
var _bin5 = {binop: 5, beforeExpr: true, preprocess: true}, _bin6 = {binop: 6, beforeExpr: true, preprocess: true};
var _bin7 = {binop: 7, beforeExpr: true, preprocess: true}, _bin8 = {binop: 8, beforeExpr: true, preprocess: true};
var _bin10 = {binop: 10, beforeExpr: true, preprocess: true};
// Provide access to the token types for external users of the
// tokenizer.
@@ -903,6 +905,19 @@ var preIfLevel = 0;
preprocesSkipRestOfLine();
break;
case _preIf:
if (preNotSkipping) {
preIfLevel++;
preprocessReadToken();
var expr = preprocessParseExpression();
var test = preprocessEvalExpression(expr);
if (!test)
preNotSkipping = false
preprocessSkipToElseOrEndif(!test);
} else {
return finishTokenFunction(_preIf);
}
break;
case _preIfdef:
if (preNotSkipping) {
@@ -954,6 +969,7 @@ var preIfLevel = 0;
if (preIfLevel) {
if (preNotSkipping) {
preIfLevel--;
break;
}
} else {
raise(preTokStart, "#endif without #if");
@@ -981,6 +997,46 @@ var preIfLevel = 0;
return readToken();
}
function preprocessEvalExpression(expr) {
return exports.walk.recursive(expr, {}, {
BinaryExpression: function(node, st, c) {
var left = c(node.left, st), right = c(node.right, st);
switch(node.operator) {
case "+":
return left + right;
case "-":
return left - right;
case "*":
return left * right;
case "/":
return left / right;
case "%":
return left % right;
case "<":
return left < right;
case ">":
return left > right;
case "=":
case "==":
case "===":
return left === right;
case "<=":
return left <= right;
case ">=":
return left >= right;
case "&&":
return left && right;
case "||":
return left || right;
}
},
Literal: function(node, st, c) {
return node.value;
}
}, {});
}
function getTokenFromCode(code, finishToken, allowEndOfLineToken) {
switch(code) {
// The interpretation of a dot depends on whether it is followed
@@ -1119,6 +1175,7 @@ var preIfLevel = 0;
function preprocessReadToken() {
preTokStart = tokPos;
preTokInput = input;
if (tokPos >= inputLen) return _eof;
var code = input.charCodeAt(tokPos);
if (preprocessMacroParamterListMode && code !== 41 && code !== 44) { // ')', ','
@@ -1155,12 +1212,21 @@ var preIfLevel = 0;
preprocessSkipSpace();
}
// Continue to the next token.
function preprocessNext() {
preLastStart = tokStart;
preLastEnd = tokEnd;
//lastEndLoc = tokEndLoc;
return preprocessReadToken();
}
// Predicate that tests whether the next token is of the given
// type, and if yes, consumes it as a side effect.
function preprocessEat(type) {
if (preTokType === type) {
preprocessReadToken();
preprocessNext();
return true;
}
}
@@ -1175,10 +1241,107 @@ var preIfLevel = 0;
function preprocessParseIdent() {
var ident = preTokType === _name ? preTokVal : preTokType.keyword || raise(preTokStart, "Expected Macro identifier");
preprocessReadToken();
preprocessNext();
return ident;
}
// Parse an expression — either a single token that is an
// expression, an expression started by a keyword like `defined`,
// or an expression wrapped in punctuation like `()`.
function preprocessParseExpression() {
return preprocessParseExprOps();
}
// Start the precedence parser.
function preprocessParseExprOps() {
return preprocessParseExprOp(preprocessParseMaybeUnary(), -1);
}
// Parse binary operators with the operator precedence parsing
// algorithm. `left` is the left-hand side of the operator.
// `minPrec` provides context that allows the function to stop and
// defer further parser to one of its callers when it encounters an
// operator that has a lower precedence than the set it is parsing.
function preprocessParseExprOp(left, minPrec) {
var prec = preTokType.binop;
if (prec) {
if (!preTokType.preprocess) raise(preTokStart, "Unsupported macro operator");
if (prec > minPrec) {
var node = startNodeFrom(left);
node.left = left;
node.operator = preTokVal;
preprocessNext();
node.right = preprocessParseExprOp(preprocessParseMaybeUnary(), prec);
var node = preprocessFinishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression");
return preprocessParseExprOp(node, minPrec);
}
}
return left;
}
// Parse an unary expression if possible
function preprocessParseMaybeUnary() {
if (preTokType.preprocess && preTokType.prefix) {
var node = startNode();
node.operator = tokVal;
node.prefix = true;
preprocessNext();
node.argument = preprocessParseMaybeUnary();
return preprocessFinishNode(node, "UnaryExpression");
}
return preprocessParseExprAtom();
}
// Parse an atomic macro expression — either a single token that is an
// expression, an expression started by a keyword like `defined`,
// or an expression wrapped in punctuation like `()`.
function preprocessParseExprAtom() {
switch (preTokType) {
case _name:
return preprocessParseIdent();
case _num: case _string:
return preprocessParseStringNumLiteral();
case _parenL:
var tokStart1 = preTokStart;
preprocessNext();
var val = preprocessParseExpression();
val.start = tokStart1;
val.end = preTokEnd;
preprocessExpect(_parenR, "Expected closing ')' in macro expression");
return val;
case _preDefined:
var node = startNode();
preprocessNext();
node.expr = preprocessParseExpression();
return preprocessFinishNode(node, "DefinedExpression");
default:
unexpected();
}
}
function preprocessParseStringNumLiteral() {
var node = startNode();
node.value = preTokVal;
node.raw = preTokInput.slice(preTokStart, preTokEnd);
preprocessNext();
return preprocessFinishNode(node, "Literal");
}
function preprocessFinishNode(node, type) {
node.type = type;
node.end = preLastEnd;
return node;
}
function readToken(forceRegexp) {
tokStart = tokPos;
tokInput = input;
+2 -2
View File
@@ -41,9 +41,9 @@ if (!exports.acorn) {
exports.recursive = function(node, state, funcs, base) {
var visitor = exports.make(funcs, base);
function c(node, st, override) {
visitor[override || node.type](node, st, c);
return visitor[override || node.type](node, st, c);
}
c(node, state);
return c(node, state);
};
// Used to create a custom walker. Will fill in all missing node