How to specify a custom attribute on a generic parameter in VB.NET? - vb.net

Textbook question, but I've done my googling, and I couldn't find anything.
Given a custom attribute named SomeAttribute, how do you do the following, in VB.NET?
void SomeMethod<[Some] T>()
{
}
I tried this:
Sub SomeMethod(<Some> Of T)()
End Sub
and
Sub SomeMethod(Of <Some> T)()
End Sub
But both fail to compile, with the error pointing at <Some>.

Given the silence here, and because I really needed an answer, I dug into the VB.NET Language Specification.
It never says explicitly whether this is supported or not, but it does have some formal grammar definitions which suggest that this isn't supported by VB.NET.
Specifically, section 9.2.1 defines the following productions for method declaration:
SubSignature ::= Sub Identifier [ TypeParameterList ]
[ OpenParenthesis [ ParameterList ] CloseParenthesis ]
In 9.2.5, parameters are defined as follows:
ParameterList ::=
Parameter |
ParameterList Comma Parameter
Parameter ::=
[ Attributes ] [ ParameterModifier+ ] ParameterIdentifier [ As TypeName ]
[ Equals ConstantExpression ]
And section 13.3 defines TypeParameterList:
TypeParameterList ::=
OpenParenthesis Of TypeParameters CloseParenthesis
TypeParameters ::=
TypeParameter |
TypeParameters Comma TypeParameter
TypeParameter ::=
[ VarianceModifier ] Identifier [ TypeParameterConstraints ]
VarianceModifier ::=
In | Out
TypeParameterConstraints ::=
As Constraint |
As OpenCurlyBrace ConstraintList CloseCurlyBrace
ConstraintList ::=
ConstraintList Comma Constraint |
Constraint
Constraint ::= TypeName | New | Structure | Class
Attributes make an appearance in the parameter list (and, for functions, in the return type), but the TypeParameterList is completely devoid of anything related to attributes.
So I'm going to go ahead and claim that VB.NET 10 (shipping with VS2012) does not support attributes on generic type parameters.

Related

How to find all methods available in Smalltalk and search by name?

In Smalltalk, is there a way to search for all methods available (of any object), say, that contain the word convert (case insensitive search), and also contain the word string? (the method name, not the source code)
In Smalltalk you have direct access to all classes, their methods and their source code, so you can go through them.
Pharo
Go over all the classes and then from each class select all methods that match your needs (or use the Finder tool).
Object withAllSubclasses flatCollect: [ :cls |
cls methods select: [ :method |
(method selector includesSubstring: 'convert' caseSensitive: false) and: [
(method selector includesSubstring: 'string' caseSensitive: false) ]
]
].
GNU Smalltalk
GST doesn't have as nice API, but it can be done also.
(Object withAllSubclasses collect: [ :cls |
cls methodDictionary ifNotNil: [ :dict |
dict values select: [ :method |
(method selector asLowercase indexOfSubCollection: 'convert' asLowercase) > 0 and: [
(method selector asLowercase indexOfSubCollection: 'string' asLowercase) > 0 ]
]
]
]) join
VisualWorks
(also Pharo and Squeak, and with ifNotNil: also GNU Smalltalk)
VW doesn't have #flatten, so it's implemented explicitly. For case-insensitive search #findSameAs:startingAt:wildcard: can also be used.
(Object withAllSubclasses collect: [ :cls |
cls methodDictionary values select: [ :method |
(method selector asLowercase findString: 'convert' asLowercase startingAt: 1) > 0 and: [
(method selector asLowercase findString: 'string' asLowercase startingAt: 1) > 0 ]
]
]) inject: #() into: [ :arr :each | arr, each ]
Dolphin
Dolphin seems to have different object model, see Leandro's answer below.
This may not work on all smalltalk dialects, but it works at least
with squeak and pharo (other smalltalks may have similar tools/classes)
SystemNavigation default browseAllSelect:[:e |
(e selector includesSubstring:'convert' caseSensitive:false)
and:[e selector includesSubstring:'string' caseSensitive:false]]
This is more a complement to the answer given by #Peter.
Be aware that in some dialects (e.g., Dolphin) the message #withAllSubclasses will only collect classes, and not metaclasses. Because of that the enumerations in #Peter's answer should add all metaclasses in an explicit way.
For instance,
selectors := OrderedCollection new.
Object withAllSubclasses do: [:class | | matching |
matching := class selectors select: [:s |
(s includesString: 'onvert') and: [s includesString: 'tring']].
selectors addAll: matching.
matching := class class selectors select: [:s |
(s includesString: 'onvert') and: [s includesString: 'tring']].
selectors addAll: matching].
^selectors
Note BTW that I've removed the first letter from both 'convert' and 'string' to cheaply prevent case mismatches.
Another difference with my code is that it iterates over the selectors of a class rather than over its methods.
UPDATE
Note that I've used two enumerations because we cannot do this:
class selectors , class class selectors select: [:s |
etc. The reason is that the selectors of a class come in a Set and these do not understand #,.
We could have done instead:
all := class selectors addAll: class class selectors; yourself.
all selectors select: [:s |
etc. (note the use of #yourself)
This is to add different flavor to the list above.
#Smalltalk/X
Object withAllSubclasses flatCollect: [ :cls |
cls methodDictionary values select: [ :method |
(method selector includesSubstring: 'convert' caseSensitive: false) and: [
(method selector includesSubstring: 'string' caseSensitive: false) ]
]
].

How to retrieve a function from a series of functions and call it

I'm trying to create a dispatcher of functions in Rebol 3, so that for each string the program receives there's an associated function to be called.
For example:
handlers: make map! [
"foo" foo-func
"bar" bar-func
]
where foo-func and bar-func are functions:
foo-func: func [ a b ] [ print "foo" ]
bar-func: func [ a b ] [ print "bar" ]
The idea is to select the function starting from the string, so:
f: select handlers "foo"
so that executing f is the same as executing foo-func and then call f with some arguments:
f param1 param2
I tried quoting the words in the map!, or using get-words but without success.
Using a get-word! at the console, without passing through a map! it works:
>> a: func [] [ print "Hello world!" ]
>> a
Hello world!
>> b: :a
>> b
Hello world!
Any help appreciated.
select handlers "foo" only get the word foo-func:
f: select handlers "foo"
probe f ;will get: foo-func
You need to get its content:
f: get f
f 1 2 ;will print "foo"
Or more compact:
f: get select handlers "foo"
It's better to actually have the reference to the function in the map, rather than a word that refers to the function. If you store a word then you have to make sure the word is bound to an object which has a reference to that function, like this:
handlers: object [
foo-func: func [ a b ] [ print "foo" ]
bar-func: func [ a b ] [ print "bar" ]
]
handler-names: map [
"foo" foo-func
"bar" bar-func
]
apply get in handlers select handler-names name args
But if you just have a reference to the function in your map, you don't have to do the double indirect, and your code looks like this:
handlers: map reduce [
"foo" func [ a b ] [ print "foo" ]
"bar" func [ a b ] [ print "bar" ]
]
apply select handlers name args
Cleaner code, and more efficient too. Or if you're careful enough, like this:
handlers/(name) a b
The path method above will also work if you want the code to do nothing if there is no handler - common in cases where you have optional handlers, such as in GUIs.
You can even have more than one reference to the same function with different key names. You don't have to assign functions to words, they're just values. You can also use the path method to collect the handlers in the first place, saving a reduce.
handlers: make map! 10 ; preallocate as many entries as you expect
handlers/("foo"): func [ a b ] [ print "foo" ]
handlers/("bar"): func [ a b ] [ print "bar" ]
handlers/("baz"): select handlers "bar" ; multiple references
That path syntax is just another way to call poke, but some prefer it. We have to put the string values in parens because of a (hopefully temporary) syntax conflict, but within those parens the string keys work. It's a faster alternative to do select or poke.
foo-func in your map is just an unevaluated word
>> type? select handlers "foo"
== word!
You should first create your functions and then reduce the block, you use for creating your handler map so
handlers: make map! reduce [
"foo" :foo-func
"bar" :bar-func
]
then you have functions inside your map
>> type? select handlers "foo"
== function!
Try:
....
f: do select handlers "foo"
....

ANTLR v.3- Use of syntactic predicate for lookahead

Still learning how to properly use ANTLR... Here's my problem.
Say you have a (subset) of an UML grammar and an ANTLR Lexer/Parser with the following rules :
// Parser Rules
model
: 'MODEL' IDENTIFIER list_dec
;
list_dec
: declaration*
;
declaration
: class_dec ';'
| association ';'
| generalization ';'
| aggregation ';'
;
class_dec
: 'CLASS' IDENTIFIER class_content
;
...
association
: 'RELATION' IDENTIFIER 'ROLES' two_roles
;
two_roles
: role ',' role
;
role
: 'CLASS' IDENTIFIER multiplicity
;
...
I would like the 'role' rule to only allow the IDENTIFIER token if it matches an existing class IDENTIFIER. In other words, if you are given an input file and you run the lexer/parser on it, then all the classes that are referenced (e.g. the IDENTIFIER in the association rule) should exist. The problem is that a class might not exist (yet) at runtime (it can be declared anywhere in the file). What is the best approach to this ?
Thanks in advance...
This is probably best done after parsing. The parser creates some sort of tree for you, and afterwards you walk the tree and collect information about declared classes, and walk it a second time to validate the role tree/rule.
Of course, some things could be done with a bit of custom code:
grammar G;
options {
...
}
#parser::members {
java.util.Set<String> declaredClasses = new java.util.HashSet<String>();
}
model
: 'MODEL' IDENTIFIER list_dec
;
...
class_dec
: 'CLASS' id=IDENTIFIER class_content
{
declaredClasses.add($id.text);
}
;
...
role
: 'CLASS' id=IDENTIFIER multiplicity
{
if(!declaredClasses.contains($id.text)) {
// warning or exception in here
}
}
;
...
EDIT
Or with custom methods:
#parser::members {
java.util.Set<String> declaredClasses = new java.util.HashSet<String>();
void addClass(String id) {
boolean added = declaredClasses.add(id);
if(!added) {
// 'id' was already present, do something, perhaps?
}
}
void checkClass(String id) {
if(!declaredClasses.contains(id)) {
// exception, error or warning?
}
}
}
...
class_dec
: 'CLASS' id=IDENTIFIER class_content {addClass($id.text);}
;
role
: 'CLASS' id=IDENTIFIER multiplicity {checkClass($id.text);}
;

How to get all items in from of Text from Ordered collection

simple question i got
|list string|
list:= #('ab' 'efghij' 'lmnopqrst'). "Ordered collection"
list do:[:each| "string with:each" i know this is not right how do i add items ].
I tried streams too it returned me this "an ordered collection('ab' 'efghij' 'lmnopqrst')"
All i need is a single Text that has
'abc efghij lmnopqrst '
In Pharo you can do
Character space join: list
If join: is not available and it should perform well then you can use a stream variant
String streamContents: [:stream|
list
do [:each| stream nextPutAll: each ]
separatedBy: [ stream nextPut: Character space ]
Object class has defined a #asString message that reads:
"Answer a string that represents the receiver."
So, you can do:
| aList aStringsList |
aList := #('ab' 'efghij' 'lmnopqrst'). "Array"
aStringsList := aList collect: [ :each | each asString ]
And aStringsList will be an Array of the Strings returned by the invocation of #asString in each of aList's members.
If you want to concatenate all of them in a single String, you can use the #inject:into: method of the collections instead of #collect::
aList inject: '' into: [ :text :each | text , each asString , ' ' ]
If you print that you'll get the 'ab efghij lmnopqrst ' you want :)

What SQLite column name can be/cannot be?

Is there any rule for the SQLite's column name?
Can it have characters like '/'?
Can it be UTF-8?
Can it have characters like '/'?
All examples are from SQlite 3.5.9 running on Linux.
If you surround the column name in double quotes, you can:
> CREATE TABLE test_forward ( /test_column INTEGER );
SQL error: near "/": syntax error
> CREATE TABLE test_forward ("/test_column" INTEGER );
> INSERT INTO test_forward("/test_column") VALUES (1);
> SELECT test_forward."/test_column" from test_forward;
1
That said, you probably shouldn't do this.
The following answer is based on the SQLite source code, mostly relying on the file parse.y (input for the lemon parser).
TL;DR:
The allowed series of characters for column and table names in CREATE TABLE statements are
'-escaped strings of any kind (even keywords)
Identifiers, which means
``` and "-escaped strings of any kind (even keywords)
a series of the MSB=1 8-bit ASCII characters or 7-bit ASCII characters with 1 in the following table that doesn't form a keyword:
Keyword INDEXED because it's non-standard
Keyword JOIN for reason that is unknown to me.
The allowed series of characters for result columns in a SELECT statement are
Either a string or an identifier as described above
All of the above if used as a column alias written after AS
Now to the exploration process itself
let's look at the syntax for CREATE TABLE columns
// The name of a column or table can be any of the following:
//
%type nm {Token}
nm(A) ::= id(X). {A = X;}
nm(A) ::= STRING(X). {A = X;}
nm(A) ::= JOIN_KW(X). {A = X;}
digging deeper, we find out that
// An IDENTIFIER can be a generic identifier, or one of several
// keywords. Any non-standard keyword can also be an identifier.
//
%type id {Token}
id(A) ::= ID(X). {A = X;}
id(A) ::= INDEXED(X). {A = X;}
"Generic identifier" sounds unfamiliar. A quick look into tokenize.c however brings forth the definition
/*
** The sqlite3KeywordCode function looks up an identifier to determine if
** it is a keyword. If it is a keyword, the token code of that keyword is
** returned. If the input is not a keyword, TK_ID is returned.
*/
/*
** If X is a character that can be used in an identifier then
** IdChar(X) will be true. Otherwise it is false.
**
** For ASCII, any character with the high-order bit set is
** allowed in an identifier. For 7-bit characters,
** sqlite3IsIdChar[X] must be 1.
**
** Ticket #1066. the SQL standard does not allow '$' in the
** middle of identfiers. But many SQL implementations do.
** SQLite will allow '$' in identifiers for compatibility.
** But the feature is undocumented.
*/
For a full map of identifier characters, please consult the tokenize.c.
It is still unclear what are the available names for a result-column (i. e. the column name or alias assigned in the SELECT statement). parse.y is again helpful here.
// An option "AS <id>" phrase that can follow one of the expressions that
// define the result set, or one of the tables in the FROM clause.
//
%type as {Token}
as(X) ::= AS nm(Y). {X = Y;}
as(X) ::= ids(Y). {X = Y;}
as(X) ::= . {X.n = 0;}
Except for placing "illegal" identifier names between double quotes "identifier#1", [ before and ] after works as well [identifire#2].
Example:
sqlite> create table a0.tt ([id#1] integer primary key, [id#2] text) without rowid;
sqlite> insert into tt values (1,'test for [x] id''s');
sqlite> select * from tt
...> ;
id#1|id#2
1|test for [x] id's
Valid field names are subject to the same rules as valid Table names. Checked this with SQlite administrator.
Only Alphanumeric characters and underline are allowed
The field name must begin with an alpha character or underline
Stick to these and no escaping is needed and it may avoid future problems.
This isn't a full answer but may help - these are the keywords from https://www.sqlite.org/lang_keywords.html converted to an array.
["ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ALWAYS", "ANALYZE", "AND", "AS",
"ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY", "CASCADE", "CASE", "CAST",
"CHECK", "COLLATE", "COLUMN", "COMMIT", "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT", "CURRENT_DATE",
"CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT",
"DO", "DROP", "EACH", "ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUDE", "EXCLUSIVE", "EXISTS",
"EXPLAIN", "FAIL", "FILTER", "FIRST", "FOLLOWING", "FOR", "FOREIGN", "FROM", "FULL", "GENERATED",
"GLOB", "GROUP", "GROUPS", "HAVING", "IF", "IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED",
"INITIALLY", "INNER", "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY",
"LAST", "LEFT", "LIKE", "LIMIT", "MATCH", "MATERIALIZED", "NATURAL", "NO", "NOT", "NOTHING",
"NOTNULL", "NULL", "NULLS", "OF", "OFFSET", "ON", "OR", "ORDER", "OTHERS", "OUTER",
"OVER", "PARTITION", "PLAN", "PRAGMA", "PRECEDING", "PRIMARY", "QUERY", "RAISE", "RANGE", "RECURSIVE",
"REFERENCES", "REGEXP", "REINDEX", "RELEASE", "RENAME", "REPLACE", "RESTRICT", "RETURNING", "RIGHT", "ROLLBACK",
"ROW", "ROWS", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP", "TEMPORARY", "THEN", "TIES",
"TO", "TRANSACTION", "TRIGGER", "UNBOUNDED", "UNION", "UNIQUE", "UPDATE", "USING", "VACUUM", "VALUES",
"VIEW", "VIRTUAL", "WHEN", "WHERE", "WINDOW", "WITH", "WITHOUT"]