Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. The Lounge
  3. There are many gotos, but these ones are mine

There are many gotos, but these ones are mine

Scheduled Pinned Locked Moved The Lounge
49 Posts 16 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • T trønderen

    Compilers will generate jump statements. I don't mind, as long as I don't have to trace them, and maintain the code at that level. Other generators may generate source format goto statements. I don't mind, as long as I don't have to trace them, and maintain the code at that level. If the gotos and labels you presented are created by a generator, and you never will have to trace them and maintain the code at that level, I do not consider them "your" gotos. Not any more than I consider the conditional and unconditional jumps generated from your source code to be "your" jmp instructions. I'd be curious to also see your input to Visual FA to generate this code, as well as the table driven code generated by Visual FA! If you have any reason at all to relate to the generated code: Trading readability and maintainability for "slightly faster code" is generally a bad move. Besides: From the snippet you presented, I am surprised that a table driven variety generated from the same input can be even "slightly" slower. I really wonder what that generator generates! (That is why I'd like to see it.) I have never been using Visual FA, but from what I gather from a quick net search, it looks like you are not at all using SM as a development too. You are just generating code for different virtual machines, one with a state oriented execution model, one with a C/C++ oriented execution model. A comparison between them is like compiling some (arbitrary language) source code for x64 and for AArch64 and observing that the x64 is "slightly faster". To me, the SM table is not the result delivered by a generator - it is a modeling tool for the human developer. The driver is typically a score code statements, independent of the model (a.k.a. transition table). I certainly agree that an editor tailored to transition table editing is a great thing to have. I have tried to maintain a compile time initialized C++ array for a transition table, using Np++. Even for trivial SMs, that is almost impossible (unless you just copy the table from e.g. a standard document and it will be carved in stone).

    Religious freedom is the freedom to say that two plus two make five.

    H Offline
    H Offline
    honey the codewitch
    wrote on last edited by
    #9

    trønderen wrote:

    If you have any reason at all to relate to the generated code. Trading readability and maintainability for "slightly faster code" is generally a bad move.

    . I generally agree with you. However, as we both know there are exceptions, which is why you used the word "generally" I'm sure. This is one of those cases, as lexing is always in a critical code path, and a generalized lexer must be able to handle bulk input as efficiently as possible. My input to visual fa is one or more regular expressions. Literally just that. Here is the full input for that generated lexer, in my .rl Rolex lexer format, but it should be easy enough to discern the grammar below without knowing the format.

    Object = "{"
    ObjectEnd = "}"
    Array = "["
    ArrayEnd = "]"
    FieldSeparator = ":"
    Comma = ","
    Number = '-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?'
    Boolean = 'true|false'
    Null = "null"
    String = '"([^\n"\\]|\\([btrnf"\\/]|(u[0-9A-Fa-f]{4})))*"'
    WhiteSpace = '[ \t\r\n]+'

    The table driven code is run on a flat array of integers. It might be more efficient to unflatten it in this case - maybe? I used to run a more complicated array of structs for this, and I don't remember there being a performance difference. But anyway, there is also an array of int arrays for a feature called block ends, which simulate lazy matching on a DFA. (I have the details of all of it documented in my Visual FA series). It's also simpler in operation than it looks. I do actually use gotos in a couple of places here to restart the state machine. It was much less complicated than orchestrating a while with breaks. I should state that I didn't comment the code here because it wouldn't help me. It may help others, but I didn't really care about that. This pattern is burned into my brain after writing more than half a dozen lexers that follow the same. It honestly would just clutter it for me, as the code makes immediate sense to me despite how it looks, and I didn't write it for a team.

    private FAMatch _NextImpl(
    #if FALIB_SPANS
    ReadOnlySpan s
    #else
    string s
    #endif
    )
    {
    int tlen;
    int tto;
    int prlen;
    int pmin;
    int pmax;
    int i;
    int j;
    int state = 0;
    int acc;
    if (position == -1)
    {
    // first read
    ++position;
    }
    int len = 0;
    long cursor_pos = position;
    int line = this.line;
    int column = this.column;
    int ch = -1;
    Advance(s, ref ch, ref len, true);
    start_dfa:
    acc = _dfa[state];

    1 Reply Last reply
    0
    • R Ravi Bhavnani

      k5054 wrote:

      C/C++ certainly allows you to goto into a contained block, ...

      Thanks for the correction.  The C# compiler doesn't permit that. /ravi

      My new year resolution: 2048 x 1536 Home | Articles | My .NET bits | Freeware ravib(at)ravib(dot)com

      H Offline
      H Offline
      honey the codewitch
      wrote on last edited by
      #10

      C++ is painfully permissive at times, but not nearly so bad as C. :) C# brought a bit of sense to the mess, even if it sacrifices the ability to do some of the black magic.

      Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix

      1 Reply Last reply
      0
      • T trønderen

        Compilers will generate jump statements. I don't mind, as long as I don't have to trace them, and maintain the code at that level. Other generators may generate source format goto statements. I don't mind, as long as I don't have to trace them, and maintain the code at that level. If the gotos and labels you presented are created by a generator, and you never will have to trace them and maintain the code at that level, I do not consider them "your" gotos. Not any more than I consider the conditional and unconditional jumps generated from your source code to be "your" jmp instructions. I'd be curious to also see your input to Visual FA to generate this code, as well as the table driven code generated by Visual FA! If you have any reason at all to relate to the generated code: Trading readability and maintainability for "slightly faster code" is generally a bad move. Besides: From the snippet you presented, I am surprised that a table driven variety generated from the same input can be even "slightly" slower. I really wonder what that generator generates! (That is why I'd like to see it.) I have never been using Visual FA, but from what I gather from a quick net search, it looks like you are not at all using SM as a development too. You are just generating code for different virtual machines, one with a state oriented execution model, one with a C/C++ oriented execution model. A comparison between them is like compiling some (arbitrary language) source code for x64 and for AArch64 and observing that the x64 is "slightly faster". To me, the SM table is not the result delivered by a generator - it is a modeling tool for the human developer. The driver is typically a score code statements, independent of the model (a.k.a. transition table). I certainly agree that an editor tailored to transition table editing is a great thing to have. I have tried to maintain a compile time initialized C++ array for a transition table, using Np++. Even for trivial SMs, that is almost impossible (unless you just copy the table from e.g. a standard document and it will be carved in stone).

        Religious freedom is the freedom to say that two plus two make five.

        H Offline
        H Offline
        honey the codewitch
        wrote on last edited by
        #11

        trønderen wrote:

        To me, the SM table is not the result delivered by a generator - it is a modeling tool for the human developer. The driver is typically a score code statements, independent of the model (a.k.a. transition table). I certainly agree that an editor tailored to transition table editing is a great thing to have. I have tried to maintain a compile time initialized C++ array for a transition table, using Np++. Even for trivial SMs, that is almost impossible (unless you just copy the table from e.g. a standard document and it will be carved in stone).

        I didn't address this part of your post. I should. I don't expect transition tables to be especially readable. Visual FA is called "Visual" because it can produce images - directed graphs of the state machines that match 1:1 with the generated tables and code. For example, q0 in the graph corresponds to the q0: label in the goto table. It's perspicuous enough and yet concise enough that I've used the graphs as a guide to hand roll lexers when i needed the lexer to perform additional work beyond what a strict DFA could provide. I've also used them to debug. Since the correlation is 1:1 between the graphs and the compiled code, it's easier to follow along with than the tables, but both can be followed with the graphs. The graphs effectively become in part, documentation for the state machine, and for that they work pretty well.

        Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix

        1 Reply Last reply
        0
        • R Ravi Bhavnani

          Most developers know goto statements are "bad" but very few know why or have even read Dijkstra's letter in CACM.  And I'm willing to bet most developers haven't heard of the ACM. :( goto statements that target entry into a block (as you could do in older versions of Fortran and Basic) are frowned upon because they make automated program verification impossible - aka "I can't say with certainty how you got here".  Well behaved goto statements are not only fine, you couldn't write code without them. To make it harder for novice programmers to misuse the goto statement, many languages such as C, C++, Java and C# (and many others) have created statements that implement well behaved goto's.  They are:

          • break - goto the end of a switch or terminate the closest enclosing iteration statement
          • continue - start a new iteration of the closest enclosing iteration statement
          • return - exit the function in which it appears and return to the caller

          And most (I suspect all) modern compilers won't allow specifying the target of a goto into another block.  So use goto's, but use them the way nature intended. :) /ravi

          My new year resolution: 2048 x 1536 Home | Articles | My .NET bits | Freeware ravib(at)ravib(dot)com

          J Offline
          J Offline
          jmaida
          wrote on last edited by
          #12

          totally agree, Ravi. Well said.

          "A little time, a little trouble, your better day" Badfinger

          1 Reply Last reply
          0
          • T trønderen

            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.

            D Offline
            D Offline
            den2k88
            wrote on last edited by
            #13

            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

            T 1 Reply Last reply
            0
            • D den2k88

              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

              T Offline
              T Offline
              trønderen
              wrote on last edited by
              #14

              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.

              1 Reply Last reply
              0
              • H honey the codewitch

                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);

                K Offline
                K Offline
                klinkenbecker
                wrote on last edited by
                #15

                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.

                H 1 Reply Last reply
                0
                • H honey the codewitch

                  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);

                  T Offline
                  T Offline
                  TNCaver
                  wrote on last edited by
                  #16

                  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)

                  1 Reply Last reply
                  0
                  • K klinkenbecker

                    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.

                    H Offline
                    H Offline
                    honey the codewitch
                    wrote on last edited by
                    #17

                    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

                    K 1 Reply Last reply
                    0
                    • H honey the codewitch

                      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

                      K Offline
                      K Offline
                      klinkenbecker
                      wrote on last edited by
                      #18

                      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.

                      H 1 Reply Last reply
                      0
                      • K klinkenbecker

                        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.

                        H Offline
                        H Offline
                        honey the codewitch
                        wrote on last edited by
                        #19

                        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

                        K 1 Reply Last reply
                        0
                        • H honey the codewitch

                          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

                          K Offline
                          K Offline
                          klinkenbecker
                          wrote on last edited by
                          #20

                          honey the codewitch wrote:

                          I clearly misunderstood you. Sorry.

                          Np, irony is easily missed in short messaging!

                          1 Reply Last reply
                          0
                          • T trønderen

                            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.

                            G Offline
                            G Offline
                            giulicard
                            wrote on last edited by
                            #21

                            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

                            H 1 Reply Last reply
                            0
                            • H honey the codewitch

                              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);

                              D Offline
                              D Offline
                              dandy72
                              wrote on last edited by
                              #22

                              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.

                              1 Reply Last reply
                              0
                              • H honey the codewitch

                                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);

                                G Offline
                                G Offline
                                glennPattonWork3
                                wrote on last edited by
                                #23

                                Try writing Assembly with out them (the fabled JMP!). They are a tool that get misused (kinda like the powered screw driver).

                                1 Reply Last reply
                                0
                                • H honey the codewitch

                                  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);

                                  P Offline
                                  P Offline
                                  Payton Byrd 2023
                                  wrote on last edited by
                                  #24

                                  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
                                  
                                  H 1 Reply Last reply
                                  0
                                  • G giulicard

                                    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

                                    H Offline
                                    H Offline
                                    honey the codewitch
                                    wrote on last edited by
                                    #25

                                    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

                                    G T 2 Replies Last reply
                                    0
                                    • P Payton Byrd 2023

                                      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
                                      
                                      H Offline
                                      H Offline
                                      honey the codewitch
                                      wrote on last edited by
                                      #26

                                      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

                                      P 1 Reply Last reply
                                      0
                                      • H honey the codewitch

                                        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

                                        P Offline
                                        P Offline
                                        Payton Byrd 2023
                                        wrote on last edited by
                                        #27

                                        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.

                                        H 1 Reply Last reply
                                        0
                                        • T trønderen

                                          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.

                                          J Offline
                                          J Offline
                                          jochance
                                          wrote on last edited by
                                          #28

                                          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.

                                          H 1 Reply Last reply
                                          0
                                          Reply
                                          • Reply as topic
                                          Log in to reply
                                          • Oldest to Newest
                                          • Newest to Oldest
                                          • Most Votes


                                          • Login

                                          • Don't have an account? Register

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • World
                                          • Users
                                          • Groups