|
I took that from "at some point you'll have to debug and maintain it", along with your general focus on gotos being "yours" when they are generated by Visual FA - you clearly relate to the generated Visual FA code as something you have a right to (modify).
If you really are as ignorant of the code generated by Visual FA as you are of the code generated by the gcc compiler, why do you post it here?
Religious freedom is the freedom to say that two plus two make five.
|
|
|
|
|
Except with generated code. The input specification is what is maintained. Like I said.
As long as Visual FA can produce code given an input specification, there is no need to maintain the generated code.
I'm not sure how else I can put it, to be honest.
If you really are as ignorant of the code generated by Visual FA as you are of the code generated by the gcc compiler
You're reading all kinds of things into what I wrote. Don't. I didn't write that I was ignorant about the GCC compiler's generated code. You assumed that. I didn't write that I was ignorant about the code I wrote the god damned generator for, so don't assume I am.
I wrote exactly what I meant, and your assumptions are leading you to argue with me about them. They're yours. Don't make them mine.
In other words, it's not my job to unravel your assumptions for you, so maybe assume less about what I wrote.
PS: The title of my OP a reference to a famous line in a movie called Full Metal Jacket.
"There are many rifles, but this one is mine"
That is the only reason I referred to the gotos as mine - that and I wrote the code generator that produces them.
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
modified 14-May-24 20:57pm.
|
|
|
|
|
"As long as Visual FA can produce code given an input specification, there is no need to maintain the generated code."
But you state that you do see a need to maintain the Visual FA generated code. So you do not trust your generator/compiler.
Why do you base this entire thread on Visual FA generated code? Because you relate closely to it, and consider the compiled code "yours" ("There are many gotos, but these ones are mine"). If you honestly have no intention of touching the generated code (any more than you touch gcc generated relocatable code), then why do you bring up properties of the generated code?
Make up your mind: Either, you are not going to maintain the code at the level generated by Visual FA, and then the use of gotos are as irrelevant (and not "yours") as a conditional or unconditional jump in binary code. OR, you think that you are more clever than the compiler, and can improve the result by random poking the generated code.
If you at all intend to follow the second alternative, modifying the code generated by Visual FA, then you are in the the "Random Poke" group. If you are not, never intending to modify the compilation result, then the compiled code is not subject to discussion. And the gotos are no more "yours" than the binary jump instructions in any binary compiled module.
Religious freedom is the freedom to say that two plus two make five.
|
|
|
|
|
trønderen wrote: But you state that you do see a need to maintain the Visual FA generated code. So you do not trust your generator/compiler.
That would be fair, if I ever said that. However, I did not say that.
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|
|
|
|
|
honey the codewitch wrote: That would be fair, if I ever said that. However, I did not say that. You only said that "the maintenance of that is moved to the generated code's input specification - in other words, whatever document or resource it uses to generate the code from. THAT is what needs to be maintained" and your subject line clearly suggests that you expect to have full control over the gotos produced by your Visual FA generator.
Close your eyes for the gotos generated by your Visual FA compiler, as well as the conditional and unconditional jump instructions generated by any other compiler you use! You cannot both claim that the gotos are "your" gotos, and at the same time that you are oblivious to them!
Religious freedom is the freedom to say that two plus two make five.
|
|
|
|
|
It's not my fault you're fixated on the title of my post. You probably don't understand it because you never saw Full Metal Jacket, unlike most Americans here.
I said that the maintenance is moved to the input spec, and the generated code NEED NOT BE MAINTAINED.
I don't know why you refuse to understand that simple concept when I say it, but when you say it you seem to have no similar misunderstanding.
If I didn't know better I'd think I was being trolled.
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|
|
|
|
|
trønderen wrote: our subject line clearly suggests that you expect to have full control over the gotos produced by your Visual FA generator.
Wrong. You are simply wrong. My subject line does not suggest any such thing. You assumed that. You are trying to read my mind. You suck at it, as I tried to gently imply last time you did it.
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|
|
|
|
|
I'm not sure why it wouldn't be pretty straightforward to [TestCase()] for each of the branching?
I don't think this code is very cyclomatically complex?
But yeah when you say table driven state machine I'm pretty sure that's where my head is too if you're basically talking a direct map of the case statements to data.
|
|
|
|
|
There is one issue with that.
The compiled ones can be augmented in a way that the table driven ones cannot.
For example, I wrote an embedded JSON pull parser in C++. I used compiled DFA code, and then I parsed floats, ints, and bools out of the stream *as* I was lexing, making the API both easier to use and marginally more performant because you didn't have to get the string back and then reexamine it in order to call atoi() or whatever. It was a simple little surgery on the generated code, with excellent results.
I admit this isn't the most common case out there, but I have used this technique several times.
Edited to add: It's also easier in practice to debug and step through a generated lexer than it is a table driven lexer. And with my Visual FA project, it produces images of directed graphs that map one to one to the labels/jump points in the code. q0: maps to the state q0 in the graph. It makes it really easy to see what it's doing, in terms of documenting it.
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|
|
|
|
|
Wow, too much with the GOTO's already - they put it in the language for a reason. It will always be in certain languages for the same reasons. We are just debating normal human failings that have nothing to do with GOTO.
We are engineers, we should know that ALL humans a fallible and can make a mess of anything.
Careless use of GOTO helps us make a mess faster, careful use of GOTO makes our code run faster.
|
|
|
|
|
Fair enough. Implement a DFA state machine without gotos, achieving comparable performance. I'll wait.
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|
|
|
|
|
I was agreeing with you - my point was, nobody should care if you are using GOTO's, they should only care if you are making a mess with them.
I have never seen you produce a mess, quite the opposite in fact.
|
|
|
|
|
I clearly misunderstood you. Sorry.
And thanks!
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|
|
|
|
|
honey the codewitch wrote: I clearly misunderstood you. Sorry.
Np, irony is easily missed in short messaging!
|
|
|
|
|
My CP sig used to be something like, "If you think GOTO's are bad try coding in Assembler without using JMP."
A programmer I once worked with had the opinion that subroutines (that's what methods were called back then) should only have one exit point. Since you couldn't RETURN from where the routine might need to exit and you couldn't use GOTO his longer methods tended to have dozens of embedded IF blocks. Yuck.
There are no solutions, only trade-offs. - Thomas Sowell
A day can really slip by when you're deliberately avoiding what you're supposed to do. - Calvin (Bill Watterson, Calvin & Hobbes)
|
|
|
|
|
When all you have is a hammer, every problem looks like a nail.
Goto's have their place, the problem is when they're being abused because all the developer sees is nails and can't come up with a better solution.
That being said, I'm looking glancing at your code and clearly I'm in no position to criticize.
|
|
|
|
|
Try writing Assembly with out them (the fabled JMP!). They are a tool that get misused (kinda like the powered screw driver).
|
|
|
|
|
Code runs in LinqPad.
Code runs in LinqPad.
This should be significantly faster than your original code because it speeds up the conditionals by using pattern matching instead of overloadable operators. Also, the local functions can be in-lined, meaning they will be executed in place, which is even more efficient than the `Goto` statements. And now it's not pure spaghetti.
string json = """
{
"test": 0,
"data": "value"
}
""";
JsonStringRunner runner = new();
List<FAMatch> matches = new();
FAMatch current = default;
Stopwatch sw = new();
sw.Start();
do{
current = runner.GetMatch(json);
matches.Add(current);
} while(!runner.isDone);
sw.Stop();
matches.Dump();
sw.Dump();
internal record struct FAMatch(int token, string match, int position, int length, int column)
{
internal static FAMatch Create(int token, string match, int position, int length, int column)
=> new(token, match, position, length, column);
}
internal abstract class FAStringRunner
{
protected int position = -1, line = 0, column = 0;
internal bool isDone = false;
}
internal sealed partial class JsonStringRunner : FAStringRunner
{
private void Advance(string s, ref int ch, ref int len, bool flag)
{
ch = s[position];
position++;
len++;
isDone = !(position < s.Length);
}
private FAMatch NextMatchImpl(string s)
{
int ch;
int len;
int l;
int c;
ch = -1;
len = 0;
if ((this.position is -1))
{
this.position = 0;
}
int p = this.position;
l = this.line;
c = this.column;
this.Advance(s, ref ch, ref len, true);
switch (ch)
{
case 9 or 10 or 13 or 32:
if(ch is 10 or 13){
l = line++;
}
return q1();
case 34:
return q2();
case 44:
return q9();
case 45:
return q10();
case 48:
return q11();
case >= 49 and <= 57:
return q17();
case 58:
return q18();
case 91:
return q19();
case 93:
return q20();
case 102:
return q21();
case 110:
return q26();
case 116:
return q30();
case 123:
return q32();
case 125:
return q33();
}
return errorout();
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q1()
{
while (ch is 9 or 10 or 13 or 32)
{
this.Advance(s, ref ch, ref len, false);
}
return FAMatch.Create(10, s.Substring(p, len), p, l, c);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q2()
{
while (ch is (((((>= 0)
and (<= 9))
or ((>= 11)
and (<= 33)))
or ((>= 35)
and (<= 91)))
or ((>= 93)
and (<= 1114111))))
{
this.Advance(s, ref ch, ref len, false);
}
if ((ch is 34))
{
this.Advance(s, ref ch, ref len, false);
return q3();
}
if ((ch is 92))
{
this.Advance(s, ref ch, ref len, false);
return q4();
}
return default;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q3() => FAMatch.Create(9, s.Substring(p, len), p, l, c);
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q4()
{
if (ch is 34 or 47
or 92
or 98
or 102
or 110
or 114
or 116)
{
this.Advance(s, ref ch, ref len, false);
return q2();
}
if (ch is 117)
{
this.Advance(s, ref ch, ref len, false);
return q5();
}
return errorout();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q5()
{
if (ch is (>= 48 and <= 57) or (>= 65 and <= 70)
or (>= 97 and <= 102))
{
this.Advance(s, ref ch, ref len, false);
return q6();
}
return errorout();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q6()
{
if (ch is (>= 48 and <= 57) or (>= 65 and <= 70)
or (>= 97 and <= 102))
{
this.Advance(s, ref ch, ref len, false);
return q7();
}
return errorout();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q7() => FAMatch.Create(7, s.Substring(p, len), p, l, c);
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q9() => FAMatch.Create(9, s.Substring(p, len), p, l, c);
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q10() => FAMatch.Create(10, s.Substring(p, len), p, l, c);
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q11() => FAMatch.Create(11, s.Substring(p, len), p, l, c);
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q17() => FAMatch.Create(17, s.Substring(p, len), p, l, c);
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q18() => FAMatch.Create(18, s.Substring(p, len), p, l, c);
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q19() => FAMatch.Create(19, s.Substring(p, len), p, l, c);
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q20() => FAMatch.Create(20, s.Substring(p, len), p, l, c);
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q21() => FAMatch.Create(21, s.Substring(p, len), p, l, c);
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q26() => FAMatch.Create(26, s.Substring(p, len), p, l, c);
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q30() => FAMatch.Create(110, s.Substring(p, len), p, l, c);
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q32() => FAMatch.Create(123, s.Substring(p, len), p, l, c);
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch q33() => FAMatch.Create(125, s.Substring(p, len), p, l, c);
[MethodImpl(MethodImplOptions.AggressiveInlining)] FAMatch errorout() => FAMatch.Create(0, s.Substring(p, len), p, l, c);
}
public FAMatch GetMatch(string s) => NextMatchImpl(s);
}
|
|
|
|
|
I'll have to try a variation of this, but what you produced won't function due to the returns. How are you going to loop?
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|
|
|
|
|
Without the full code I didn't know what the logic inside of the various labelled location did, so I simply returned the current substring as a FAMatch. Your method dumps out as an FAMatch so I defaulted to that behavior. The point is that inlined local methods are going to be just as fast as gotos and the pattern matching is much more efficient.
|
|
|
|
|
Sure, I understand. I did say it was a DFA state machine implementation but unless you're a total FA nerd like I am that probably doesn't mean anything.
I'm very curious about the inlined local method and pattern matching approach, particularly the IL it generates, because I don't understand how it would be faster than the IL my code produces - particularly my direct compiler which can short circuit the if tests because the comparisons are in sorted order.
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|
|
|
|
|
Seems likely that would be faster with an array look up versus those sequential ifs.
if (match[ch])
...
honey the codewitch wrote: if (((((ch >= 9)
&& (ch <= 10))
|| (ch == 13))
|| (ch == 32))) {
Seems unlikely that that would be better than
(ch == 9) || (ch == 10) || (ch == 13) || (ch == 32))
|
|
|
|
|
What's funny is my table driven code does exactly that. Sometimes I get different results depending on the lexer complexity, but for simple lexers at least the compiled versions run slightly faster.
With large lexers the table method starts to outstrip it.
I should note, the lexer size has nothing to do with the number of comparisons in those ifs - but rather in essense the number of ifs - really the number of goto labels.
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|
|
|
|
|
We're running a heavy process that's causing some of our servers to creak a little. Sorry about any slowness you might experience today. It'll be over soon, I hope.
cheers
Chris Maunder
|
|
|
|
|
No problem, I had to attend a funeral today
(no, not the funeral of the CodeProject website)
|
|
|
|
|