[Vala] [PATCH] Implement alias functionality for 'using' directive
- From: Jim Peters <jim uazu net>
- To: vala-list gnome org
- Cc: jim uazu net
- Subject: [Vala] [PATCH] Implement alias functionality for 'using' directive
- Date: Sun, 3 Apr 2011 18:11:31 -0500
Required to disambiguate symbol references in case of matches from
multiple 'using' statements.
---
tests/namespaces/aliases.vala | 30 ++++++++++++
vala/valacodewriter.vala | 6 ++-
vala/valamemberaccess.vala | 26 +++++++----
vala/valaparser.vala | 17 ++++++-
vala/valasymbolresolver.vala | 51 ++++++++++++-------
vala/valausingdirective.vala | 105 ++++++++++++++++++++++++++++++++++++++--
6 files changed, 197 insertions(+), 38 deletions(-)
create mode 100644 tests/namespaces/aliases.vala
diff --git a/tests/namespaces/aliases.vala b/tests/namespaces/aliases.vala
new file mode 100644
index 0000000..277ae6e
--- /dev/null
+++ b/tests/namespaces/aliases.vala
@@ -0,0 +1,30 @@
+// Based on example of ambuiguity resolution from C# language
+// reference 9.3.2
+
+namespace N1 {
+ public class A {
+ public A() {}
+ public int test() { return 1234; }
+ }
+}
+namespace N2 {
+ public class A {
+ public A() {}
+ public int test() { return 5678; }
+ }
+}
+
+namespace N3 {
+ using N1;
+ using N2;
+ using A = N1.A;
+ public class B : A { // A means N1.A
+ public B() { base(); }
+ }
+}
+
+public static void main() {
+ assert(new N1.A().test() == 1234);
+ assert(new N2.A().test() == 5678);
+ assert(new N3.B().test() == 1234);
+}
diff --git a/vala/valacodewriter.vala b/vala/valacodewriter.vala
index b6e31c2..8cecbe8 100644
--- a/vala/valacodewriter.vala
+++ b/vala/valacodewriter.vala
@@ -124,7 +124,11 @@ public class Vala.CodeWriter : CodeVisitor {
public override void visit_using_directive (UsingDirective ns) {
if (type == CodeWriterType.FAST) {
- write_string ("using %s;\n".printf (ns.namespace_symbol.name));
+ if (ns.alias != null) {
+ write_string ("using %s = %s;\n".printf (ns.alias, ns.symbol.name));
+ } else {
+ write_string ("using %s;\n".printf (ns.symbol.name));
+ }
}
}
diff --git a/vala/valamemberaccess.vala b/vala/valamemberaccess.vala
index dfb0eb6..9026bb3 100644
--- a/vala/valamemberaccess.vala
+++ b/vala/valamemberaccess.vala
@@ -277,17 +277,25 @@ public class Vala.MemberAccess : Expression {
}
if (symbol_reference == null && source_reference != null) {
- foreach (UsingDirective ns in source_reference.using_directives) {
- var local_sym = ns.namespace_symbol.scope.lookup (member_name);
- if (local_sym != null) {
- if (symbol_reference != null && symbol_reference !=
local_sym) {
- error = true;
- Report.error (source_reference, "`%s' is an ambiguous
reference between `%s' and `%s'".printf (member_name, symbol_reference.get_full_name (),
local_sym.get_full_name ()));
- return false;
- }
- symbol_reference = local_sym;
+ var scanner = new UsingDirective.Scanner (member_name, false);
+ foreach (UsingDirective ud in source_reference.using_directives) {
+ scanner.try_match (ud);
+ }
+ if (scanner.ambiguous) {
+ // Rescan to pick up errors
+ scanner = new UsingDirective.Scanner (member_name, true);
+ foreach (UsingDirective ud in source_reference.using_directives) {
+ scanner.try_match (ud);
+ }
+ if (scanner.alias_match) {
+ Report.error (source_reference, "`%s' is an ambiguous
reference to duplicate aliases:%s".printf (member_name, scanner.error_string.str));
+ } else {
+ Report.error (source_reference, "`%s' is an ambiguous
reference; add one of these aliases to resolve:%s".printf (member_name, scanner.error_string.str));
}
+ error = true;
+ return false;
}
+ symbol_reference = scanner.match;
}
} else {
if (inner.error) {
diff --git a/vala/valaparser.vala b/vala/valaparser.vala
index 136bf01..f042bb9 100644
--- a/vala/valaparser.vala
+++ b/vala/valaparser.vala
@@ -2383,9 +2383,20 @@ public class Vala.Parser : CodeVisitor {
do {
var begin = get_location ();
var sym = parse_symbol_name ();
- var ns_ref = new UsingDirective (sym, get_src (begin));
- scanner.source_file.add_using_directive (ns_ref);
- ns.add_using_directive (ns_ref);
+ if (accept (TokenType.ASSIGN)) {
+ var alias_sym = parse_symbol_name ();
+ if (sym.inner != null) {
+ Report.error (get_src (begin), "alias name must be a simple
identifier");
+ } else {
+ var ud = new UsingDirective.with_alias (sym.name, alias_sym,
get_src (begin));
+ scanner.source_file.add_using_directive (ud);
+ ns.add_using_directive (ud);
+ }
+ } else {
+ var ns_ref = new UsingDirective (sym, get_src (begin));
+ scanner.source_file.add_using_directive (ns_ref);
+ ns.add_using_directive (ns_ref);
+ }
} while (accept (TokenType.COMMA));
expect (TokenType.SEMICOLON);
}
diff --git a/vala/valasymbolresolver.vala b/vala/valasymbolresolver.vala
index 3f874fa..69a84bb 100644
--- a/vala/valasymbolresolver.vala
+++ b/vala/valasymbolresolver.vala
@@ -215,12 +215,18 @@ public class Vala.SymbolResolver : CodeVisitor {
}
public override void visit_using_directive (UsingDirective ns) {
- var unresolved_symbol = ns.namespace_symbol as UnresolvedSymbol;
+ var unresolved_symbol = ns.symbol as UnresolvedSymbol;
if (unresolved_symbol != null) {
- ns.namespace_symbol = resolve_symbol (unresolved_symbol);
- if (!(ns.namespace_symbol is Namespace)) {
+ ns.symbol = resolve_symbol (unresolved_symbol);
+ if (ns.alias == null) {
+ if (!(ns.symbol is Namespace)) {
+ ns.error = true;
+ Report.error (ns.source_reference, "The namespace name `%s' could not
be found".printf (unresolved_symbol.to_string ()));
+ return;
+ }
+ } else if (ns.symbol == null) {
ns.error = true;
- Report.error (ns.source_reference, "The namespace name `%s' could not be
found".printf (unresolved_symbol.to_string ()));
+ Report.error (ns.source_reference, "The symbol `%s' could not be
found".printf (unresolved_symbol.to_string ()));
return;
}
}
@@ -243,28 +249,35 @@ public class Vala.SymbolResolver : CodeVisitor {
scope = scope.parent_scope;
}
+
if (sym == null && unresolved_symbol.source_reference != null) {
+ var scanner = new UsingDirective.Scanner(unresolved_symbol.name, false);
foreach (UsingDirective ns in
unresolved_symbol.source_reference.using_directives) {
- if (ns.error || ns.namespace_symbol is UnresolvedSymbol) {
+ Symbol local_sym = ns.symbol;
+ if (ns.error || !(local_sym is Namespace || local_sym is TypeSymbol
|| local_sym is TypeParameter)) {
continue;
}
-
- var local_sym = ns.namespace_symbol.scope.lookup
(unresolved_symbol.name);
-
- // only look for types and type containers
- if (!(local_sym is Namespace || local_sym is TypeSymbol || sym is
TypeParameter)) {
- local_sym = null;
- }
-
- if (local_sym != null) {
- if (sym != null && sym != local_sym) {
- unresolved_symbol.error = true;
- Report.error (unresolved_symbol.source_reference,
"`%s' is an ambiguous reference between `%s' and `%s'".printf (unresolved_symbol.name, sym.get_full_name (),
local_sym.get_full_name ()));
- return null;
+ scanner.try_match (ns);
+ }
+ if (scanner.ambiguous) {
+ // Rescan to build up error string
+ scanner = new UsingDirective.Scanner(unresolved_symbol.name, true);
+ foreach (UsingDirective ns in
unresolved_symbol.source_reference.using_directives) {
+ Symbol local_sym = ns.symbol;
+ if (ns.error || !(local_sym is Namespace || local_sym is
TypeSymbol || local_sym is TypeParameter)) {
+ continue;
}
- sym = local_sym;
+ scanner.try_match (ns);
+ }
+ if (scanner.alias_match) {
+ Report.error (unresolved_symbol.source_reference, "`%s' is an
ambiguous reference to duplicate aliases:%s".printf (unresolved_symbol.name, scanner.error_string.str));
+ } else {
+ Report.error (unresolved_symbol.source_reference, "`%s' is an
ambiguous reference; add one of these aliases to resolve:%s".printf (unresolved_symbol.name,
scanner.error_string.str));
}
+ unresolved_symbol.error = true;
+ return null;
}
+ sym = scanner.match;
}
return sym;
} else {
diff --git a/vala/valausingdirective.vala b/vala/valausingdirective.vala
index 9fb0766..9260cbc 100644
--- a/vala/valausingdirective.vala
+++ b/vala/valausingdirective.vala
@@ -27,22 +27,115 @@ using GLib;
*/
public class Vala.UsingDirective : CodeNode {
/**
- * The symbol of the namespace this using directive is referring to.
+ * The symbol that this using directive is referring to, either a
+ * namespace (alias == null) or the aliased symbol (alias != null).
*/
- public Symbol namespace_symbol { get; set; }
+ public Symbol symbol { get; set; }
/**
- * Creates a new using directive.
+ * Alias name, for alias using directives, or null if this is a
+ * namespace using directive.
+ */
+ public string? alias { get; set; }
+
+ /**
+ * Lookup a symbol according to this directive.
+ * @param name Name to lookup
+ * @return Symbol, or null if not found
+ */
+ public Symbol? lookup (string name) {
+ if (alias == null)
+ return symbol.scope.lookup (name);
+ if (name == alias)
+ return symbol;
+ return null;
+ }
+
+ /**
+ * Creates a new namespace using directive.
+ *
+ * @param symbol namespace symbol
+ * @return newly created using directive
+ */
+ public UsingDirective (Symbol symbol, SourceReference? source_reference = null) {
+ this.symbol = symbol;
+ this.source_reference = source_reference;
+ }
+
+ /**
+ * Creates a new alias using directive.
*
- * @param namespace_symbol namespace symbol
+ * @param alias alias name
+ * @param symbol symbol referenced by alias
* @return newly created using directive
*/
- public UsingDirective (Symbol namespace_symbol, SourceReference? source_reference = null) {
- this.namespace_symbol = namespace_symbol;
+ public UsingDirective.with_alias (string alias, Symbol symbol, SourceReference? source_reference =
null) {
+ this.alias = alias;
+ this.symbol = symbol;
this.source_reference = source_reference;
}
public override void accept (CodeVisitor visitor) {
visitor.visit_using_directive (this);
}
+
+ /**
+ * Place to accumulate results of scanning a number of
+ * UsingDirectives looking for matches.
+ */
+ public static class Scanner {
+ public string name;
+ public Symbol? match;
+ public bool alias_match;
+ public bool ambiguous;
+ public bool for_errors;
+ public StringBuilder error_string;
+
+ /**
+ * Constructor.
+ * @param name symbol name to look up
+ * @param for_errors true to accumulate messages for ambiguous matches in error_string
+ */
+ public Scanner(string name, bool for_errors) {
+ this.name = name;
+ this.for_errors = for_errors;
+ if (for_errors)
+ error_string = new StringBuilder();
+ }
+
+ /**
+ * Try a match with the given UsingDirective and accumulate
+ * results in the Scanner. An alias match always overrides any
+ * ambiguity due to multiple namespace matches (see C# language
+ * spec 9.3.2).
+ */
+ public void try_match(UsingDirective ud) {
+ Symbol? sym = ud.lookup(name);
+ if (sym == null)
+ return;
+
+ bool ud_is_alias = ud.alias != null;
+ if (sym == match && alias_match == ud_is_alias)
+ return;
+
+ // We have a new match to process
+ if (for_errors) {
+ if (ud_is_alias && !alias_match)
+ error_string = new StringBuilder();
+ if (ud_is_alias)
+ error_string.append_printf("\n %s", sym.get_full_name());
+ else
+ error_string.append_printf("\n using %s = %s;", name,
sym.get_full_name());
+ }
+ if (match == null || (ud_is_alias && !alias_match)) {
+ match = sym;
+ alias_match = ud_is_alias;
+ ambiguous = false;
+ } else if (!ud_is_alias && alias_match) {
+ // Alias overrides using
+ } else {
+ ambiguous = true;
+ }
+ }
+ }
}
--
1.7.2.5
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]