-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add IsDefined builtin and AddToPool/#pool #40
Add IsDefined builtin and AddToPool/#pool #40
Conversation
|
||
public Maybe<ILineNode> Execute(EAParser p, Token self, IList<IParamNode> parameters, MergeableGenerator<Token> tokens) | ||
{ | ||
foreach (List<Token> line in p.PooledLines) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will write the pooled lines in reverse order. Since order isn't meant to matter, it's probably fine, but I want to make sure it's intensional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting quirk here, since the tokens are tagged with their original location, "relocating" them via pool shouldn't affect error parsing.
I think. Please test error messages to confirm they error on the AddToPool() and not on the #pool.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error messages are indeed being linked to the AddToPool invocation (and not the #pool
). However it may be a good idea to add in a note: pooled at xyz
or something but I don't really know how we would go about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would probably be extraneous/difficult to implement. (hm, if I were to implement it, I'd probably do some kind of algebraic effects on the error handler to wrap adding a "pooled at" message to the default handler... within the pool directive scope. but endsidethought)
ColorzCore/Parser/EAParser.cs
Outdated
@@ -79,6 +82,9 @@ public EAParser(Dictionary<string, IList<Raw>> raws, Log log, DirectiveHandler d | |||
Definitions = new Dictionary<string, Definition>(); | |||
Inclusion = ImmutableStack<bool>.Nil; | |||
this.directiveHandler = directiveHandler; | |||
|
|||
PooledLines = new List<List<Token>>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error or warning upon completion of parsing if the pool is not emptied at the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, thoughts on associating pooled lines to scope, and thus erroring if there are still pooled lines at the end of a scope?
Or do you think pooled lines should be able to escape to outer scopes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like this. Putting extra data somewhere without the user explicitly asking for it sounds like a bad idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you misunderstand me. I mean if I have
{
AddToPool(blah)
}
#pool
Should this error? Arguably, pooled data should be required only within its scope, so lack of a #pool in the scope may be an error, so we may want to enforce pooling on a scope level... was my thought
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 I think pooling outside of scope that way is fine, I think it is only problematic if the #pool
were to be inside a child scope and not parent scope... But even then maybe it isn't idk
{
AddToPool(blah)
}
#pool // ok
AddToPool(blah)
{
#pool // maybe not ok?
}
(but yes I did misread/read too quickly and thought doing silent stuff instead of err sorry).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait I think I see a (technical) issue now: you wouldn't be able to access scope local labels referenced within the AddToPool expression if the corresponding #pool
was outside... ugh
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's associate pooled lines with the scope they're used in, then, and error at the end of a scope if there are undumped pooled lines.
{ | ||
List<Token> line = new List<Token>(6 + parameters[0].Count); | ||
|
||
string labelName = ParentParser.MakePoolLabelName(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To not overload the role of EAParser doing everything (which it is dangerously close to), let's try to have this as a static method here.
{ | ||
if (parameters[0].Count != 1) | ||
{ | ||
// TODO: err somehow |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably do some param checking in the invocation of built in macros akin to how it is for preproc defs.
(you don't need to address this comment if you don't want to; I intend to)
foreach (List<Token> line in p.PooledLines) | ||
{ | ||
tokens.PrependEnumerator(line.GetEnumerator()); | ||
MergeableGenerator<Token> tempGenerator = new MergeableGenerator<Token>(line); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I miscommunicated. I meant you didn't neeeeed to emit a node. But if this works it's probably fine? BlockNode might have issues with scope, but if it doesn't, this is probably fine to leave as-is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made this mostly because I felt like it was cleaner, not because of any specific thing you mentioned (no offense).
Tho in the end this proved to be useful to fix scope stuff (as I ended up needing to parse those "manually" anyway to be able to pass scope from AddToPool
to the pooled statements).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use an explicit for loop to avoid the case where AddToPool is nested (eg
AddToPool(UNIT whatever AddToPool(REDA whatever); UNIT)
)
In the interpretation of this, I believe the latter add to pool would get added to the pooled lines, but never iterated over, and then cleared in line 32.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it makes much sense to have a EAParser level Pool and have to associate each pooled line with a scope -- it makes keeping track of which line is in which scope hard (imo).
What I was thinking was each scope (as a class) has a list of pooled lines. Errors would occur upon scope destruction... which is currently at the end of ParseBlock only, I think (not looking too closely rn).
I believe that has organizational advantages over this approach, such as dumping a pool is simply dumping the lines in the current scope.
Feel free to convince me this (current approach) is the better approach, of course.
I just kind of want to be able to do things like this:
Having a pool at the end of each scope here would be annoying to the user imo, and I don't think it makes things any clearer or safer (again, to the user). EDIT: Also to be somewhat consistent with nl!EA behaviour, which allows this. |
Makes sense. So we want to allow for bleeding out of scopes... I think personally it makes more sense to force more often pooling rather than have cases that hace issues like that. Edit: I'm okay with diverging from original EA behavior on this one because a) it wasn't commonly used b) it had a lot of issues (which is what we are pointing out with it right now). B probably contributed to A tbh. |
I fixed that in my last commit (pooled statements have the scope of the |
Ah, I misunderstood your fix. So each pooled line gets parsed as if in the closure of its original AddToPool, then the pooled label gets added into the global namespace? Hm, yeah I guess that works. |
The pooled label gets into that same scope too (as the label definition is just part of the pooled statements). |
foreach (List<Token> line in p.PooledLines) | ||
{ | ||
tokens.PrependEnumerator(line.GetEnumerator()); | ||
MergeableGenerator<Token> tempGenerator = new MergeableGenerator<Token>(line); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use an explicit for loop to avoid the case where AddToPool is nested (eg
AddToPool(UNIT whatever AddToPool(REDA whatever); UNIT)
)
In the interpretation of this, I believe the latter add to pool would get added to the pooled lines, but never iterated over, and then cleared in line 32.
going back to add things into the scope seems unintuitive to me (as there's no other language mechanism to add definitions to a scope once outside it), but again, pooled labels are referred to at only one location, so there shouldn't be any fishy behavior from it |
They work essentially the same way they did in legacy EA.
IsDefined(Name)
expands to1
if a definition or macro (why are those different things) of given name exists, expands to0
otherwise.AddToPool(tokens...)
expands to the name of a label referring to data described by the listed tokens (that would be pooled by a subsequent#pool
directive).AddToPool(tokens..., Alignment)
is the same as above, with the additional guarantee that the pooled data will be aligned with requested Alignment. This one is isn't in legacy EA.Pool stuff is still a bit jank as pool labels are still affected by scope shenanigans (so in order for all of this to work, theDumped pooled data is now parsed as if label scope is the same as the one from the#pool
directive needs to be in a label scope that is visible to theAddToPool
macros). We could make this slightly better if #37 gets implemented.AddToPool
expression.Pool labels are internally under the form
__POOLED$<number>
. They have a$
character in them to guarantee no user label can conflict with pool labels.(I need to figure out how I can expand full expressions within builtin macros so I can implement more of the old stuff (Stuff like
Signum
,Switch
, and maybe a variant ofIsDefined
that takes a second integer parameter that checks for a macro with that parameter count specifically)).