Jump to content




Error Message Parsing W/ Patterns


  • You cannot reply to this topic
3 replies to this topic

#1 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 19 October 2013 - 12:59 AM

Firstly, ugh sometimes I hate patterns. I'm still not the best I can be with patterns, there are much better people out there, so I've come to you all for help.

Problem

Ok so I'm currently writing a game, and I've got a function, lets call it validate, that can validate supplied values and invokes error when said values are incorrect. The problem I'm facing at the moment is that this validate function can be called from any level, but I want to blame to still be pointed to the original caller.

For example I've got a "class" tree, and lets say class x calls class y as it "extends" it, and class y throws and error because the data supplied to it by x, which is supplied by another function is wrong, the blame needs to be pointed at the function that calls x, not x which is how it currently stands, as such one specific level cannot be supplied for the error/assert function.


Problem Solution

So in order to fix this problem I've created a function that I call, called tryCatch which is the following
function tryCatch(func, ...)
  local ok, err = pcall(func, ...)
  if not ok then
	error(err, 3)
  end
  return err
end


Problem w/ Solution

The above solution works fine, however for the messages to be displayed nicely when raising an error anywhere I have to use a level of 0, otherwise the error string is polluted with for example "pcall: <<message>>", this is not ideal, I cannot assume that a level of 0 will always be used due to some of the functions that need to be called with the tryCatch function, I also plan on making this function available to anyone else wishing to add extra modules/content to the game.

As such I need to parse the error message that comes back from the pcall, so that no matter the level, the error message will be all that is raised again. This is where I'm running into problems. The error messages themselves can be a range of formats throughout, here are some examples of format (note, these are not my actual error messages)
1. error("Invalid arg #1: you dun-derped", 2) --# example output...  test:5: Invalid arg #1: you dun-derped
2. error("Not initialised", 0) --# example output...  Not initialised
3. error("Fail: you will always: fail: fear me!", 3) --# example output...  test:7: Fail: you will always: fail: fear me!
4. error("Nope: fail.png", 0) --# P.S. I know this one will be impossible, it's not important if this is handled correctly... example output...  Nope: fail.png


Attempts to Solve Solution Problem

Here are my attempts at message parsing
Spoiler
That is about all that I know how to do, like I stated, my knowledge in patterns isn't that great, but as you can see the last attempt is the closest I've got, I just cannot seem to get it to work for the third case, I don't really care about #4 it'd be great to have it supported, but I know it'll just be an edge case that get's handled wrong.

Hopefully what I've said makes sense, and you can understand my problem. Maybe you can see another way that I can handle the original problem instead of using tryCatch (do note however, it has to assume dumb users, and not require a throwback level argument). Any help is greatly appreciated.

— BIT

#2 Lyqyd

    Lua Liquidator

  • Moderators
  • 8,465 posts

Posted 19 October 2013 - 02:31 AM

Seems like it might be wise to return nil, "error text" if it should be thrown at a line number and error("error text", 0) if it shouldn't. Callers need to verify that a non-nil return value is returned and throw the appropriate error message from their location if an error message is returned.

#3 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 19 October 2013 - 03:16 AM

Assuming you want to stick with having your functions throwing "regular" errors for this tryCatch function to handle, I would stick "bios", "pcall" and "shell" in a table along with the script name and first check to see if the start of whatever error it gets exists in that table (using a basic string.sub for eg). Depending on which string from the table hit a match I'd apply a different regex (eg, in the case of #4 I'd get no match and apply no filtering at all).

It's not as elegant as a single regex (you'd have to iterate and all, assuming you can't cram all those strings into a regex and have it only look for a line number if certain ones are found??), but assuming random function names can't come back to muddy the waters (and I admit I'm also not familiar enough with errors to know) I don't see how it could be made more reliable. You'd have to go rather more out of your way to trip it up then in the examples given.

(Actually, reading further it looks like you can do conditional matches, so maybe look into that.)

#4 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 19 October 2013 - 03:32 AM

View PostLyqyd, on 19 October 2013 - 02:31 AM, said:

Seems like it might be wise to return nil, "error text" if it should be thrown at a line number and error("error text", 0) if it shouldn't. Callers need to verify that a non-nil return value is returned and throw the appropriate error message from their location if an error message is returned.
Yeh I was considering doing that, but obviously if I could find a better solution that didn't rely on people remembering to use error("msg", 0) it would have been better. I think I may just have to go back error level with 0.

View PostBomb Bloke, on 19 October 2013 - 03:16 AM, said:

Assuming you want to stick with having your functions throwing "regular" errors for this tryCatch function to handle, I would stick "bios", "pcall" and "shell" in a table along with the script name and first check to see if the start of whatever error it gets exists in that table (using a basic string.sub for eg). Depending on which string from the table hit a match I'd apply a different regex (eg, in the case of #4 I'd get no match and apply no filtering at all).
I was also considering this at one point, I just couldn't find a solution that worked well enough, I though using patterns may do the job better.

View PostBomb Bloke, on 19 October 2013 - 03:16 AM, said:

Actually, reading further it looks like you can do conditional matches, so maybe look into that.
If only Lua used regex and not patterns :P

EDIT: Oh! Thought just came to me!
Maybe I could set the function's environment to override the error function, so that even when they provide a level, it just uses 0, then restore the environment after its run. That should work right?
EDIT 2: I'm thinking this should work, will test now
Code
Spoiler
EDIT 3: Ok yep that seems to have worked perfectly, the blame was always thrown back to the expected line in this example with the correct error message... of course there was a simpler method than using patterns :P
Test code
Spoiler

Edited by theoriginalbit, 19 October 2013 - 04:03 AM.






3 user(s) are reading this topic

0 members, 3 guests, 0 anonymous users