OSDN Git Service

composition の依存関係の循環のチェックを追加
authorstarg <starg@users.osdn.me>
Tue, 6 Sep 2016 19:23:22 +0000 (04:23 +0900)
committerstarg <starg@users.osdn.me>
Tue, 6 Sep 2016 19:23:22 +0000 (04:23 +0900)
include/ir2midi/context.hpp
include/ir2midi/ir2midi.hpp
include/message/id.hpp
src/driver/msgcallback.cpp
src/ir2midi/command_insert.cpp
src/ir2midi/ir2midi.cpp
src/ir2midi/pch.hpp

index bc19119..2eb3548 100644 (file)
@@ -4,6 +4,7 @@
 #include <string>
 #include <vector>
 
+#include <ast/sourcelocation.hpp>
 #include <midi/event.hpp>
 
 namespace YAMML
@@ -50,7 +51,7 @@ public:
 
     virtual std::string GetSourceName() const = 0;
     virtual TrackCompilerContext& GetTrackContext(int trackNumber) = 0;
-    virtual bool CompileTrackBlock(const std::string& trackBlockName) = 0;
+    virtual void CompileTrackBlock(const std::string& trackBlockName, const AST::SourceLocation& location) = 0;
     virtual bool HasTrackBlock(const std::string& trackBlockName) const = 0;
 };
 
index b6c4e7d..2703bcc 100644 (file)
@@ -1,6 +1,7 @@
 
 #pragma once
 
+#include <deque>
 #include <memory>
 #include <string>
 #include <unordered_map>
@@ -9,6 +10,7 @@
 #include <boost/variant.hpp>
 
 #include <ast/composition.hpp>
+#include <ast/sourcelocation.hpp>
 #include <compiler/base.hpp>
 #include <ir/module.hpp>
 #include <ir2midi/command.hpp>
@@ -21,6 +23,13 @@ namespace YAMML
 namespace IR2MIDI
 {
 
+class NameAndLocation
+{
+public:
+    std::string Name;
+    AST::SourceLocation Location;
+};
+
 class IR2MIDICompiler final : public Compiler::CompilerBase, public IIR2MIDICompiler, public boost::static_visitor<>
 {
 public:
@@ -53,15 +62,16 @@ public:
 
     virtual std::string GetSourceName() const override;
     virtual TrackCompilerContext& GetTrackContext(int trackNumber) override;
+    virtual void CompileTrackBlock(const std::string& trackBlockName, const AST::SourceLocation& location) override;
     virtual bool HasTrackBlock(const std::string& trackBlockName) const override;
 
 private:
     void AddCommandProcessor(std::unique_ptr<ICommandProcessor> pProcessor);
     void InitializeCommandProcessors();
-    virtual bool CompileTrackBlock(const std::string& trackBlockName) override;
     void CompileBlock(int trackNumber, IR::BlockReference blockRef);
     void Finalize();
 
+    void CheckForRecursion(const std::string& trackBlockName, const AST::SourceLocation& location);
     void CheckForUnprocessedAttributes(const std::vector<AST::Attribute>& attributes);
     void EnsureTrackInitialized(int number);
 
@@ -73,6 +83,7 @@ private:
     MIDI::MIDIFile m_MIDI;
     std::vector<TrackCompilerContext> m_Contexts;
     std::unordered_map<std::string, std::unique_ptr<ICommandProcessor>> m_CommandProcessors;
+    std::deque<NameAndLocation> m_TrackBlockCompilationStack;
     int m_LastEventTime = 0;
 };
 
index de21be8..7c7749a 100644 (file)
@@ -82,6 +82,9 @@ enum class MessageID : int
 
     // IR2MIDI
     InvalidCommandName,
+    TrackBlockCompilationRecursion,
+    TrackBlockCompilationBackTrace,
+    TrackBlockCompilationBackTraceEntryPoint,
     WrongNumberOfCommandArguments,
     WrongTypeOfCommandArgument,
 
index dda7af8..59a5071 100644 (file)
@@ -103,6 +103,9 @@ MessagePrinter::MessagePrinter(IStdErrWriter* pStdErrWriter)
 
         // IR2MIDI
         {Message::MessageID::InvalidCommandName, "invalid command '{0}'"},
+        {Message::MessageID::TrackBlockCompilationRecursion, "composition '{0}': circular dependency detected"},
+        {Message::MessageID::TrackBlockCompilationBackTrace, "composition '{0}' was inserted from here"},
+        {Message::MessageID::TrackBlockCompilationBackTraceEntryPoint, "compilation started from composition '{0}'"},
         {Message::MessageID::WrongNumberOfCommandArguments, "wrong number of arguments passed to command '{0}'; {2} expected, {1} found"},
         {Message::MessageID::WrongTypeOfCommandArgument, "command argument {1} has a wrong type; expecting '{2}' here"},
 
index 365aa1c..c77a7c4 100644 (file)
@@ -39,24 +39,13 @@ public:
     virtual void Process(const AST::Command& ast) override
     {
         ValidateArguments(ast);
-        GetCompiler()->CompileTrackBlock(boost::get<std::string>(ast.Arguments[0].Value));
+        GetCompiler()->CompileTrackBlock(boost::get<std::string>(ast.Arguments[0].Value), ast.Location);
     }
 
     void ValidateArguments(const AST::Command& ast)
     {
         ValidateArgCount(ast, 1);
         ValidateArgType(ast, 0, typeid(std::string));
-
-        auto name = boost::get<std::string>(ast.Arguments[0].Value);
-
-        if (!GetCompiler()->HasTrackBlock(name))
-        {
-            ThrowMessage(
-                Message::MessageID::NoSuchCompositionName,
-                ast.Location,
-                {name}
-            );
-        }
     }
 
 private:
index 11a4802..a31d565 100644 (file)
@@ -6,6 +6,8 @@
 
 #include <boost/variant.hpp>
 
+#include <ast/sourcelocation.hpp>
+#include <common/containerutil.hpp>
 #include <exceptions/messageexception.hpp>
 #include <ir2midi/ir2midi.hpp>
 #include <message/message.hpp>
@@ -87,11 +89,7 @@ bool IR2MIDICompiler::Compile(const std::string& entryPoint)
 {
     try
     {
-        if (!CompileTrackBlock(entryPoint))
-        {
-            return false;
-        }
-
+        CompileTrackBlock(entryPoint, {});
         Finalize();
         return !HasErrors();
     }
@@ -211,7 +209,7 @@ void IR2MIDICompiler::InitializeCommandProcessors()
     AddCommandProcessor(CreateVolumeCommandProcessor(this));
 }
 
-bool IR2MIDICompiler::CompileTrackBlock(const std::string& trackBlockName)
+void IR2MIDICompiler::CompileTrackBlock(const std::string& trackBlockName, const AST::SourceLocation& location)
 {
     auto itTrack = m_IR.TrackBlockNameMap.find(trackBlockName);
 
@@ -222,12 +220,17 @@ bool IR2MIDICompiler::CompileTrackBlock(const std::string& trackBlockName)
                 Message::MessageKind::Error,
                 Message::MessageID::NoSuchCompositionName,
                 m_IR.Name,
-                {0, 0},
+                location,
                 {trackBlockName}
             }
         );
     }
 
+    CheckForRecursion(trackBlockName, location);
+
+    m_TrackBlockCompilationStack.push_back(NameAndLocation{trackBlockName, location});
+    Common::AutoPop<decltype(m_TrackBlockCompilationStack)> autoPop(m_TrackBlockCompilationStack);
+
     // with bounds checking
     CheckForUnprocessedAttributes(m_IR.TrackBlocks.at(itTrack->second.ID).Attributes);
 
@@ -235,8 +238,6 @@ bool IR2MIDICompiler::CompileTrackBlock(const std::string& trackBlockName)
     {
         i.apply_visitor(*this);
     }
-
-    return true;
 }
 
 void IR2MIDICompiler::CompileBlock(int trackNumber, IR::BlockReference blockRef)
@@ -273,6 +274,61 @@ void IR2MIDICompiler::Finalize()
     }
 }
 
+void IR2MIDICompiler::CheckForRecursion(const std::string& trackBlockName, const AST::SourceLocation& location)
+{
+    auto it = std::find_if(
+        m_TrackBlockCompilationStack.begin(),
+        m_TrackBlockCompilationStack.end(),
+        [&trackBlockName] (auto&& x)
+        {
+            return x.Name == trackBlockName;
+        }
+    );
+
+    if (it != m_TrackBlockCompilationStack.end())
+    {
+        AddMessage(
+            Message::MessageItem{
+                Message::MessageKind::Error,
+                Message::MessageID::TrackBlockCompilationRecursion,
+                GetSourceName(),
+                location,
+                {trackBlockName}
+            }
+        );
+
+        if (m_TrackBlockCompilationStack.size() > 1)
+        {
+            std::for_each(
+                m_TrackBlockCompilationStack.rbegin(),
+                m_TrackBlockCompilationStack.rend() - 1,
+                [this] (auto&& x)
+                {
+                    this->AddMessage(
+                        Message::MessageItem{
+                            Message::MessageKind::Note,
+                            Message::MessageID::TrackBlockCompilationBackTrace,
+                            this->GetSourceName(),
+                            x.Location,
+                            {x.Name}
+                        }
+                    );
+                }
+            );
+        }
+
+        throw Exceptions::MessageException(
+            Message::MessageItem{
+                Message::MessageKind::Note,
+                Message::MessageID::TrackBlockCompilationBackTraceEntryPoint,
+                GetSourceName(),
+                {},
+                {m_TrackBlockCompilationStack.at(0).Name}
+            }
+        );
+    }
+}
+
 void IR2MIDICompiler::CheckForUnprocessedAttributes(const std::vector<AST::Attribute>& attributes)
 {
     if (!attributes.empty())
index 81af478..a1bdf58 100644 (file)
@@ -2,6 +2,7 @@
 #pragma once
 
 #include <algorithm>
+#include <deque>
 #include <exception>
 #include <memory>
 #include <string>