Processing Parenthesis Correctly

Posted by Gohan_TheDragonball on Thu 07 Dec 2006 02:02 PM — 37 posts, 128,074 views.

USA #0
I am trying to make it so my scripting system will process internal parenthesis first, but I am having a hard time, with the loop, data keeps getting lost somewhere. Here is an example of what I am having my prog function process:

setvar $n Dende.KillBoars $f.increment($n.getvar(Dende.KillBoars))


    int left = 0, z, cur;

    cmnd[0] = '\0';

    for ( x=0; parse[x] != '\0'; x++ )
    {
	if ( parse[x] == '(' )
	    left++;
    }

    if ( left > 0 )
    {
	char *funcs[left];
	char func[MIL];
	bool infunc = FALSE;
	int marker = left;

    	while ( marker > 0 )
    	{
	    cur = 0;
	    func[0] = '\0';
	    infunc = FALSE;

	    for ( z = 0; parse[z] != '\0'; z++ )
	    {
		if ( infunc )
		{
		    if ( parse[z] == ')' )
		    {
			funcs[marker] = parse_setvar( func, mob, actor, obj, vo, rndm );
			break;
		    }
		    else
		    {
			add_letter( func, parse[z] );
			continue;
		    }
		}

	    	if ( parse[z] == '(' )
		    cur++;

	    	if ( cur == marker )
	    	{
		    infunc = TRUE;
	    	}
	    }
	    marker--;
	}

	cur = 0;

	for ( z = 0; z < strlen(parse); z++ )
	{
	    if ( parse[z] == '(' )
	    {
		int a;

		for ( a = 0; funcs[cur][a] != '\0'; a++ )
		{
		    add_letter( cmnd, funcs[cur][a] );
		}
		z += strlen(funcs[cur]);
		cur++;
	    }
	    else
	    {
		add_letter( cmnd, parse[z] );
	    }
	}
    }
USA #1
Could you give an example of the data loss? And what exactly are your language specifications? Do you allow arbitrary nested expressions? Just one?

This kind of thing reminds me why I'm so unhappy with the mudprog language. Sigh.

I would approach this problem with recursion, such that each opened parenthesis would be a new recursive call. That way all this stuff takes care of itself instead of having to implement this messy and error-prone iterative solution.
USA #2
The language is C, its not an independent scripting system, i am just used to calling the mud progz from smaug scripts. everything is written in smaug for smaug, which is why i posted on the smaug board. i put a debug check in and the loss is a little less than i had thought at first:


setvar $n Dende.KillBoars $f.increment($n.getvar(Dende.KillBoars))

becomes:

setvar $n Dende.KillBoars $f.increment(n.getvarDende.KillBoars))


I also put in a couple debugs in the actual functions themselves. Apparently the GETVAR function is being called, however the only thing being sent to the function is GETVAR, and nothing else:

[PROGS] GetVar: Word = getvar, Mob #27412.
[PROGS] Function GETVAR missing opening parenthesis(, Mob #27412.

The part: parenthesis(

Is supposed to be: parenthesis(%c), but since only GETVAR is being sent that specific point is the '\0' char.

When all is said and done I want the following to happen:


setvar $n Dende.KillBoars $f.increment($n.getvar(Dende.KillBoars))

becomes:

setvar $n Dende.KillBoars $f.increment($n.getvar(Dende.KillBoars))

becomes:

setvar $n Dende.KillBoars $f.increment(10)

becomes:

setvar $n Dende.KillBoars 11
USA #3
Ok I went back and redid my code so that a single parenthesis is evaluated in each pass of parse_setvar, I am really close to this being right:

setvar $n Dende.KillBoars $f.increment($n.getvar(Dende.KillBoars))

now becomes:

setvar $n Dende.KillBoars $f.increment(Dende.KillBoars0))

Good news, GETVAR is finally being evaluated. The 0 is correct, however the first part shouldn't be going back there and neither should one of those closing parenthesis. Here is the new code.


  if ( !str_infix( "(", parse ) )
  {
    int left = 0, z, right = 0;
    char func[MIL];
    bool infunc = FALSE, skipToRightParen = FALSE;

    cmnd[0] = '\0';

    for ( x=0; parse[x] != '\0'; x++ )
    {
        if ( parse[x] == '(' )
            left++;
    }

    func[0] = '\0';
    infunc = FALSE;

    for ( z = 0; parse[z] != '\0'; z++ )
    {
        if ( infunc )
        {
            if ( parse[z] == ')' )
            {
                right++;
                if ( right == left )
                {
                    sprintf( func, "%s", parse_setvar( func, mob, actor, obj, vo, rndm ) );
                    break;
                }
                add_letter( func, parse[z] );
                continue;
            }
            else
            {
                add_letter( func, parse[z] );
                continue;
            }
        }

        if ( parse[z] == '(' )
        {
            infunc = TRUE;
        }
    }

    for ( z = 0; parse[z] != '\0'; z++ )
    {
        if ( skipToRightParen && parse[z] != ')' )
            continue;

        if ( parse[z] == '(' )
        {
            int a;

            add_letter( cmnd, parse[z] );

            for ( a = 0; func[a] != '\0'; a++ )
            {
                add_letter( cmnd, func[a] );
            }
            skipToRightParen = TRUE;
        }
        else
        {
            add_letter( cmnd, parse[z] );
        }
    }
  }
USA #4
Quote:
The language is C, its not an independent scripting system, i am just used to calling the mud progz from smaug scripts. everything is written in smaug for smaug, which is why i posted on the smaug board.
This statement makes no sense to me which makes me think we were talking about completely different things. My comment was that the SMAUG mudprog language is very annoying to work with and is turning into a monstrous hack as people want more and more features. The implementation of if-checks, for example, is complicated beyond belief given how easy it should be.

Basically it is a language that is the result of people who don't know how to write compilers who try to hack together a compiler, and as a result there are mistakes, unnecessary complications, weirdnesses and all around bad times.


For example, what you are trying to do here should be trivial but due to the bad way these programs are implemented has turned out to be quite difficult.

Again, I would try implementing this with recursion so that nested parentheses are handled more naturally. Are you familiar with writing recursive functions?
USA #5
I am not too sure what a recursive function is, but I thought thats what I was doing, as this code is the beginning of my parse_setvar function, so the code to handle the parenthesis is calling the same function it is in, hence recursive.


parse_setvar: setvar $n Dende.KillBoars $f.increment($n.getvar(Dende.KillBoars))
parse_setvar: $n.getvar(Dende.KillBoars)
parse_setvar: Dende.KillBoars

oh and i meant the code i had up was C, at least i think thats what smaug is. the 'setvar' stuff is an addition to the mudprog. whether ot not the mudprog is hacked together, i would still like to get this to work.
Australia Forum Administrator #6
Quote:

My comment was that the SMAUG mudprog language is very annoying to work with and is turning into a monstrous hack as people want more and more features. The implementation of if-checks, for example, is complicated beyond belief given how easy it should be.


Honestly, I think the whole thing should be converted to Lua, which works reliably. There is probably a conversion issue for existing scripts, not sure how big an issue that would be.
USA #7
Quote:
Honestly, I think the whole thing should be converted to Lua, which works reliably. There is probably a conversion issue for existing scripts, not sure how big an issue that would be.
I agree Nick. It's possible that the conversion wouldn't actually be that bad -- the mudprog language has such primitive syntax that you could probably convert everything automatically. And it's not as if there are huge numbers of scripts, either. The real problem I see is reimplementing all of the functions that mudprog uses to hook into the MUD; they'd have to be "Luified". That hurt the scripting language I implemented; it was getting to be too much work to add all the functions. Knowing what I know now, though, I could have chosen a much better solution, an automatic binding of sorts, but anyhow...

To return to the problem at hand:

Gohan, it's true that technically if a function calls itself, that is recursion, but there's a lot more to it than just calling yourself. You need to be thinking about the problem in a fairly specific way. Recursion takes a big problem, and breaks it into smaller parts, and then solves those smaller parts, using the solutions to create the bigger part's solution.

In your case, if we were to implement this recursively (which I think would be much, much better, mainly because it's more robust [1]) we would need to think carefully about what exactly the problem is, and what exactly the expected behavior is.

[1] for example, your code would not work with functions of several arguments with nested sub-expressions, because you only recurse when you have found the same number of rights as of lefts.

In general, what we are trying to do here is evaluate sub-expressions before evaluating expressions. Since mudprog is all text-based and doesn't actually have program structure trees, we have to replace the sub-expression with some text (the result of the evaluation) and use that in the bigger expression.

Here is how you should be thinking about the problem:

Seeing an open parenthesis means you are entering a new expression context. As soon as you do that, you should leave your old context and work on your next context. As soon as you find the close parenthesis, you can leave your context. Nested expressions are handled naturally, because finding a new open parenthesis entails opening a new context.

Once you leave a context, you may evaluate the sub-expression however you want to, and then replace the result in the greater expression. Unfortunately, C makes that a little harder than it should be, but whatever; the idea is that you would declare the "result" to be everything up until your sub-expression, then the result of evaluating your sub-expression, and then everything from the sub-expression. Creating this dynamic string is a little annoying in C, but so be it.

Anyhow, this kind of problem is very easy to get lost in. (In fact, it's so hard to deal with because it was written by people who probably didn't really understand the issues, and just banged at it until it looked like it worked.) In particular, it's easy to do something that looks like it works but doesn't; in your code, for instance, even if you got it working for your test case you would have made the nasty discovery further along the line that multiple arguments weren't handled.
My suggestion is to be very careful with this. Try to develop a very precise understanding of what you are trying to do. Define what should happen and under what circumstances. If you can't write it in natural language, there is no way you will be able to write it in a programming language.

Incidentally, what you're trying to do isn't really that easy. So don't get discouraged if you have trouble with it. Take your time and proceed with caution.
Australia Forum Administrator #8
Quote:

The real problem I see is reimplementing all of the functions that mudprog uses to hook into the MUD; they'd have to be "Luified".


I think you could write some "glue" routines fairly easily.
USA #9
Yes, that's what I was referring to regarding my new knowledge. Still it'd be somewhat of a hassle, and the glue routines would be messy. For one, you'd have to enumerate the functions you want glued. OK, no big problem, you have to do that anyhow. But more importantly you'd have to convert the Lua arguments to a string of sorts, give it to the mudprog function, and figure out how to get a result back to Lua.

I worked on something like this over the summer at D. E. Shaw, taking a program that had embedded Tcl and adding embedded Perl as well. (Perl embedding is soooo nasty compared to, say, Lua.) This would be even harder because unlike Tcl, mudprog doesn't have a proper return value structure.

One very useful command would be a "mudprog" command of sorts that would allow Lua to execute an arbitrary string of mudprog. E.g., mudprog("mset david health 200"). The hard part, again, is getting a return value.

In fact, the hardest challenge I faced was not at all running mudprog commands (which was easy: just do what I said above), it was reimplementing all the if-checks. mudprog if-checks aren't implemented as functions, so it's fairly hard to extract a value from them.

I really don't like mudprog. :-(
USA #10
So do you have any clue as to what is wrong with the code I have posted, any ideas at all.
USA #11
One thing I already mentioned is that you won't be able to handle multiple pairs of parentheses.

One thing you should check very carefully is how you are reconstructing your string. It's a little hard to know for sure without seeing the function. Also, the method is so confusing that it's very hard to debug in general.

Normally I would be able to write this fairly quickly, but at the moment I'm so busy that I don't time for my own hobbies. :(
USA #12

char *parse_setvar( char *cmnd, CHAR_DATA *mob, CHAR_DATA *actor, OBJ_DATA *obj, void *vo, CHAR_DATA *rndm )
{
  static char results[MSL*2];
  int x;
  CHAR_DATA *chkchar = NULL;
  OBJ_DATA *chkobj = NULL;
  ROOM_INDEX_DATA *chkroom = NULL;
  bool function = FALSE;
  char parse[MIL];
  sprintf( parse, "%s", cmnd );
  results[0] = '\0';

  if ( !str_infix( "(", parse ) )
  {
    int left = 0, z, right = 0;
    char func[MIL];
    bool infunc = FALSE, skipToRightParen = FALSE;

    cmnd[0] = '\0';

    for ( x=0; parse[x] != '\0'; x++ )
    {
        if ( parse[x] == '(' )
            left++;
    }

    func[0] = '\0';
    infunc = FALSE;

    for ( z = 0; parse[z] != '\0'; z++ )
    {
        if ( infunc )
        {
            if ( parse[z] == ')' )
            {
                right++;
                if ( right == left )
                {
                    sprintf( func, "%s", parse_setvar( func, mob, actor, obj, vo, rndm ) );
                    break;
                }
                else
                    add_letter( func, parse[z] );
                continue;
            }
            else
            {
                add_letter( func, parse[z] );
                continue;
            }
        }

        if ( parse[z] == '(' )
        {
            infunc = TRUE;
        }
    }

    right = 0;
    for ( z = 0; parse[z] != '\0'; z++ )
    {
        if ( parse[z] == ')' )
            right++;

        if ( skipToRightParen )
        {
            if ( right < left )
                continue;

            skipToRightParen = FALSE;
        }

        if ( parse[z] == '(' )
        {
            int a;

            add_letter( cmnd, parse[z] );

            for ( a = 0; func[a] != '\0'; a++ )
            {
                add_letter( cmnd, func[a] );
            }
            skipToRightParen = TRUE;
        }
        else
        {
            add_letter( cmnd, parse[z] );
        }
    }
  }

  for ( x=0; cmnd[x] != '\0'; x++)
  {
    chkchar = NULL;
    chkobj = NULL;
    chkroom = NULL;
    function = FALSE;
    if (cmnd[x] == '\0')
      return results;
    if (cmnd[x] != '$')
      add_letter( results, cmnd[x]);
    else
    {
       ... section not to do with string manipulation
    }
    if (chkchar || chkobj || chkroom || function)  // Ok, so one of them is valid, lets find out what the info we're looking at is.
    {
      x+=2; // skip over the identifier and go straight into whats next...
      if (cmnd[x] != '.' || cmnd[x] == '\0') //  not a period, so this isnt a pointer, just a variable.
      {
         add_letter( results, cmnd[x-2]); // Put this piece back
         add_letter( results, cmnd[x-1]); // Put this piece back
         add_letter( results, cmnd[x]); // Put this piece back
         if ( cmnd[x] == '\0')
           return results;
         continue; // and keep going.
      }
      else  // Ok, they used the right syntax, lets fight out what our 'word' is.
      {
         char word[MIL];;
         word[0] = '\0';
         x++;
         while ( !isspace(cmnd[x]) && cmnd[x] != '\0' )
         {
            add_letter( word, cmnd[x]);
            x++;
         }
         // If we get here, we assume we now have the word.
         if (chkchar != NULL)  // Begin looking for possible character matches.
         {
             if (!str_cmp( word, "getvar"))
             {
                VAR_DATA *var;
                char arg[MIL];
                bool found = FALSE;
                arg[0] = '\0';
                int y;

                if ( word[6] != '(' )
                {
                    progbug( format( "Function GETVAR missing opening parenthesis(%c)", word[6] ), mob );
                    return results;
                }

                for ( y = 7; y <= strlen(word); y++ )
                {
                    if ( word[y] == ')' )
                        break;

                    if ( word[y] == '\0' )
                    {
                        progbug( "Illegal use of GETVAR function (End of String Reached Before Finding a Closing Parenthesis)", mob );
                        return results;
                    }
                    add_letter( arg, word[y] );
                }

                for ( var = chkchar->first_var; var; var = var->next )
                {
                    if ( !str_cmp( var->name, arg ) )
                    {
                        strcat( results, format( "%s", var->value ) );
                        found = TRUE;
                    }
                }

                if ( !found )
                {
                    progbug( format( "Getvar: Var not found on char(%s)", arg ), mob );
                    strcat( results, "NullVar" );
                }
             }
         }
         if (cmnd[x] == '\0')
          return results;
         else
          add_letter( results, cmnd[x]);
      }
    }
  }
  return results;
}
Amended on Mon 08 Jan 2007 05:19 PM by Gohan_TheDragonball
USA #13
called in function mprog_do_ifcheck()

doneargs:
    q = p;
    while ( isoperator(*p) ) ++p;
    strncpy(opr, q, p-q);
    opr[p-q] = '\0';
    while ( isspace(*p) ) *p++ = '\0';
    rval = parse_setvar( p, mob, actor, obj, vo, rndm );


in case you haven't enountered it before, the add_letter function

void add_letter( char *string, char letter)
{
  char buf[MIL];

  sprintf(buf, "%c", letter);
  strcat(string, buf);
  return;
}
USA #14
i finally had some time off from work and went through and fixed this code, in case anyone was waiting for the fix i commented around the addition to the code that fixes it, the following code is flawlessly working version.


char *parse_setvar( char *cmnd, CHAR_DATA *mob, CHAR_DATA *actor, OBJ_DATA *obj, void *vo, CHAR_DATA *rndm )
{
  static char results[MSL*2];
  int x;
  CHAR_DATA *chkchar = NULL;
  OBJ_DATA *chkobj = NULL;
  ROOM_INDEX_DATA *chkroom = NULL;
  bool function = FALSE;
  char parse[MIL];
  sprintf( parse, "%s", cmnd );
  results[0] = '\0';

  if ( !str_infix( "(", parse ) )
  {
    int left = 0, z, right = 0;
    char func[MIL];
    bool infunc = FALSE, skipToRightParen = FALSE;

    cmnd[0] = '\0';

    for ( x=0; parse[x] != '\0'; x++ )
    {
        if ( parse[x] == '(' )
            left++;
    }

    func[0] = '\0';
    infunc = FALSE;

    for ( z = 0; parse[z] != '\0'; z++ )
    {
        if ( infunc )
        {
            if ( parse[z] == ')' )
            {
                right++;
                if ( right == left )
                {
                    sprintf( func, "%s", parse_setvar( func, mob, actor, obj, vo, rndm ) );
                    break;
                }
                else
                    add_letter( func, parse[z] );
                continue;
            }
            else
            {
                add_letter( func, parse[z] );
                continue;
            }
        }

        if ( parse[z] == '(' )
        {
            infunc = TRUE;
        }
    }

    right = 0;
    for ( z = 0; parse[z] != '\0'; z++ )
    {
        if ( parse[z] == ')' )
            right++;

        if ( skipToRightParen )
        {
            if ( right < left )
                continue;

            skipToRightParen = FALSE;
        }

        if ( parse[z] == '(' )
        {
            int a;

            add_letter( cmnd, parse[z] );

            for ( a = 0; func[a] != '\0'; a++ )
            {
                add_letter( cmnd, func[a] );
            }
            skipToRightParen = TRUE;
        }
        else
        {
            add_letter( cmnd, parse[z] );
        }
    }
  }

  // Fixed with one simple correction
  results[0] = '\0';
  // End Fix

  for ( x=0; cmnd[x] != '\0'; x++)
  {
    chkchar = NULL;
    chkobj = NULL;
    chkroom = NULL;
    function = FALSE;
    if (cmnd[x] == '\0')
      return results;
    if (cmnd[x] != '$')
      add_letter( results, cmnd[x]);
    else
    {
       ... section not to do with string manipulation
    }
    if (chkchar || chkobj || chkroom || function)  // Ok, so one of them is valid, lets find out what the info we're looking at is.
    {
      x+=2; // skip over the identifier and go straight into whats next...
      if (cmnd[x] != '.' || cmnd[x] == '\0') //  not a period, so this isnt a pointer, just a variable.
      {
         add_letter( results, cmnd[x-2]); // Put this piece back
         add_letter( results, cmnd[x-1]); // Put this piece back
         add_letter( results, cmnd[x]); // Put this piece back
         if ( cmnd[x] == '\0')
           return results;
         continue; // and keep going.
      }
      else  // Ok, they used the right syntax, lets fight out what our 'word' is.
      {
         char word[MIL];;
         word[0] = '\0';
         x++;
         while ( !isspace(cmnd[x]) && cmnd[x] != '\0' )
         {
            add_letter( word, cmnd[x]);
            x++;
         }
         // If we get here, we assume we now have the word.
         if (chkchar != NULL)  // Begin looking for possible character matches.
         {
             if (!str_prefix( "getvar", word))
             {
                VAR_DATA *var;
                char arg[MIL];
                bool found = FALSE;
                arg[0] = '\0';
                int y;

                if ( word[6] != '(' )
                {
                    progbug( format( "Function GETVAR missing opening parenthesis(%c)", word[6] ), mob );
                    return results;
                }

                for ( y = 7; y <= strlen(word); y++ )
                {
                    if ( word[y] == ')' )
                        break;

                    if ( word[y] == '\0' )
                    {
                        progbug( "Illegal use of GETVAR function (End of String Reached Before Finding a Closing Parenthesis)", mob );
                        return results;
                    }
                    add_letter( arg, word[y] );
                }

                for ( var = chkchar->first_var; var; var = var->next )
                {
                    if ( !str_cmp( var->name, arg ) )
                    {
                        strcat( results, format( "%s", var->value ) );
                        found = TRUE;
                    }
                }

                if ( !found )
                {
                    progbug( format( "Getvar: Var not found on char(%s)", arg ), mob );
                    strcat( results, "NullVar" );
                }
             }
         }
         if (cmnd[x] == '\0')
          return results;
         else
          add_letter( results, cmnd[x]);
      }
    }
  }
  return results;
}
USA #15
Are you ever going to release your full script system maybe one day?
USA #16
I released the script system on mudbytes a couple months ago. however sometime this week i will update it to include the new parenthesis evaluator addition.

http://www.mudbytes.net/index.php?a=files&s=viewfile&fid=740

Another thing I will also include is my update to the variable code. I made it so builders can create and access variables on a player mobile. Originally I used some snippet that allowed access to 5 variables on a character, but I felt it was limited, so I went added the ability to do stuff like:

setvar $n lastloc South City

In this example I have a policeman with a quest to help find his baton, and that variable is used to help with what city the baton might have been lost in, since the city it is lost in changes each time.

say I am not too sure, but I would suggest looking in the vicinity of $i.getvar(lastloc) its the last place i remember having it.
USA #17
So is this script system a replacement of, or an addition to the existing prog system? It looks like much of what you've done finishes the system that the original Smaugdevs probably wanted. Like so many other things they wrote but never finished. :)
USA #18
Well many of it is in addition to prog system with minor modifications to the original system. The script system before was merely a prog that performed at a specific time at which it went line by line, ifchecks were evaluated but not adhered to when the next line was performed as it returned each time. So what I did was do a force mpsleep when a script goes off, saving the data like ifchecks and performers each time. It still goes line by line, but now will perform correctly. Another thing I did was make it so you can have multiple scripts inside one base script group.

Group = Mining
Script 0: Setup Variables for Miner, Verify shovel, check if miner has max amount of minerals.
Script 1: Decide whether to try and mine current room or travel towards predetermined room from script 0.
Script 2: Travel back towards assigned shop and drop off minerals.

Unless specified by the script, whichever script the group is currently running will loop itself. If the script needs to stopped they can jump to script -1, and as long as the group is on -1 it won't run.

Also added was the variable stuff. The variables have a name, they have a value, and also have two fields called allow and restrict. this way a builder could restrict that variable from only being modified by a specific vnum, whether that be room, object, or mobile. An example of the ifcheck for these is.

if var($i, randnum) <= 30

Also I fixed it so the above ifcheck works, stock smaug didn't work very well with multiple arguments that weren't the symbols like $n or $t.

Another thing added was the $f siginfier, which allows a builder to access a coded function like rand or increment or add, eg.

setvar $i randrank $f.rand(20,100)

or

if str($n) > $f.add(20,$f.rand(10,15))
USA #19
You know, it seems to me that it would be very easy to write a scripting system in Lua, that keeps basically the same syntax (modulo just a few tiny changes) and gives you the full power of Lua. The hard part would be binding the functions to their C equivalents, but really, that isn't all that hard to do, either.

It seems to me that it would be a much better use of time to do that (or use any other scripting language) than to continue trying to hack at the fundamentally broken SMAUG mudprog language.

I'll give this a think and see what comes up. Of course, my codebase is quite far from stock, so I'm not sure how useful it would be to other people if I released anything I might do for my own code.
USA #20
well i guess that depends on what would need to be changed, considering you would mostly likely have to completely redo everything anyways, whether or not your mud is stock or almsot a new codebase should not matter.

and no matter what you keep saying, i think the mob progz in smaug work wonderfully, and with the additions i have made to it, i don't need any other system.
USA #21
No, to introduce Lua you wouldn't have to redo everything at all. You'd need to redo the entrance points of the script, and figure out a way to bridge to the if-checks and so forth. That would be the hardest part.
But if I were to do something for my MUD, it wouldn't be easy to port to stock SMAUG(FUSS) because the data structures by this point are quite different (it's C++, for one). I mean, it wouldn't be impossible, but for inexperienced coders looking for a plug'n'play solution, it wouldn't be at all appropriate.

The reason the mudprog language isn't good is because the whole thing is a hack. Even all this work you've done isn't right yet. Does it handle multiple sets of parentheses? Nested parentheses? Have you implemented functions? Do you have proper control flow? Can you make function calls inside function calls that use player attributes? Can you do all of this with functions of several arguments? Can you construct expressions, e.g. boolean expressions, of arbitrary complexity? Do you have any kind of type checking, script variables, and so forth? Do you have complete (or even very high) confidence that what you wrote will work in all cases it claims to cover?

Sure, mudprog "gets the job done" (usually), but the fact that you've had to make all these changes should raise a red flag. And you're still a very far ways away from a proper language that lets you express more or less arbitrary constructs. I just don't see the point anymore of spending so much effort on mudprog, when the whole thing is built on a hack and it flies in the face of any kind of good compiler practice.

See, mudprog becomes so hackish that the code becomes very hard to read and understand. It's built by people who (no offense) didn't really know much about compilers and so did what they could and got something that seems to work. But there are much simpler ways of achieving their goals, if you know something about compilers. The resulting code is much cleaner and easier to maintain. I look at your code and it's nearly incomprehensible; I have to stop and look at it for a long time before it starts making sense. It shouldn't be that way; especially given how easy this problem should have been, that's a sign that the code is dangerously complex.

But hey, if you're happy with it and it serves your purposes, more power to you. I'm just trying to share the benefit of my experience over the years. The short version of what I'm trying to say is:

You (or anybody developing their own scripting system, as I was a few years ago) will spend more time developing your home-grown, very likely to be inferior solution, than you would embedding a tested and supported 3rd-party scripting language, which would give you a considerably superior solution anyhow.

YMMV, of course.
USA #22
With all due respect, you aren't likely to find very many people in mudding who do know anything about compilers. Aside from "type make, then pray". I'm going to take a wild guess here and say Gohan probably is one of those who does not know enough about compilers to "do it right", so he's doing the best he can with what's available.

Mudprogs may be a foul hack, but they're a foul hack that works. I see nothing at all wrong with trying to turn it into less of a foul hack and more into something that works. Even if it's messy as hell, builders won't be digging around in the source code and marveling at the huge mess :P

I'm sure there would be plenty of people who would appreciate seeing something shiny, new, modern, and non-hackish. But the vast majority of folks I talk to, and myself included, are not up to the task of converting Smaug or any other codebase to use Lua instead of the prog system. I might be able to take an existing implementation and backport it to the C codebase because I'm familiar enough with how that stuff sticks together but I'm sure there's plenty of folks who can't even do that much because their C/C++ knowledge isn't that far along.
USA #23
No, like I said, if it works, then great, but sometimes it is better (both in terms of time necessary and quality of end result) to start over than to work on top of a "foul hack". I am convinced that mudprog is one of those cases. That's why I started writing my own scripting system several years ago, but it never got anywhere because (a) I did not at the time have enough background to do it right, and I was lacking a lot of useful features like type checking, and (b) I realized (in part due to e.g. Nick's suggestions) how silly it was to reinvent the wheel when there were perfectly good scripting engines, such as Lua (or Python or Ruby or whatever) that had passed the test of time/usage and (more importantly) were actively maintained and bug-fixed by someone else.

Well, tell you what. I'll give serious thought to adding Lua to the SMAUGfuss codebase, or on my own codebase and hope that somebody can back-port it. I don't know if I'll have time, but I'll see what I can do. I've been thinking of doing it for my own codebase anyhow, and with any luck the bulk of it will be portable. It would probably do the community quite some good, I imagine. Of course, existing mudprogs would have to be rewritten somewhat, e.g. say hello there, how are you would have to become say "hello there, how are you".
USA #24
Samson, is SMAUGfuss 1.7 the most appropriate code for me to work on were I to submit a patch for this Lua stuff?
USA #25
Yes, SmaugFUSS 1.7 is the most current. The download is kept up to date with the official bugfix posts, so don't let the file date in the downloads area fool you.

And I thank you ahead of time for at least looking into it. If you end up not having time to backport it yourself, you can feel free to offer what you can from your own codebase and I'm sure someone involved with FUSS will be able to help backport it if I end up not having time for that myself.
USA #26
If you could add lua to smaugfuss, would it use the same editor or a new one? If it was the same the snippet to allow "/" to make multple lines to be treated as one, would have to be removed right?
USA #27
I was thinking of using the same editor, yes. One problem is that SMAUG uses twiddles to end strings, but Lua uses them for such things as the ~= test. They'd need to be escaped one way or the other.

Lua doesn't really care about line endings, though. So the question of treating several lines as one wouldn't really come up, because the Lua parser takes care of that for you.

One nifty feature would be for the Lua display to have syntax coloring...

As I think about this, I will add more posts discussing the various issues. The main issue I am thinking about is how, if at all, to manage coroutines. E.g., pausing a script while waiting for player input.
USA #28
I though it was a tilde, heh. However you said say would become more like say "String" well in my mud if it went over multiple lines, using it the way it is, would give me say "Line one/ Line two" and that'd give us a nice pretty / in there. And into the Mpsleep thing, wouldn't following how Mpsleep worked work just as well, or I know I've seen Nick argue about this once or twice about the forum's talking about it'd freeze the entire mud or some such?
USA #29
Well, Lua doesn't allow multi-line strings with the "..." syntax; you need to use [[...]] to span multiple lines. So that would be one complication, yes.

As for mpsleep, the problem is that we need a way to pause the interpreter at that point in the script and resume it later on, while allowing other scripts to be run in the meantime, which means that you can't simple pause the entire interpreter. Lua's natural solution is to use co-routines, which implies that some kind of management is needed to keep track of which ones are running, and which ones need to be resumed, and so forth.

Of course, once you implement a coroutine manager like that, you are so close to having much, much more power available, namely letting users use coroutines to do really powerful stuff like pause until user input is received. The balance I am trying to strike is to support as many powerful features as possible, while at the same keeping the project manageable so that I can implement it in a small amount of time.

The issue of backwards compatibility is also, naturally, rather worrisome.
USA #30
naturally.....(long long stare as his mind is soooo jumbled right now)
USA #31
Well Backwards capable could be worried after the first is working then dumbed down later or some such?
USA #32
What do you mean? I was hoping to have some kind of system where a SMAUGfuss MUD could be more or less seamlessly moved from the current system to a Lua system. Unfortunately I'm not sure how feasible that is, although it would probably work in most cases. But what do you mean about having it only at first and then dumbing it down later?
USA #33
Well normally when one thinks of backwards capabilty you think of having it work with older verison of said thing. I was implying why not just get it working the way you think it should with the stock, and then if it was needed on older ones, to dumb it down to have it work in the older verisons after the orginal is finished? I donno exactly how things work, but thats usually what I think of when I hear backwards capability.
USA #34
Oh... no, by backwards compatibility I mean letting old mudprog scripts work in the new version. (Backwards compatibility means that older things (e.g. data) still work in the new version of the program, not that the new one works in older versions.)
USA #35
Couldn't you just make something to automatticly change the bulk of the old mess and convert it to the new? Then what really cant be converted give instruction on how to convert it. Or is trying to convert it to much, and wanting to keep things "ease" have to make it read the ifs and all that from the old system...
USA #36
That was my plan, yes, to automatically convert everything that can be reasonably converted. I would need to write a small program that parses mudprog and outputs equivalent Lua. Hopefully, all constructs will be convertable; mudprog doesn't do anything terribly complicated.

My hunch at the moment is that I should cross this bridge when I come to it. It might turn out that this is really easy to do; or, it might turn out that it's so complicated that it'd be better to just rewrite things in Lua. The syntax is very similar, after all.