blob: 180c53a8a569948bca00bd4e4d9bec7a0a369d59 [file] [log] [blame]
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of dart._internal;
/**
* Implementation of [core.Symbol]. This class uses the same name as
* a core class so a user can't tell the difference.
*
* The purpose of this class is to hide [_name] from user code, but
* make it accessible to Dart platform code via the static method
* [getName].
*/
class Symbol implements core.Symbol {
final String _name;
/**
* Source of RegExp matching Dart reserved words.
*
* Reserved words are not allowed as identifiers.
*/
static const String reservedWordRE =
r'(?:assert|break|c(?:a(?:se|tch)|lass|on(?:st|tinue))|d(?:efault|o)|'
r'e(?:lse|num|xtends)|f(?:alse|inal(?:ly)?|or)|i[fns]|n(?:ew|ull)|'
r'ret(?:hrow|urn)|s(?:uper|witch)|t(?:h(?:is|row)|r(?:ue|y))|'
r'v(?:ar|oid)|w(?:hile|ith))';
/**
* Source of RegExp matching any public identifier.
*
* A public identifier is a valid identifier (not a reserved word)
* that doesn't start with '_'.
*/
static const String publicIdentifierRE =
r'(?!' '$reservedWordRE' r'\b(?!\$))[a-zA-Z$][\w$]*';
/**
* Source of RegExp matching any identifier.
*
* It matches identifiers but not reserved words. The identifiers
* may start with '_'.
*/
static const String identifierRE =
r'(?!' '$reservedWordRE' r'\b(?!\$))[a-zA-Z$_][\w$]*';
/**
* Source of RegExp matching a declarable operator names.
*
* The operators that can be declared using `operator` declarations are
* also the only ones allowed as symbols. The name of the operators is
* the same as the operator itself except for unary minus, where the name
* is "unary-".
*/
static const String operatorRE =
r'(?:[\-+*/%&|^]|\[\]=?|==|~/?|<[<=]?|>[>=]?|unary-)';
// Grammar if symbols:
// symbol ::= qualifiedName | <empty>
// qualifiedName ::= publicIdentifier '.' qualifiedName | name
// name ::= publicIdentifier
// | publicIdentifier '='
// | operator
// where publicIdentifier is any valid identifier (not a reserved word)
// that isn't private (doesn't start with '_').
//
// Railroad diagram of the accepted grammar:
//
// /----------------\
// | |
// | /-[.]-/ /-[=]-\
// \ / / \
// -------[id]------------------------->
// \ /
// \------[operator]---/
// \ /
// \------------/
//
/**
* RegExp that validates a non-empty non-private symbol.
*
* The empty symbol is handled before this regexp is used, and is not
* accepted.
*/
static final RegExp publicSymbolPattern = new RegExp(
'^(?:$operatorRE\$|$publicIdentifierRE(?:=?\$|[.](?!\$)))+?\$');
// The grammar of symbols that may be private is the same as for public
// symbols, except that "publicIdentifier" is replaced by "identifier",
// which matches any identifier.
/**
* RegExp that validates a non-empty symbol.
*
* Private symbols are accepted.
*
* The empty symbol is handled before this regexp is used, and is not
* accepted.
*/
static final RegExp symbolPattern =
new RegExp('^(?:$operatorRE\$|$identifierRE(?:=?\$|[.](?!\$)))+?\$');
const Symbol(String name) : this._name = name;
/**
* Platform-private method used by the mirror system to create
* otherwise invalid names.
*/
const Symbol.unvalidated(this._name);
// This is called by dart2js.
Symbol.validated(String name) : this._name = validatePublicSymbol(name);
bool operator ==(other) => other is Symbol && _name == other._name;
int get hashCode {
const arbitraryPrime = 664597;
return 0x1fffffff & (arbitraryPrime * _name.hashCode);
}
toString() => 'Symbol("${getUnmangledName(this)}")';
/// Platform-private accessor which cannot be called from user libraries.
static String getName(Symbol symbol) => symbol._name;
static String validatePublicSymbol(String name) {
if (name.isEmpty || publicSymbolPattern.hasMatch(name)) return name;
if (name.startsWith('_')) {
// There may be other private parts in a qualified name than the first
// one, but this is a common case that deserves a specific error
// message.
throw new ArgumentError('"$name" is a private identifier');
}
throw new ArgumentError('"$name" is not a valid (qualified) symbol name');
}
/**
* Checks whether name is a valid symbol name.
*
* This test allows both private and non-private symbols.
*/
static bool isValidSymbol(String name) {
return (name.isEmpty || symbolPattern.hasMatch(name));
}
static getUnmangledName(Symbol symbol) {
String string = Symbol.getName(symbol);
// Remove closurization hash mark
// #foo -> foo
if (string.startsWith('#')) {
string = string.substring(1);
}
// get:foo -> foo
// set:foo -> foo=
// get:_foo@xxx -> _foo
// set:_foo@xxx -> _foo=
// Class._constructor@xxx -> Class._constructor
// _Class@xxx._constructor@xxx -> _Class._constructor
// lib._S@xxx with lib._M1@xxx, lib._M2@xxx -> lib._S with lib._M1, lib._M2
StringBuffer result = new StringBuffer();
bool add_setter_suffix = false;
var pos = 0;
if (string.length >= 4 && string[3] == ':') {
// Drop 'get:' or 'set:' prefix.
pos = 4;
if (string[0] == 's') {
add_setter_suffix = true;
}
}
// Skip everything between AT and PERIOD, SPACE, COMMA or END
bool skip = false;
for (; pos < string.length; pos++) {
var char = string[pos];
if (char == '@') {
skip = true;
} else if (char == '.' || char == ' ' || char == ',') {
skip = false;
}
if (!skip) {
result.write(char);
}
}
if (add_setter_suffix) {
result.write('=');
}
return result.toString();
}
}