Skip to content
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
21 changes: 21 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,27 @@ jobs:
echo "Running $test"
./$test
done
- run:
name: Run Compile .o and .so Tests
command: |
check_success() {
if [ $? -eq 0 ]; then
echo "$1"
else
echo "$1"
exit 1
fi
}

cd src/shadowdash

g++ -w -std=c++17 -fPIC -c manifest.cc manifest.h
check_success ".o compiled successfully"

g++ -w -shared -o manifest.so manifest.o
check_success ".so compiled successfully"

cd ../..


workflows:
Expand Down
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,9 @@
/.vs/
/out/

commands.md
commands.md

# New Language files
src/shadowdash/manifest.o
src/shadowdash/manifest.so
src/shadowdash/manifest.h.gch
87 changes: 87 additions & 0 deletions src/shadowdash/manifest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#include "manifest.h"

using namespace shadowdash;

BuildsReturn manifest() {
// example of defining a variable named "flags"
// the value "-03" is a literal token
let(flags, "-O3"); // original language: flags = -03
let(pool_depth, "4"); // original language: pool_depth = 4
// variable examples

// examples of creating pool objects:
auto heavy_object_pool = pool_(bind(depth, "pool_depth"_v));
// passing the argument via macro
auto heavy_object_pool2 = pool_(binding("depth", {"pool_depth"_v}));
// passing the argument via creating a binding object
/*
in original language:
pool heavy_object_pool:
depth = pool_depth
*/
// pool examples

// examples of creating rules in the new language
make_rule(compile,
bind(command, "g++", "flags"_v, "-c", in, "-o", out),
bind(pool, "heavy_object_pool"_v)
);
/*
in original language:
rule compile:
command = gcc $flags -c $in -o $out
pool = heavy_object_pool
*/

make_rule(link,
bind(command, "g++", in, "-o", out)
);
// rule examples

// examples of creating builds in the new language:
auto build_c = build(list{ str{ "hello.o" } },
{},
compile,
list{ str{ "hello.cc" } },
{},
{},
{ bind(flags, "-O2"),
bind(pool, "console"_v) }
);
/*
in original language:
build hello.o : compile hello.cc
flags = -02
pool = console
*/

auto build_l = build(list{ str{ "hello" } },
{},
link,
list{ str{ "hello.o" } },
{},
{},
{}
);

auto build_p = build(list{ str{ "dummy" } },
{},
phony,
{},
{},
{},
{}
);
// build examples

// examples of adding defaults in new language
default_(str{ "hello" });
default_(list{ str{ "foo1" }, str{ "foo2" } });
/*
in original language:
default hello
default foo1 foo2
*/
// default examples
return {build_c, build_l, build_p}; // returns all the builds
}
243 changes: 243 additions & 0 deletions src/shadowdash/manifest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
#pragma once

#include <initializer_list>
#include <string_view>
#include <string>
#include <vector>

namespace shadowdash {

// The atom "string" object in the new language with type identifying whether
// a string object is a "Literal" or a "Variable"
class Token {
public:
enum Type {LITERAL, VAR};
// Token type: LITERAL (constant) or VAR (variable).

// Constructor for a Token with a specified type and value.
constexpr Token(Type type, std::string_view value) :
type_(type), value_(value) {}

// Constructor for a Token defaulting to LITERAL type.
constexpr Token(std::string_view value) : Token(Token::LITERAL, value) {}

// Constructor for a Token from a C-style string, defaulting to LITERAL.
constexpr Token(const char* value) : Token(Token::LITERAL, value) {}

Type type_; // The type of the token.
std::string_view value_; // The value of the token (string content).
};

// User-defined literal to create a LITERAL token.
constexpr Token operator"" _l(const char* value, std::size_t len) {
return Token(Token::Type::LITERAL, { value, len });
}

// User-defined literal to create a VAR token.
constexpr Token operator"" _v(const char* value, std::size_t len) {
return Token(Token::Type::VAR, { value, len });
}

// Represents a string as a collection of tokens.
class str {
public:
// Constructor accepting an initializer list of tokens.
str(std::initializer_list<Token> tokens) : tokens_(tokens) {}

// Returns the number of tokens in the string.
constexpr size_t size() const { return tokens_.size(); }

// Access a specific token by index.
constexpr const Token& operator[](size_t i) const
{ return *(tokens_.begin() + i); }

std::initializer_list<Token> tokens_;
// The list of tokens forming the string.
};

// Alias for a name-value binding, where the value is a `str`.
// typically used when passing in arguments for rules, builds, etc.
// example: depth = 4 -> binding("depth", {"4"}) in binding language
using binding = std::pair<std::string, str>;

// map: alias for an initializaiton_list of bindings
// represents a list of all the arguments
using map = std::initializer_list<binding>;

// Represents a list of `str` objects.
class list {
public:
// Constructor accepting an initializer list of `str` objects.
list(std::initializer_list<str> values) : values_(values) {}

// Returns the size of the list.
constexpr size_t size() const { return values_.size(); }

// Access a specific `str` by index.
constexpr const str& operator[](size_t i) const
{ return *(values_.begin() + i); }

std::initializer_list<str> values_; // The list of strings.
};

// Ninja variables with attributes of name and value
class var {
public:
const char* name_;
str value_;

var(const char* name, str value) : name_(name), value_(value) {}
};

// Ninja rule object
class rule {
public:
// Constructor with a name and bindings.
constexpr rule(std::string_view name, map bindings) :
name(name), bindings_(bindings) {}

// Constructor with only a name.
constexpr rule(std::string_view name) : name(name) {}

// Get the name of the rule.
constexpr std::string_view get_rule_name() const { return name; }

// Returns the number of bindings in the rule.
constexpr size_t size() const { return bindings_.size(); }

// Access a specific binding by index.
constexpr const binding& operator[](size_t i) const
{ return *(bindings_.begin() + i); }

std::string_view name; // Name of the rule.
map bindings_;
//
/*
bindings_: {
(type binding, {op1, {args}}),
(type binding, {op2, {args}}),
...
}
records operations that will be executed within this rule
as well as arguments required for each operation in list
of tokens
*/
//
};

// Exception class for invalid pool arguments.
class PoolException : public std::exception {
public:
// Error message for invalid pool arguments.
const char* what() const noexcept override {
return "Pool has argument that is not depth!";
}
};

// Represents a pool with a depth binding.
class pool_ {
public:
// Constructor that validates the binding is for "depth".
pool_(binding depth) : depth_(depth) {
if (depth.first != "depth") throw PoolException();
// throw an excpetion when pool_ objects received arguments other than depth
}

// Get the depth binding.
constexpr const binding& get_depth() const { return depth_; }

binding depth_; // The depth argument of pool.
};

// Represents a build configuration with inputs, outputs, and rules.
class build {
public:
build(
list outputs, // List of output files.
list implicit_outputs, // Implicit output files.
rule& rule, // Associated build rule.
list inputs, // List of input files.
list implicit_inputs, // Implicit input files.
list order_only_inputs, // Order-only dependencies.
map bindings // Additional arguments.
) : outputs_(outputs),
implicit_outputs_(implicit_outputs),
rule_(rule),
inputs_(inputs),
implicit_inputs_(implicit_inputs),
order_only_inputs_(order_only_inputs),
bindings_(bindings) {}

list outputs_; // Outputs of the build.
list implicit_outputs_; // Implicit outputs.
rule& rule_; // Associated rule.
list inputs_; // Inputs of the build.
list implicit_inputs_; // Implicit inputs.
list order_only_inputs_; // Order-only dependencies.
map bindings_; // Additional arguments.
};

// Represents a collection of builds returned by a manifest.
struct BuildsReturn {
// Constructor accepting a list of builds.
BuildsReturn(std::initializer_list<build> builds) : builds(builds) {}

// Returns the number of builds.
constexpr size_t size() const { return builds.size(); }

// Access a specific build by index.
constexpr const build& operator[](size_t i) const
{ return *(builds.begin() + i); }

std::initializer_list<build> builds; // List of builds.
};

// Represents default build targets.
class default_ {
public:
// Add a list of targets to the defaults.
default_(list addedList) {
for (size_t i = 0; i < addedList.size(); i++)
targets_.push_back(addedList[i]);
}

// Add a single target to the defaults.
default_(str addedTarget) {
targets_.push_back(addedTarget);
}

static std::vector<str> targets_; // List of default targets.
};

std::vector<str> default_::targets_; // Definition of static targets list.

// Predefined constants for common variables.
static constexpr auto in = "in"_v; // Input variable, used in rules
static constexpr auto out = "out"_v; // Output variable, used in rules
static auto phony = rule{{}}; // Default phony rule.
static auto console = pool_(binding("depth", str{ {"1"} }));
// Default console pool.

} // namespace shadowdash

// Macro to define a variable with a name and value(s).
#define let(name, ...) \
var name { \
#name, str { \
__VA_ARGS__ \
} \
}

// Macro to create a binding with a name and value(s).
#define bind(name, ...) \
{ \
#name, str { \
__VA_ARGS__ \
} \
}

// Macro to define a rule with a name and bindings.
#define make_rule(name, ...) \
rule name { \
#name, {__VA_ARGS__} \
}