From: Jürg Billeter Date: Fri, 3 Apr 2009 07:37:00 +0000 (+0200) Subject: Support conditional compilation X-Git-Tag: 0.7.0~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3f0e7474f8cb772cdd7e19f1e062d4706f8e598b;p=thirdparty%2Fvala.git Support conditional compilation Add -D SYMBOL commandline option to define conditional symbols. Support precondition directives #if, #elif, #else, and #endif. Fixes bug 434515. --- diff --git a/compiler/valacompiler.vala b/compiler/valacompiler.vala index 6becda937..0506388ba 100644 --- a/compiler/valacompiler.vala +++ b/compiler/valacompiler.vala @@ -58,6 +58,9 @@ class Vala.Compiler { static string[] cc_options; static string dump_tree; static bool save_temps; + [CCode (array_length = false, array_null_terminated = true)] + [NoArrayLength] + static string[] defines; static bool quiet_mode; private CodeContext context; @@ -76,6 +79,7 @@ class Vala.Compiler { { "output", 'o', 0, OptionArg.FILENAME, ref output, "Place output in file FILE", "FILE" }, { "debug", 'g', 0, OptionArg.NONE, ref debug, "Produce debug information", null }, { "thread", 0, 0, OptionArg.NONE, ref thread, "Enable multithreading support", null }, + { "define", 'D', 0, OptionArg.STRING_ARRAY, ref defines, "Define SYMBOL", "SYMBOL..." }, { "disable-assert", 0, 0, OptionArg.NONE, ref disable_assert, "Disable assertions", null }, { "enable-checking", 0, 0, OptionArg.NONE, ref enable_checking, "Enable additional run-time checks", null }, { "enable-deprecated", 0, 0, OptionArg.NONE, ref deprecated, "Enable deprecated features", null }, @@ -188,6 +192,12 @@ class Vala.Compiler { context.thread = thread; context.save_temps = save_temps; + if (defines != null) { + foreach (string define in defines) { + context.add_define (define); + } + } + int glib_major = 2; int glib_minor = 12; if (target_glib != null && target_glib.scanf ("%d.%d", out glib_major, out glib_minor) != 2) { diff --git a/vala/valacodecontext.vala b/vala/valacodecontext.vala index abf171925..3babfa81f 100644 --- a/vala/valacodecontext.vala +++ b/vala/valacodecontext.vala @@ -171,6 +171,8 @@ public class Vala.CodeContext { private Gee.List packages = new ArrayList (str_equal); + private Set defines = new HashSet (str_hash, str_equal); + static StaticPrivate context_stack_key = StaticPrivate (); /** @@ -298,6 +300,14 @@ public class Vala.CodeContext { } } + public void add_define (string define) { + defines.add (define); + } + + public bool is_defined (string define) { + return (define in defines); + } + public string? get_package_path (string pkg, string[] vapi_directories) { string basename = "%s.vapi".printf (pkg); string filename = null; diff --git a/vala/valascanner.vala b/vala/valascanner.vala index 383186c15..068cff797 100644 --- a/vala/valascanner.vala +++ b/vala/valascanner.vala @@ -37,6 +37,14 @@ public class Vala.Scanner { string _comment; + Conditional[] conditional_stack; + + struct Conditional { + public bool matched; + public bool else_found; + public bool skip_section; + } + public Scanner (SourceFile source_file) { this.source_file = source_file; @@ -776,17 +784,272 @@ public class Vala.Scanner { return true; } + bool pp_whitespace () { + bool found = false; + while (current < end && current[0] == ' ') { + found = true; + current++; + column++; + } + return found; + } + + void pp_directive () { + do { + current++; + column++; + } while (current < end && current[0] == ' '); + + char* begin = current; + int len = 0; + while (current < end && current[0].isalnum ()) { + current++; + column++; + len++; + } + + if (len == 2 && matches (begin, "if")) { + parse_pp_if (); + } else if (len == 4 && matches (begin, "elif")) { + parse_pp_elif (); + } else if (len == 4 && matches (begin, "else")) { + parse_pp_else (); + } else if (len == 5 && matches (begin, "endif")) { + parse_pp_endif (); + } else { + Report.error (new SourceReference (source_file, line, column - len, line, column), "syntax error, invalid preprocessing directive"); + } + + if (conditional_stack.length > 0 + && conditional_stack[conditional_stack.length - 1].skip_section) { + // skip lines until next preprocessing directive + bool bol = false; + while (current < end) { + if (bol && current[0] == '#') { + return; + } + if (current[0] == '\n') { + line++; + column = 0; + bol = true; + } else if (current[0] != ' ') { + bol = false; + } + current++; + column++; + } + } + } + + void pp_eol () { + pp_whitespace (); + if (current >= end || current[0] != '\n') { + Report.error (new SourceReference (source_file, line, column, line, column), "syntax error, expected newline"); + } + } + + void parse_pp_if () { + pp_whitespace (); + + bool condition = parse_pp_expression (); + + pp_eol (); + + conditional_stack += Conditional (); + + if (condition && (conditional_stack.length == 1 || !conditional_stack[conditional_stack.length - 2].skip_section)) { + // condition true => process code within if + conditional_stack[conditional_stack.length - 1].matched = true; + } else { + // skip lines until next preprocessing directive + conditional_stack[conditional_stack.length - 1].skip_section = true; + } + } + + void parse_pp_elif () { + pp_whitespace (); + + bool condition = parse_pp_expression (); + + pp_eol (); + + if (conditional_stack.length == 0 || conditional_stack[conditional_stack.length - 1].else_found) { + Report.error (new SourceReference (source_file, line, column, line, column), "syntax error, unexpected #elif"); + return; + } + + if (condition && !conditional_stack[conditional_stack.length - 1].matched + && (conditional_stack.length == 1 || !conditional_stack[conditional_stack.length - 2].skip_section)) { + // condition true => process code within if + conditional_stack[conditional_stack.length - 1].matched = true; + conditional_stack[conditional_stack.length - 1].skip_section = false; + } else { + // skip lines until next preprocessing directive + conditional_stack[conditional_stack.length - 1].skip_section = true; + } + } + + void parse_pp_else () { + pp_eol (); + + if (conditional_stack.length == 0 || conditional_stack[conditional_stack.length - 1].else_found) { + Report.error (new SourceReference (source_file, line, column, line, column), "syntax error, unexpected #else"); + return; + } + + if (!conditional_stack[conditional_stack.length - 1].matched + && (conditional_stack.length == 1 || !conditional_stack[conditional_stack.length - 2].skip_section)) { + // condition true => process code within if + conditional_stack[conditional_stack.length - 1].matched = true; + conditional_stack[conditional_stack.length - 1].skip_section = false; + } else { + // skip lines until next preprocessing directive + conditional_stack[conditional_stack.length - 1].skip_section = true; + } + } + + void parse_pp_endif () { + pp_eol (); + + if (conditional_stack.length == 0) { + Report.error (new SourceReference (source_file, line, column, line, column), "syntax error, unexpected #endif"); + return; + } + + conditional_stack.length--; + } + + bool parse_pp_symbol () { + int len = 0; + while (current < end && is_ident_char (current[0])) { + current++; + column++; + len++; + } + + if (len == 0) { + Report.error (new SourceReference (source_file, line, column, line, column), "syntax error, expected identifier"); + return false; + } + + string identifier = ((string) (current - len)).ndup (len); + bool defined; + if (identifier == "true") { + defined = true; + } else if (identifier == "false") { + defined = false; + } else { + defined = source_file.context.is_defined (identifier); + } + + return defined; + } + + bool parse_pp_primary_expression () { + if (current >= end) { + Report.error (new SourceReference (source_file, line, column, line, column), "syntax error, expected identifier"); + } else if (is_ident_char (current[0])) { + return parse_pp_symbol (); + } else if (current[0] == '(') { + current++; + column++; + pp_whitespace (); + bool result = parse_pp_expression (); + pp_whitespace (); + if (current < end && current[0] == ')') { + current++; + column++; + } else { + Report.error (new SourceReference (source_file, line, column, line, column), "syntax error, expected `)'"); + } + return result; + } else { + Report.error (new SourceReference (source_file, line, column, line, column), "syntax error, expected identifier"); + } + return false; + } + + bool parse_pp_unary_expression () { + if (current < end && current[0] == '!') { + current++; + column++; + pp_whitespace (); + return !parse_pp_unary_expression (); + } + + return parse_pp_primary_expression (); + } + + bool parse_pp_equality_expression () { + bool left = parse_pp_unary_expression (); + pp_whitespace (); + while (true) { + if (current < end - 1 && current[0] == '=' && current[1] == '=') { + current += 2; + column += 2; + pp_whitespace (); + bool right = parse_pp_unary_expression (); + left = (left == right); + } else if (current < end - 1 && current[0] == '!' && current[1] == '=') { + current += 2; + column += 2; + pp_whitespace (); + bool right = parse_pp_unary_expression (); + left = (left != right); + } else { + break; + } + } + return left; + } + + bool parse_pp_and_expression () { + bool left = parse_pp_equality_expression (); + pp_whitespace (); + while (current < end - 1 && current[0] == '&' && current[1] == '&') { + current += 2; + column += 2; + pp_whitespace (); + bool right = parse_pp_equality_expression (); + left = left && right; + } + return left; + } + + bool parse_pp_or_expression () { + bool left = parse_pp_and_expression (); + pp_whitespace (); + while (current < end - 1 && current[0] == '|' && current[1] == '|') { + current += 2; + column += 2; + pp_whitespace (); + bool right = parse_pp_and_expression (); + left = left || right; + } + return left; + } + + bool parse_pp_expression () { + return parse_pp_or_expression (); + } + bool whitespace () { bool found = false; + bool bol = (column == 1); while (current < end && current[0].isspace ()) { if (current[0] == '\n') { line++; column = 0; + bol = true; } found = true; current++; column++; } + if (bol && current[0] == '#') { + pp_directive (); + return true; + } return found; }