There are many gotos, but these ones are mine
-
Generally you are right, usually the codewitch works on small embedded systems where performances and code footprint can be extremely stringent. I found myself doing things I would have abhorred only a few scant years ago...
GCS/GE d--(d) s-/+ a C+++ U+++ P-- L+@ E-- W+++ N+ o+ K- w+++ O? M-- V? PS+ PE Y+ PGP t+ 5? X R+++ tv-- b+(+++) DI+++ D++ G e++ h--- r+++ y+++* Weapons extension: ma- k++ F+2 X The shortest horror story: On Error Resume Next
Table driven implementations usually reduces the control code to typically a couple hundred bytes (or even less), at the expense of data space for the table. By using data elements no larger than required in the transition table entries, each entry can be kept to a very moderate size. One possible issue is the number of states and events. It takes some experience to control both, to keep the table size (#states * #events) under control. A common trick is to introduce 'state variables'. Some times you can use 2+ small tables rather than a huge one, e.g. if you implement a communication protocol: One table for the connect phase, one for the data transfer phase. Many transition tables are rather sparse anyway, but a lot of methods for space efficient storage of sparse matrices are basic data structure knowledge. E.g. non-empty entries may be factored out to a packed, linear array, and the large table contains indexes to this array. Often, several transitions are identical (typically in one state, for different events, or for one event in several different states) - then a linear table need to hold only a single copy. Certainly, really old embedded processors (such as 8051) had very little data space; expanding code space was far easier (e.g. through banking hardware). While we would usually call the transition table 'data', it is 100% read-only, and may very well be burnt in ROM (ok, call it 'flash' nowadays) together with the driver code. If you consider CLR for an embedded CPU (don't try that on an 8051!), then you definitely can fit a packed transition table. My guess is that the total code+data size would be significantly smaller than an equivalent implementation with switch cases and/or if/else-sequences. And faster, even if a packed table will lead to a couple more indirections. I will maintain that table driven state machines can be a very good solution for embedded processors.
Religious freedom is the freedom to say that two plus two make five.
-
Gotos are frowned on. You should not use gotos. Long live gotos. Until someone comes up with a better/faster/concise way of expressing the following DFA state machine (presented in part) I will continue to defend the use of gotos, even if their use cases have gotten significantly more narrow as progress has marched on. When you need them, there is no better tool.
internal sealed partial class JsonStringRunner : FAStringRunner {
private FAMatch NextMatchImpl(string s) {
int ch;
int len;
int p;
int l;
int c;
ch = -1;
len = 0;
if ((this.position == -1)) {
this.position = 0;
}
p = this.position;
l = this.line;
c = this.column;
this.Advance(s, ref ch, ref len, true);
// q0:
// [\t-\n\r ]
if (((((ch >= 9)
&& (ch <= 10))
|| (ch == 13))
|| (ch == 32))) {
this.Advance(s, ref ch, ref len, false);
goto q1;
}
// [\"]
if ((ch == 34)) {
this.Advance(s, ref ch, ref len, false);
goto q2;
}
// [,]
if ((ch == 44)) {
this.Advance(s, ref ch, ref len, false);
goto q9;
}
// [\-]
if ((ch == 45)) {
this.Advance(s, ref ch, ref len, false);
goto q10;
}
// [0]
if ((ch == 48)) {
this.Advance(s, ref ch, ref len, false);
goto q11;
}
// [1-9]
if (((ch >= 49)
&& (ch <= 57))) {
this.Advance(s, ref ch, ref len, false);
goto q17;
}
// [\:]
if ((ch == 58)) {
this.Advance(s, ref ch, ref len, false);
goto q18;
}
// [\[]
if ((ch == 91)) {
this.Advance(s, ref ch, ref len, false);
goto q19;
}
// [\]]
if ((ch == 93)) {
this.Advance(s, ref ch, ref len, false);
goto q20;
}
// [f]
if ((ch == 102)) {
this.Advance(s, ref ch, ref len, false);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.
-
Gotos are frowned on. You should not use gotos. Long live gotos. Until someone comes up with a better/faster/concise way of expressing the following DFA state machine (presented in part) I will continue to defend the use of gotos, even if their use cases have gotten significantly more narrow as progress has marched on. When you need them, there is no better tool.
internal sealed partial class JsonStringRunner : FAStringRunner {
private FAMatch NextMatchImpl(string s) {
int ch;
int len;
int p;
int l;
int c;
ch = -1;
len = 0;
if ((this.position == -1)) {
this.position = 0;
}
p = this.position;
l = this.line;
c = this.column;
this.Advance(s, ref ch, ref len, true);
// q0:
// [\t-\n\r ]
if (((((ch >= 9)
&& (ch <= 10))
|| (ch == 13))
|| (ch == 32))) {
this.Advance(s, ref ch, ref len, false);
goto q1;
}
// [\"]
if ((ch == 34)) {
this.Advance(s, ref ch, ref len, false);
goto q2;
}
// [,]
if ((ch == 44)) {
this.Advance(s, ref ch, ref len, false);
goto q9;
}
// [\-]
if ((ch == 45)) {
this.Advance(s, ref ch, ref len, false);
goto q10;
}
// [0]
if ((ch == 48)) {
this.Advance(s, ref ch, ref len, false);
goto q11;
}
// [1-9]
if (((ch >= 49)
&& (ch <= 57))) {
this.Advance(s, ref ch, ref len, false);
goto q17;
}
// [\:]
if ((ch == 58)) {
this.Advance(s, ref ch, ref len, false);
goto q18;
}
// [\[]
if ((ch == 91)) {
this.Advance(s, ref ch, ref len, false);
goto q19;
}
// [\]]
if ((ch == 93)) {
this.Advance(s, ref ch, ref len, false);
goto q20;
}
// [f]
if ((ch == 102)) {
this.Advance(s, ref ch, ref len, false);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 SowellA day can really slip by when you're deliberately avoiding what you're supposed to do.
- Calvin (Bill Watterson, Calvin & Hobbes) -
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
-
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 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
-
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!
-
If I were given the responsibility for a state machine implementation like that, I would immediately run to my boss asking for permission to rewrite the whole thing as a table driven machine. There is no way, with code like that, that I could guarantee that all inputs/events are properly handled in all cases (or given the proper error treatment). I would have to make a huge effort if I were to report a complete set of normal (non-error) ways to go from a given state to another, and which inputs/events would lead to which error states. I've never written any CP article, but code like this makes my fingers itch to compose an article about proper table driven state machine implementation! Maybe I some day get around to do it :-)
Religious freedom is the freedom to say that two plus two make five.
trønderen wrote:
If I were given the responsibility for a state machine implementation like that, I would immediately run to my boss asking for permission to rewrite the whole thing as a table driven machine.
... or as a state machine that returns function pointers instead of using tables and state variables:
#include #include // Fn ptrs defs
typedef void (*RT)( int input );
typedef RT (*TER)( int input );// Forward declarations
extern TER state1( int input );
extern TER state2( int input );
extern TER state3( int input );// First state
TER state1( int input )
{
printf( "one\t" );
return input < 10 ? (TER)&state2 : (TER)NULL;
}// Second state
TER state2( int input )
{
printf( "two\t" );
return (TER)&state3;
}// Third state
TER state3( int input )
{
printf( "three\t" );
return (TER)&state1;
}int main(int argc, char* argv[])
{
int n;// Set Start state TER state = (TER)&state1; // Exercises the state machine. Ends when state == NULL for ( n = 0 ; state ; ++n ) { // Executes the current state (state variable) then goes to the next state state = (TER)( state( n ) ); } printf( "\\n\\nPress any key\\n" ); getch(); return 0;
}
Type casts are useful because in C it's impossible to declare function pointers that return function pointers that return function pointers that return function pointers... :) Regards
-
Gotos are frowned on. You should not use gotos. Long live gotos. Until someone comes up with a better/faster/concise way of expressing the following DFA state machine (presented in part) I will continue to defend the use of gotos, even if their use cases have gotten significantly more narrow as progress has marched on. When you need them, there is no better tool.
internal sealed partial class JsonStringRunner : FAStringRunner {
private FAMatch NextMatchImpl(string s) {
int ch;
int len;
int p;
int l;
int c;
ch = -1;
len = 0;
if ((this.position == -1)) {
this.position = 0;
}
p = this.position;
l = this.line;
c = this.column;
this.Advance(s, ref ch, ref len, true);
// q0:
// [\t-\n\r ]
if (((((ch >= 9)
&& (ch <= 10))
|| (ch == 13))
|| (ch == 32))) {
this.Advance(s, ref ch, ref len, false);
goto q1;
}
// [\"]
if ((ch == 34)) {
this.Advance(s, ref ch, ref len, false);
goto q2;
}
// [,]
if ((ch == 44)) {
this.Advance(s, ref ch, ref len, false);
goto q9;
}
// [\-]
if ((ch == 45)) {
this.Advance(s, ref ch, ref len, false);
goto q10;
}
// [0]
if ((ch == 48)) {
this.Advance(s, ref ch, ref len, false);
goto q11;
}
// [1-9]
if (((ch >= 49)
&& (ch <= 57))) {
this.Advance(s, ref ch, ref len, false);
goto q17;
}
// [\:]
if ((ch == 58)) {
this.Advance(s, ref ch, ref len, false);
goto q18;
}
// [\[]
if ((ch == 91)) {
this.Advance(s, ref ch, ref len, false);
goto q19;
}
// [\]]
if ((ch == 93)) {
this.Advance(s, ref ch, ref len, false);
goto q20;
}
// [f]
if ((ch == 102)) {
this.Advance(s, ref ch, ref len, false);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.
-
Gotos are frowned on. You should not use gotos. Long live gotos. Until someone comes up with a better/faster/concise way of expressing the following DFA state machine (presented in part) I will continue to defend the use of gotos, even if their use cases have gotten significantly more narrow as progress has marched on. When you need them, there is no better tool.
internal sealed partial class JsonStringRunner : FAStringRunner {
private FAMatch NextMatchImpl(string s) {
int ch;
int len;
int p;
int l;
int c;
ch = -1;
len = 0;
if ((this.position == -1)) {
this.position = 0;
}
p = this.position;
l = this.line;
c = this.column;
this.Advance(s, ref ch, ref len, true);
// q0:
// [\t-\n\r ]
if (((((ch >= 9)
&& (ch <= 10))
|| (ch == 13))
|| (ch == 32))) {
this.Advance(s, ref ch, ref len, false);
goto q1;
}
// [\"]
if ((ch == 34)) {
this.Advance(s, ref ch, ref len, false);
goto q2;
}
// [,]
if ((ch == 44)) {
this.Advance(s, ref ch, ref len, false);
goto q9;
}
// [\-]
if ((ch == 45)) {
this.Advance(s, ref ch, ref len, false);
goto q10;
}
// [0]
if ((ch == 48)) {
this.Advance(s, ref ch, ref len, false);
goto q11;
}
// [1-9]
if (((ch >= 49)
&& (ch <= 57))) {
this.Advance(s, ref ch, ref len, false);
goto q17;
}
// [\:]
if ((ch == 58)) {
this.Advance(s, ref ch, ref len, false);
goto q18;
}
// [\[]
if ((ch == 91)) {
this.Advance(s, ref ch, ref len, false);
goto q19;
}
// [\]]
if ((ch == 93)) {
this.Advance(s, ref ch, ref len, false);
goto q20;
}
// [f]
if ((ch == 102)) {
this.Advance(s, ref ch, ref len, false);Try writing Assembly with out them (the fabled JMP!). They are a tool that get misused (kinda like the powered screw driver).
-
Gotos are frowned on. You should not use gotos. Long live gotos. Until someone comes up with a better/faster/concise way of expressing the following DFA state machine (presented in part) I will continue to defend the use of gotos, even if their use cases have gotten significantly more narrow as progress has marched on. When you need them, there is no better tool.
internal sealed partial class JsonStringRunner : FAStringRunner {
private FAMatch NextMatchImpl(string s) {
int ch;
int len;
int p;
int l;
int c;
ch = -1;
len = 0;
if ((this.position == -1)) {
this.position = 0;
}
p = this.position;
l = this.line;
c = this.column;
this.Advance(s, ref ch, ref len, true);
// q0:
// [\t-\n\r ]
if (((((ch >= 9)
&& (ch <= 10))
|| (ch == 13))
|| (ch == 32))) {
this.Advance(s, ref ch, ref len, false);
goto q1;
}
// [\"]
if ((ch == 34)) {
this.Advance(s, ref ch, ref len, false);
goto q2;
}
// [,]
if ((ch == 44)) {
this.Advance(s, ref ch, ref len, false);
goto q9;
}
// [\-]
if ((ch == 45)) {
this.Advance(s, ref ch, ref len, false);
goto q10;
}
// [0]
if ((ch == 48)) {
this.Advance(s, ref ch, ref len, false);
goto q11;
}
// [1-9]
if (((ch >= 49)
&& (ch <= 57))) {
this.Advance(s, ref ch, ref len, false);
goto q17;
}
// [\:]
if ((ch == 58)) {
this.Advance(s, ref ch, ref len, false);
goto q18;
}
// [\[]
if ((ch == 91)) {
this.Advance(s, ref ch, ref len, false);
goto q19;
}
// [\]]
if ((ch == 93)) {
this.Advance(s, ref ch, ref len, false);
goto q20;
}
// [f]
if ((ch == 102)) {
this.Advance(s, ref ch, ref len, false);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 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) { // Assuming Advance takes consecutive characters in the string. 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); // q0: switch (ch) { // \[\\t-\\n\\r \] 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
-
trønderen wrote:
If I were given the responsibility for a state machine implementation like that, I would immediately run to my boss asking for permission to rewrite the whole thing as a table driven machine.
... or as a state machine that returns function pointers instead of using tables and state variables:
#include #include // Fn ptrs defs
typedef void (*RT)( int input );
typedef RT (*TER)( int input );// Forward declarations
extern TER state1( int input );
extern TER state2( int input );
extern TER state3( int input );// First state
TER state1( int input )
{
printf( "one\t" );
return input < 10 ? (TER)&state2 : (TER)NULL;
}// Second state
TER state2( int input )
{
printf( "two\t" );
return (TER)&state3;
}// Third state
TER state3( int input )
{
printf( "three\t" );
return (TER)&state1;
}int main(int argc, char* argv[])
{
int n;// Set Start state TER state = (TER)&state1; // Exercises the state machine. Ends when state == NULL for ( n = 0 ; state ; ++n ) { // Executes the current state (state variable) then goes to the next state state = (TER)( state( n ) ); } printf( "\\n\\nPress any key\\n" ); getch(); return 0;
}
Type casts are useful because in C it's impossible to declare function pointers that return function pointers that return function pointers that return function pointers... :) Regards
I hate function pointer dispatch code in general. Because at some point you'll have to debug and maintain it, and you end up with impossible to follow pointer arrays hiding the flow of your app.
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
-
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 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) { // Assuming Advance takes consecutive characters in the string. 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); // q0: switch (ch) { // \[\\t-\\n\\r \] 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
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
-
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.
-
If I were given the responsibility for a state machine implementation like that, I would immediately run to my boss asking for permission to rewrite the whole thing as a table driven machine. There is no way, with code like that, that I could guarantee that all inputs/events are properly handled in all cases (or given the proper error treatment). I would have to make a huge effort if I were to report a complete set of normal (non-error) ways to go from a given state to another, and which inputs/events would lead to which error states. I've never written any CP article, but code like this makes my fingers itch to compose an article about proper table driven state machine implementation! Maybe I some day get around to do it :-)
Religious freedom is the freedom to say that two plus two make five.
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.
-
I hate function pointer dispatch code in general. Because at some point you'll have to debug and maintain it, and you end up with impossible to follow pointer arrays hiding the flow of your app.
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:
with impossible to follow pointer arrays
There are no pointer arrays in my code.
Sorry, I was speaking generally about dispatch function pointers. Your statement just remind me of it. Sorry I wasn't clear. I just woke up when I wrote that. :)
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
-
Sorry, I was speaking generally about dispatch function pointers. Your statement just remind me of it. Sorry I wasn't clear. I just woke up when I wrote that. :)
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
-
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