Firstly, in "DeadMG++" you can gain the string by another route anyway, like loading it from a file, reading it from a console, or even from a GUI, at compile-time, which avoids the escaping problem of C++.
Secondly, I really, really think you're doing it wrong with things like regexes. String literals are not designed to have complex semantics expressed in them. Regular expressions could easily have a more functional API, which would be vastly clearer to read for everyone.
For example:
alphabetical := Regex.Range("a", "z") || Regex.Range("A", "Z")
alphanumeric := alphabetical || Regex.Range("0", "9");
identifier := ("_" || alphabetical)
&& Regex.ZeroOrMore("_" || alphanumeric);
match := identifier(true)(input);
// throws if non-matching, returns match else.
if_matched = identifier(false)(input, match);
// Doesn't throw, returns bool, or puts match into "match"
They exist Under The Hood™ as FSMs anyway- this is just clearer. Secondly, this API doesn't violate DRY- whereas if you write [a-zA-Z_][a-zA-Z0-9_]*, then you specified the same alphabetical-or-underscore twice.
Of course, the above aren't especially internationalizable, which is a definite flaw. I'd have to write in Letter, Number etc as Standard.
Another thing that I've been re-evaluating is the effectiveness of compile-time/runtime deduction. I've been indecisive about how effective this can really be, but I've decided that ultimately, I should give it a shot. Partly because I feel that most code won't need to operate at both times quite like the allocator does. I am thinking right now of a kind of "supported" induction.
// usings
type AllocatorType {
// Default is private non-static non-virtual runtime
compiletime {
HashMap(int, VariableReference) allocators;
Default := HeapAllocator; // "malloc", essentially
GetAllocator(Size) {
if (Size > 52) // arbitrary for now
return Default;
if (allocators.Find(Size) == allocators.End())
allocators[Size] = PoolAllocator(Size);
return allocators[Size];
}
}
public { // runtime default
Allocate(Type)() {
VariableReference Allocator := GetAllocator(Type.Size());
// calls a compile-time function
return Allocator.Allocate(Type);
// calls a run-time function- and returns
}
Allocate(Size)() {
VariableReference Allocator := GetAllocator(Size);
return Allocator.Allocate(Size);
}
// stuff
}
}
Having the grammar much closer to completion is really allowing me to get a better feel for coding like this. How about D's invariants? Now, the API for editing functions, I'm not too sure of right now, but worst comes to worst, I'll just directly replace it. Right now, I think that, say, adding a local variable with no name that calls the function in constructor and destructor as the first statement ought to do the trick.
// usings
Invariant(Type, Function) {
invariant_type := type {
Type.Pointer() This;
type(this) {
This = this;
Function(This);
}
~type() {
Function(This);
}
};
ForEach(Type.MemberFunction, [&](MemberFunc) {
this := MemberFunc.Arguments["this"];
MemberFunc.Statements.PushFront(Statement.ConstructedVariableDefinition("", invariant_type, this));
});
return Type;
}
This brings me back to a slight grammatical change. Previously, type definitions and type literals were different things. Now, I've decided to "promote" type definitions to full expressions. This means that you can do something like
type MyType {
// .... some stuff here
}.Invariant([](this) {
assert(i != 0); // semantics of "assert" TBD
});
Now, all I might have to do is create a simple "action" function which will take expressions or functions and execute them immediately...
type MyType {
int i = 0;
public {
SetI(value) { i = value; }
GetI() { return value; }
}.Actions(
[&] {
MyType somevar;
assert(somevar.GetI() == 0);
assert(somevar.GetI() == somevar.i);
somevar.SetI(5);
assert(somevar.GetI() == 5);
}
});
Gosh, it's like, a unit test or something! Not that I have ever written a single unit test in my entire life. But here's to hoping that arbitrary compile-time per-type actions can be used to implement unit tests.
No comments:
Post a Comment