#include <string>
#include <vector>
+#include <ast/sourcelocation.hpp>
#include <midi/event.hpp>
namespace YAMML
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;
};
#pragma once
+#include <deque>
#include <memory>
#include <string>
#include <unordered_map>
#include <boost/variant.hpp>
#include <ast/composition.hpp>
+#include <ast/sourcelocation.hpp>
#include <compiler/base.hpp>
#include <ir/module.hpp>
#include <ir2midi/command.hpp>
namespace IR2MIDI
{
+class NameAndLocation
+{
+public:
+ std::string Name;
+ AST::SourceLocation Location;
+};
+
class IR2MIDICompiler final : public Compiler::CompilerBase, public IIR2MIDICompiler, public boost::static_visitor<>
{
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);
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;
};
// IR2MIDI
InvalidCommandName,
+ TrackBlockCompilationRecursion,
+ TrackBlockCompilationBackTrace,
+ TrackBlockCompilationBackTraceEntryPoint,
WrongNumberOfCommandArguments,
WrongTypeOfCommandArgument,
// 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"},
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:
#include <boost/variant.hpp>
+#include <ast/sourcelocation.hpp>
+#include <common/containerutil.hpp>
#include <exceptions/messageexception.hpp>
#include <ir2midi/ir2midi.hpp>
#include <message/message.hpp>
{
try
{
- if (!CompileTrackBlock(entryPoint))
- {
- return false;
- }
-
+ CompileTrackBlock(entryPoint, {});
Finalize();
return !HasErrors();
}
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);
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);
{
i.apply_visitor(*this);
}
-
- return true;
}
void IR2MIDICompiler::CompileBlock(int trackNumber, IR::BlockReference blockRef)
}
}
+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())
#pragma once
#include <algorithm>
+#include <deque>
#include <exception>
#include <memory>
#include <string>