[vala] Support conditional compilation



commit 3f0e7474f8cb772cdd7e19f1e062d4706f8e598b
Author: Jürg Billeter <j bitron ch>
Date:   Fri Apr 3 09:37:00 2009 +0200

    Support conditional compilation
    
    Add -D SYMBOL commandline option to define conditional symbols.
    Support precondition directives #if, #elif, #else, and #endif.
    Fixes bug 434515.
---
 compiler/valacompiler.vala |   10 ++
 vala/valacodecontext.vala  |   10 ++
 vala/valascanner.vala      |  263 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 283 insertions(+), 0 deletions(-)

diff --git a/compiler/valacompiler.vala b/compiler/valacompiler.vala
index 6becda9..0506388 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 abf1719..3babfa8 100644
--- a/vala/valacodecontext.vala
+++ b/vala/valacodecontext.vala
@@ -171,6 +171,8 @@ public class Vala.CodeContext {
 
 	private Gee.List<string> packages = new ArrayList<string> (str_equal);
 
+	private Set<string> defines = new HashSet<string> (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 383186c..068cff7 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;
 	}
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]