Skip to content
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ if (LIBSCRATCHCPP_USE_LLVM)
target_sources(scratchcpp
PUBLIC
include/scratchcpp/dev/compiler.h
include/scratchcpp/dev/compilercontext.h
include/scratchcpp/dev/compilervalue.h
include/scratchcpp/dev/compilerconstant.h
include/scratchcpp/dev/compilerlocalvariable.h
Expand Down
12 changes: 10 additions & 2 deletions include/scratchcpp/dev/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
namespace libscratchcpp
{

class CompilerContext;
class IEngine;
class Target;
class ExecutableCode;
Expand All @@ -21,6 +22,7 @@ class Variable;
class List;
class Input;
class Field;
class BlockPrototype;
class CompilerPrivate;

/*! \brief The Compiler class provides API for compiling Scratch scripts. */
Expand All @@ -39,6 +41,7 @@ class LIBSCRATCHCPP_EXPORT Compiler
using ArgTypes = std::vector<StaticType>;
using Args = std::vector<CompilerValue *>;

Compiler(CompilerContext *ctx);
Compiler(IEngine *engine, Target *target);
Compiler(const Compiler &) = delete;

Expand All @@ -60,7 +63,10 @@ class LIBSCRATCHCPP_EXPORT Compiler
CompilerValue *addListItemIndex(List *list, CompilerValue *item);
CompilerValue *addListContains(List *list, CompilerValue *item);
CompilerValue *addListSize(List *list);
CompilerValue *addProcedureArgument(const std::string &name);

CompilerValue *addInput(const std::string &name);
CompilerValue *addInput(Input *input);

CompilerValue *createAdd(CompilerValue *operand1, CompilerValue *operand2);
CompilerValue *createSub(CompilerValue *operand1, CompilerValue *operand2);
Expand Down Expand Up @@ -127,14 +133,16 @@ class LIBSCRATCHCPP_EXPORT Compiler
void createYield();
void createStop();

void createProcedureCall(BlockPrototype *prototype, const Compiler::Args &args);

Input *input(const std::string &name) const;
Field *field(const std::string &name) const;

const std::unordered_set<std::string> &unsupportedBlocks() const;

private:
CompilerValue *addInput(Input *input);
static std::shared_ptr<CompilerContext> createContext(IEngine *engine, Target *target);

private:
spimpl::unique_impl_ptr<CompilerPrivate> impl;
};

Expand Down
30 changes: 30 additions & 0 deletions include/scratchcpp/dev/compilercontext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include "../global.h"
#include "../spimpl.h"

namespace libscratchcpp
{

class IEngine;
class Target;
class CompilerContextPrivate;

/*! \brief The CompilerContext represents a context for a specific target which is used with the Compiler class. */
class LIBSCRATCHCPP_EXPORT CompilerContext
{
public:
CompilerContext(IEngine *engine, Target *target);
CompilerContext(const CompilerContext &) = delete;
virtual ~CompilerContext() { }

IEngine *engine() const;
Target *target() const;

private:
spimpl::unique_impl_ptr<CompilerContextPrivate> impl;
};

} // namespace libscratchcpp
3 changes: 3 additions & 0 deletions src/dev/engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ target_sources(scratchcpp
compiler.cpp
compiler_p.cpp
compiler_p.h
compilercontext.cpp
compilercontext_p.cpp
compilercontext_p.h
compilervalue.cpp
compilervalue_p.cpp
compilervalue_p.h
Expand Down
153 changes: 97 additions & 56 deletions src/dev/engine/compiler.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Apache-2.0

#include <scratchcpp/dev/compiler.h>
#include <scratchcpp/dev/compilercontext.h>
#include <scratchcpp/dev/compilerconstant.h>
#include <scratchcpp/block.h>
#include <scratchcpp/input.h>
Expand All @@ -12,7 +13,13 @@

using namespace libscratchcpp;

/*! Constructs Compiler. */
/*! Constructs Compiler using the given context. */
Compiler::Compiler(CompilerContext *ctx) :
impl(spimpl::make_unique_impl<CompilerPrivate>(ctx))
{
}

/*! Constructs Compiler using a new context for the given target. */
Compiler::Compiler(IEngine *engine, Target *target) :
impl(spimpl::make_unique_impl<CompilerPrivate>(engine, target))
{
Expand All @@ -21,13 +28,13 @@ Compiler::Compiler(IEngine *engine, Target *target) :
/*! Returns the Engine of the project. */
IEngine *Compiler::engine() const
{
return impl->engine;
return impl->ctx->engine();
}

/*! Returns the Target of this compiler. */
Target *Compiler::target() const
{
return impl->target;
return impl->ctx->target();
}

/*! Returns currently compiled block. */
Expand All @@ -39,7 +46,21 @@ std::shared_ptr<libscratchcpp::Block> Compiler::block() const
/*! Compiles the script starting with the given block. */
std::shared_ptr<ExecutableCode> Compiler::compile(std::shared_ptr<Block> startBlock)
{
impl->builder = impl->builderFactory->create(impl->target, startBlock->id(), false);
BlockPrototype *procedurePrototype = nullptr;

if (startBlock) {
// TODO: Move procedure definition logic to the custom blocks extension
auto input = startBlock->inputAt(0);

if (input && input->valueBlock()) {
procedurePrototype = input->valueBlock()->mutationPrototype();

if (procedurePrototype && procedurePrototype->procCode().empty())
procedurePrototype = nullptr;
}
}

impl->builder = impl->builderFactory->create(impl->ctx, procedurePrototype);
impl->substackTree.clear();
impl->substackHit = false;
impl->emptySubstack = false;
Expand Down Expand Up @@ -169,12 +190,74 @@ CompilerValue *Compiler::addListSize(List *list)
return impl->builder->addListSize(list);
}

/*! Adds the procedure argument with the given name to the code. */
CompilerValue *Compiler::addProcedureArgument(const std::string &name)
{
return impl->builder->addProcedureArgument(name);
}

/*! Compiles the given input (resolved by name) and adds it to the compiled code. */
CompilerValue *Compiler::addInput(const std::string &name)
{
return addInput(impl->block->inputAt(impl->block->findInput(name)).get());
}

/*! Compiles the given input and adds it to the compiled code. */
CompilerValue *Compiler::addInput(Input *input)
{
if (!input)
return addConstValue(Value());

switch (input->type()) {
case Input::Type::Shadow:
case Input::Type::NoShadow: {
if (input->pointsToDropdownMenu())
return addConstValue(input->selectedMenuItem());
else {
CompilerValue *ret = nullptr;
auto previousBlock = impl->block;
impl->block = input->valueBlock();

if (impl->block) {
if (impl->block->compileFunction())
ret = impl->block->compile(this);
else {
std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl;
impl->unsupportedBlocks.insert(impl->block->opcode());
ret = addConstValue(Value());
}
} else
ret = addConstValue(input->primaryValue()->value());

impl->block = previousBlock;
return ret;
}
}

case Input::Type::ObscuredShadow: {
CompilerValue *ret = nullptr;
auto previousBlock = impl->block;
impl->block = input->valueBlock();

if (impl->block) {
if (impl->block->compileFunction())
ret = impl->block->compile(this);
else {
std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl;
impl->unsupportedBlocks.insert(impl->block->opcode());
ret = addConstValue(Value());
}
} else
ret = input->primaryValue()->compile(this);

impl->block = previousBlock;
return ret;
}
}

return nullptr;
}

/*! Creates an add instruction. */
CompilerValue *Compiler::createAdd(CompilerValue *operand1, CompilerValue *operand2)
{
Expand Down Expand Up @@ -552,6 +635,12 @@ void Compiler::createStop()
impl->builder->createStop();
}

/*! Creates a call to the procedure with the given prototype. */
void Compiler::createProcedureCall(BlockPrototype *prototype, const libscratchcpp::Compiler::Args &args)
{
impl->builder->createProcedureCall(prototype, args);
}

/*! Convenience method which returns the field with the given name. */
Input *Compiler::input(const std::string &name) const
{
Expand All @@ -570,57 +659,9 @@ const std::unordered_set<std::string> &Compiler::unsupportedBlocks() const
return impl->unsupportedBlocks;
}

CompilerValue *Compiler::addInput(Input *input)
/*! Creates a compiler context for the given target. */
std::shared_ptr<CompilerContext> Compiler::createContext(IEngine *engine, Target *target)
{
if (!input)
return addConstValue(Value());

switch (input->type()) {
case Input::Type::Shadow:
case Input::Type::NoShadow: {
if (input->pointsToDropdownMenu())
return addConstValue(input->selectedMenuItem());
else {
CompilerValue *ret = nullptr;
auto previousBlock = impl->block;
impl->block = input->valueBlock();

if (impl->block) {
if (impl->block->compileFunction())
ret = impl->block->compile(this);
else {
std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl;
impl->unsupportedBlocks.insert(impl->block->opcode());
ret = addConstValue(Value());
}
} else
ret = addConstValue(input->primaryValue()->value());

impl->block = previousBlock;
return ret;
}
}

case Input::Type::ObscuredShadow: {
CompilerValue *ret = nullptr;
auto previousBlock = impl->block;
impl->block = input->valueBlock();

if (impl->block) {
if (impl->block->compileFunction())
ret = impl->block->compile(this);
else {
std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl;
impl->unsupportedBlocks.insert(impl->block->opcode());
ret = addConstValue(Value());
}
} else
ret = input->primaryValue()->compile(this);

impl->block = previousBlock;
return ret;
}
}

return nullptr;
CompilerPrivate::initBuilderFactory();
return CompilerPrivate::builderFactory->createCtx(engine, target);
}
17 changes: 15 additions & 2 deletions src/dev/engine/compiler_p.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: Apache-2.0

#include <scratchcpp/dev/compiler.h>
#include <scratchcpp/block.h>

#include "compiler_p.h"
Expand All @@ -9,9 +10,21 @@

using namespace libscratchcpp;

CompilerPrivate::CompilerPrivate(CompilerContext *ctx) :
ctx(ctx)
{
assert(ctx);
initBuilderFactory();
}

CompilerPrivate::CompilerPrivate(IEngine *engine, Target *target) :
engine(engine),
target(target)
ctxPtr(Compiler::createContext(engine, target)),
ctx(ctxPtr.get())
{
initBuilderFactory();
}

void CompilerPrivate::initBuilderFactory()
{
if (!builderFactory)
builderFactory = CodeBuilderFactory::instance().get();
Expand Down
8 changes: 6 additions & 2 deletions src/dev/engine/compiler_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
namespace libscratchcpp
{

class CompilerContext;
class IEngine;
class Target;
class Block;
Expand All @@ -24,12 +25,15 @@ struct CompilerPrivate
IfStatement
};

CompilerPrivate(CompilerContext *ctx);
CompilerPrivate(IEngine *engine, Target *target);

static void initBuilderFactory();

void substackEnd();

IEngine *engine = nullptr;
Target *target = nullptr;
std::shared_ptr<CompilerContext> ctxPtr; // for self-managed contexts
CompilerContext *ctx = nullptr;

std::shared_ptr<Block> block;
int customIfStatementCount = 0;
Expand Down
25 changes: 25 additions & 0 deletions src/dev/engine/compilercontext.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: Apache-2.0

#include <scratchcpp/dev/compilercontext.h>

#include "compilercontext_p.h"

using namespace libscratchcpp;

/*! Constructs CompilerContext. */
CompilerContext::CompilerContext(IEngine *engine, Target *target) :
impl(spimpl::make_unique_impl<CompilerContextPrivate>(engine, target))
{
}

/*! Returns the engine of the project. */
IEngine *CompilerContext::engine() const
{
return impl->engine;
}

/*! Returns the target of this context. */
Target *CompilerContext::target() const
{
return impl->target;
}
Loading
Loading