Edit: I highlighted the changes to make them easier to see.
That gives me an error: 'max value modifer is not a number'. (Mind you, I'm testing this with a custom action using characterlevel instead of classlevel so that I don't have to resave a homebrew just to test snippet syntax. I'll see what will make this work.)
They want “10 + Cha mod (minimum 1) + the level of their Pact Magic slots. Because of the way Pact Magic progresses, that should be “Warlock Level divided by 2 (rounded up), to a maximum of 5 at 9th level.”
They want “10 + Cha mod (minimum 1) + the level of their Pact Magic slots. Because of the way Pact Magic progresses, that should be “Warlock Level divided by 2 (rounded up), to a maximum of 5 at 9th level.”
That gives 'Unknown value modifier type: roundup)@max'
But this works:
{{(classlevel/2)@roundup,max:5}}
Like I said, the parsing is.... odd.
Edit: I... I just... wanna... rip into the parser code and fix it sooooo bad....
Hm-mm! Well, I'm glad you implemented that PMDAS, though it feels rough around the edges. But yeah, the parser doesn't allow spaces. Also no negative operator after an open-paren, which at least can be accomplished with "(0-[expression])".
I've done programming work in the past involving parsers and script interpreters, including some reverse-engineering, so I pick up on these things. I've been tempted to do a complete analysis of the snippet parser since it can be tricky to suss out why it refuses some syntax but not others; unfortunately I also fear causing a lockup. Can you recall any especially "oh heck don't type that!" code we should avoid?
Also, did you use a particular library to implement the parser, or like regex?
I believe negative operators should work. I am pretty sure I had specific tests for that. Again I had a range of tests I wrote in the rewrite to handle all kinds of cases and obviously don't have access to them anymore.
And under the hood its just a while loop that parses out groups, replaces them with a placeholder, and then calculates the individual placeholders and returns values. The reason why I did placeholders was so that if you did the same operation twice, why do the work more than once. ex: {{(modifier:con*2)+(modifier:con*2)}} gets placeholdered down to {{(__PLACEHOLDER__1__)+(__PLACEHOLDER__1__)}} and i evaluate what modifier:con*2 returns and place it back into the equation.
And there are no libs involved in the parsing, I looked into some math libs and seemed overkill for what I needed. I just researched some opensource simple PEMDAS libs and modified it to fit our needs.
The way the snippet parser would have seen this is the following simplifications: classlevel/2@roundup
It interprets that as classLevel / (2 rounded up).
It is no different than making a snippet that says 2+3*4/5@roundup. The snippet parser can't infer that you want to round up that whole equation once it has been evaluated and is why you should use it on a "group" with parenthesis: (2+3*4/5)@roundup
They want “10 + Cha mod (minimum 1) + the level of their Pact Magic slots. Because of the way Pact Magic progresses, that should be “Warlock Level divided by 2 (rounded up), to a maximum of 5 at 9th level.”
That gives 'Unknown value modifier type: roundup)@max'
But this works:
{{(classlevel/2)@roundup,max:5}}
Like I said, the parsing is.... odd.
Edit: I... I just... wanna... rip into the parser code and fix it sooooo bad....
This one has two bugs I see and then it also would run in to the same problem as above
Bug 1: Doing a snippet that is wrapped in parenths must not be handled
works: {{(characterlevel/2)@roundup}} doesn't work but should: {{((characterlevel/2)@roundup)}}
Bug 2: The variable modifier regex is being too greedy and extends past where it should
It sees this snippet {{(((classlevel/2)@roundup)@max:5)}} and thinks the modifier key is "roundup)@max"
Even if the two bugs were solved, this exact snippet still wouldn't work because of the same problem as above where you are attempting to max a fixed number which isn't supported.
where classlevel = 10 would be first processed from {{(((classlevel/2)@roundup)@max:5)}} and would do one pass and get it to {{((5)@max:5)}} which becomes a problem
You say they should work but don’t. Why would ((5)@max:5) be a problem?!? Isn’t the whole point of @max:X to set a limiter on what the highest number generated should be?!?
Obviously ((10+modifier:cha)@min:1) works just fine.
Obviously (classlevel/2)@roundup works just fine.
Obviously it should all work to allow a calculation of (10+modifier:cha)+(((classlevel/2)@roundup)@max:5)
I've also not found a way to pass a non-integer or a negative number to max or min. Like, try making changes to my Suffocation snippet, adding parens at places where it'd make sense:
@DDB if you folks would like someone who can fix a parser/regex tokenizer (given enough time to know your particular software), well, I'm underemployed...
Rollback Post to RevisionRollBack
Helpful rewriter of Japanese->English translation and delver into software codebases (she/e/they)
Not trying to rag on you Scobenes, parsers/compilers/interpreters are an arcane art in themselves. Thank you so, so much for the information and insight you're providing.
Rollback Post to RevisionRollBack
Helpful rewriter of Japanese->English translation and delver into software codebases (she/e/they)
Not trying to rag on you Scobenes, parsers/compilers/interpreters are an arcane art in themselves. Thank you so, so much for the information and insight you're providing.
yea no offense taken. I'm human and definitely make mistakes but I enjoyed putting it together.
Just FYI it is not just regexs, it is like 3 or 4 regex that convert strings to meta information, and then evaluates fragments, and sub fragments that are replaced in multiple passes. Glad to give ya insight. We had lots of cool ideas for snippets I remember spec'ing out with Stormknight.
Also the more I've thought about it, the more I remember doing stuff with negative numbers, but I just can't remember the specifics. I don't think I would have tested passing negative numbers to min/max. That seems like a case of "why would someone do that", until someone would have pointed out an example of a legit case why.
You are right that decimals are not something that it handles well. This is because fractions are just not something that are in the DND rules. It is generally all whole numbers, so there wasn't a big press to support them since you could do something like you said: {{(1/3)*modifier:con}}
You say they should work but don’t. Why would ((5)@max:5) be a problem?!? Isn’t the whole point of @max:X to set a limiter on what the highest number generated should be?!?
Obviously ((10+modifier:cha)@min:1) works just fine.
Obviously (classlevel/2)@roundup works just fine.
Obviously it should all work to allow a calculation of (10+modifier:cha)+(((classlevel/2)@roundup)@max:5)
So why doesn’t it?!?
I am saying they should work because there is nothing technically wrong with it, just a specific edge case I probably didn't have a test for. I remember doing multiple groupings of parenths stacked on each other, but I don't remember wrapping just a parenths around everything that doesn't provide "value". And in isolation, (5)@max:5 is not technically a problem, but I believe I set it up so that certain variable types (in this case 5 is a fixed value) don't get to use variable modifiers. I Believe the snippet in the third example that doesn't work is because (classlevel/2)@roundup is replaced in one pass, and then the fragment is evaluated again, and sees it as (5)@max:5 and that is not coded to work bc it is "pointless" bc it sees it as you typing that in, and if you were saying that, why not just put 5. It doesn't keep track of what you typed in the snippet and what it replaced and re-evaluated at that level. Again I expected people in this case to just do @roundup,max:5 which sidesteps the problem. Also ((classlevel/2)@roundup)@max:5 confuses the parser right now, I guarantee I did not have a test case for this and its the reason why it complains about "roundup)@max" it just doesn't understand ((fragment)@thing)@secondthing
Oh, I could tell that there were multiple regex and processing steps interwoven. Didn't know that about the "placeholder" processing to identify unique expressions and then evaluate them once... it's an interesting idea? Closer to what I'd expect a compiler to do than an interpreter. It feels like the code was optimized too soon, before the syntax /grammar got fully defined.
And a number of folks with a programming background, myself included, are used to "when order of operation is in doubt, put ()s around a sub-expression." But if this parser is using wrapping parens as a sign to process an expression down into a fixed value, which max or min then considers an invalid input... can you see why so many of us are confused by the results, and feel like we're being punished for using parens?
I'm also wondering what language is the basis for the syntax. The use of '@' feels familiar but I can't quite put my finger on it...
Edit: Okay, about the "there are just no fractions in the D&D rules": they exist, they're just written out, e.g. "half" or "third". It seems WotC was trying to avoid writing actual decimal or fractional values within the rules.
Is it possible to use snippets to grant characters a feat at 1st-level character creation? How would one do that?
No. Snippets don’t “do” anything, they just display information. Any character can add a feat at 1st level by simply adding a feat at 1st level. On the character sheet under Features & Traits->Feats->Manage Feats.
Not trying to rag on you Scobenes, parsers/compilers/interpreters are an arcane art in themselves. Thank you so, so much for the information and insight you're providing.
yea no offense taken. I'm human and definitely make mistakes but I enjoyed putting it together.
Just FYI it is not just regexs, it is like 3 or 4 regex that convert strings to meta information, and then evaluates fragments, and sub fragments that are replaced in multiple passes. Glad to give ya insight. We had lots of cool ideas for snippets I remember spec'ing out with Stormknight.
Also the more I've thought about it, the more I remember doing stuff with negative numbers, but I just can't remember the specifics. I don't think I would have tested passing negative numbers to min/max. That seems like a case of "why would someone do that", until someone would have pointed out an example of a legit case why.
You are right that decimals are not something that it handles well. This is because fractions are just not something that are in the DND rules. It is generally all whole numbers, so there wasn't a big press to support them since you could do something like you said: {{(1/3)*modifier:con}}
You say they should work but don’t. Why would ((5)@max:5) be a problem?!? Isn’t the whole point of @max:X to set a limiter on what the highest number generated should be?!?
Obviously ((10+modifier:cha)@min:1) works just fine.
Obviously (classlevel/2)@roundup works just fine.
Obviously it should all work to allow a calculation of (10+modifier:cha)+(((classlevel/2)@roundup)@max:5)
So why doesn’t it?!?
I am saying they should work because there is nothing technically wrong with it, just a specific edge case I probably didn't have a test for. I remember doing multiple groupings of parenths stacked on each other, but I don't remember wrapping just a parenths around everything that doesn't provide "value". And in isolation, (5)@max:5 is not technically a problem, but I believe I set it up so that certain variable types (in this case 5 is a fixed value) don't get to use variable modifiers. I Believe the snippet in the third example that doesn't work is because (classlevel/2)@roundup is replaced in one pass, and then the fragment is evaluated again, and sees it as (5)@max:5 and that is not coded to work bc it is "pointless" bc it sees it as you typing that in, and if you were saying that, why not just put 5. It doesn't keep track of what you typed in the snippet and what it replaced and re-evaluated at that level. Again I expected people in this case to just do @roundup,max:5 which sidesteps the problem. Also ((classlevel/2)@roundup)@max:5 confuses the parser right now, I guarantee I did not have a test case for this and its the reason why it complains about "roundup)@max" it just doesn't understand ((fragment)@thing)@secondthing
What's that now? Are you speaking in tongues?
Rollback Post to RevisionRollBack
To post a comment, please login or register a new account.
Oh, if you wanna see a hella complex snippet, here's mine for Suffocation:
Can hold breath for {{((1+modifier:con)@min:0+((0-(modifier:con@max:0))@max:1/2))#unsigned}} minute(s)
Helpful rewriter of Japanese->English translation and delver into software codebases (she/e/they)
Still getting the error message - Value modifier cannot be used at current location: @roundup
Maybe I am being a bit too adventurous and drop the pact magic part??
Please paste the snippet you are trying currently.
Helpful rewriter of Japanese->English translation and delver into software codebases (she/e/they)
Okay, then try rearranging the parentheses like this:
{{((10+modifier:cha@min:1)+((classlevel/2)@roundup,max:5))}}
Edit: I highlighted the changes to make them easier to see.
Creating Epic Boons on DDB
DDB Buyers' Guide
Hardcovers, DDB & You
Content Troubleshooting
That gives me an error: 'max value modifer is not a number'. (Mind you, I'm testing this with a custom action using characterlevel instead of classlevel so that I don't have to resave a homebrew just to test snippet syntax. I'll see what will make this work.)
Edit: try this syntax:
{{(10+modifier:cha@min:1)+(characterlevel/2)@roundup,max:5#unsigned}}
Needs the #unsigned or else it adds a "+" to the front.
The parser doesn't seem to handle parentheses in ways we'd expect from being used to other coding languages.
Helpful rewriter of Japanese->English translation and delver into software codebases (she/e/they)
Thanks.
Try just this part on its own:
{{(((classlevel/2)@roundup)@max:5)}}
They want “10 + Cha mod (minimum 1) + the level of their Pact Magic slots. Because of the way Pact Magic progresses, that should be “Warlock Level divided by 2 (rounded up), to a maximum of 5 at 9th level.”
Creating Epic Boons on DDB
DDB Buyers' Guide
Hardcovers, DDB & You
Content Troubleshooting
Quote from IamSposta >>
That gives 'Unknown value modifier type: roundup)@max'
But this works:
{{(classlevel/2)@roundup,max:5}}
Like I said, the parsing is.... odd.
Edit: I... I just... wanna... rip into the parser code and fix it sooooo bad....
Helpful rewriter of Japanese->English translation and delver into software codebases (she/e/they)
I believe negative operators should work. I am pretty sure I had specific tests for that. Again I had a range of tests I wrote in the rewrite to handle all kinds of cases and obviously don't have access to them anymore.
And under the hood its just a while loop that parses out groups, replaces them with a placeholder, and then calculates the individual placeholders and returns values. The reason why I did placeholders was so that if you did the same operation twice, why do the work more than once. ex: {{(modifier:con*2)+(modifier:con*2)}} gets placeholdered down to {{(__PLACEHOLDER__1__)+(__PLACEHOLDER__1__)}} and i evaluate what modifier:con*2 returns and place it back into the equation.
And there are no libs involved in the parsing, I looked into some math libs and seemed overkill for what I needed. I just researched some opensource simple PEMDAS libs and modified it to fit our needs.
The reason why your original code was giving you that error is because you are trying to roundup a "fixed" number.
((10+modifier:cha@min:1)+((classlevel/2@roundup)@max:5))
The way the snippet parser would have seen this is the following simplifications: classlevel/2@roundup
It interprets that as classLevel / (2 rounded up).
It is no different than making a snippet that says 2+3*4/5@roundup. The snippet parser can't infer that you want to round up that whole equation once it has been evaluated and is why you should use it on a "group" with parenthesis: (2+3*4/5)@roundup
This one has two bugs I see and then it also would run in to the same problem as above
Bug 1:
Doing a snippet that is wrapped in parenths must not be handled
works: {{(characterlevel/2)@roundup}}
doesn't work but should: {{((characterlevel/2)@roundup)}}
Bug 2:
The variable modifier regex is being too greedy and extends past where it should
It sees this snippet {{(((classlevel/2)@roundup)@max:5)}}
and thinks the modifier key is "roundup)@max"
Even if the two bugs were solved, this exact snippet still wouldn't work because of the same problem as above where you are attempting to max a fixed number which isn't supported.
where classlevel = 10 would be first processed from {{(((classlevel/2)@roundup)@max:5)}} and would do one pass and get it to {{((5)@max:5)}} which becomes a problem
Well you coded it.
You say they should work but don’t. Why would ((5)@max:5) be a problem?!? Isn’t the whole point of @max:X to set a limiter on what the highest number generated should be?!?
So why doesn’t it?!?
Creating Epic Boons on DDB
DDB Buyers' Guide
Hardcovers, DDB & You
Content Troubleshooting
I've also not found a way to pass a non-integer or a negative number to max or min. Like, try making changes to my Suffocation snippet, adding parens at places where it'd make sense:
{{((1+modifier:con)@min:0+((0-(modifier:con@max:0))@max:1/2))#unsigned}}
This is what I had to do to make a minimum time to hold breath of 1/2 minutes.
The parser also doesn't like decimals.
Helpful rewriter of Japanese->English translation and delver into software codebases (she/e/they)
@DDB if you folks would like someone who can fix a parser/regex tokenizer (given enough time to know your particular software), well, I'm underemployed...
Helpful rewriter of Japanese->English translation and delver into software codebases (she/e/they)
Not trying to rag on you Scobenes, parsers/compilers/interpreters are an arcane art in themselves. Thank you so, so much for the information and insight you're providing.
Helpful rewriter of Japanese->English translation and delver into software codebases (she/e/they)
yea no offense taken. I'm human and definitely make mistakes but I enjoyed putting it together.
Just FYI it is not just regexs, it is like 3 or 4 regex that convert strings to meta information, and then evaluates fragments, and sub fragments that are replaced in multiple passes. Glad to give ya insight. We had lots of cool ideas for snippets I remember spec'ing out with Stormknight.
Also the more I've thought about it, the more I remember doing stuff with negative numbers, but I just can't remember the specifics. I don't think I would have tested passing negative numbers to min/max. That seems like a case of "why would someone do that", until someone would have pointed out an example of a legit case why.
You are right that decimals are not something that it handles well. This is because fractions are just not something that are in the DND rules. It is generally all whole numbers, so there wasn't a big press to support them since you could do something like you said: {{(1/3)*modifier:con}}
I am saying they should work because there is nothing technically wrong with it, just a specific edge case I probably didn't have a test for. I remember doing multiple groupings of parenths stacked on each other, but I don't remember wrapping just a parenths around everything that doesn't provide "value". And in isolation, (5)@max:5 is not technically a problem, but I believe I set it up so that certain variable types (in this case 5 is a fixed value) don't get to use variable modifiers. I Believe the snippet in the third example that doesn't work is because (classlevel/2)@roundup is replaced in one pass, and then the fragment is evaluated again, and sees it as (5)@max:5 and that is not coded to work bc it is "pointless" bc it sees it as you typing that in, and if you were saying that, why not just put 5. It doesn't keep track of what you typed in the snippet and what it replaced and re-evaluated at that level. Again I expected people in this case to just do @roundup,max:5 which sidesteps the problem. Also ((classlevel/2)@roundup)@max:5 confuses the parser right now, I guarantee I did not have a test case for this and its the reason why it complains about "roundup)@max" it just doesn't understand ((fragment)@thing)@secondthing
Oh, I could tell that there were multiple regex and processing steps interwoven. Didn't know that about the "placeholder" processing to identify unique expressions and then evaluate them once... it's an interesting idea? Closer to what I'd expect a compiler to do than an interpreter. It feels like the code was optimized too soon, before the syntax /grammar got fully defined.
And a number of folks with a programming background, myself included, are used to "when order of operation is in doubt, put ()s around a sub-expression." But if this parser is using wrapping parens as a sign to process an expression down into a fixed value, which max or min then considers an invalid input... can you see why so many of us are confused by the results, and feel like we're being punished for using parens?
I'm also wondering what language is the basis for the syntax. The use of '@' feels familiar but I can't quite put my finger on it...
Edit: Okay, about the "there are just no fractions in the D&D rules": they exist, they're just written out, e.g. "half" or "third". It seems WotC was trying to avoid writing actual decimal or fractional values within the rules.
Helpful rewriter of Japanese->English translation and delver into software codebases (she/e/they)
This is why I didn’t pursue programming. 🙄
Creating Epic Boons on DDB
DDB Buyers' Guide
Hardcovers, DDB & You
Content Troubleshooting
Is it possible to use snippets to grant characters a feat at 1st-level character creation? How would one do that?
No. Snippets don’t “do” anything, they just display information. Any character can add a feat at 1st level by simply adding a feat at 1st level. On the character sheet under Features & Traits->Feats->Manage Feats.
Creating Epic Boons on DDB
DDB Buyers' Guide
Hardcovers, DDB & You
Content Troubleshooting
Thank you! Obviously a rookie mistake.
/salute
What's that now? Are you speaking in tongues?