[vala] Genie: Added string templating



commit e8142d02d0b0a16d3792cdffc31257e621690325
Author: Jamie McCracken <jamie mccrack gmail com>
Date:   Mon May 24 13:52:55 2010 -0400

    Genie: Added string templating

 vala/valagenieparser.vala    |   28 ++++++-
 vala/valageniescanner.vala   |  184 +++++++++++++++++++++++++++++++++++++++--
 vala/valagenietokentype.vala |    3 +
 3 files changed, 204 insertions(+), 11 deletions(-)
---
diff --git a/vala/valagenieparser.vala b/vala/valagenieparser.vala
index 02c759f..6397596 100644
--- a/vala/valagenieparser.vala
+++ b/vala/valagenieparser.vala
@@ -342,6 +342,9 @@ public class Vala.Genie.Parser : CodeVisitor {
 		case TokenType.STRING_LITERAL:
 			next ();
 			return new StringLiteral (get_last_string (), get_src (begin));
+		case TokenType.TEMPLATE_STRING_LITERAL:
+			next ();
+			return new StringLiteral ("\"%s\"".printf (get_last_string ()), get_src (begin));
 		case TokenType.VERBATIM_STRING_LITERAL:
 			next ();
 			string raw_string = get_last_string ();
@@ -623,6 +626,7 @@ public class Vala.Genie.Parser : CodeVisitor {
 		case TokenType.REAL_LITERAL:
 		case TokenType.CHARACTER_LITERAL:
 		case TokenType.STRING_LITERAL:
+		case TokenType.TEMPLATE_STRING_LITERAL:
 		case TokenType.VERBATIM_STRING_LITERAL:
 		case TokenType.NULL:
 			expr = parse_literal ();
@@ -635,6 +639,9 @@ public class Vala.Genie.Parser : CodeVisitor {
 		case TokenType.OPEN_PARENS:
 			expr = parse_tuple ();
 			break;
+		case TokenType.OPEN_TEMPLATE:
+			expr = parse_template ();
+			break;
 		case TokenType.THIS:
 			expr = parse_this_access ();
 			break;
@@ -705,6 +712,21 @@ public class Vala.Genie.Parser : CodeVisitor {
 		return expr;
 	}
 
+	Expression parse_template () throws ParseError {
+		var begin = get_location ();
+		var template = new Template ();
+
+		expect (TokenType.OPEN_TEMPLATE);
+		while (current () != TokenType.CLOSE_TEMPLATE) {
+			template.add_expression (parse_expression ());
+			expect (TokenType.COMMA);
+		}
+		expect (TokenType.CLOSE_TEMPLATE);
+
+		template.source_reference = get_src (begin);
+		return template;
+	}
+
 	Expression parse_tuple () throws ParseError {
 		expect (TokenType.OPEN_PARENS);
 		var expr_list = new ArrayList<Expression> ();
@@ -1170,6 +1192,7 @@ public class Vala.Genie.Parser : CodeVisitor {
 					case TokenType.REAL_LITERAL:
 					case TokenType.CHARACTER_LITERAL:
 					case TokenType.STRING_LITERAL:
+					case TokenType.TEMPLATE_STRING_LITERAL:
 					case TokenType.VERBATIM_STRING_LITERAL:
 					case TokenType.NULL:
 					case TokenType.THIS:
@@ -3063,14 +3086,15 @@ public class Vala.Genie.Parser : CodeVisitor {
 		if (ModifierFlags.NEW in flags) {
 			sig.hides = true;
 		}
-		set_attributes (sig, attrs);
 		
 		if (ModifierFlags.STATIC in flags) {
 			throw new ParseError.SYNTAX (get_error ("`static' modifier not allowed on signals"));
 		} else if (ModifierFlags.CLASS in flags) {
 			throw new ParseError.SYNTAX (get_error ("`class' modifier not allowed on signals"));
 		}
-		
+
+		set_attributes (sig, attrs);
+
 		foreach (FormalParameter formal_param in params) {
 			sig.add_parameter (formal_param);
 		}
diff --git a/vala/valageniescanner.vala b/vala/valageniescanner.vala
index 50a02a5..aacc3ed 100644
--- a/vala/valageniescanner.vala
+++ b/vala/valageniescanner.vala
@@ -58,6 +58,16 @@ public class Vala.Genie.Scanner {
 		public bool else_found;
 		public bool skip_section;
 	}
+
+	State[] state_stack;
+
+	enum State {
+		PARENS,
+		BRACE,
+		BRACKET,
+		TEMPLATE,
+		TEMPLATE_PART
+	}
 	
 	public Scanner (SourceFile source_file) {
 		this.source_file = source_file;
@@ -82,6 +92,14 @@ public class Vala.Genie.Scanner {
 		
 	}
 
+	bool in_template () {
+		return (state_stack.length > 0 && state_stack[state_stack.length - 1] == State.TEMPLATE);
+	}
+
+	bool in_template_part () {
+		return (state_stack.length > 0 && state_stack[state_stack.length - 1] == State.TEMPLATE_PART);
+	}
+
 	bool is_ident_char (char c) {
 		return (c.isalnum () || c == '_');
 	}
@@ -459,7 +477,128 @@ public class Vala.Genie.Scanner {
 		return TokenType.IDENTIFIER;
 	}
 
+	
+	public TokenType read_template_token (out SourceLocation token_begin, out SourceLocation token_end) {
+		TokenType type;
+		char* begin = current;
+		token_begin.pos = begin;
+		token_begin.line = line;
+		token_begin.column = column;
+
+		int token_length_in_chars = -1;
+
+		if (current >= end) {
+			type = TokenType.EOF;
+		} else {
+			switch (current[0]) {
+			case '"':
+				type = TokenType.CLOSE_TEMPLATE;
+				current++;
+				state_stack.length--;
+				break;
+			case '$':
+				token_begin.pos++; // $ is not part of following token
+				current++;
+				if (current[0].isalpha () || current[0] == '_') {
+					int len = 0;
+					while (current < end && is_ident_char (current[0])) {
+						current++;
+						len++;
+					}
+					type = TokenType.IDENTIFIER;
+					state_stack += State.TEMPLATE_PART;
+				} else if (current[0] == '(') {
+					current++;
+					column += 2;
+					state_stack += State.PARENS;
+					return read_token (out token_begin, out token_end);
+				} else if (current[0] == '$') {
+					type = TokenType.TEMPLATE_STRING_LITERAL;
+					current++;
+					state_stack += State.TEMPLATE_PART;
+				} else {
+					Report.error (new SourceReference (source_file, line, column + 1, line, column + 1), "unexpected character");
+					return read_template_token (out token_begin, out token_end);
+				}
+				break;
+			default:
+				type = TokenType.TEMPLATE_STRING_LITERAL;
+				token_length_in_chars = 0;
+				while (current < end && current[0] != '"' && current[0] != '$') {
+					if (current[0] == '\\') {
+						current++;
+						token_length_in_chars++;
+						if (current >= end) {
+							break;
+						}
+
+						switch (current[0]) {
+						case '\'':
+						case '"':
+						case '\\':
+						case '0':
+						case 'b':
+						case 'f':
+						case 'n':
+						case 'r':
+						case 't':
+							current++;
+							token_length_in_chars++;
+							break;
+						case 'x':
+							// hexadecimal escape character
+							current++;
+							token_length_in_chars++;
+							while (current < end && current[0].isxdigit ()) {
+								current++;
+								token_length_in_chars++;
+							}
+							break;
+						default:
+							Report.error (new SourceReference (source_file, line, column + token_length_in_chars, line, column + token_length_in_chars), "invalid escape sequence");
+							break;
+						}
+					} else if (current[0] == '\n') {
+						break;
+					} else {
+						unichar u = ((string) current).get_char_validated ((long) (end - current));
+						if (u != (unichar) (-1)) {
+							current += u.to_utf8 (null);
+							token_length_in_chars++;
+						} else {
+							current++;
+							Report.error (new SourceReference (source_file, line, column + token_length_in_chars, line, column + token_length_in_chars), "invalid UTF-8 character");
+						}
+					}
+				}
+				if (current >= end || current[0] == '\n') {
+					Report.error (new SourceReference (source_file, line, column + token_length_in_chars, line, column + token_length_in_chars), "syntax error, expected \"");
+					state_stack.length--;
+					return read_token (out token_begin, out token_end);
+				}
+				state_stack += State.TEMPLATE_PART;
+				break;
+			}
+		}
+
+		if (token_length_in_chars < 0) {
+			column += (int) (current - begin);
+		} else {
+			column += token_length_in_chars;
+		}
+
+		token_end.pos = current;
+		token_end.line = line;
+		token_end.column = column - 1;
+
+		return type;
+	}
+
+
 	public TokenType read_token (out SourceLocation token_begin, out SourceLocation token_end) {
+		
+
+
 		/* emit dedents if outstanding before checking any other chars */
 
 		if (pending_dedents > 0) {
@@ -481,6 +620,23 @@ public class Vala.Genie.Scanner {
 		}
 
 
+		if (in_template ()) {
+			return read_template_token (out token_begin, out token_end);
+		} else if (in_template_part ()) {
+			state_stack.length--;
+
+			token_begin.pos = current;
+			token_begin.line = line;
+			token_begin.column = column;
+
+			token_end.pos = current;
+			token_end.line = line;
+			token_end.column = column - 1;
+
+			return TokenType.COMMA;
+		}
+
+
 		if ((_indent_spaces == 0 ) || (last_token != TokenType.EOL)) {
 			/* scrub whitespace (excluding newlines) and comments */		
 			space ();
@@ -583,19 +739,20 @@ public class Vala.Genie.Scanner {
 			}
 			type = get_identifier_or_keyword (begin, len);
 		} else if (current[0] == '@') {
-			int len = 0;
-			if (current[1] == '@') {
-				token_begin.pos += 2; // @@ is not part of the identifier
+			if (current < end - 1 && current[1] == '"') {
+				type = TokenType.OPEN_TEMPLATE;
 				current += 2;
+				state_stack += State.TEMPLATE;
 			} else {
+				token_begin.pos++; // @ is not part of the identifier
 				current++;
-				len = 1;
-			}
-			while (current < end && is_ident_char (current[0])) {
-				current++;
-				len++;
+				int len = 0;
+				while (current < end && is_ident_char (current[0])) {
+					current++;
+					len++;
+				}
+				type = TokenType.IDENTIFIER;
 			}
-			type = TokenType.IDENTIFIER;
 		} else if (current[0].isdigit ()) {
 			while (current < end && current[0].isdigit ()) {
 				current++;
@@ -652,29 +809,38 @@ public class Vala.Genie.Scanner {
 			case '{':
 				type = TokenType.OPEN_BRACE;
 				open_brace_count++;
+				state_stack += State.BRACE;
 				current++;
 				break;
 			case '}':
 				type = TokenType.CLOSE_BRACE;
 				open_brace_count--;
+				state_stack.length--;
 				current++;
 				break;
 			case '(':
 				type = TokenType.OPEN_PARENS;
 				open_parens_count++;
+				state_stack += State.PARENS;
 				current++;
 				break;
 			case ')':
 				type = TokenType.CLOSE_PARENS;
 				open_parens_count--;
 				current++;
+				state_stack.length--;
+				if (in_template ()) {
+					type = TokenType.COMMA;
+				}
 				break;
 			case '[':
 				type = TokenType.OPEN_BRACKET;
+				state_stack += State.BRACKET;
 				current++;
 				break;
 			case ']':
 				type = TokenType.CLOSE_BRACKET;
+				state_stack.length--;
 				current++;
 				break;
 			case '.':
diff --git a/vala/valagenietokentype.vala b/vala/valagenietokentype.vala
index a17807a..30ed7ba 100644
--- a/vala/valagenietokentype.vala
+++ b/vala/valagenietokentype.vala
@@ -50,6 +50,7 @@ public enum Vala.Genie.TokenType {
 	CLOSE_BRACE,
 	CLOSE_BRACKET,
 	CLOSE_PARENS,
+	CLOSE_TEMPLATE,
 	COLON,
 	COMMA,
 	CONST,
@@ -120,6 +121,7 @@ public enum Vala.Genie.TokenType {
 	OPEN_BRACE,
 	OPEN_BRACKET,
 	OPEN_PARENS,
+	OPEN_TEMPLATE,
 	OVERRIDE,
 	OWNED,
 	PARAMS,
@@ -146,6 +148,7 @@ public enum Vala.Genie.TokenType {
 	STRING_LITERAL,
 	STRUCT,
 	SUPER,
+	TEMPLATE_STRING_LITERAL,
 	THIS,
 	TILDE,
 	TO,



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