Asset - Scripts [FREE] NSP 2 - A GML String Interpreter

Discussion in 'Marketplace' started by Surgeon_, Jun 23, 2016.

  1. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    Good day, people of GMC! Some of you already know what this is. Some of you don't. Either way, it's nothing new, just a continuation of an old thread (Link) about an old asset.

    [​IMG]

    OVERVIEW:
    -Name: N String Parser 2
    -Version: 2.6.5 (16th of March 2018)
    -Purpose: Interpreting GML strings.
    -Game Maker version: Studio 1.x*
    *-At the time of writing this (16th of March 2018) the asset is also compatible with GMS2.0.
    -Target Platform: Any
    -Price: Free
    -Documentation (.txt): Dropbox link

    -View on Marketplace: Link
    (NOTE: Due to the forum rules, I'm offering this asset through the Marketplace only; thus the license is now also changed - but it's still as unconstrained as it was before.)

    GENERAL DESCRIPTION:
    Before Game Maker: Studio, before compiling, when Game Maker was still an interpreted language, there was this function, execute_string(...). Admittedly, it was a source of much controversy, and not without good reasons - it was slow, unsafe, encouraged bad practices and pointed beginner programmers in a wrong direction. However, here's the thing - nowadays, when you run your game to test it, and realize something is not set up properly, you have to exit it, change and recompile it - which could be a very tedious process with larger games. While in the old days, you could simply pop a get_string(...) window and execute what ever you inputted, during runtime, "on the fly". There were some situations where it was simply convenient rather than necessary (parsing math expressions, for example), but, same as all functions, it was a tool at the programmer's disposal and, again, same as all functions, it would cause problems if misused. The goal of this asset is to restore that tool as much as possible, and it's up to programmers to decide in which way they will use it.

    DEFINING FEATURES:
    -The good:
    -Can read and execute GML code.
    -Can solve arithmtical and logical equations, and return their outcomes.
    -It supports if/else and with statements, and even Repeat and While loops.
    -You can set up your own tokens and operators.
    -It shouldn't crash your game in case of a syntax error, but only abort execution and report it to you. (However, error checking is not 100% foolproof and in some situations, like when trying to access uninitialized variables, your game will crash anyway.)
    -Takes up almost no memory when not in use.
    -It's free.

    -The neutral:
    -I tried to make NSP able to recognise any syntax that would be viable in GML. And while it's not perfect (You still need to put semicolons at the end of every line), it does do a pretty good job at it. On the other hand, all the loops it performs "just to be safe" make it slower than it could have been. But it's intended only for testing / debugging or minimal use when absolutely needed so losing a frame or two (and that's only if you input really long and complicated code) shouldn't matter.

    -The bad:
    -Does not support arrays / access to dynamic structures with accessors (and this is no longer a planned feature since it really is outside of the scope of this engine).
    -NSP's speed, or rather lack thereof. It is glacial and don't say I didn't warn you (though this can be offset somewhat by saving partially parsed strings as stated above).
    -Cannot directly access variables and built-in scripts, so this needs to be set up manually at initialization.
    -Underlying functions are commented in a way that probably only I can understand. But the ones you should use don't have that problem.

    -Here are some examples of what NSP managed to execute / evaluate:
    - x=my_number(x+y) / ( (20 mod 14) * ('a'=='a') / (3 - (6+4==7)) ) +16;
    - instance_create(x,y,obj_tester).vspeed=2;
    - with (other) {instance_destroy()};
    - with (instance_create(32,32,obj_tester)) hspeed=1;
    - a="my_number(1+my_number( ... +my_number(1))))))))))))))))))))))))))))))";

    -Additional:
    -DSM: NSP has a quick access functionality for a single global ds_map (use nspDsMsp to reference it if you need to). In the nsp_initialize script you'll need to enable DSM and set a reference word for it (by default it's "NSP_DSM"). Later, when executing code using NSP you can write NSP_DSM.a=5, (or instead NSP_DSM whatever name you defined), and it will assign 5 to the key "a" in that ds_map. Later, when you need to retreive that value, just use NSP_DSM.a again. You can also use NSP_DS+=a, NSP_DSM*=a etc. DSM is useful because ds_map keys can be strings and so can be accessed dynamically (in other words, when using DSM you can use any variable name, not just those defined in nsp_variable_*functions)

    -Saving: Before doing anything, NSP converts the input string into two lists, which is a more organized and accessible format for it. It turns out that this takes much more time than what comes afterwards, and that if you cave those lists you can execute the same string many times with less strain on the CPU.

    === CHANGELOG: ===
    (16/03/2018) v2.6.5 Released:
    -No changes to NSP functionality-wise, but it's now compatible with GMS2.0.

    (11/03/2018) v2.6.2 Released:
    -Fixed a bug which caused memory leaks if you passed an empty string to execute.

    (18/02/2018) v2.6.1 Released:
    -Fixed a bug where identifiers containing "mod" or "div: would be interpreted as operands.

    (21/10/2017) v2.5.7 Released:
    -Fixed a bug which caused the parser to remove pluses if they were standing directly after a closed parenthesis.

    (08/09/2017) - v2.5.6 Released:
    -Fixed errors that occurred when an operator was at the beginning of the string.
    -Added support for unary + and -
    -The parser can now utilize variable_instance_* and variable_global_* functions to access any variables dynamically and in a safer manner.
    -Added more competent string recognition through a new tidy-and-verify phase which means that you'll get fatal errors less often now.
    -Added a new function: NSP_check_saved(...) - more info in the documentation.

    (27/08/2016) - v2.5.1 Released:
    -Added support for "else" statements to complement the use of "if" statements.

    (17/08/2016) - v2.5.0 Released:
    -Added support for "return" statements, so the string-executing functions can now also return a value.
    -Fixed a small bug which would in some cases show an error for no reason.

    (23/06/2016) - v2.4.9 Released:
    -Fixed a bug which caused DSM to not be freed when freeing the rest of the parser.

    (27/02/2016) - v2.4.5 Released:
    -Some useful bug fixes.
    -Better bug reports where it was possible.

    (26/01/2016) - v2.4 Released:
    -Fixes some serious issues with control structures (if, while etc.) which would crash your game in most cases.
    -As of now, NSP 2 is considered a "stable" version.

    (25/01/2016) - v2.3 Released:
    -Fixed a bug with variable scoping which prevented long strings / code from executing properly.
    -Fixed a bug with executing strings, where NSP would pass malformed arguments if they were strings.
    -Fixed a bug with assigning values to global variables, where the assigned value would be malformed if it was a string.

    (21/11/2015) - v2.2 Released:
    -Fixed issues with DSM; It should work properly now.
    -Added support for constants via nsp_get_constant(...). Read the Documentation for more info.
    -Better syntax error checking.
    -Updated documentation.

    (20/11/2015) - v2.1 Released:
    -Fixed a major bug which prevented ifs and loops from working properly.
    -Fixed some other, smaller bugs in the code.
    -NSP_free(...) can now also clear any data stored by using NSP_save(...) function so you don't have to do it manually.
    -Removed all usage of "globalvar".

    (07/11/2015) - v2.0 Released:
    -Initial release.
    === ===

    IMPORTANT NOTE:
    Some scripts in this parser are user-editable (namely the script execution one and NSP_variable_* ones) to enable additional functionality - calling Game Maker's built-in functions or using instance variables in expressions. Just remember to save your edited version prior to updating to a new version so that you can restore your modifications later. I don't think any of these scripts will be changed in future updates, but if it happens, I'll make sure to point it out.

    Regards, Surgeon_
     
    Last edited: Mar 16, 2018
  2. ThunkGames

    ThunkGames Member

    Joined:
    Jun 20, 2016
    Posts:
    151
    Just saw this and wanted to say that I've been using these sripts for the last months or so and I haven't had a problem yet. I can't imagine how tedious it'd be to create this so thank you, Surgeon_, for truly taking one for the team.

    -David
     
  3. Yambam

    Yambam Member

    Joined:
    Jun 21, 2016
    Posts:
    82
    Last edited: Jun 23, 2016
  4. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    @ThunkGames It's really not taking one for the team, one man's tedious is another man's fun :) But I'm glad it's useful to you.

    @Yambam I'll take a look at your parser when I get the chance. As for the loops, a "for" loop is just a less versatile "while" loop, but harder to parse. So, for now, I'm not planning to add it to the parser.
     
    RichHopefulComposer likes this.
  5. chance

    chance predictably random Forum Staff Moderator

    Joined:
    Apr 22, 2016
    Posts:
    774
    Glad to see you posted this on the Marketplace. I've been thinking about making a graphing calculator, and your parser could be useful. I'll have a look at it.
     
  6. ZeDuval

    ZeDuval Member

    Joined:
    Jun 20, 2016
    Posts:
    116
    Right now NSP_evaluate_string returns the result of a expression while NSP_execute_string executes a statement(hopefully I used the correct terms). Would it be possible to let NSP_exe_str also have a return value? If the last part of a codeblock would be 'return a;', could that be parsed? I can end the codeblock with 'r=whatever;' and use then the var r outside your function but an actual return value would be even better!
     
  7. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    @ZeDuval You used the right terms and yes, it would be possible. I looked at the code right now and I don't think it would be too hard, it will probably be done by tomorrow evening. While I'm at it, some other improvements I'm planning to add are:
    • else statements,
    • script whitelists and/or blacklists,
    • #define directive (which will probably be slow as hell)
    I've also written a new lexer, which may (or may not) improve performance in terms of speed once implemented.

    When will these additional things come, that I do not know. I'm implementing them on a whim since nobody actually requested them (other than the script blacklist).

    EDIT: I'm curious to hear what you're using this for (if it's not a secret :D )
     
  8. ZeDuval

    ZeDuval Member

    Joined:
    Jun 20, 2016
    Posts:
    116
    Woah, that's awesome! Thanks in advance! I don't think I would have been able to tweak your code myself. :D
    Else-statement sounds superb and white/blacklist is of course a good addition if one wants to use your parser to let the end-user have access to the whole thing. Sudden idea: A game where the player has to enhance his Alter Ego by coding new skills and such, hehe.
    Can you explain the part about "#define directive"? I know that #define can be found in the gml-files that get exported and then added to an extension, but what are you planning exactly?

    It's not a secret. There are two things I'm working on:

    1.) Having scripts for everything seems like a unnecessary waste to me. That's why I made my obj.Method extension. The next step was a Code-Snippet-Function I will surely release at some point when certain associated scripts/functions are done. I often thought: Man, it would be great if I could provide a block of code to a function just like a variable or a value. So the next step is to create the possibility to write/use Throwaway-Code, like unnamed, immediately invoked functions in Javascript or something akin to lambda-functions or the new Arrow-functions in Javascript ES6. That's where your asset enters the arena. :)

    2.) I'm working mainly with the HTML5-Module, so I have not only GML but also Javascript at hand and can use one or the other depending on their strength - or even both combined, calling JS-Functions from GML and and vice versa. My plan is to bring this synergy to the next level by utilizing your parser. But I'm still in the phase where I try this and that, I'll let you know when there are some "tangible results" - if you're interested.

    Keep on rocking. I found NSP some month ago...somewhere?! and I think it's one of the most amazing assets out there. It makes some weird experimental stuff possible(which is fun) and even more important: It's inspiring and thought-provoking, it makes you think "Mhhh what could I do with this?"(which is awesome). I'm gonna drop a review in the marketplace but I will wait now for the Marketplace Reviewers Jam to start.
     
  9. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    @ZeDuval Thank you for the kind words. As for the #define directive I mean the thing I linked - you could instruct the parser to replace an arbitrary identifier with something else. For example NSP_define("KILL","instance_destroy()"); NSP_execute_string("if (global.health<=0) KILL;"); would just remove the calling instance if global.health would fall below 1. So basically it would be like making small macros for user convenience.

    In response to your usage, I can say that I had something similar in mind - for example sometimes I use an execute_delayed() script which takes as arguments the number of steps to wait and the code to execute as a string. And it's as simple as creating an object with a counter variable and the string to execute once the counter reaches zero. In places where it does not bottleneck performance it's much better than having dedicated scripts for every contingency.
     
  10. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    Updated to version 2.5.0

    Added the "return" statement as requested. Here's a snippet from the documentation:
    • you can use the "return" statement within the code that's executed to return a value. If you do, execution will stop there (which is expected behavior with "return" calls within scripts). As NSP can only work with numbers and strings, the returned values can only be numbers and strings. Attempting to return, for example, an array pointer will result in undefined behavior. If no return calls get triggered in the string being executed, NSP_execute_string() will return undefined.
    Also, are there any other mathematical operations you'd like to see operators for? (Like root, factorial etc.)
    As a final note, I'll probably add bitwise operators at some point.

    EDIT: This is my 100th post - Woo! :D
     
    Last edited: Aug 17, 2016
  11. Salvakiya

    Salvakiya Member

    Joined:
    Jul 17, 2016
    Posts:
    84
    would be cool if there was the option so that the code is compiled to some sort of binary or bit code... like take the script and load the individual tokens onto a buffer of some sort. so each time you execute a given string(assuming you execute the same string more than once) the second time would be a bit faster as it no longer has to parse GML text but rather execute commands.

    it would be cool if this was optimised/geared towards giving GM nammed methods/functions... or like inline scripts or something.

    there would still be a great performance hit but it would be neat.
     
  12. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    You can use NSP_save() in order to preserve the ds_lists generated from lexical analysis - and since that analysis takes up more than half the time it takes to process the whole NSP_execute_string() call, you can get a large performance boost if you plan to execute the same string multiple times.

    But for the past few days I've been contemplating writing a bytecode interpreter in GML - the lexer and parser would convert a GML string into bytecode which would then be passed to a stack-based virtual machine to process (this is basically how Python works). Now, this is all in the concept phase and wouldn't be operational any time soon, but you can hope...
     
    ThunkGames likes this.
  13. Salvakiya

    Salvakiya Member

    Joined:
    Jul 17, 2016
    Posts:
    84
    you know what... come to think of it I am not sure if buffer performance is up to snuff yet. you would have to do your own tests but iirc ds_lists perform faster for this kind of operation.
     
  14. Yambam

    Yambam Member

    Joined:
    Jun 21, 2016
    Posts:
    82
    That's funny, because that is exactly what I have been working on this week, a bytecode interpreter! xD I'm retrying to make a topic now, because the moderator chance didn't let my old topic in Tutorials and Examples which I posted two weeks ago (he couldn't think of a place to put my topic he said, I also thought, why is there no place to post scripts or assets so people can save them individually and import them manually?), so I'm going to try in Marketplace now, even though it's actually meant for Marketplace assets as far as I can see. :)
     
    Last edited: Aug 19, 2016
  15. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    Updated to version 2.5.1
    Added support for else statements to complement the use of if statements. No further explanation needed, you all know what else does...
     
    ThunkGames likes this.
  16. ThunkGames

    ThunkGames Member

    Joined:
    Jun 20, 2016
    Posts:
    151
    How do we upgrade? Do we just re-download and then re-add everything? This will overwrite variables we have added, etc though.
     
  17. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    I did not change the user-editable scripts, so save them externally and copy/paste them when you download the new version.
     
  18. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    Just a quick note guys, if you're using NSP you may also want to take a look at @ZeDuval's Function_Execute asset, it should synergize well as it accepts string inputs and NSP doesn't support many built-in functions "out of the box".
     
    ZeDuval likes this.
  19. ZeDuval

    ZeDuval Member

    Joined:
    Jun 20, 2016
    Posts:
    116
    Well, I more than once thought: If you'd like to and if it's not too much work/hassle - why not implement it into NSP? What do you think?
     
    ThunkGames likes this.
  20. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    I'll look into it when I have the time.
     
  21. Shadowblitz16

    Shadowblitz16 Member

    Joined:
    Jun 23, 2016
    Posts:
    587
    can you customize code like this..
    nsp_add_local(var name->string, get->bool, set->bool) //adds instance vars
    nsp_add_global(var name->string, get->bool, set->bool) //adds global vars
    nsp_add_constant(var name->string) //adds constants
    nsp_add_operator(var name->string, expression->string) //adds operator that performs expression
    nsp_add_function(var name->string, function->script) -> adds function
    nsp_add_keyword(var name->string, keyword->string) -> takes a game maker keyword and defines it as string
    nsp_add_special(var name->string, expression->string) -> adds special from string
    nsp_add_syntax(var name->string, syntax->string) -> takes a game maker syntax symbol and replaces it with string

    it could start off with nothing defined and you can allow the user to make their own language by calling these scripts
    so I might want to make my if keywords called "is" and my codes braces and replace them with "[", "]" charaters
    so instead of..
    Code:
    if (test == 1)
    {
        test = 0;
    };
    
    it could be
    Code:
    is (test == 1)
    [
        test = 0;
    ];
    
     
  22. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    Well, that's difficult... Best I could do is introduce a function which would sort of mimic the #define directive from C languages, that is, it would allow you to replace symbols or identifiers with something else (like macros) so you would have some freedom to define your own language, for example:
    Code:
    nsp_define("{", "[");
    nsp_define("is", "if");
    nsp_define("kill", "if (global.health<=0) instance_destroy()");
    But, ultimately, it would fall back to the typical GML syntax it uses now and that's something I cannot change. And as for variables and constants, GameMaker has no pointers, references or similar, so there would be no way (unfortunately) to add them during runtime with functions.

    EDIT: It would also be even slower that it already is, I might add. Also, if you're making a game with or around this, be careful as to not let users input bad syntax because NSP definitely is not 100% foolproof and it could crash the game.
     
    Last edited: Oct 17, 2016
  23. Shadowblitz16

    Shadowblitz16 Member

    Joined:
    Jun 23, 2016
    Posts:
    587
    ok thats to bad hopefully in the future
     
  24. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    I'm sorry to disappoint you, but I'm not even going to try and make a parser which would accept language/syntax arbitrarily defined during runtime. Even if we ignore how difficult it would be, it's completely beside the point of this asset.
     
  25. Shadowblitz16

    Shadowblitz16 Member

    Joined:
    Jun 23, 2016
    Posts:
    587
    oh ok :(
    can you at least make variables and functions defined by a function instead of having to manually edit the scripts?
     
  26. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    I'd live to but it's impossible due to GameMaker being a compiled language now (there's no way to connect a string and an identifier, and GameMaker's built-in functions have no ID's with which to reference them).

    But go ahead and tell me why you need all this, maybe I could help you then? Maybe you'd be better off working with an interpreted language such as Python, or you're just doing something wrong.
     
  27. Shadowblitz16

    Shadowblitz16 Member

    Joined:
    Jun 23, 2016
    Posts:
    587
    well I was thinking of possibly using a ds_list and then loop though it every time you reach something that could be a variable.
    something that starts with a letter has no symbols in it and is not surround by quotes.
    if it has a "(" at the end before another character it checks if it is a function else it checks if it is a variable.
    if its neither it could trow an error.

    I wanted it so that I could easily make a modding api for my game without having to edit the scripts
     
  28. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    @Shadowblitz16 You can use the DSM (read in the opening post) for dynamic "variables".

    But anyway, I've said this before and I'll say it again - This parser is simply way too slow (hundreds to thousands of times slower than native code) to be a viable modding mechanism, and that's not even its purpose. So it would be in your best interest to open a new thread and see with other people how they handle mods in their games.

    I'm sorry I couldn't help you more than that.
     
  29. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    Until today I didn't realize that the download counter for free assets didn't work, so for the last few months I thought that NSP2 was forgotten and stuck at its 68 downloads... But I counted it manually and it's actually about 530 at the moment :D I can't know who bought it, so I'll thank people in general for the recognition (it feels nice knowing I've made something that's actually being used, haha).
     
  30. JeTSpice

    JeTSpice Member

    Joined:
    Nov 14, 2016
    Posts:
    56
    Oh, yeah, this is great. I'm primarily using this for config files which have a random element to them. For example, werewolf.txt might have a line that says strength=irandom(4) + 2. The advantage being, if werewolves are too tough, I can adjust their strength easily without having to re-publish the game everywhere. But it looks like the "irandom" command is not supported, so I'm digging digging to see if I can make it work. If you know off-hand what class to look in, let me know. This is just the type of powerful parser I was hoping was available.

    Edit: ok, that was actually pretty easy. There's a switch...case in nsp_execute_script. putting this code in there made it work:

    case "irandom":
    return irandom(argument1[0]);

    I've only tested for my needs, which is just using literal numbers. I haven't tested irandom(someVariableName)

    in GML, a line like this:
    var werewolfStrength = NSP_evaluate("irandom(10) + 100");
    will instantiate werewolfStrength to a number from 100 to 110, inclusive.
     
    Last edited: Jul 11, 2017
  31. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    Let me quote the NSP manual (included in the download), which I assume you did not read:
    Code:
    >>SUPPORTED BUILT-IN SCRIPTS:
      - show_message(),
      - instance_create(),
      - instance_destroy(),
      - game_restart(),
      - room_goto_next().
     
      *How to add more: Because asset_get_index does not work on built-in scripts, these have to be added
                        manually. Find the nsp_execute_script(...) script (in the Main parser section).
                        Follow the already established pattern to add more scripts to use (though you can
                        also remove them to prevent them from being used).
    Also, are you sure putting randomized stats in the configuration file is a good idea?
     
  32. twenty3

    twenty3 Member

    Joined:
    Jul 13, 2017
    Posts:
    1
    It seems like whitespace messes up the interpreter quite a bit (v2_5_1). Any ideas of how to execute something like:
    Code:
    if (keyboard_check(vk_right)) { x += 1; }
    if (keyboard_check(vk_left)) { x -= 1; }
    
     
  33. JeTSpice

    JeTSpice Member

    Joined:
    Nov 14, 2016
    Posts:
    56
    @Surgeon_ Yes, you are right, I did not read far enough into the "how to make more" section.

    Random stats in a config file - its for third parties who may want to configure, but not code.
     
  34. YellowAfterlife

    YellowAfterlife ᴏɴʟɪɴᴇ ᴍᴜʟᴛɪᴘʟᴀʏᴇʀ Forum Staff Moderator

    Joined:
    Apr 21, 2016
    Posts:
    2,298
    If you want to execute "proper code" rather than separate expressions, should probably use something that is made for that in particular. There are already two extensions that run Lua (one by me, one by Rousr) and a few attempts to write scripting languages in GML itself (like SM Language).
     
  35. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    That's strange, it should by all means work with whitespaces. If you have a proper line break between those two lines of code maybe that's the issue? I'll take a look at it when I have the time...

    Edit: After a bit of tinkering, I concluded that it's the newline and not the whote space that screws it up. For example, this works fine:
    Code:
    NSP_execute_string("if (keyboard_check(vk_right)) { x += 4; }; if (keyboard_check(vk_left)) { x -= 4; }");
    Also, you need to put semicolons after closing curly brackets if you intend to have more code after it (that's how it works).
     
    Last edited: Jul 24, 2017
  36. Geqsogen

    Geqsogen Member

    Joined:
    Aug 26, 2017
    Posts:
    4
    Hello.
    I consider the result of a string variable.
    Example:
    left_side=NSP_evaluate("8/2+5-4*9");
    And everything is fine.
    But when there is an error in the equation, the program closes with an error:
    left_side=NSP_evaluate("/2+-*9");
    And I want that I get the answer - error in equation.
    How to solve a problem?

    Error if the variable starts with a sign (+ - * /).
    Example:
    left_side=NSP_evaluate("-8");
    Code:
    string_digits argument 1 incorrect type (undefined) expecting a String (YYGS)
     at gml_Script_nsp_is_number (line 10) - if string_digits(argument0)=""
    ############################################################################################
    --------------------------------------------------------------------------------------------
    stack frame is
    gml_Script_nsp_is_number (line 10)
    called from - gml_Script_nsp_convert_to_list (line 197) -      if !nsp_is_number(list[|i-1]) and nsp_is_number(list[|i+1]) and real(list[|i+1])>0 {
    called from - gml_Script_NSP_evaluate (line 15) - nsp_convert_to_list(argument0,nspListStr);
    called from - gml_Script_scr_formula_compare (line 19) - left_side=NSP_evaluate(left_side);
    called from - gml_Object_obj_button_yes_LeftButtonPressed_1 (line 3) - scr_formula_compare();
     
    Last edited: Aug 26, 2017
  37. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    Hi, @Geqsogen.

    I'll be honest with you here. When making this asset, I was counting on users providing correct code because parsing code is very difficult and I don't think that I'd be able to implement a completely fool-proof syntax checker, at least not in any reasonable timespan. With that said, I'll see what I can do about this but really the safer solution would be for you to limit the user's input: use only numbers, mathematical operators and parenthesis, and prevent them from putting two operators next to each other, or at the beginning and end of the string to be evaluated. Something along those lines.

    Best of luck mate :)
     
  38. Geqsogen

    Geqsogen Member

    Joined:
    Aug 26, 2017
    Posts:
    4
    Hello, Surgeon_
    I understood you. I will look for variants of preliminary verification - I even found something "GML Regular Expressions".
    But you have a mistake in the code! An expression with a minus at the beginning of a variable is correct and must return the result. But your code closes the program with an error. Example: "-8" or "-956 * 2 + 9"
     
  39. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    Updated to version 2.5.6
    Introduced some big improvements:
    -Fixed errors that occurred when an operator was at the beginning of the string.
    -Added support for unary +, but more importantly, for unary -, which means that you can now have expressions like " - ( - ( -4 + 7) ) ". It's not the fastest solution but it works.
    -The parser can now utilize the not-so-new-anymore variable_instance_* and variable_global_* functions to access any variables dynamically and in a safer manner, though the old system is still there and available should you need it.
    -Added more competent string recognition through a new tidy-and-verify phase which means that you'll get fatal errors less often now.
    -Added a new function: NSP_check_saved(...) which can be used to check whether pre-parsing a string (using NSP_save(...) ) produced usable results or detected faulty code (still not 100% foolproof though).

    @Geqsogen You should try it out now and see if it works for you.

    @Everyone There were some big rewrites for this update so if you catch any bugs / issues please let me know.
     
    Last edited: Sep 8, 2017
    Geqsogen likes this.
  40. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    Updated to version 2.5.7
    -Fixed a bug which caused the parser to remove pluses if they were standing directly after a closed parenthesis.
    [Thanks to @DT Mark for spotting it :)]
     
    Geqsogen and DT Mark like this.
  41. ze1

    ze1 Member

    Joined:
    Jul 9, 2016
    Posts:
    15
    Does this work with Game Maker Studio 2?
     
  42. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    I couldn't tell you with certainty, as I don't have GMS2. But I'm guessing it would work because it's all native GML, no DLLs or anything like that (I'm not sure if they changed the function names though). Well, it's free, so it doesn't hurt to try...
     
  43. Wadlo

    Wadlo Member

    Joined:
    Dec 16, 2017
    Posts:
    40
    Surgeon_, this is a remarkable bit of code you've written. It's proving to be very useful for debugging. I do have a question, though. It seems the code is unable to run recursively. An example I was trying to run to understand your code a little better was this:

    Code:
    objectOne create event {
        NSP_initialize();
        NSP_execute_string("instance_create(0,0,objectTwo);")
    }
    
    objectTwo create event {
        NSP_execute_string("show_message('hello world');");
    }
    Maybe I'm just doing something wrong. But what I think is happening is the NSP_execute_string function requires the changing of some global values created in NSP_initialize(). Since the first NSP_execute_string hasn't yet finished and the creation of the new object calls the function again, the global values needed by the first call are replaced. Hope I'm making sense. Anyway, what would have to be rewritten in order to fix this problem? Thanks again for your contribution of this useful code.
     
  44. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    @Wadlo You're right. It does it use globals and that's why it doesn't work (I don't know why I thought that was a good idea at the time...). Anyway, I can fix that for you - it shouldn't take too much time.
     
  45. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    Updated to version 2.6.0
    -Refactored NSP2 so that it doesn't use global variables anymore, meaning that calls can be recursive now. To my great amusement, you can even do NSP_execute_string("NSP_execute_string('x+=32;')"); and the like :) (but you probably shouldn't).
    -Fixed some minor bugs...

    @Wadlo Here, I fixed it for you :)
     
  46. Wadlo

    Wadlo Member

    Joined:
    Dec 16, 2017
    Posts:
    40
    Thank you. It definitely would have taken me a very long time to do it myself :D...
     
  47. Samuel Venable

    Samuel Venable Time Killer

    Joined:
    Sep 13, 2016
    Posts:
    1,166
    This reminds me a lot of my extension that can execute and evaluate VBScript and JScript. It's very similar, only it is interpreting GML instead. Do you intend to ever release the source code? This could easily be combined with my extension actually, and could also be used to create ActiveX DLL's running GML, which could be called from VBScript's CreateObject("ProgID") or JScript's ActiveXObject("ProgID") functions.
     
  48. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    This is not really an "extension" in GameMaker terms, but rather just a set of scripts. You just download them from the marketplace and the source is all there. (Obviously it's native GML so not very fast.)
     
    Samuel Venable likes this.
  49. Samuel Venable

    Samuel Venable Time Killer

    Joined:
    Sep 13, 2016
    Posts:
    1,166
    Oh I gotcha, then I guess what I had in mind isn't really possible. lol. But this is still a really cool feature. Well done. :)
     
  50. Surgeon_

    Surgeon_ Symbian Curator

    Joined:
    Jun 22, 2016
    Posts:
    248
    Updated to version 2.6.1
    I uploaded a small patch that fixes a bug that didn't allow you to use identifiers containing "div" or "mod" because the interpreter would interpret those as operators.
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice