diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/wixl/msi.vala | 2 | ||||
-rw-r--r-- | tools/wixl/preprocessor.vala | 237 | ||||
-rw-r--r-- | tools/wixl/util.vala | 4 |
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; |