Skip to content
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

Merged
merged 8 commits into from
Mar 21, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion ColorzCore/ColorzCore.csproj
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
Expand Down Expand Up @@ -108,6 +108,9 @@
<Compile Include="Raws\Raw.cs" />
<Compile Include="IO\Log.cs" />
<Compile Include="IO\IncludeFileSearcher.cs" />
<Compile Include="Parser\Macros\IsDefined.cs" />
<Compile Include="Parser\Macros\AddToPool.cs" />
<Compile Include="Preprocessor\Directives\PoolDirective.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
Expand Down
4 changes: 4 additions & 0 deletions ColorzCore/Parser/EAParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class EAParser
}
public ImmutableStack<bool> Inclusion { get; set; }

public List<List<Token>> PooledLines { get; private set; }

private readonly DirectiveHandler directiveHandler;

private Stack<Tuple<int, bool>> pastOffsets; // currentOffset, offsetInitialized
Expand Down Expand Up @@ -79,6 +81,8 @@ 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>>();
Copy link
Member

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.

Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Member

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

Copy link
Member Author

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).

Copy link
Member Author

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

Copy link
Member

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.

}

public bool IsReservedName(string name)
Expand Down
65 changes: 65 additions & 0 deletions ColorzCore/Parser/Macros/AddToPool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using ColorzCore.Lexer;

namespace ColorzCore.Parser.Macros
{
class AddToPool : BuiltInMacro
{
/*
* Macro Usage:
* AddToPool(tokens...): adds token to pool, and expands to label name referring to those tokens
* AddToPool(tokens..., alignment): adds token to pool and make sure pooled tokens are aligned given alignment
*/

public EAParser ParentParser { get; private set; }

private long poolLabelCounter;

public AddToPool(EAParser parent)
{
ParentParser = parent;
poolLabelCounter = 0;
}

public override IEnumerable<Token> ApplyMacro(Token head, IList<IList<Token>> parameters)
{
List<Token> line = new List<Token>(6 + parameters[0].Count);

string labelName = MakePoolLabelName();

if (parameters.Count == 2)
{
// Add Alignment directive

line.Add(new Token(TokenType.IDENTIFIER, head.Location, "ALIGN"));
line.Add(parameters[1][0]);
line.Add(new Token(TokenType.SEMICOLON, head.Location, ";"));
}

// TODO: Make label declaration global (when this feature gets implemented)
// This way the name will be available as long as it is pooled (reguardless of pool scope)

line.Add(new Token(TokenType.IDENTIFIER, head.Location, labelName));
line.Add(new Token(TokenType.COLON, head.Location, ":"));

line.AddRange(parameters[0]);
line.Add(new Token(TokenType.NEWLINE, head.Location, "\n"));

ParentParser.PooledLines.Add(line);

yield return new Token(TokenType.IDENTIFIER, head.Location, labelName);
}

public override bool ValidNumParams(int num)
{
return num == 1 || num == 2;
}

protected string MakePoolLabelName()
{
// The presence of $ in the label name guarantees that it can't be a user label
return string.Format("__POOLED${0}", poolLabelCounter++);
}
}
}
58 changes: 58 additions & 0 deletions ColorzCore/Parser/Macros/IsDefined.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using ColorzCore.Lexer;

namespace ColorzCore.Parser.Macros
{
class IsDefined : BuiltInMacro
{
public EAParser ParentParser { get; private set; }

public IsDefined(EAParser parent)
{
ParentParser = parent;
}

public override IEnumerable<Token> ApplyMacro(Token head, IList<IList<Token>> parameters)
{
if (parameters[0].Count != 1)
{
// TODO: err somehow
Copy link
Member

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)

yield return MakeFalseToken(head.Location);
}
else
{
Token token = parameters[0][0];

if ((token.Type == TokenType.IDENTIFIER) && IsReallyDefined(token.Content))
{
yield return MakeTrueToken(head.Location);
}
else
{
yield return MakeFalseToken(head.Location);
}
}
}

public override bool ValidNumParams(int num)
{
return num == 1;
}

protected bool IsReallyDefined(string name)
{
return ParentParser.Definitions.ContainsKey(name) || ParentParser.Macros.ContainsName(name);
Crazycolorz5 marked this conversation as resolved.
Show resolved Hide resolved
}

protected static Token MakeTrueToken(DataTypes.Location location)
{
return new Token(TokenType.NUMBER, location, "1");
}

protected static Token MakeFalseToken(DataTypes.Location location)
{
return new Token(TokenType.NUMBER, location, "0");
}
}
}
7 changes: 6 additions & 1 deletion ColorzCore/Parser/Macros/MacroCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ public MacroCollection(EAParser parent)
{
Macros = new Dictionary<string, Dictionary<int, IMacro>>();
Parent = parent;
BuiltInMacros = new Dictionary<string, BuiltInMacro> { { "String", String.Instance } };

BuiltInMacros = new Dictionary<string, BuiltInMacro> {
{ "String", new String() },
{ "IsDefined", new IsDefined(parent) },
{ "AddToPool", new AddToPool(parent) },
};
}

public bool HasMacro(string name, int paramNum)
Expand Down
6 changes: 0 additions & 6 deletions ColorzCore/Parser/Macros/String.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,6 @@ namespace ColorzCore.Parser.Macros
{
class String : BuiltInMacro
{
private static String instance = new String();

public static String Instance { get { return instance; } }

private String() { }

public override IEnumerable<Token> ApplyMacro(Token head, IList<IList<Token>> parameters)
{
yield return new Token(TokenType.IDENTIFIER, head.Location, "BYTE");
Expand Down
5 changes: 3 additions & 2 deletions ColorzCore/Preprocessor/DirectiveHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ public DirectiveHandler(IncludeFileSearcher includeSearcher, IncludeFileSearcher
{ "ifndef", new IfNotDefinedDirective() },
{ "else", new ElseDirective() },
{ "endif", new EndIfDirective() },
{ "define", new DefineDirective() }, //TODO: pool
{ "undef", new UndefineDirective() }
{ "define", new DefineDirective() },
{ "pool", new PoolDirective() },
{ "undef", new UndefineDirective() },
};
}

Expand Down
37 changes: 37 additions & 0 deletions ColorzCore/Preprocessor/Directives/PoolDirective.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using ColorzCore.DataTypes;
using ColorzCore.Lexer;
using ColorzCore.Parser;
using ColorzCore.Parser.AST;

namespace ColorzCore.Preprocessor.Directives
{
class PoolDirective : IDirective
{
public int MinParams => 0;
public int? MaxParams => 0;
public bool RequireInclusion => true;

public Maybe<ILineNode> Execute(EAParser p, Token self, IList<IParamNode> parameters, MergeableGenerator<Token> tokens)
{
BlockNode result = new BlockNode();

foreach (List<Token> line in p.PooledLines)
Copy link
Member

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.

Copy link
Member

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.

Copy link
Member Author

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?

Copy link
Member

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)

{
MergeableGenerator<Token> tempGenerator = new MergeableGenerator<Token>(line);
Copy link
Member

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.

Copy link
Member Author

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).

Copy link
Member

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.

tempGenerator.MoveNext();

while (!tempGenerator.EOS)
{
p.ParseLine(tempGenerator, p.GlobalScope).IfJust(
(lineNode) => result.Children.Add(lineNode));
}
}

p.PooledLines.Clear();

return new Just<ILineNode>(result);
}
}
}