summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@gmail.com>2013-04-12 03:24:37 +0200
committerMarc-André Lureau <marcandre.lureau@gmail.com>2013-04-12 03:31:21 +0200
commit7d50947ca464d4cbad135919385cde86fd2c4d69 (patch)
tree43d4a12255e703bdf25bf773e9852401248d46b5 /tools
parentb69a90b060eea355ae5a35f0a0f9eb0d32c1d3af (diff)
downloadmsitools-7d50947ca464d4cbad135919385cde86fd2c4d69.zip
msitools-7d50947ca464d4cbad135919385cde86fd2c4d69.tar.gz
msitools-7d50947ca464d4cbad135919385cde86fd2c4d69.tar.xz
preprocessor: add condition evaluator
Diffstat (limited to 'tools')
-rw-r--r--tools/wixl/msi.vala2
-rw-r--r--tools/wixl/preprocessor.vala237
-rw-r--r--tools/wixl/util.vala4
3 files changed, 242 insertions, 1 deletions
diff --git a/tools/wixl/msi.vala b/tools/wixl/msi.vala
index 855f711..ed3c949 100644
--- a/tools/wixl/msi.vala
+++ b/tools/wixl/msi.vala
@@ -647,7 +647,7 @@ namespace Wixl {
critical ("Unhandled property type");
}
- public void save (Libmsi.Database db) throws GLib.Error {
+ public void save (Libmsi.Database db) throws GLib.Error {
properties.save (db);
}
diff --git a/tools/wixl/preprocessor.vala b/tools/wixl/preprocessor.vala
index 16f30a0..b5b7f63 100644
--- a/tools/wixl/preprocessor.vala
+++ b/tools/wixl/preprocessor.vala
@@ -84,6 +84,238 @@ namespace Wixl {
return result + str[end:str.length];
}
+ class EvalCondition: Object {
+ File? file;
+ Preprocessor preprocessor;
+ Scanner scanner;
+
+ enum Op {
+ NONE,
+ EQUAL,
+
+ OPEN_VAR,
+ EQ,
+ EC,
+ NE,
+ LT,
+ LTE,
+ GT,
+ GTE,
+ AND,
+ OR,
+ NOT,
+ }
+
+ construct {
+ scanner = new Scanner (null);
+ scanner.config.scan_identifier_1char = true;
+ scanner.config.cset_identifier_nth =
+ (string)scanner.config.cset_identifier_nth + ".";
+ scanner.scope_add_symbol (0, "and", (void*)Op.AND);
+ scanner.scope_add_symbol (0, "or", (void*)Op.OR);
+ scanner.scope_add_symbol (0, "not", (void*)Op.NOT);
+ }
+
+ public EvalCondition (Preprocessor pp, File? file = null) {
+ this.file = file;
+ this.preprocessor = pp;
+ }
+
+ bool to_bool (Value val) {
+ Value result = false;
+ val.transform (ref result);
+ return (bool) result;
+ }
+
+ bool accept (int type) {
+ var accepted = (int)scanner.peek_next_token () == type;
+ if (accepted)
+ scanner.get_next_token ();
+ return accepted;
+ }
+
+ bool expect (int type) throws GLib.Error {
+ if (!accept (type))
+ throw new Wixl.Error.FAILED ("Expected token %d", type);
+
+ return true;
+ }
+
+ bool accept_symbol (Op symbol) {
+ bool accepted = false;
+ if (scanner.peek_next_token () == TokenType.SYMBOL) {
+ accepted = (int)scanner.next_value.symbol == symbol;
+ }
+
+ if (accepted)
+ scanner.get_next_token ();
+ return accepted;
+ }
+
+ Value term () throws GLib.Error {
+ if (accept (TokenType.LEFT_PAREN)) {
+ Value val = expr ();
+ expect (TokenType.RIGHT_PAREN);
+ return val;
+ } else if (accept (TokenType.INT)) {
+ return scanner.cur_value ().int;
+ } else if (accept (TokenType.STRING)) {
+ return scanner.cur_value ().string;
+ } else if (accept (TokenType.IDENTIFIER)) {
+ return scanner.cur_value ().identifier;
+ } else if (accept ('$')) {
+ if (accept (TokenType.LEFT_PAREN)) {
+ expect (TokenType.IDENTIFIER);
+ var val = preprocessor.eval_variable (scanner.cur_value ().identifier, file);
+ expect (TokenType.RIGHT_PAREN);
+ return val;
+ }
+ }
+
+#if 0
+ debug ("term %d", scanner.peek_next_token ());
+ if (accept (TokenType.SYMBOL))
+ message ("symbol: %d", (int)scanner.cur_value ().symbol);
+#endif
+
+ throw new Wixl.Error.FAILED ("Invalid term");
+ }
+
+ int to_int (Value val) {
+ Value result = 0;
+ val.transform (ref result);
+ return result.get_int ();
+ }
+
+ string to_string (Value val) {
+ Value result = "";
+ val.transform (ref result);
+ return result.get_string ();
+ }
+
+ bool cmp_term () throws GLib.Error {
+ var op = Op.NONE;
+ Value left = term ();
+
+ if (accept ('<'))
+ op = Op.LT;
+ else if (accept ('>'))
+ op = Op.GT;
+ else if (accept (TokenType.EQUAL_SIGN))
+ return to_string (left) == to_string (term ());
+ else if (accept ('!') && expect ('='))
+ return to_string (left) != to_string (term ());
+ else if (accept ('~') && expect ('=')) {
+ return to_string (left).ascii_casecmp (to_string (term ())) == 0;
+ }
+
+ if (op != Op.NONE) {
+ bool eq = false;
+ if (accept ('='))
+ eq = true;
+
+ var a = to_int (left);
+ var b = to_int (term ());
+
+ if (eq && a == b)
+ return true;
+
+ if (op == Op.GT)
+ return a > b;
+ else if (op == Op.LT)
+ return a < b;
+ }
+
+ return to_bool (left);
+ }
+
+ bool not_term () throws GLib.Error {
+ if (accept_symbol (Op.NOT))
+ return !not_term ();
+ else
+ return cmp_term ();
+ }
+
+ bool expr () throws GLib.Error {
+ bool handled = false;
+
+ var result = not_term ();
+ do {
+ // TODO: I think wixl condition are left-evaled
+ handled = true;
+ if (accept_symbol (Op.AND))
+ result = not_term () && result;
+ else if (accept_symbol (Op.OR))
+ result = not_term () || result;
+ else
+ handled = false;
+ } while (handled);
+
+ return result;
+ }
+
+ public bool eval (string str) throws GLib.Error {
+ scanner.input_text (str, str.length);
+ var result = expr ();
+
+ if (!accept (TokenType.EOF))
+ warning ("condition invaluation incomplete, next token %d",
+ scanner.peek_next_token ());
+
+ return result;
+ }
+ }
+
+#if 0
+ void test (string v, bool expect = true) {
+ var evaluator = new EvalCondition (this);
+
+ message (v);
+ if (evaluator.eval (v) != expect) {
+ error ("expected %s", yesno (expect));
+ }
+ }
+
+ void tests () {
+ Environment.set_variable ("FOO", "bar", true);
+ test ("1");
+ test ("0", false);
+ test ("not 1", false);
+ test ("not not 1");
+ test ("((not (not (1))))");
+ test ("\"not1\"", false);
+ test ("\"foo\"", false);
+ test ("\"\"", false);
+ test ("\"foo\" = \"foo\"");
+ test ("1 = 1");
+ test ("1 = 2", false);
+ test ("1 != 2");
+ test ("1 < 2");
+ test ("1 <= 2");
+ test ("2 <= 2");
+ test ("2 >= 2");
+ test ("2 >= 1");
+ test ("2 > 1");
+ test ("\"foo\" ~= \"fOo\"");
+ test ("\"foo\" = \"bar\"", false);
+ test ("not (1 != 2)", false);
+ test ("0 or 0", false);
+ test ("0 or 1", true);
+ test ("0 and 1", false);
+ test ("1 and 1", true);
+ test ("not (1 != 2) or (1 != 2)", true);
+ test ("$(env.FOO) = \"bar\"", true);
+ test ("$(env.FOO) = bar", true);
+ Environment.set_variable ("FOO", "2", true);
+ test ("$(env.FOO) < 3", true);
+ }
+#endif
+
+ public bool eval_condition (string str, File? file) throws GLib.Error {
+ var evaluator = new EvalCondition (this, file);
+ return evaluator.eval (str);
+ }
+
class Location: Object {
public File file;
public int line;
@@ -173,6 +405,11 @@ namespace Wixl {
throw new Wixl.Error.FAILED ("Unmatched endif");
context = ifstack.pop_head ();
break;
+ case "if":
+ ifstack.push_head (context);
+ var value = reader.const_value ().strip ();
+ context = new IfContext (context.enabled && context.is_true, eval_condition (value, file), IfContext.State.IF);
+ break;
default:
handled = false;
break;
diff --git a/tools/wixl/util.vala b/tools/wixl/util.vala
index 1b201ec..a3033cb 100644
--- a/tools/wixl/util.vala
+++ b/tools/wixl/util.vala
@@ -104,6 +104,10 @@ namespace Wixl {
return prefix + hash[0:32].up ();
}
+ public string yesno (bool yes) {
+ return yes ? "yes" : "no";
+ }
+
public bool parse_yesno (string? str, bool default = false) {
if (str == null)
return default;