diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala
index e890951..18121ce 100644
--- a/codegen/valaccodebasemodule.vala
+++ b/codegen/valaccodebasemodule.vala
@@ -176,6 +176,7 @@ internal class Vala.CCodeBaseModule : CCodeModule {
 	public DataType int64_type;
 	public DataType uint64_type;
 	public DataType string_type;
+	public DataType regex_type;
 	public DataType float_type;
 	public DataType double_type;
 	public TypeSymbol gtype_type;
@@ -310,6 +311,7 @@ internal class Vala.CCodeBaseModule : CCodeModule {
 		float_type = new FloatingType ((Struct) root_symbol.scope.lookup ("float"));
 		double_type = new FloatingType ((Struct) root_symbol.scope.lookup ("double"));
 		string_type = new ObjectType ((Class) root_symbol.scope.lookup ("string"));
+		regex_type = new ObjectType ((Class) root_symbol.scope.lookup ("GLib").scope.lookup ("Regex"));
 
 		var unichar_struct = (Struct) root_symbol.scope.lookup ("unichar");
 		if (unichar_struct != null) {
@@ -3368,6 +3370,26 @@ internal class Vala.CCodeBaseModule : CCodeModule {
 		expr.ccodenode = new CCodeConstant (expr.value);
 	}
 
+	public override void visit_regex_literal (RegexLiteral expr) {
+		string[] parts = expr.value.split ("/", 3);
+		string re = parts[2].escape ("");
+		string flags = "0";
+
+		if (parts[1].contains ("i")) {
+			flags += " | G_REGEX_CASELESS";
+		}
+		if (parts[1].contains ("m")) {
+			flags += " | G_REGEX_MULTILINE";
+		}
+		if (parts[1].contains ("s")) {
+			flags += " | G_REGEX_DOTALL";
+		}
+		if (parts[1].contains ("x")) {
+			flags += " | G_REGEX_EXTENDED";
+		}
+		expr.ccodenode = new CCodeConstant ("g_regex_new (\"" + re + "\", " + flags + ", 0, NULL)");
+	}
+
 	public override void visit_null_literal (NullLiteral expr) {
 		if (context.profile != Profile.GOBJECT) {
 			source_declarations.add_include ("stddef.h");
diff --git a/codegen/valaccodegenerator.vala b/codegen/valaccodegenerator.vala
index 35dff33..58cdf56 100644
--- a/codegen/valaccodegenerator.vala
+++ b/codegen/valaccodegenerator.vala
@@ -260,6 +260,10 @@ public class Vala.CCodeGenerator : CodeGenerator {
 		head.visit_string_literal (expr);
 	}
 
+	public override void visit_regex_literal (RegexLiteral expr) {
+		head.visit_regex_literal (expr);
+	}
+
 	public override void visit_null_literal (NullLiteral expr) {
 		head.visit_null_literal (expr);
 	}
diff --git a/codegen/valaccodemodule.vala b/codegen/valaccodemodule.vala
index 2fee6b9..76f0ec8 100644
--- a/codegen/valaccodemodule.vala
+++ b/codegen/valaccodemodule.vala
@@ -239,6 +239,10 @@ public abstract class Vala.CCodeModule {
 		next.visit_string_literal (expr);
 	}
 
+	public virtual void visit_regex_literal (RegexLiteral re) {
+		next.visit_regex_literal (re);
+	}
+
 	public virtual void visit_null_literal (NullLiteral expr) {
 		next.visit_null_literal (expr);
 	}
diff --git a/tests/objects/regex.vala b/tests/objects/regex.vala
new file mode 100644
index 0000000..f110cd6
--- /dev/null
+++ b/tests/objects/regex.vala
@@ -0,0 +1,41 @@
+using GLib;
+
+
+static int main (string[] args)
+{
+	MatchInfo info;
+
+	// Simple greedy regular expression matching.
+	var str1 = "mississippi";
+	if (@/(is.*ip)/.match (str1, 0, out info)) {
+		stdout.printf ("Part of %s is '%s'...\n", str1, info.fetch (1));
+	} else {
+		stdout.printf ("Did not match at all.\n");
+	}
+
+	// Match caseless.
+	var str2 = "demonStration";
+	if (@/mon(str.*o)n/i.match (str2, 0, out info)) {
+		stdout.printf ("Part of %s is '%s'...\n", str2, info.fetch (1));
+	} else {
+		stdout.printf ("%s did not match at all.\n", str2);
+	}
+
+	// Match and pick substrings.
+	var ts   = "Time: 10:42:12";
+	if (@/Time: (..):(..):(..)/.match (ts, 0, out info)) {
+		stdout.printf ("%s\n\thours = %s\n\tminutes = %s\n\tseconds = %s\n\n", ts, info.fetch (1), info.fetch (2), info.fetch (3));
+	}
+
+	// Replace demo: word swapping
+	try {
+		var str = "apple grape";
+		stdout.printf ("'%s' becomes '%s'\n", str, @/^([^ ]*) *([^ ]*)/.replace (str, -1, 0, """\2 \1"""));
+ 	} catch (RegexError err) {
+		// Replacing still needs exception catching
+		message (err.message);
+	}
+
+	return 0;
+}
+
diff --git a/vala/Makefile.am b/vala/Makefile.am
index 57d5b24..8ceeada 100644
--- a/vala/Makefile.am
+++ b/vala/Makefile.am
@@ -117,6 +117,8 @@ libvalacore_la_VALASOURCES = \
 	valarealliteral.vala \
 	valareferencetransferexpression.vala \
 	valareferencetype.vala \
+	valaregex.vala \
+	valaregexliteral.vala \
 	valareport.vala \
 	valareturnstatement.vala \
 	valascanner.vala \
diff --git a/vala/valacodevisitor.vala b/vala/valacodevisitor.vala
index 7744674..f07a6b8 100644
--- a/vala/valacodevisitor.vala
+++ b/vala/valacodevisitor.vala
@@ -469,6 +469,16 @@ public abstract class Vala.CodeVisitor {
 	}
 
 	/**
+	 * Visit operation called for regex literals.
+	 *
+	 * @param lit a string literal
+	 */
+	public virtual void visit_regex_literal (RegexLiteral lit) {
+message ("generic regex visitor");
+//stdout.printf ("%s", (string) (2));
+	}
+
+	/**
 	 * Visit operation called for string templates.
 	 *
 	 * @param tmpl a string template
diff --git a/vala/valaparser.vala b/vala/valaparser.vala
index 4b3c7fd..e77695c 100644
--- a/vala/valaparser.vala
+++ b/vala/valaparser.vala
@@ -288,6 +288,13 @@ public class Vala.Parser : CodeVisitor {
 		case TokenType.TEMPLATE_STRING_LITERAL:
 			next ();
 			return new StringLiteral ("\"%s\"".printf (get_last_string ()), get_src (begin));
+		case TokenType.REGEX_LITERAL:
+			next ();
+			string match_part = get_last_string ();
+			SourceReference src_begin = get_src (begin);
+			expect (TokenType.CLOSE_REGEX);
+			string close_token = get_last_string ();
+			return new RegexLiteral ("%s/%s".printf (close_token, match_part), src_begin);
 		case TokenType.VERBATIM_STRING_LITERAL:
 			next ();
 			string raw_string = get_last_string ();
@@ -546,6 +553,7 @@ public class Vala.Parser : CodeVisitor {
 		case TokenType.STRING_LITERAL:
 		case TokenType.TEMPLATE_STRING_LITERAL:
 		case TokenType.VERBATIM_STRING_LITERAL:
+		case TokenType.REGEX_LITERAL:
 		case TokenType.NULL:
 			expr = parse_literal ();
 			break;
@@ -558,6 +566,9 @@ public class Vala.Parser : CodeVisitor {
 		case TokenType.OPEN_TEMPLATE:
 			expr = parse_template ();
 			break;
+		case TokenType.OPEN_REGEX:
+			expr = parse_regex ();
+			break;
 		case TokenType.THIS:
 			expr = parse_this_access ();
 			break;
@@ -665,6 +676,19 @@ public class Vala.Parser : CodeVisitor {
 		return template;
 	}
 
+	Expression parse_regex () throws ParseError {
+		var begin = get_location ();
+		var regex = new Vala.Regex ();
+
+		expect (TokenType.OPEN_REGEX);
+
+		var expr = parse_literal ();
+		regex.add_expression (expr);
+
+		regex.source_reference = get_src (begin);
+		return regex;
+	}
+
 	Expression parse_member_access (SourceLocation begin, Expression inner) throws ParseError {
 		expect (TokenType.DOT);
 		string id = parse_identifier ();
@@ -951,6 +975,7 @@ public class Vala.Parser : CodeVisitor {
 					case TokenType.STRING_LITERAL:
 					case TokenType.TEMPLATE_STRING_LITERAL:
 					case TokenType.VERBATIM_STRING_LITERAL:
+					case TokenType.REGEX_LITERAL:
 					case TokenType.NULL:
 					case TokenType.THIS:
 					case TokenType.BASE:
diff --git a/vala/valaregex.vala b/vala/valaregex.vala
new file mode 100644
index 0000000..1763e82
--- /dev/null
+++ b/vala/valaregex.vala
@@ -0,0 +1,78 @@
+/* valaregex.vala
+ *
+ * Copyright (C) 2010  Jukka-Pekka Iivonen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Author:
+ * 	Jukka-Pekka Iivonen <jp0409@jippii.fi>
+ */
+
+
+public class Vala.Regex : Expression {
+	private List<Expression> expression_list = new ArrayList<Expression> ();
+
+	public Regex () {
+	}
+
+	public void add_expression (Expression expr) {
+		expression_list.add (expr);
+	}
+
+	public List<Expression> get_expressions () {
+		return expression_list;
+	}
+
+	public override bool is_pure () {
+		return false;
+	}
+
+	Expression stringify (Expression expr) {
+		if (expr is RegexLiteral) {
+			return expr;
+		} else {
+			return new MethodCall (new MemberAccess (expr, "to_string", expr.source_reference), expr.source_reference);
+		}
+	}
+
+	public override bool check (SemanticAnalyzer analyzer) {
+		if (checked) {
+			return !error;
+		}
+
+		checked = true;
+
+		Expression expr;
+
+		if (expression_list.size == 0) {
+			expr = new StringLiteral ("\"\"", source_reference);
+		} else {
+			expr = stringify (expression_list[0]);
+			if (expression_list.size > 1) {
+				var concat = new MethodCall (new MemberAccess (expr, "concat", source_reference), source_reference);
+				for (int i = 1; i < expression_list.size; i++) {
+					concat.add_argument (stringify (expression_list[i]));
+				}
+				expr = concat;
+			}
+		}
+		expr.target_type = target_type;
+
+		analyzer.replaced_nodes.add (this);
+		parent_node.replace_expression (this, expr);
+		return expr.check (analyzer);
+	}
+}
+
diff --git a/vala/valaregexliteral.vala b/vala/valaregexliteral.vala
new file mode 100644
index 0000000..e4472a1
--- /dev/null
+++ b/vala/valaregexliteral.vala
@@ -0,0 +1,93 @@
+/* valaregexliteral.vala
+ *
+ * Copyright (C) 2010  Jukka-Pekka Iivonen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Author:
+ * 	Jukka-Pekka Iivonen <jp0409@jippii.fi>
+ */
+
+using GLib;
+
+/**
+ * Represents a regular expression literal in the source code.
+ */
+public class Vala.RegexLiteral : Literal {
+	/**
+	 * The literal value.
+	 */
+	public string value { get; set; }
+
+	/**
+	 * Creates a new regular expression literal.
+	 *
+	 * @param s      the literal value
+	 * @param source reference to source code
+	 * @return       newly created string literal
+	 */
+	public RegexLiteral (string value, SourceReference? source_reference = null) {
+		this.value = value;
+		this.source_reference = source_reference;
+	}
+
+	/**
+	 * Evaluates the literal string value.
+	 *
+	 * @return the unescaped string
+	 */	
+	public string? eval () {
+		if (value == null) {
+			return null;
+		}
+		
+		/* remove quotes */
+		var noquotes = value.substring (1, (uint) (value.len () - 2));
+		/* unescape string */
+		return noquotes.compress ();
+	}
+	
+	public override void accept (CodeVisitor visitor) {
+		visitor.visit_regex_literal (this);
+
+		visitor.visit_expression (this);
+	}
+
+	public override bool is_pure () {
+		return true;
+	}
+
+	public override bool is_non_null () {
+		return true;
+	}
+
+	public override string to_string () {
+		return value;
+	}
+
+	public override bool check (SemanticAnalyzer analyzer) {
+		if (checked) {
+			return !error;
+		}
+
+		checked = true;
+
+		analyzer.visit_regex_literal (this);
+		value_type = analyzer.regex_type.copy ();
+
+		return !error;
+	}
+}
+
diff --git a/vala/valascanner.vala b/vala/valascanner.vala
index 9a1d3ea..5e6881e 100644
--- a/vala/valascanner.vala
+++ b/vala/valascanner.vala
@@ -18,6 +18,7 @@
  *
  * Author:
  * 	Jürg Billeter <j@bitron.ch>
+ *      Jukka-Pekka Iivonen <jp0409@jippii.fi>
  */
 
 using GLib;
@@ -51,7 +52,8 @@ public class Vala.Scanner {
 		BRACE,
 		BRACKET,
 		TEMPLATE,
-		TEMPLATE_PART
+		TEMPLATE_PART,
+		REGEX
 	}
 
 	public Scanner (SourceFile source_file) {
@@ -74,6 +76,10 @@ public class Vala.Scanner {
 		return (state_stack.length > 0 && state_stack[state_stack.length - 1] == State.TEMPLATE_PART);
 	}
 
+	bool in_regex () {
+		return (state_stack.length > 0 && state_stack[state_stack.length - 1] == State.REGEX);
+	}
+
 	bool is_ident_char (char c) {
 		return (c.isalnum () || c == '_');
 	}
@@ -561,6 +567,146 @@ public class Vala.Scanner {
 		return type;
 	}
 
+	public TokenType read_regex_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_REGEX;
+				current++;
+				state_stack.length--;
+				var fl_i = false;
+				var fl_s = false;
+				var fl_m = false;
+				var fl_x = false;
+				while (current[0] == 'i' || current[0] == 's' || current[0] == 'm' || current[0] == 'x') {
+					switch (current[0]) {
+					case 'i':
+						if (fl_i) {
+							Report.error (new SourceReference (source_file, line, column + token_length_in_chars, line, column + token_length_in_chars), "modifier 'i' used more than once");
+						}
+						fl_i = true;
+						break;
+					case 's':
+						if (fl_s) {
+							Report.error (new SourceReference (source_file, line, column + token_length_in_chars, line, column + token_length_in_chars), "modifier 's' used more than once");
+						}
+						fl_s = true;
+						break;
+					case 'm':
+						if (fl_m) {
+							Report.error (new SourceReference (source_file, line, column + token_length_in_chars, line, column + token_length_in_chars), "modifier 'm' used more than once");
+						}
+						fl_m = true;
+						break;
+					case 'x':
+						if (fl_x) {
+							Report.error (new SourceReference (source_file, line, column + token_length_in_chars, line, column + token_length_in_chars), "modifier 'x' used more than once");
+						}
+						fl_x = true;
+						break;
+					}
+					current++;
+					token_length_in_chars++;
+				}
+				break;
+			default:
+				type = TokenType.REGEX_LITERAL;
+				token_length_in_chars = 0;
+				while (current < end && current[0] != '/') {
+					if (current[0] == '\\') {
+						current++;
+						token_length_in_chars++;
+						if (current >= end) {
+							break;
+						}
+
+						switch (current[0]) {
+						case '\'':
+						case '"':
+						case '\\':
+						case '/':
+						case '0':
+						case 'b':
+						case 'B':
+						case 'f':
+						case 'n':
+						case 'r':
+						case 't':
+						case 'a':
+						case 'A':
+						case 'p':
+						case 'P':
+						case 'e':
+						case 'd':
+						case 'D':
+						case 's':
+						case 'S':
+						case 'w':
+						case 'W':
+						case 'G':
+						case 'z':
+						case 'Z':
+							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);
+				}
+				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) {
 		if (in_template ()) {
 			return read_template_token (out token_begin, out token_end);
@@ -576,6 +722,8 @@ public class Vala.Scanner {
 			token_end.column = column - 1;
 
 			return TokenType.COMMA;
+		} else if (in_regex ()) {
+			return read_regex_token (out token_begin, out token_end);
 		}
 
 		space ();
@@ -602,6 +750,10 @@ public class Vala.Scanner {
 				type = TokenType.OPEN_TEMPLATE;
 				current += 2;
 				state_stack += State.TEMPLATE;
+			} else if (current < end - 1 && current[1] == '/') {
+				type = TokenType.OPEN_REGEX;
+				current += 2;
+				state_stack += State.REGEX;
 			} else {
 				token_begin.pos++; // @ is not part of the identifier
 				current++;
diff --git a/vala/valasemanticanalyzer.vala b/vala/valasemanticanalyzer.vala
index b18fe96..0b0eb71 100644
--- a/vala/valasemanticanalyzer.vala
+++ b/vala/valasemanticanalyzer.vala
@@ -101,6 +101,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
 	public DataType void_type = new VoidType ();
 	public DataType bool_type;
 	public DataType string_type;
+	public DataType regex_type;
 	public DataType uchar_type;
 	public DataType short_type;
 	public DataType ushort_type;
@@ -143,6 +144,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
 
 		bool_type = new BooleanType ((Struct) root_symbol.scope.lookup ("bool"));
 		string_type = new ObjectType ((Class) root_symbol.scope.lookup ("string"));
+		regex_type = new ObjectType ((Class) root_symbol.scope.lookup ("GLib").scope.lookup ("Regex"));
 
 		uchar_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uchar"));
 		short_type = new IntegerType ((Struct) root_symbol.scope.lookup ("short"));
@@ -687,6 +689,16 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
 		return false;
 	}
 
+	public override void visit_regex_literal (RegexLiteral re) {
+		try {
+			GLib.Regex tmp = new GLib.Regex (re.value);
+			if (tmp != null) {}
+		} catch (RegexError err) {
+			re.error = true;
+			Report.error (re.source_reference, "Invalid regular expression `%s'.".printf (re.value));
+		}
+	}
+
 	public void visit_member_initializer (MemberInitializer init, DataType type) {
 		init.symbol_reference = symbol_lookup_inherited (type.data_type, init.name);
 		if (!(init.symbol_reference is Field || init.symbol_reference is Property)) {
diff --git a/vala/valatokentype.vala b/vala/valatokentype.vala
index bfacde1..3aadf31 100644
--- a/vala/valatokentype.vala
+++ b/vala/valatokentype.vala
@@ -49,6 +49,7 @@ public enum Vala.TokenType {
 	CLOSE_BRACE,
 	CLOSE_BRACKET,
 	CLOSE_PARENS,
+	CLOSE_REGEX,
 	CLOSE_TEMPLATE,
 	COLON,
 	COMMA,
@@ -108,6 +109,7 @@ public enum Vala.TokenType {
 	OPEN_BRACE,
 	OPEN_BRACKET,
 	OPEN_PARENS,
+	OPEN_REGEX,
 	OPEN_TEMPLATE,
 	OVERRIDE,
 	OWNED,
@@ -119,6 +121,7 @@ public enum Vala.TokenType {
 	PUBLIC,
 	REAL_LITERAL,
 	REF,
+	REGEX_LITERAL,
 	REQUIRES,
 	RETURN,
 	SEMICOLON,
@@ -176,6 +179,7 @@ public enum Vala.TokenType {
 		case CLOSE_BRACE: return "`}'";
 		case CLOSE_BRACKET: return "`]'";
 		case CLOSE_PARENS: return "`)'";
+		case CLOSE_REGEX: return "`/'";
 		case COLON: return "`:'";
 		case COMMA: return "`,'";
 		case CONST: return "`const'";
@@ -231,6 +235,7 @@ public enum Vala.TokenType {
 		case OPEN_BRACE: return "`{'";
 		case OPEN_BRACKET: return "`['";
 		case OPEN_PARENS: return "`('";
+		case OPEN_REGEX: return "`@/'";
 		case OVERRIDE: return "`override'";
 		case OWNED: return "`owned'";
 		case PARAMS: return "`params'";
@@ -241,6 +246,7 @@ public enum Vala.TokenType {
 		case PUBLIC: return "`public'";
 		case REAL_LITERAL: return "real literal";
 		case REF: return "`ref'";
+		case REGEX_LITERAL: return "regex literal";
 		case REQUIRES: return "`requires'";
 		case RETURN: return "`return'";
 		case SEMICOLON: return "`;'";
