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

Syntax includes directories support #340

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
47 changes: 41 additions & 6 deletions VSRAD.Syntax/Core/Parser/AbstractCodeParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@ internal abstract class AbstractCodeParser : IParser
private readonly IDocumentFactory _documentFactory;
private readonly AsmType _asmType;
private HashSet<string> _instructions;
private IReadOnlyList<string> _includes;
protected DocumentManager _manager;

protected AbstractCodeParser(IDocumentFactory documentFactory, IInstructionListManager instructionListManager, AsmType asmType)
protected AbstractCodeParser(IDocumentFactory documentFactory, IInstructionListManager instructionListManager,
IReadOnlyList<string> includes, DocumentManager manager, AsmType asmType)
{
_asmType = asmType;
_documentFactory = documentFactory;
_instructions = new HashSet<string>();
_includes = includes;
_manager = manager;
OtherInstructions = new HashSet<string>();

instructionListManager.InstructionsUpdated += InstructionsUpdated;
Expand Down Expand Up @@ -55,32 +60,49 @@ private void InstructionsUpdated(IInstructionListManager sender, AsmType asmType
}
}

protected async Task AddExternalDefinitionsAsync(string path, TrackingToken includeStr, IBlock block, DefinitionContainer definitionContainer)
protected async Task AddExternalDefinitionsAsync(IDocument document, string path, TrackingToken includeStr, IBlock block)
{
try
{
var externalFileName = includeStr.GetText(block.Snapshot).Trim('"');
var externalFilePath = Path.Combine(Path.GetDirectoryName(path), externalFileName);
var externalDocument = _documentFactory.GetOrCreateDocument(externalFilePath);

if (externalDocument == null)
{
foreach(var curPath in _includes)
{
externalFilePath = Path.Combine(curPath, externalFileName);
externalDocument = _documentFactory.GetOrCreateDocument(externalFilePath);
if (externalDocument != null) break;
}
}

if (externalDocument != null)
{
var externalDocumentAnalysis = externalDocument.DocumentAnalysis;
var externalAnalysisResult = await externalDocumentAnalysis
.GetAnalysisResultAsync(externalDocument.CurrentSnapshot)
.ConfigureAwait(false);
_manager.AddChild(document, externalDocument);

foreach (var externalDefinition in externalAnalysisResult.GetGlobalDefinitions())
definitionContainer.Add(block, externalDefinition);
//var pContainer = _manager.GetContainerForDoc(document);
//foreach (var externalDefinition in externalAnalysisResult.GetGlobalDefinitions())
// pContainer.Add(block, externalDefinition);
}
}
catch (Exception e) when (e is ArgumentException || e is FileNotFoundException) { /* invalid path */ }
}

protected bool TryAddReference(string tokenText, TrackingToken token, IBlock block, ITextSnapshot version, DefinitionContainer definitionContainer, CancellationToken cancellation)
protected bool TryAddReference(IDocument doc, string tokenText, TrackingToken token, IBlock block,
ITextSnapshot version, CancellationToken cancellation)
{
cancellation.ThrowIfCancellationRequested();
if (definitionContainer.TryGetDefinition(tokenText, out var definitionToken))

var node = _manager.GetNodeForDoc(doc);
var definitionToken = SearchForToken(node, tokenText);

if (definitionToken != null)
{
RadAsmTokenType referenceType;
switch (definitionToken.Type)
Expand Down Expand Up @@ -116,6 +138,19 @@ protected bool TryAddReference(string tokenText, TrackingToken token, IBlock blo
return false;
}

private DefinitionToken SearchForToken(DocumentNode node, string tokenText)
{
DefinitionToken token;
if (node.DefinitionContainer.TryGetDefinition(tokenText, out token))
return token; // look for token in current node
foreach (var child in node.Children) // going deeper in recursion
{
token = SearchForToken(child, tokenText);
if (token != null) break; // found token in child node
}
return token; // can still be null, in case non of children contains token
}

protected bool TryAddInstruction(string tokenText, TrackingToken token, IBlock block, ITextSnapshot version)
{
if (_instructions.Contains(tokenText))
Expand Down
14 changes: 8 additions & 6 deletions VSRAD.Syntax/Core/Parser/Asm1Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using VSRAD.Syntax.Helpers;
using VSRAD.Syntax.Options.Instructions;
using VSRAD.SyntaxParser;
using VSRAD.Syntax.Options;

namespace VSRAD.Syntax.Core.Parser
{
Expand All @@ -23,12 +24,13 @@ internal sealed class Asm1Parser : AbstractCodeParser
var serviceProvider = ServiceProvider.GlobalProvider;
var documentFactory = serviceProvider.GetMefService<IDocumentFactory>();
var instructionListManager = serviceProvider.GetMefService<IInstructionListManager>();
var options = serviceProvider.GetMefService<OptionsProvider>();

return new Asm1Parser(documentFactory, instructionListManager);
return new Asm1Parser(documentFactory, instructionListManager, options.IncludePaths);
});

private Asm1Parser(IDocumentFactory documentFactory, IInstructionListManager instructionListManager)
: base(documentFactory, instructionListManager, AsmType.RadAsm) { }
private Asm1Parser(IDocumentFactory documentFactory, IInstructionListManager instructionListManager, IReadOnlyList<string> includes)
: base(documentFactory, instructionListManager, includes, null, AsmType.RadAsm) { }

public override Task<IParserResult> RunAsync(IDocument document, ITextSnapshot version,
ITokenizerCollection<TrackingToken> trackingTokens, CancellationToken cancellation)
Expand Down Expand Up @@ -192,7 +194,7 @@ private async Task<IParserResult> ParseAsync(IDocument document, ITextSnapshot v
{
var tokenText = token.GetText(version);
if (!TryAddInstruction(tokenText, token, currentBlock, version) &&
!TryAddReference(tokenText, token, currentBlock, version, definitionContainer, cancellation))
!TryAddReference(document, tokenText, token, currentBlock, version, cancellation))
{
if (tokens.Length - i > 1 && tokens[i + 1].Type == RadAsm1Lexer.EQ)
{
Expand All @@ -211,7 +213,7 @@ private async Task<IParserResult> ParseAsync(IDocument document, ITextSnapshot v
{
if (tokens.Length - i > 1 && tokens[i + 1].Type == RadAsm1Lexer.STRING_LITERAL)
{
await AddExternalDefinitionsAsync(document.Path, tokens[i + 1], currentBlock, definitionContainer);
await AddExternalDefinitionsAsync(document, document.Path, tokens[i + 1], currentBlock);
i += 1;
}
}
Expand All @@ -236,7 +238,7 @@ private async Task<IParserResult> ParseAsync(IDocument document, ITextSnapshot v

foreach (var (text, trackingToken, block) in referenceCandidates)
{
if (!TryAddReference(text, trackingToken, block, version, definitionContainer, cancellation) && OtherInstructions.Contains(text))
if (!TryAddReference(document, text, trackingToken, block, version, cancellation) && OtherInstructions.Contains(text))
errors.Add(new ErrorToken(trackingToken, version, ErrorMessages.InvalidInstructionSetErrorMessage));
}

Expand Down
19 changes: 11 additions & 8 deletions VSRAD.Syntax/Core/Parser/Asm2Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using VSRAD.Syntax.Helpers;
using VSRAD.Syntax.Options.Instructions;
using VSRAD.SyntaxParser;
using VSRAD.Syntax.Options;

namespace VSRAD.Syntax.Core.Parser
{
Expand All @@ -23,12 +24,14 @@ internal sealed class Asm2Parser : AbstractCodeParser
var serviceProvider = ServiceProvider.GlobalProvider;
var documentFactory = serviceProvider.GetMefService<IDocumentFactory>();
var instructionListManager = serviceProvider.GetMefService<IInstructionListManager>();
var options = serviceProvider.GetMefService<OptionsProvider>();
var manager = serviceProvider.GetMefService<DocumentManager>();

return new Asm2Parser(documentFactory, instructionListManager);
return new Asm2Parser(documentFactory, instructionListManager, options.IncludePaths, manager);
});

private Asm2Parser(IDocumentFactory documentFactory, IInstructionListManager instructionListManager)
: base(documentFactory, instructionListManager, AsmType.RadAsm2) { }
private Asm2Parser(IDocumentFactory documentFactory, IInstructionListManager instructionListManager, IReadOnlyList<string> includes, DocumentManager manager)
: base(documentFactory, instructionListManager, includes, manager, AsmType.RadAsm2) { }

public override Task<IParserResult> RunAsync(IDocument document, ITextSnapshot version,
ITokenizerCollection<TrackingToken> trackingTokens, CancellationToken cancellation)
Expand All @@ -54,9 +57,9 @@ private async Task<IParserResult> ParseAsync(IDocument document, ITextSnapshot v
.WithCancellation(cancellation)
.ToArray();

var definitionContainer = new DefinitionContainer();
var referenceCandidates = new LinkedList<(string text, TrackingToken trackingToken, IBlock block)>();

var definitionContainer = _manager.GetContainerForDoc(document);
var blocks = new List<IBlock>();
var errors = new List<IErrorToken>();
IBlock currentBlock = new Block(version);
Expand Down Expand Up @@ -196,7 +199,7 @@ private async Task<IParserResult> ParseAsync(IDocument document, ITextSnapshot v
else if (token.Type == RadAsm2Lexer.CLOSURE_IDENTIFIER)
{
var tokenText = token.GetText(version).Substring(1); // remove first '#' symbol
if (!TryAddReference(tokenText, token, currentBlock, version, definitionContainer, cancellation))
if (!TryAddReference(document, tokenText, token, currentBlock, version, cancellation))
{
referenceCandidates.AddLast((tokenText, token, currentBlock));
}
Expand All @@ -205,7 +208,7 @@ private async Task<IParserResult> ParseAsync(IDocument document, ITextSnapshot v
{
var tokenText = token.GetText(version);
if (!TryAddInstruction(tokenText, token, currentBlock, version) &&
!TryAddReference(tokenText, token, currentBlock, version, definitionContainer, cancellation))
!TryAddReference(document, tokenText, token, currentBlock, version, cancellation))
{
referenceCandidates.AddLast((tokenText, token, currentBlock));
}
Expand All @@ -214,7 +217,7 @@ private async Task<IParserResult> ParseAsync(IDocument document, ITextSnapshot v
{
if (tokens.Length - i > 1 && tokens[i + 1].Type == RadAsm2Lexer.STRING_LITERAL)
{
await AddExternalDefinitionsAsync(document.Path, tokens[i + 1], currentBlock, definitionContainer);
await AddExternalDefinitionsAsync(document, document.Path, tokens[i + 1], currentBlock);
i += 1;
}
}
Expand Down Expand Up @@ -244,7 +247,7 @@ private async Task<IParserResult> ParseAsync(IDocument document, ITextSnapshot v

foreach (var (text, trackingToken, block) in referenceCandidates)
{
if (!TryAddReference(text, trackingToken, block, version, definitionContainer, cancellation) && OtherInstructions.Contains(text))
if (!TryAddReference(document, text, trackingToken, block, version, cancellation) && OtherInstructions.Contains(text))
errors.Add(new ErrorToken(trackingToken, version, ErrorMessages.InvalidInstructionSetErrorMessage));
}

Expand Down
1 change: 1 addition & 0 deletions VSRAD.Syntax/Core/Parser/DefinitionContainer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.ComponentModel.Composition;
using VSRAD.Syntax.Core.Blocks;
using VSRAD.Syntax.Core.Tokens;

Expand Down
54 changes: 54 additions & 0 deletions VSRAD.Syntax/Core/Parser/DocumentManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using VSRAD.Syntax.Core.Blocks;

namespace VSRAD.Syntax.Core.Parser
{
[Export(typeof(DocumentManager))]
public class DocumentManager
{
private List<DocumentNode> _documents;

public DocumentManager()
{
_documents = new List<DocumentNode>();
}

public DocumentNode GetNodeForDoc(IDocument document)
{
var node = _documents.FirstOrDefault(d => d.Document == document);
if (node == default(DocumentNode))
{
node = new DocumentNode(document);
_documents.Add(node);
}
return node;
}

public DefinitionContainer GetContainerForDoc(IDocument document)
=> GetNodeForDoc(document).DefinitionContainer;

public void AddChild(IDocument parent, IDocument child)
{
var pNode = GetNodeForDoc(parent);
var cNode = GetNodeForDoc(child);
if (!pNode.Children.Contains(cNode))
pNode.Children.Add(cNode);
}
}

public sealed class DocumentNode
{
public readonly IDocument Document;
public DefinitionContainer DefinitionContainer;
public readonly List<DocumentNode> Children;

public DocumentNode(IDocument doc)
{
Document = doc;
DefinitionContainer = new DefinitionContainer();
Children = new List<DocumentNode>();
}
}
}
2 changes: 2 additions & 0 deletions VSRAD.Syntax/Options/GeneralOptionProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public OptionsProvider()
Asm1FileExtensions = Constants.DefaultFileExtensionAsm1;
Asm2FileExtensions = Constants.DefaultFileExtensionAsm2;
InstructionsPaths = GetDefaultInstructionDirectoryPath();
IncludePaths = new List<string>();
Asm1InstructionSet = string.Empty;
Asm2InstructionSet = string.Empty;
AutocompleteInstructions = false;
Expand All @@ -50,6 +51,7 @@ public OptionsProvider()
public IReadOnlyList<string> Asm1FileExtensions;
public IReadOnlyList<string> Asm2FileExtensions;
public string InstructionsPaths;
public IReadOnlyList<string> IncludePaths;
public string Asm1InstructionSet;
public string Asm2InstructionSet;
public bool AutocompleteInstructions;
Expand Down
21 changes: 20 additions & 1 deletion VSRAD.Syntax/Options/GeneralOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,24 @@ public string Asm2FileExtensions

[Category("Instructions")]
[DisplayName("Instruction folder paths")]
[Description("List of folder path separated by semicolon wit assembly instructions with .radasm file extension")]
[Description("List of folder paths separated by semicolon with assembly instructions with .radasm file extension")]
[Editor(typeof(FolderPathsEditor), typeof(System.Drawing.Design.UITypeEditor))]
public string InstructionsPaths
{
get => _optionsProvider.InstructionsPaths;
set => _optionsProvider.InstructionsPaths = value;
}

[Category("Instructions")]
[DisplayName("Include paths")]
[Description("List of folder paths separated by semicolon that contains sources to be included")]
[Editor(typeof(FolderPathsEditor), typeof(System.Drawing.Design.UITypeEditor))]
public string IncludePaths
{
get => ConvertExtensionsTo(_optionsProvider.IncludePaths);
set => _optionsProvider.IncludePaths = ConvertExtensionsFrom(value);
}

[Category("Instructions")]
[DisplayName("Asm1 selected set")]
[Browsable(false)]
Expand Down Expand Up @@ -156,6 +166,10 @@ public override async Task LoadAsync()
InstructionsPaths = userSettingsStore.PropertyExists(InstructionCollectionName, nameof(InstructionsPaths))
? userSettingsStore.GetString(InstructionCollectionName, nameof(InstructionsPaths))
: OptionsProvider.GetDefaultInstructionDirectoryPath();

IncludePaths = userSettingsStore.PropertyExists(InstructionCollectionName, nameof(IncludePaths))
? userSettingsStore.GetString(InstructionCollectionName, nameof(IncludePaths))
: string.Empty;
}

public override async Task SaveAsync()
Expand All @@ -174,6 +188,11 @@ public override async Task SaveAsync()
{
userSettingsStore.SetString(InstructionCollectionName, nameof(InstructionsPaths), InstructionsPaths);
}

if (!string.IsNullOrEmpty(IncludePaths))
{
userSettingsStore.SetString(InstructionCollectionName, nameof(IncludePaths), IncludePaths);
}
}

private static IReadOnlyList<string> ConvertExtensionsFrom(string str) =>
Expand Down
1 change: 1 addition & 0 deletions VSRAD.Syntax/VSRAD.Syntax.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
<Compile Include="Core\Parser\Asm2Parser.cs" />
<Compile Include="Core\Parser\AsmDocParser.cs" />
<Compile Include="Core\Parser\Asm1Parser.cs" />
<Compile Include="Core\Parser\DocumentManager.cs" />
<Compile Include="Core\Parser\IParser.cs" />
<Compile Include="Core\Parser\DefinitionContainer.cs" />
<Compile Include="Core\ITokenizerResult.cs" />
Expand Down