Skip to content

Commit

Permalink
Merge pull request #51 from moorestech/feature/エラーのyaml上での位置をjsonnode…
Browse files Browse the repository at this point in the history
…に含めるようにする

Feature/エラーのyaml上での位置をjsonnodeに含めるようにする
  • Loading branch information
KurisuJuha authored Jan 29, 2025
2 parents 92c3412 + 26b3e41 commit f93e02a
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 160 deletions.
11 changes: 10 additions & 1 deletion mooresmaster.Generator/Analyze/Analysis.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using mooresmaster.Generator.Json;

namespace mooresmaster.Generator.Analyze;

Expand All @@ -26,6 +27,7 @@ public void ThrowDiagnostics()
public interface IDiagnostics
{
string Message { get; }
public Location Location { get; }
}

public class AnalyzeException : Exception
Expand All @@ -36,7 +38,14 @@ public AnalyzeException(IDiagnostics[] diagnosticsArray)
{
DiagnosticsArray = diagnosticsArray;
var messages = new List<string>();
foreach (var diagnostics in diagnosticsArray) messages.Add($"type: {diagnostics.GetType().Name}\n {diagnostics.Message.Replace("\n", "\n ")}");
foreach (var diagnostics in diagnosticsArray)
messages.Add(
$"""
type: {diagnostics.GetType().Name}
location: {diagnostics.Location}
{diagnostics.Message.Replace("\n", "\n ")}
"""
);

Message = string.Join("\n", messages);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Immutable;
using System.Linq;
using mooresmaster.Generator.Json;
using mooresmaster.Generator.JsonSchema;
using mooresmaster.Generator.Semantic;

Expand All @@ -15,35 +16,45 @@ public void PostSemanticsLayerAnalyze(Analysis analysis, Semantics semantics, Im

// 依存対象のinterfaceも後で調査するため再起的に全て調査する必要はない
foreach (var (implementationInterfaceId, implementation) in implementations.Select(i => (i, semantics.InterfaceSemanticsTable[i])))
{
var node = interfaceSemantics.Interface.ImplementationNodes[implementation.Interface.InterfaceName];
var location = node.Location;

if (interfaceSemantics.Interface.IsGlobal)
{
// グローバルなら依存対象も全てglobalでないといけない
if (implementation.Interface.IsGlobal) continue;
analysis.ReportDiagnostics(new DefineInterfaceGlobalScopeDiagnostics(id, implementationInterfaceId, semantics));

analysis.ReportDiagnostics(new DefineInterfaceGlobalScopeDiagnostics(id, implementationInterfaceId, semantics, location));
}
else
{
// ローカルなら依存対象はglobalか同じscopeのlocalでないといけない
if (implementation.Interface.IsGlobal) continue;
if (implementation.Schema.SchemaId == interfaceSemantics.Schema.SchemaId) continue;

analysis.ReportDiagnostics(new DefineInterfaceLocalScopeDiagnostics(id, implementationInterfaceId, semantics));
analysis.ReportDiagnostics(new DefineInterfaceLocalScopeDiagnostics(id, implementationInterfaceId, semantics, location));
}
}
}
}

public class DefineInterfaceGlobalScopeDiagnostics(InterfaceId targetInterfaceId, InterfaceId implementationInterfaceId, Semantics semantics) : IDiagnostics
public class DefineInterfaceGlobalScopeDiagnostics(InterfaceId targetInterfaceId, InterfaceId implementationInterfaceId, Semantics semantics, Location location) : IDiagnostics
{
public string Message => $"""
Global interface cannot depend on local interface.
TargetInterface {semantics.InterfaceSemanticsTable[targetInterfaceId].Interface.InterfaceName}
ImplementationInterface {semantics.InterfaceSemanticsTable[implementationInterfaceId].Interface.InterfaceName}
""";

public Location Location => location;
}

public class DefineInterfaceLocalScopeDiagnostics(InterfaceId targetInterfaceId, InterfaceId implementationInterfaceId, Semantics semantics) : IDiagnostics
public class DefineInterfaceLocalScopeDiagnostics(InterfaceId targetInterfaceId, InterfaceId implementationInterfaceId, Semantics semantics, Location location) : IDiagnostics
{
public Location Location { get; } = location;

public string Message => $"""
Local interface can only depend on global interface or same scope local interface.
Expand Down
11 changes: 10 additions & 1 deletion mooresmaster.Generator/DefineInterface.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
using System.Collections.Generic;
using mooresmaster.Generator.Json;
using mooresmaster.Generator.JsonSchema;

namespace mooresmaster.Generator;

public class DefineInterface(string rootSchemaId, string interfaceName, Dictionary<string, IDefineInterfacePropertySchema> properties, string[] implementationInterfaces, bool isGlobal)
public class DefineInterface(
string rootSchemaId,
string interfaceName,
Dictionary<string, IDefineInterfacePropertySchema> properties,
string[] implementationInterfaces,
Dictionary<string, JsonString> implementationNodes,
bool isGlobal
)
{
public string[] ImplementationInterfaces = implementationInterfaces;
public Dictionary<string, JsonString> ImplementationNodes = implementationNodes;
public string InterfaceName = interfaceName;
public bool IsGlobal = isGlobal;
public Dictionary<string, IDefineInterfacePropertySchema> Properties = properties;
Expand Down
173 changes: 47 additions & 126 deletions mooresmaster.Generator/Json/JsonParser.cs
Original file line number Diff line number Diff line change
@@ -1,172 +1,93 @@
using System;
using System.Collections.Generic;
using YamlDotNet.Core;
using YamlDotNet.RepresentationModel;

namespace mooresmaster.Generator.Json;

public struct Location
{
public string FilePath;
public long StartLine;
public long StartColumn;
public long EndLine;
public long EndColumn;

private static Location Create(string filePath, Mark start, Mark end)
{
return new Location
{
StartLine = start.Line,
StartColumn = start.Column,
EndLine = end.Line,
EndColumn = end.Column,
FilePath = filePath
};
}

public static Location Create(string filePath, YamlNode yamlNode)
{
return Create(filePath, yamlNode.Start, yamlNode.End);
}

public override string ToString()
{
return $"({StartLine}:{StartColumn} - {EndLine}:{EndColumn}) in {FilePath}";
}
}

public interface IJsonNode
{
IJsonNode? Parent { get; }
string? PropertyName { get; }
Location Location { get; }
}

public record JsonObject(Dictionary<string, IJsonNode> Nodes, IJsonNode? Parent, string? PropertyName) : IJsonNode
public record JsonObject(Dictionary<string, IJsonNode> Nodes, IJsonNode? Parent, string? PropertyName, Location Location) : IJsonNode
{
public readonly Dictionary<string, IJsonNode> Nodes = Nodes;
public IJsonNode? this[string key] => Nodes.ContainsKey(key) ? Nodes[key] : null;
public IJsonNode? Parent { get; } = Parent;
public string? PropertyName { get; } = PropertyName;
public Location Location { get; } = Location;
}

public record JsonArray(IJsonNode[] Nodes, IJsonNode? Parent, string? PropertyName) : IJsonNode
public record JsonArray(IJsonNode[] Nodes, IJsonNode? Parent, string? PropertyName, Location Location) : IJsonNode
{
public IJsonNode[] Nodes = Nodes;
public IJsonNode? this[int index] => Nodes.Length > index ? Nodes[index] : null;
public IJsonNode? Parent { get; } = Parent;
public string? PropertyName { get; } = PropertyName;
public Location Location { get; } = Location;
}

public record JsonString(string Literal, IJsonNode? Parent, string? PropertyName) : IJsonNode
public record JsonString(string Literal, IJsonNode? Parent, string? PropertyName, Location Location) : IJsonNode
{
public readonly string Literal = Literal;
public string? PropertyName { get; } = PropertyName;
public Location Location { get; } = Location;
public IJsonNode? Parent { get; } = Parent;
public readonly string Literal = Literal;
}

public record JsonBoolean(bool Literal, IJsonNode? Parent, string? PropertyName) : IJsonNode
public record JsonBoolean(bool Literal, IJsonNode? Parent, string? PropertyName, Location Location) : IJsonNode
{
public readonly bool Literal = Literal;
public IJsonNode? Parent { get; } = Parent;
public string? PropertyName { get; } = PropertyName;
public Location Location { get; } = Location;
}

public record JsonNumber(double Literal, IJsonNode? Parent, string? PropertyName) : IJsonNode
public record JsonNumber(double Literal, IJsonNode? Parent, string? PropertyName, Location Location) : IJsonNode
{
public readonly double Literal = Literal;
public IJsonNode? Parent { get; } = Parent;
public string? PropertyName { get; } = PropertyName;
public Location Location { get; } = Location;
}

public record JsonInt(long Literal, IJsonNode? Parent, string? PropertyName) : IJsonNode
public record JsonInt(long Literal, IJsonNode? Parent, string? PropertyName, Location Location) : IJsonNode
{
public readonly long Literal = Literal;
public IJsonNode? Parent { get; } = Parent;
public string? PropertyName { get; } = PropertyName;
}

public static class JsonParser
{
public static IJsonNode Parse(Token[] tokens)
{
var iterator = new Iterator(tokens);
return Parse(ref iterator, null, null);
}

private static IJsonNode Parse(ref Iterator iterator, IJsonNode? parent, string? name)
{
return iterator.CurrentToken.Type switch
{
TokenType.String => ParseString(ref iterator, parent, name),
TokenType.LBrace => ParseObject(ref iterator, parent, name),
TokenType.LSquare => ParseArray(ref iterator, parent, name),
TokenType.True or TokenType.False => ParseBoolean(ref iterator, parent, name),
TokenType.Number => ParseNumber(ref iterator, parent, name),
TokenType.Int => ParseInt(ref iterator, parent, name),
_ => throw new Exception($"""Unexpected token: {iterator.CurrentToken.Type} "{iterator.CurrentToken.Literal}" """)
};
}

private static IJsonNode ParseMinus(ref Iterator iterator, IJsonNode? parent, string? name)
{
iterator.CurrentIndex++; // skip '-'
switch (iterator.CurrentToken.Type)
{
case TokenType.Int:
var intValue = ParseInt(ref iterator, parent, name);
return new JsonInt(-intValue.Literal, parent, name);
case TokenType.Number:
var numberValue = ParseNumber(ref iterator, parent, name);
return new JsonNumber(-numberValue.Literal, parent, name);
default:
throw new ArgumentOutOfRangeException();
}
}

private static JsonInt ParseInt(ref Iterator iterator, IJsonNode? parent, string? name)
{
var value = iterator.CurrentToken.Literal;
iterator.CurrentIndex++; // skip int
return new JsonInt(long.Parse(value), parent, name);
}

private static JsonNumber ParseNumber(ref Iterator iterator, IJsonNode? parent, string? name)
{
var value = iterator.CurrentToken.Literal;
iterator.CurrentIndex++; // skip number
return new JsonNumber(double.Parse(value), parent, name);
}

private static JsonBoolean ParseBoolean(ref Iterator iterator, IJsonNode? parent, string? name)
{
var value = iterator.CurrentToken.Literal;
iterator.CurrentIndex++; // skip boolean
return new JsonBoolean(value == "true", parent, name);
}

private static JsonString ParseString(ref Iterator iterator, IJsonNode? parent, string? name)
{
var value = iterator.CurrentToken.Literal;
iterator.CurrentIndex++; // skip string
return new JsonString(value, parent, name);
}

private static JsonArray ParseArray(ref Iterator iterator, IJsonNode? parent, string? name)
{
var nodes = new List<IJsonNode>();
iterator.CurrentIndex++; // skip '['

var jsonNode = new JsonArray([], parent, name);

while (iterator.CurrentToken.Type != TokenType.RSquare)
{
nodes.Add(Parse(ref iterator, jsonNode, null));
if (iterator.CurrentToken.Type == TokenType.RSquare) break;
iterator.CurrentIndex++; // skip ','
}

iterator.CurrentIndex++; // skip ']'

jsonNode.Nodes = nodes.ToArray();

return jsonNode;
}

private static JsonObject ParseObject(ref Iterator iterator, IJsonNode? parent, string? name)
{
var nodes = new Dictionary<string, IJsonNode>();
iterator.CurrentIndex++; // skip '{'

var jsonNode = new JsonObject(nodes, parent, name);

while (iterator.CurrentToken.Type != TokenType.RBrace)
{
var key = iterator.CurrentToken.Literal;
iterator.CurrentIndex++; // skip string
iterator.CurrentIndex++; // skip ':'
var value = Parse(ref iterator, jsonNode, key);
nodes.Add(key, value);
if (iterator.CurrentToken.Type == TokenType.RBrace) break;
iterator.CurrentIndex++; // skip ','
}

iterator.CurrentIndex++; // skip '}'

return jsonNode;
}

public struct Iterator(Token[] tokens)
{
public int CurrentIndex;
public Token CurrentToken => tokens.Length > CurrentIndex ? tokens[CurrentIndex] : new Token(TokenType.Illegal, "");
public Token NextToken => tokens.Length > CurrentIndex + 1 ? tokens[CurrentIndex + 1] : new Token(TokenType.Illegal, "");
}
public Location Location { get; } = Location;
}
15 changes: 0 additions & 15 deletions mooresmaster.Generator/Json/Yaml.cs

This file was deleted.

13 changes: 10 additions & 3 deletions mooresmaster.Generator/JsonSchema/JsonSchemaParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,25 @@ private static DefineInterface ParseDefineInterface(string id, JsonObject node,
}

// interfaceの継承情報を取得
var implementationInterfaces = new List<string>();
var implementationNodes = new Dictionary<string, JsonString>();
if (node.Nodes.TryGetValue(Tokens.ImplementationInterfaceKey, out var implementationInterfacesNode) && implementationInterfacesNode is JsonArray nodesArray)
foreach (var implementationInterfaceNode in nodesArray.Nodes)
{
var name = (JsonString)implementationInterfaceNode;
implementationInterfaces.Add(name.Literal);
implementationNodes[name.Literal] = name;
}

if (interfaceName == null) throw new Exception("interfaceName is null");
if (properties == null) throw new Exception("properties is null");

return new DefineInterface(id, interfaceName, properties, implementationInterfaces.ToArray(), isGlobal);
return new DefineInterface(
id,
interfaceName,
properties,
implementationNodes.Keys.ToArray(),
implementationNodes,
isGlobal
);
}

private static SchemaId Parse(JsonObject root, SchemaId? parent, SchemaTable schemaTable)
Expand Down
Loading

0 comments on commit f93e02a

Please sign in to comment.